]> git.basschouten.com Git - openhab-addons.git/blob
6dc10918cf9a197f7a1bc23b6ff3930c146ad60e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.insteon.internal.command;
14
15 import java.text.SimpleDateFormat;
16 import java.util.Arrays;
17 import java.util.Date;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Set;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.insteon.internal.InsteonBinding;
25 import org.openhab.binding.insteon.internal.device.DeviceFeature;
26 import org.openhab.binding.insteon.internal.device.InsteonAddress;
27 import org.openhab.binding.insteon.internal.device.InsteonDevice;
28 import org.openhab.binding.insteon.internal.handler.InsteonNetworkHandler;
29 import org.openhab.binding.insteon.internal.message.FieldException;
30 import org.openhab.binding.insteon.internal.message.InvalidMessageTypeException;
31 import org.openhab.binding.insteon.internal.message.Msg;
32 import org.openhab.binding.insteon.internal.message.MsgListener;
33 import org.openhab.binding.insteon.internal.utils.Utils;
34 import org.openhab.core.io.console.Console;
35 import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
36 import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
37 import org.osgi.service.component.annotations.Component;
38 import org.osgi.service.component.annotations.Reference;
39 import org.osgi.service.component.annotations.ReferenceCardinality;
40 import org.osgi.service.component.annotations.ReferencePolicy;
41 import org.osgi.service.component.annotations.ReferencePolicyOption;
42
43 /**
44  *
45  * Console commands for the Insteon binding
46  *
47  * @author Rob Nielsen - Initial contribution
48  */
49 @NonNullByDefault
50 @Component(service = ConsoleCommandExtension.class)
51 public class InsteonCommandExtension extends AbstractConsoleCommandExtension implements MsgListener {
52     private static final String DISPLAY_DEVICES = "display_devices";
53     private static final String DISPLAY_CHANNELS = "display_channels";
54     private static final String DISPLAY_LOCAL_DATABASE = "display_local_database";
55     private static final String DISPLAY_MONITORED = "display_monitored";
56     private static final String START_MONITORING = "start_monitoring";
57     private static final String STOP_MONITORING = "stop_monitoring";
58     private static final String SEND_STANDARD_MESSAGE = "send_standard_message";
59     private static final String SEND_EXTENDED_MESSAGE = "send_extended_message";
60     private static final String SEND_EXTENDED_MESSAGE_2 = "send_extended_message_2";
61
62     private enum MessageType {
63         STANDARD,
64         EXTENDED,
65         EXTENDED_2
66     }
67
68     @Nullable
69     private InsteonNetworkHandler handler;
70     @Nullable
71     private Console console;
72     private boolean monitoring = false;
73     private boolean monitorAllDevices = false;
74     private Set<InsteonAddress> monitoredAddresses = new HashSet<>();
75
76     public InsteonCommandExtension() {
77         super("insteon", "Interact with the Insteon integration.");
78     }
79
80     @Override
81     public void execute(String[] args, Console console) {
82         if (args.length > 0) {
83             InsteonNetworkHandler handler = this.handler; // fix eclipse warnings about nullable
84             if (handler == null) {
85                 console.println("No Insteon network bridge configured.");
86             } else {
87                 switch (args[0]) {
88                     case DISPLAY_DEVICES:
89                         if (args.length == 1) {
90                             handler.displayDevices(console);
91                         } else {
92                             printUsage(console);
93                         }
94                         break;
95                     case DISPLAY_CHANNELS:
96                         if (args.length == 1) {
97                             handler.displayChannels(console);
98                         } else {
99                             printUsage(console);
100                         }
101                         break;
102                     case DISPLAY_LOCAL_DATABASE:
103                         if (args.length == 1) {
104                             handler.displayLocalDatabase(console);
105                         } else {
106                             printUsage(console);
107                         }
108                         break;
109                     case DISPLAY_MONITORED:
110                         if (args.length == 1) {
111                             displayMonitoredDevices(console);
112                         } else {
113                             printUsage(console);
114                         }
115                         break;
116                     case START_MONITORING:
117                         if (args.length == 2) {
118                             startMonitoring(console, args[1]);
119                         } else {
120                             printUsage(console);
121                         }
122                         break;
123                     case STOP_MONITORING:
124                         if (args.length == 2) {
125                             stopMonitoring(console, args[1]);
126                         } else {
127                             printUsage(console);
128                         }
129                         break;
130                     case SEND_STANDARD_MESSAGE:
131                         if (args.length == 5) {
132                             sendMessage(console, MessageType.STANDARD, args);
133                         } else {
134                             printUsage(console);
135                         }
136                         break;
137                     case SEND_EXTENDED_MESSAGE:
138                         if (args.length >= 5 && args.length <= 18) {
139                             sendMessage(console, MessageType.EXTENDED, args);
140                         } else {
141                             printUsage(console);
142                         }
143                         break;
144                     case SEND_EXTENDED_MESSAGE_2:
145                         if (args.length >= 5 && args.length <= 17) {
146                             sendMessage(console, MessageType.EXTENDED_2, args);
147                         } else {
148                             printUsage(console);
149                         }
150                         break;
151                     default:
152                         console.println("Unknown command '" + args[0] + "'");
153                         printUsage(console);
154                         break;
155                 }
156             }
157         } else {
158             printUsage(console);
159         }
160     }
161
162     @Override
163     public List<String> getUsages() {
164         return Arrays.asList(new String[] {
165                 buildCommandUsage(DISPLAY_DEVICES, "display devices that are online, along with available channels"),
166                 buildCommandUsage(DISPLAY_CHANNELS,
167                         "display channels that are linked, along with configuration information"),
168                 buildCommandUsage(DISPLAY_LOCAL_DATABASE, "display Insteon PLM or hub database details"),
169                 buildCommandUsage(DISPLAY_MONITORED, "display monitored device(s)"),
170                 buildCommandUsage(START_MONITORING + " all|address",
171                         "start displaying messages received from device(s)"),
172                 buildCommandUsage(STOP_MONITORING + " all|address", "stop displaying messages received from device(s)"),
173                 buildCommandUsage(SEND_STANDARD_MESSAGE + " address flags cmd1 cmd2",
174                         "send standard message to a device"),
175                 buildCommandUsage(SEND_EXTENDED_MESSAGE + " address flags cmd1 cmd2 [up to 13 bytes]",
176                         "send extended message to a device"),
177                 buildCommandUsage(SEND_EXTENDED_MESSAGE_2 + " address flags cmd1 cmd2 [up to 12 bytes]",
178                         "send extended message with a two byte crc to a device") });
179     }
180
181     @Override
182     public void msg(Msg msg) {
183         if (monitorAllDevices || monitoredAddresses.contains(msg.getAddr("fromAddress"))) {
184             String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
185             Console console = this.console;
186             if (console != null) {
187                 console.println(date + " " + msg.toString());
188             }
189         }
190     }
191
192     @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
193     public void setInsteonNetworkHandler(InsteonNetworkHandler handler) {
194         this.handler = handler;
195     }
196
197     public void unsetInsteonNetworkHandler(InsteonNetworkHandler handler) {
198         this.handler = null;
199     }
200
201     private void displayMonitoredDevices(Console console) {
202         if (!monitoredAddresses.isEmpty()) {
203             StringBuilder builder = new StringBuilder();
204             for (InsteonAddress insteonAddress : monitoredAddresses) {
205                 if (builder.length() == 0) {
206                     builder = new StringBuilder("The individual device(s) ");
207                 } else {
208                     builder.append(", ");
209                 }
210                 builder.append(insteonAddress);
211             }
212             console.println(builder.append(" are monitored").toString());
213         } else if (monitorAllDevices) {
214             console.println("All devices are monitored.");
215         } else {
216             console.println("Not mointoring any devices.");
217         }
218     }
219
220     private void startMonitoring(Console console, String addr) {
221         if ("all".equalsIgnoreCase(addr)) {
222             if (!monitorAllDevices) {
223                 monitorAllDevices = true;
224                 monitoredAddresses.clear();
225                 console.println("Started monitoring all devices.");
226             } else {
227                 console.println("Already monitoring all devices.");
228             }
229         } else {
230             try {
231                 if (monitorAllDevices) {
232                     console.println("Already monitoring all devices.");
233                 } else if (monitoredAddresses.add(new InsteonAddress(addr))) {
234                     console.println("Started monitoring the device " + addr + ".");
235                 } else {
236                     console.println("Already monitoring the device " + addr + ".");
237                 }
238             } catch (IllegalArgumentException e) {
239                 console.println("Invalid device address" + addr + ".");
240                 return;
241             }
242         }
243
244         if (!monitoring) {
245             getInsteonBinding().getDriver().addMsgListener(this);
246
247             this.console = console;
248             monitoring = true;
249         }
250     }
251
252     private void stopMonitoring(Console console, String addr) {
253         if (!monitoring) {
254             console.println("Not mointoring any devices.");
255             return;
256         }
257
258         if ("all".equalsIgnoreCase(addr)) {
259             if (monitorAllDevices) {
260                 monitorAllDevices = false;
261                 console.println("Stopped monitoring all devices.");
262             } else {
263                 console.println("Not monitoring all devices.");
264             }
265         } else {
266             try {
267                 if (monitorAllDevices) {
268                     console.println("Not monitoring individual devices.");
269                 } else if (monitoredAddresses.remove(new InsteonAddress(addr))) {
270                     console.println("Stopped monitoring the device " + addr + ".");
271                 } else {
272                     console.println("Not monitoring the device " + addr + ".");
273                     return;
274                 }
275             } catch (IllegalArgumentException e) {
276                 console.println("Invalid address device address " + addr + ".");
277                 return;
278             }
279         }
280
281         if (!monitorAllDevices && monitoredAddresses.isEmpty()) {
282             getInsteonBinding().getDriver().removeListener(this);
283             this.console = null;
284             monitoring = false;
285         }
286     }
287
288     private void sendMessage(Console console, MessageType messageType, String[] args) {
289         InsteonDevice device = new InsteonDevice();
290         device.setDriver(getInsteonBinding().getDriver());
291
292         try {
293             device.setAddress(new InsteonAddress(args[1]));
294         } catch (IllegalArgumentException e) {
295             console.println("Invalid device address" + args[1] + ".");
296             return;
297         }
298
299         StringBuilder builder = new StringBuilder();
300         for (int i = 2; i < args.length; i++) {
301             if (!args[i].matches("\\p{XDigit}{1,2}")) {
302                 if (builder.length() > 0) {
303                     builder.append(", ");
304                 }
305                 builder.append(args[i]);
306             }
307         }
308         if (builder.length() != 0) {
309             builder.append(" is not a valid hexadecimal byte.");
310             console.print(builder.toString());
311             return;
312         }
313
314         try {
315             byte flags = (byte) Utils.fromHexString(args[2]);
316             byte cmd1 = (byte) Utils.fromHexString(args[3]);
317             byte cmd2 = (byte) Utils.fromHexString(args[4]);
318             Msg msg;
319             if (messageType == MessageType.STANDARD) {
320                 msg = device.makeStandardMessage(flags, cmd1, cmd2);
321             } else {
322                 byte[] data = new byte[args.length - 5];
323                 for (int i = 0; i + 5 < args.length; i++) {
324                     data[i] = (byte) Utils.fromHexString(args[i + 5]);
325                 }
326
327                 if (messageType == MessageType.EXTENDED) {
328                     msg = device.makeExtendedMessage(flags, cmd1, cmd2, data);
329                 } else {
330                     msg = device.makeExtendedMessageCRC2(flags, cmd1, cmd2, data);
331                 }
332             }
333             device.enqueueMessage(msg, new DeviceFeature(device, "console"));
334         } catch (FieldException | InvalidMessageTypeException e) {
335             console.println("Error while trying to create message.");
336         }
337     }
338
339     private InsteonBinding getInsteonBinding() {
340         InsteonNetworkHandler handler = this.handler;
341         if (handler == null) {
342             throw new IllegalArgumentException("No Insteon network bridge configured.");
343         }
344
345         return handler.getInsteonBinding();
346     }
347 }