]> git.basschouten.com Git - openhab-addons.git/blob
e782894a96c811b03b33be808fecf6ccc591d444
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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             if (console != null) {
186                 console.println(date + " " + msg.toString());
187             }
188         }
189     }
190
191     @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
192     public void setInsteonNetworkHandler(InsteonNetworkHandler handler) {
193         this.handler = handler;
194     }
195
196     public void unsetInsteonNetworkHandler(InsteonNetworkHandler handler) {
197         this.handler = null;
198     }
199
200     private void displayMonitoredDevices(Console console) {
201         if (!monitoredAddresses.isEmpty()) {
202             StringBuilder builder = new StringBuilder();
203             for (InsteonAddress insteonAddress : monitoredAddresses) {
204                 if (builder.length() == 0) {
205                     builder = new StringBuilder("The individual device(s) ");
206                 } else {
207                     builder.append(", ");
208                 }
209                 builder.append(insteonAddress);
210             }
211             console.println(builder.append(" are monitored").toString());
212         } else if (monitorAllDevices) {
213             console.println("All devices are monitored.");
214         } else {
215             console.println("Not mointoring any devices.");
216         }
217     }
218
219     private void startMonitoring(Console console, String addr) {
220         if (addr.equalsIgnoreCase("all")) {
221             if (monitorAllDevices != true) {
222                 monitorAllDevices = true;
223                 monitoredAddresses.clear();
224                 console.println("Started monitoring all devices.");
225             } else {
226                 console.println("Already monitoring all devices.");
227             }
228         } else {
229             try {
230                 if (monitorAllDevices) {
231                     console.println("Already monitoring all devices.");
232                 } else if (monitoredAddresses.add(new InsteonAddress(addr))) {
233                     console.println("Started monitoring the device " + addr + ".");
234                 } else {
235                     console.println("Already monitoring the device " + addr + ".");
236                 }
237             } catch (IllegalArgumentException e) {
238                 console.println("Invalid device address" + addr + ".");
239                 return;
240             }
241         }
242
243         if (monitoring == false) {
244             getInsteonBinding().getDriver().addMsgListener(this);
245
246             this.console = console;
247             monitoring = true;
248         }
249     }
250
251     private void stopMonitoring(Console console, String addr) {
252         if (monitoring == false) {
253             console.println("Not mointoring any devices.");
254             return;
255         }
256
257         if (addr.equalsIgnoreCase("all")) {
258             if (monitorAllDevices) {
259                 monitorAllDevices = false;
260                 console.println("Stopped monitoring all devices.");
261             } else {
262                 console.println("Not monitoring all devices.");
263             }
264         } else {
265             try {
266                 if (monitorAllDevices) {
267                     console.println("Not monitoring individual devices.");
268                 } else if (monitoredAddresses.remove(new InsteonAddress(addr))) {
269                     console.println("Stopped monitoring the device " + addr + ".");
270                 } else {
271                     console.println("Not monitoring the device " + addr + ".");
272                     return;
273                 }
274             } catch (IllegalArgumentException e) {
275                 console.println("Invalid address device address " + addr + ".");
276                 return;
277             }
278         }
279
280         if (monitorAllDevices == false && monitoredAddresses.isEmpty()) {
281             getInsteonBinding().getDriver().removeListener(this);
282             this.console = null;
283             monitoring = false;
284         }
285     }
286
287     private void sendMessage(Console console, MessageType messageType, String[] args) {
288         InsteonDevice device = new InsteonDevice();
289         device.setDriver(getInsteonBinding().getDriver());
290
291         try {
292             device.setAddress(new InsteonAddress(args[1]));
293         } catch (IllegalArgumentException e) {
294             console.println("Invalid device address" + args[1] + ".");
295             return;
296         }
297
298         StringBuilder builder = new StringBuilder();
299         for (int i = 2; i < args.length; i++) {
300             if (!args[i].matches("\\p{XDigit}{1,2}")) {
301                 if (builder.length() > 0) {
302                     builder.append(", ");
303                 }
304                 builder.append(args[i]);
305             }
306         }
307         if (builder.length() != 0) {
308             builder.append(" is not a valid hexadecimal byte.");
309             console.print(builder.toString());
310             return;
311         }
312
313         try {
314             byte flags = (byte) Utils.fromHexString(args[2]);
315             byte cmd1 = (byte) Utils.fromHexString(args[3]);
316             byte cmd2 = (byte) Utils.fromHexString(args[4]);
317             Msg msg;
318             if (messageType == MessageType.STANDARD) {
319                 msg = device.makeStandardMessage(flags, cmd1, cmd2);
320             } else {
321                 byte[] data = new byte[args.length - 5];
322                 for (int i = 0; i + 5 < args.length; i++) {
323                     data[i] = (byte) Utils.fromHexString(args[i + 5]);
324                 }
325
326                 if (messageType == MessageType.EXTENDED) {
327                     msg = device.makeExtendedMessage(flags, cmd1, cmd2, data);
328                 } else {
329                     msg = device.makeExtendedMessageCRC2(flags, cmd1, cmd2, data);
330                 }
331             }
332             device.enqueueMessage(msg, new DeviceFeature(device, "console"));
333         } catch (FieldException | InvalidMessageTypeException e) {
334             console.println("Error while trying to create message.");
335         }
336     }
337
338     @SuppressWarnings("null")
339     private InsteonBinding getInsteonBinding() {
340         if (handler == null) {
341             throw new IllegalArgumentException("No Insteon network bridge configured.");
342         }
343
344         @Nullable
345         InsteonBinding insteonBinding = handler.getInsteonBinding();
346         if (insteonBinding == null) {
347             throw new IllegalArgumentException("Insteon binding is null.");
348         }
349
350         return insteonBinding;
351     }
352 }