]> git.basschouten.com Git - openhab-addons.git/blob
822146bff43c94d21c0c4702c0cb345e93e1ea4b
[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.boschshc.internal.console;
14
15 import static org.openhab.binding.boschshc.internal.discovery.ThingDiscoveryService.DEVICEMODEL_TO_THINGTYPE_MAP;
16
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.TimeoutException;
22
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;
44
45 /**
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.
50  *
51  * @author Gerd Zanker - Initial contribution
52  */
53 @NonNullByDefault
54 @Component(service = ConsoleCommandExtension.class)
55 public class BoschShcCommandExtension extends AbstractConsoleCommandExtension implements ConsoleCommandCompleter {
56
57     static final String SHOW_BINDINGINFO = "showBindingInfo";
58     static final String SHOW_DEVICES = "showDevices";
59     static final String SHOW_SERVICES = "showServices";
60
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);
65
66     private final ThingRegistry thingRegistry;
67
68     @Activate
69     public BoschShcCommandExtension(final @Reference ThingRegistry thingRegistry) {
70         super(BoschSHCBindingConstants.BINDING_ID, "Interact with the Bosch Smart Home Controller.");
71         this.thingRegistry = thingRegistry;
72     }
73
74     /**
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".
79      */
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",
85                 "valvetappet");
86     }
87
88     @Override
89     public void execute(String[] args, Console console) {
90         if (args.length == 0) {
91             printUsage(console);
92             return;
93         }
94         try {
95             if (GET_BRIDGEINFO.equals(args[0])) {
96                 console.print(buildBridgeInfo());
97                 return;
98             }
99             if (GET_DEVICES.equals(args[0])) {
100                 console.print(buildDeviceInfo());
101                 return;
102             }
103             if (SHOW_BINDINGINFO.equals(args[0])) {
104                 console.print(buildBindingInfo());
105                 return;
106             }
107             if (SHOW_DEVICES.equals(args[0])) {
108                 console.print(buildSupportedDeviceStatus());
109                 return;
110             }
111             if (SHOW_SERVICES.equals(args[0])) {
112                 console.print(buildSupportedServiceStatus());
113                 return;
114             }
115         } catch (BoschSHCException | ExecutionException | TimeoutException e) {
116             console.print(String.format("Error %1s%n", e.getMessage()));
117         } catch (InterruptedException e) {
118             Thread.currentThread().interrupt();
119         }
120         // unsupported command, print usage
121         printUsage(console);
122     }
123
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);
130             }
131         }
132         return bridges;
133     }
134
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()));
141
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"));
153         }
154         return builder.toString();
155     }
156
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()));
165
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"));
171                 }
172             }
173         }
174         return builder.toString();
175     }
176
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());
183         } else {
184             builder.append("!UNSUPPORTED!");
185         }
186         builder.append(String.format("%n"));
187
188         builder.append(buildDeviceServices(device.deviceServiceIds));
189         return builder.toString();
190     }
191
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));
197
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);
202                     }
203                 }
204             } else {
205                 builder.append("!UNSUPPORTED!");
206             }
207             builder.append(String.format("%n"));
208         }
209         return builder.toString();
210     }
211
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()));
219         }
220         return builder.toString();
221     }
222
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()) {
227             builder.append(
228                     String.format(" - %1s = %1s%n", entry.getKey(), DEVICEMODEL_TO_THINGTYPE_MAP.get(entry.getKey())));
229         }
230         return builder.toString();
231     }
232
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));
239         }
240         return builder.toString();
241     }
242
243     @Override
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"));
250     }
251
252     @Override
253     public @Nullable ConsoleCommandCompleter getCompleter() {
254         return this;
255     }
256
257     @Override
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);
261         }
262         return false;
263     }
264 }