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 "communicationquality", "hsbcoloractuator", "humiditylevel", "illuminance", "intrusion", "keypad",
83 "latestmotion", "multilevelswitch", "powermeter", "powerswitch", "privacymode", "roomclimatecontrol",
84 "shuttercontact", "shuttercontrol", "silentmode", "smokedetectorcheck", "temperaturelevel", "userstate",
89 public void execute(String[] args, Console console) {
90 if (args.length == 0) {
95 if (GET_BRIDGEINFO.equals(args[0])) {
96 console.print(buildBridgeInfo());
99 if (GET_DEVICES.equals(args[0])) {
100 console.print(buildDeviceInfo());
103 if (SHOW_BINDINGINFO.equals(args[0])) {
104 console.print(buildBindingInfo());
107 if (SHOW_DEVICES.equals(args[0])) {
108 console.print(buildSupportedDeviceStatus());
111 if (SHOW_SERVICES.equals(args[0])) {
112 console.print(buildSupportedServiceStatus());
115 } catch (BoschSHCException | ExecutionException | TimeoutException e) {
116 console.print(String.format("Error %1s%n", e.getMessage()));
117 } catch (InterruptedException e) {
118 Thread.currentThread().interrupt();
120 // unsupported command, print usage
124 private List<BridgeHandler> getBridgeHandlers() {
125 List<BridgeHandler> bridges = new ArrayList<>();
126 for (Thing thing : thingRegistry.getAll()) {
127 ThingHandler thingHandler = thing.getHandler();
128 if (thingHandler instanceof BridgeHandler bridgeHandler) {
129 bridges.add(bridgeHandler);
135 String buildBridgeInfo() throws BoschSHCException, InterruptedException, ExecutionException, TimeoutException {
136 List<BridgeHandler> bridges = getBridgeHandlers();
137 StringBuilder builder = new StringBuilder();
138 for (BridgeHandler bridgeHandler : bridges) {
139 builder.append(String.format("Bridge: %1s%n", bridgeHandler.getThing().getLabel()));
140 builder.append(String.format(" access possible: %1s%n", bridgeHandler.checkBridgeAccess()));
142 PublicInformation publicInformation = bridgeHandler.getPublicInformation();
143 builder.append(String.format(" SHC Generation: %1s%n", publicInformation.shcGeneration));
144 builder.append(String.format(" IP Address: %1s%n", publicInformation.shcIpAddress));
145 builder.append(String.format(" API Versions: %1s%n", publicInformation.apiVersions));
146 builder.append(String.format(" Software Version: %1s%n",
147 publicInformation.softwareUpdateState.swInstalledVersion));
148 builder.append(String.format(" Version Update State: %1s%n",
149 publicInformation.softwareUpdateState.swUpdateState));
150 builder.append(String.format(" Available Version: %1s%n",
151 publicInformation.softwareUpdateState.swUpdateAvailableVersion));
152 builder.append(String.format("%n"));
154 return builder.toString();
157 String buildDeviceInfo() throws InterruptedException {
158 StringBuilder builder = new StringBuilder();
159 for (Thing thing : thingRegistry.getAll()) {
160 ThingHandler thingHandler = thing.getHandler();
161 if (thingHandler instanceof BridgeHandler bridgeHandler) {
162 builder.append(String.format("thing: %1s%n", thing.getLabel()));
163 builder.append(String.format(" thingHandler: %1s%n", thingHandler.getClass().getName()));
164 builder.append(String.format("bridge access possible: %1s%n", bridgeHandler.checkBridgeAccess()));
166 List<Device> devices = bridgeHandler.getDevices();
167 builder.append(String.format("devices (%1d): %n", devices.size()));
168 for (Device device : devices) {
169 builder.append(buildDeviceInfo(device));
170 builder.append(String.format("%n"));
174 return builder.toString();
177 private String buildDeviceInfo(Device device) {
178 StringBuilder builder = new StringBuilder();
179 builder.append(String.format(" deviceID: %1s%n", device.id));
180 builder.append(String.format(" type: %1s -> ", device.deviceModel));
181 if (DEVICEMODEL_TO_THINGTYPE_MAP.containsKey(device.deviceModel)) {
182 builder.append(DEVICEMODEL_TO_THINGTYPE_MAP.get(device.deviceModel).getId());
184 builder.append("!UNSUPPORTED!");
186 builder.append(String.format("%n"));
188 builder.append(buildDeviceServices(device.deviceServiceIds));
189 return builder.toString();
192 private String buildDeviceServices(List<String> deviceServiceIds) {
193 StringBuilder builder = new StringBuilder();
194 List<String> existingServices = getAllBoschShcServices();
195 for (String serviceName : deviceServiceIds) {
196 builder.append(String.format(" service: %1s -> ", serviceName));
198 if (existingServices.stream().anyMatch(s -> s.equals(serviceName.toLowerCase()))) {
199 for (String existingService : existingServices) {
200 if (existingService.equals(serviceName.toLowerCase())) {
201 builder.append(existingService);
205 builder.append("!UNSUPPORTED!");
207 builder.append(String.format("%n"));
209 return builder.toString();
212 String buildBindingInfo() {
213 StringBuilder builder = new StringBuilder();
214 builder.append(String.format("Bosch SHC Binding%n"));
215 Bundle bundle = FrameworkUtil.getBundle(getClass());
216 if (bundle != null) {
217 builder.append(String.format(" SymbolicName %1s%n", bundle.getSymbolicName()));
218 builder.append(String.format(" Version %1s%n", bundle.getVersion()));
220 return builder.toString();
223 String buildSupportedDeviceStatus() {
224 StringBuilder builder = new StringBuilder();
225 builder.append(String.format("Supported Devices (%1d):%n", DEVICEMODEL_TO_THINGTYPE_MAP.size()));
226 for (Map.Entry<String, ThingTypeUID> entry : DEVICEMODEL_TO_THINGTYPE_MAP.entrySet()) {
228 String.format(" - %1s = %1s%n", entry.getKey(), DEVICEMODEL_TO_THINGTYPE_MAP.get(entry.getKey())));
230 return builder.toString();
233 String buildSupportedServiceStatus() {
234 StringBuilder builder = new StringBuilder();
235 List<String> supportedServices = getAllBoschShcServices();
236 builder.append(String.format("Supported Services (%1d):%n", supportedServices.size()));
237 for (String service : supportedServices) {
238 builder.append(String.format(" - %1s%n", service));
240 return builder.toString();
244 public List<String> getUsages() {
245 return List.of(buildCommandUsage(SHOW_BINDINGINFO, "list detailed information about this binding"),
246 buildCommandUsage(SHOW_DEVICES, "list all devices supported by this binding"),
247 buildCommandUsage(SHOW_SERVICES, "list all services supported by this binding"),
248 buildCommandUsage(GET_DEVICES, "get all Bosch SHC devices"),
249 buildCommandUsage(GET_BRIDGEINFO, "get detailed information from Bosch SHC"));
253 public @Nullable ConsoleCommandCompleter getCompleter() {
258 public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) {
259 if (cursorArgumentIndex <= 0) {
260 return SUBCMD_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);