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.boschshc.internal.console;
15 import static org.openhab.binding.boschshc.internal.discovery.ThingDiscoveryService.DEVICEMODEL_TO_THINGTYPE_MAP;
17 import java.util.ArrayList;
18 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.TimeoutException;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
26 import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler;
27 import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
28 import org.openhab.binding.boschshc.internal.devices.bridge.dto.PublicInformation;
29 import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
30 import org.openhab.core.io.console.Console;
31 import org.openhab.core.io.console.ConsoleCommandCompleter;
32 import org.openhab.core.io.console.StringsCompleter;
33 import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
34 import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
35 import org.openhab.core.thing.Thing;
36 import org.openhab.core.thing.ThingRegistry;
37 import org.openhab.core.thing.ThingTypeUID;
38 import org.openhab.core.thing.binding.ThingHandler;
39 import org.osgi.framework.Bundle;
40 import org.osgi.framework.FrameworkUtil;
41 import org.osgi.service.component.annotations.Activate;
42 import org.osgi.service.component.annotations.Component;
43 import org.osgi.service.component.annotations.Reference;
46 * Console command to list Bosch SHC devices and openhab support.
47 * Use the SHC API to get all SHC devices and SHC services
48 * and tries to lookup openhab devices and implemented service classes.
49 * Prints each name and looked-up implementation on console.
51 * @author Gerd Zanker - Initial contribution
54 @Component(service = ConsoleCommandExtension.class)
55 public class BoschShcCommandExtension extends AbstractConsoleCommandExtension implements ConsoleCommandCompleter {
57 static final String SHOW_BINDINGINFO = "showBindingInfo";
58 static final String SHOW_DEVICES = "showDevices";
59 static final String SHOW_SERVICES = "showServices";
61 static final String GET_BRIDGEINFO = "bridgeInfo";
62 static final String GET_DEVICES = "deviceInfo";
63 private static final StringsCompleter SUBCMD_COMPLETER = new StringsCompleter(
64 List.of(SHOW_BINDINGINFO, SHOW_DEVICES, SHOW_SERVICES, GET_BRIDGEINFO, GET_DEVICES), false);
66 private final ThingRegistry thingRegistry;
69 public BoschShcCommandExtension(final @Reference ThingRegistry thingRegistry) {
70 super(BoschSHCBindingConstants.BINDING_ID, "Interact with the Bosch Smart Home Controller.");
71 this.thingRegistry = thingRegistry;
75 * Returns all implemented services of this Bosch SHC binding.
76 * This list shall contain all available services and needs to be extended when a new service is added.
77 * A unit tests checks if this list matches with the existing subfolders in
78 * "src/main/java/org/openhab/binding/boschshc/internal/services".
80 List<String> getAllBoschShcServices() {
81 return List.of("airqualitylevel", "batterylevel", "binaryswitch", "bypass", "cameranotification", "childlock",
82 "childprotection", "communicationquality", "hsbcoloractuator", "humiditylevel", "illuminance",
83 "intrusion", "keypad", "latestmotion", "multilevelswitch", "powermeter", "powerswitch", "privacymode",
84 "roomclimatecontrol", "shuttercontact", "shuttercontrol", "silentmode", "smokedetectorcheck",
85 "temperaturelevel", "userstate", "valvetappet", "waterleakagesensor", "waterleakagesensorcheck",
86 "waterleakagesensortilt");
90 public void execute(String[] args, Console console) {
91 if (args.length == 0) {
96 if (GET_BRIDGEINFO.equals(args[0])) {
97 console.print(buildBridgeInfo());
100 if (GET_DEVICES.equals(args[0])) {
101 console.print(buildDeviceInfo());
104 if (SHOW_BINDINGINFO.equals(args[0])) {
105 console.print(buildBindingInfo());
108 if (SHOW_DEVICES.equals(args[0])) {
109 console.print(buildSupportedDeviceStatus());
112 if (SHOW_SERVICES.equals(args[0])) {
113 console.print(buildSupportedServiceStatus());
116 } catch (BoschSHCException | ExecutionException | TimeoutException e) {
117 console.print(String.format("Error %1s%n", e.getMessage()));
118 } catch (InterruptedException e) {
119 Thread.currentThread().interrupt();
121 // unsupported command, print usage
125 private List<BridgeHandler> getBridgeHandlers() {
126 List<BridgeHandler> bridges = new ArrayList<>();
127 for (Thing thing : thingRegistry.getAll()) {
128 ThingHandler thingHandler = thing.getHandler();
129 if (thingHandler instanceof BridgeHandler bridgeHandler) {
130 bridges.add(bridgeHandler);
136 String buildBridgeInfo() throws BoschSHCException, InterruptedException, ExecutionException, TimeoutException {
137 List<BridgeHandler> bridges = getBridgeHandlers();
138 StringBuilder builder = new StringBuilder();
139 for (BridgeHandler bridgeHandler : bridges) {
140 builder.append(String.format("Bridge: %1s%n", bridgeHandler.getThing().getLabel()));
141 builder.append(String.format(" access possible: %1s%n", bridgeHandler.checkBridgeAccess()));
143 PublicInformation publicInformation = bridgeHandler.getPublicInformation();
144 builder.append(String.format(" SHC Generation: %1s%n", publicInformation.shcGeneration));
145 builder.append(String.format(" IP Address: %1s%n", publicInformation.shcIpAddress));
146 builder.append(String.format(" API Versions: %1s%n", publicInformation.apiVersions));
147 builder.append(String.format(" Software Version: %1s%n",
148 publicInformation.softwareUpdateState.swInstalledVersion));
149 builder.append(String.format(" Version Update State: %1s%n",
150 publicInformation.softwareUpdateState.swUpdateState));
151 builder.append(String.format(" Available Version: %1s%n",
152 publicInformation.softwareUpdateState.swUpdateAvailableVersion));
153 builder.append(String.format("%n"));
155 return builder.toString();
158 String buildDeviceInfo() throws InterruptedException {
159 StringBuilder builder = new StringBuilder();
160 for (Thing thing : thingRegistry.getAll()) {
161 ThingHandler thingHandler = thing.getHandler();
162 if (thingHandler instanceof BridgeHandler bridgeHandler) {
163 builder.append(String.format("thing: %1s%n", thing.getLabel()));
164 builder.append(String.format(" thingHandler: %1s%n", thingHandler.getClass().getName()));
165 builder.append(String.format("bridge access possible: %1s%n", bridgeHandler.checkBridgeAccess()));
167 List<Device> devices = bridgeHandler.getDevices();
168 builder.append(String.format("devices (%1d): %n", devices.size()));
169 for (Device device : devices) {
170 builder.append(buildDeviceInfo(device));
171 builder.append(String.format("%n"));
175 return builder.toString();
178 private String buildDeviceInfo(Device device) {
179 StringBuilder builder = new StringBuilder();
180 builder.append(String.format(" deviceID: %1s%n", device.id));
181 builder.append(String.format(" type: %1s -> ", device.deviceModel));
182 if (DEVICEMODEL_TO_THINGTYPE_MAP.containsKey(device.deviceModel)) {
183 builder.append(DEVICEMODEL_TO_THINGTYPE_MAP.get(device.deviceModel).getId());
185 builder.append("!UNSUPPORTED!");
187 builder.append(String.format("%n"));
189 builder.append(buildDeviceServices(device.deviceServiceIds));
190 return builder.toString();
193 private String buildDeviceServices(List<String> deviceServiceIds) {
194 StringBuilder builder = new StringBuilder();
195 List<String> existingServices = getAllBoschShcServices();
196 for (String serviceName : deviceServiceIds) {
197 builder.append(String.format(" service: %1s -> ", serviceName));
199 if (existingServices.stream().anyMatch(s -> s.equals(serviceName.toLowerCase()))) {
200 for (String existingService : existingServices) {
201 if (existingService.equals(serviceName.toLowerCase())) {
202 builder.append(existingService);
206 builder.append("!UNSUPPORTED!");
208 builder.append(String.format("%n"));
210 return builder.toString();
213 String buildBindingInfo() {
214 StringBuilder builder = new StringBuilder();
215 builder.append(String.format("Bosch SHC Binding%n"));
216 Bundle bundle = FrameworkUtil.getBundle(getClass());
217 if (bundle != null) {
218 builder.append(String.format(" SymbolicName %1s%n", bundle.getSymbolicName()));
219 builder.append(String.format(" Version %1s%n", bundle.getVersion()));
221 return builder.toString();
224 String buildSupportedDeviceStatus() {
225 StringBuilder builder = new StringBuilder();
226 builder.append(String.format("Supported Devices (%1d):%n", DEVICEMODEL_TO_THINGTYPE_MAP.size()));
227 for (Map.Entry<String, ThingTypeUID> entry : DEVICEMODEL_TO_THINGTYPE_MAP.entrySet()) {
229 String.format(" - %1s = %1s%n", entry.getKey(), DEVICEMODEL_TO_THINGTYPE_MAP.get(entry.getKey())));
231 return builder.toString();
234 String buildSupportedServiceStatus() {
235 StringBuilder builder = new StringBuilder();
236 List<String> supportedServices = getAllBoschShcServices();
237 builder.append(String.format("Supported Services (%1d):%n", supportedServices.size()));
238 for (String service : supportedServices) {
239 builder.append(String.format(" - %1s%n", service));
241 return builder.toString();
245 public List<String> getUsages() {
246 return List.of(buildCommandUsage(SHOW_BINDINGINFO, "list detailed information about this binding"),
247 buildCommandUsage(SHOW_DEVICES, "list all devices supported by this binding"),
248 buildCommandUsage(SHOW_SERVICES, "list all services supported by this binding"),
249 buildCommandUsage(GET_DEVICES, "get all Bosch SHC devices"),
250 buildCommandUsage(GET_BRIDGEINFO, "get detailed information from Bosch SHC"));
254 public @Nullable ConsoleCommandCompleter getCompleter() {
259 public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) {
260 if (cursorArgumentIndex <= 0) {
261 return SUBCMD_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);