2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.insteon.internal.command;
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;
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;
45 * Console commands for the Insteon binding
47 * @author Rob Nielsen - Initial contribution
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";
62 private enum MessageType {
69 private InsteonNetworkHandler handler;
71 private Console console;
72 private boolean monitoring = false;
73 private boolean monitorAllDevices = false;
74 private Set<InsteonAddress> monitoredAddresses = new HashSet<>();
76 public InsteonCommandExtension() {
77 super("insteon", "Interact with the Insteon integration.");
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.");
89 if (args.length == 1) {
90 handler.displayDevices(console);
95 case DISPLAY_CHANNELS:
96 if (args.length == 1) {
97 handler.displayChannels(console);
102 case DISPLAY_LOCAL_DATABASE:
103 if (args.length == 1) {
104 handler.displayLocalDatabase(console);
109 case DISPLAY_MONITORED:
110 if (args.length == 1) {
111 displayMonitoredDevices(console);
116 case START_MONITORING:
117 if (args.length == 2) {
118 startMonitoring(console, args[1]);
123 case STOP_MONITORING:
124 if (args.length == 2) {
125 stopMonitoring(console, args[1]);
130 case SEND_STANDARD_MESSAGE:
131 if (args.length == 5) {
132 sendMessage(console, MessageType.STANDARD, args);
137 case SEND_EXTENDED_MESSAGE:
138 if (args.length >= 5 && args.length <= 18) {
139 sendMessage(console, MessageType.EXTENDED, args);
144 case SEND_EXTENDED_MESSAGE_2:
145 if (args.length >= 5 && args.length <= 17) {
146 sendMessage(console, MessageType.EXTENDED_2, args);
152 console.println("Unknown command '" + args[0] + "'");
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") });
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());
192 @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
193 public void setInsteonNetworkHandler(InsteonNetworkHandler handler) {
194 this.handler = handler;
197 public void unsetInsteonNetworkHandler(InsteonNetworkHandler handler) {
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) ");
208 builder.append(", ");
210 builder.append(insteonAddress);
212 console.println(builder.append(" are monitored").toString());
213 } else if (monitorAllDevices) {
214 console.println("All devices are monitored.");
216 console.println("Not mointoring any devices.");
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.");
227 console.println("Already monitoring all devices.");
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 + ".");
236 console.println("Already monitoring the device " + addr + ".");
238 } catch (IllegalArgumentException e) {
239 console.println("Invalid device address" + addr + ".");
245 getInsteonBinding().getDriver().addMsgListener(this);
247 this.console = console;
252 private void stopMonitoring(Console console, String addr) {
254 console.println("Not mointoring any devices.");
258 if ("all".equalsIgnoreCase(addr)) {
259 if (monitorAllDevices) {
260 monitorAllDevices = false;
261 console.println("Stopped monitoring all devices.");
263 console.println("Not monitoring all devices.");
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 + ".");
272 console.println("Not monitoring the device " + addr + ".");
275 } catch (IllegalArgumentException e) {
276 console.println("Invalid address device address " + addr + ".");
281 if (!monitorAllDevices && monitoredAddresses.isEmpty()) {
282 getInsteonBinding().getDriver().removeListener(this);
288 private void sendMessage(Console console, MessageType messageType, String[] args) {
289 InsteonDevice device = new InsteonDevice();
290 device.setDriver(getInsteonBinding().getDriver());
293 device.setAddress(new InsteonAddress(args[1]));
294 } catch (IllegalArgumentException e) {
295 console.println("Invalid device address" + args[1] + ".");
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(", ");
305 builder.append(args[i]);
308 if (builder.length() != 0) {
309 builder.append(" is not a valid hexadecimal byte.");
310 console.print(builder.toString());
315 byte flags = (byte) Utils.fromHexString(args[2]);
316 byte cmd1 = (byte) Utils.fromHexString(args[3]);
317 byte cmd2 = (byte) Utils.fromHexString(args[4]);
319 if (messageType == MessageType.STANDARD) {
320 msg = device.makeStandardMessage(flags, cmd1, cmd2);
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]);
327 if (messageType == MessageType.EXTENDED) {
328 msg = device.makeExtendedMessage(flags, cmd1, cmd2, data);
330 msg = device.makeExtendedMessageCRC2(flags, cmd1, cmd2, data);
333 device.enqueueMessage(msg, new DeviceFeature(device, "console"));
334 } catch (FieldException | InvalidMessageTypeException e) {
335 console.println("Error while trying to create message.");
339 private InsteonBinding getInsteonBinding() {
340 InsteonNetworkHandler handler = this.handler;
341 if (handler == null) {
342 throw new IllegalArgumentException("No Insteon network bridge configured.");
345 return handler.getInsteonBinding();