mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[boschshc] Add command to list SHC device mappings (#15060)
* [boschshc] add command to list Bosch Smart Home Controller devices and mapping to openhab devices and related services Signed-off-by: Gerd Zanker <gerd.zanker@web.de> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
b9dba4a8b2
commit
c3b29b3508
@ -0,0 +1,264 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.boschshc.internal.console;
|
||||||
|
|
||||||
|
import static org.openhab.binding.boschshc.internal.discovery.ThingDiscoveryService.DEVICEMODEL_TO_THINGTYPE_MAP;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.PublicInformation;
|
||||||
|
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||||
|
import org.openhab.core.io.console.Console;
|
||||||
|
import org.openhab.core.io.console.ConsoleCommandCompleter;
|
||||||
|
import org.openhab.core.io.console.StringsCompleter;
|
||||||
|
import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
|
||||||
|
import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingRegistry;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
|
import org.osgi.framework.Bundle;
|
||||||
|
import org.osgi.framework.FrameworkUtil;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Console command to list Bosch SHC devices and openhab support.
|
||||||
|
* Use the SHC API to get all SHC devices and SHC services
|
||||||
|
* and tries to lookup openhab devices and implemented service classes.
|
||||||
|
* Prints each name and looked-up implementation on console.
|
||||||
|
*
|
||||||
|
* @author Gerd Zanker - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(service = ConsoleCommandExtension.class)
|
||||||
|
public class BoschShcCommandExtension extends AbstractConsoleCommandExtension implements ConsoleCommandCompleter {
|
||||||
|
|
||||||
|
static final String SHOW_BINDINGINFO = "showBindingInfo";
|
||||||
|
static final String SHOW_DEVICES = "showDevices";
|
||||||
|
static final String SHOW_SERVICES = "showServices";
|
||||||
|
|
||||||
|
static final String GET_BRIDGEINFO = "bridgeInfo";
|
||||||
|
static final String GET_DEVICES = "deviceInfo";
|
||||||
|
private static final StringsCompleter SUBCMD_COMPLETER = new StringsCompleter(
|
||||||
|
List.of(SHOW_BINDINGINFO, SHOW_DEVICES, SHOW_SERVICES, GET_BRIDGEINFO, GET_DEVICES), false);
|
||||||
|
|
||||||
|
private final ThingRegistry thingRegistry;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public BoschShcCommandExtension(final @Reference ThingRegistry thingRegistry) {
|
||||||
|
super(BoschSHCBindingConstants.BINDING_ID, "Interact with the Bosch Smart Home Controller.");
|
||||||
|
this.thingRegistry = thingRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all implemented services of this Bosch SHC binding.
|
||||||
|
* This list shall contain all available services and needs to be extended when a new service is added.
|
||||||
|
* A unit tests checks if this list matches with the existing subfolders in
|
||||||
|
* "src/main/java/org/openhab/binding/boschshc/internal/services".
|
||||||
|
*/
|
||||||
|
List<String> getAllBoschShcServices() {
|
||||||
|
return List.of("airqualitylevel", "batterylevel", "binaryswitch", "bypass", "cameranotification", "childlock",
|
||||||
|
"communicationquality", "hsbcoloractuator", "humiditylevel", "illuminance", "intrusion", "keypad",
|
||||||
|
"latestmotion", "multilevelswitch", "powermeter", "powerswitch", "privacymode", "roomclimatecontrol",
|
||||||
|
"shuttercontact", "shuttercontrol", "silentmode", "smokedetectorcheck", "temperaturelevel", "userstate",
|
||||||
|
"valvetappet");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(String[] args, Console console) {
|
||||||
|
if (args.length == 0) {
|
||||||
|
printUsage(console);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (GET_BRIDGEINFO.equals(args[0])) {
|
||||||
|
console.print(buildBridgeInfo());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (GET_DEVICES.equals(args[0])) {
|
||||||
|
console.print(buildDeviceInfo());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (SHOW_BINDINGINFO.equals(args[0])) {
|
||||||
|
console.print(buildBindingInfo());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (SHOW_DEVICES.equals(args[0])) {
|
||||||
|
console.print(buildSupportedDeviceStatus());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (SHOW_SERVICES.equals(args[0])) {
|
||||||
|
console.print(buildSupportedServiceStatus());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (BoschSHCException | ExecutionException | TimeoutException e) {
|
||||||
|
console.print(String.format("Error %1s%n", e.getMessage()));
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
// unsupported command, print usage
|
||||||
|
printUsage(console);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<BridgeHandler> getBridgeHandlers() {
|
||||||
|
List<BridgeHandler> bridges = new ArrayList<>();
|
||||||
|
for (Thing thing : thingRegistry.getAll()) {
|
||||||
|
ThingHandler thingHandler = thing.getHandler();
|
||||||
|
if (thingHandler instanceof BridgeHandler bridgeHandler) {
|
||||||
|
bridges.add(bridgeHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bridges;
|
||||||
|
}
|
||||||
|
|
||||||
|
String buildBridgeInfo() throws BoschSHCException, InterruptedException, ExecutionException, TimeoutException {
|
||||||
|
List<BridgeHandler> bridges = getBridgeHandlers();
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (BridgeHandler bridgeHandler : bridges) {
|
||||||
|
builder.append(String.format("Bridge: %1s%n", bridgeHandler.getThing().getLabel()));
|
||||||
|
builder.append(String.format(" access possible: %1s%n", bridgeHandler.checkBridgeAccess()));
|
||||||
|
|
||||||
|
PublicInformation publicInformation = bridgeHandler.getPublicInformation();
|
||||||
|
builder.append(String.format(" SHC Generation: %1s%n", publicInformation.shcGeneration));
|
||||||
|
builder.append(String.format(" IP Address: %1s%n", publicInformation.shcIpAddress));
|
||||||
|
builder.append(String.format(" API Versions: %1s%n", publicInformation.apiVersions));
|
||||||
|
builder.append(String.format(" Software Version: %1s%n",
|
||||||
|
publicInformation.softwareUpdateState.swInstalledVersion));
|
||||||
|
builder.append(String.format(" Version Update State: %1s%n",
|
||||||
|
publicInformation.softwareUpdateState.swUpdateState));
|
||||||
|
builder.append(String.format(" Available Version: %1s%n",
|
||||||
|
publicInformation.softwareUpdateState.swUpdateAvailableVersion));
|
||||||
|
builder.append(String.format("%n"));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String buildDeviceInfo() throws InterruptedException {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (Thing thing : thingRegistry.getAll()) {
|
||||||
|
ThingHandler thingHandler = thing.getHandler();
|
||||||
|
if (thingHandler instanceof BridgeHandler bridgeHandler) {
|
||||||
|
builder.append(String.format("thing: %1s%n", thing.getLabel()));
|
||||||
|
builder.append(String.format(" thingHandler: %1s%n", thingHandler.getClass().getName()));
|
||||||
|
builder.append(String.format("bridge access possible: %1s%n", bridgeHandler.checkBridgeAccess()));
|
||||||
|
|
||||||
|
List<Device> devices = bridgeHandler.getDevices();
|
||||||
|
builder.append(String.format("devices (%1d): %n", devices.size()));
|
||||||
|
for (Device device : devices) {
|
||||||
|
builder.append(buildDeviceInfo(device));
|
||||||
|
builder.append(String.format("%n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildDeviceInfo(Device device) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(String.format(" deviceID: %1s%n", device.id));
|
||||||
|
builder.append(String.format(" type: %1s -> ", device.deviceModel));
|
||||||
|
if (DEVICEMODEL_TO_THINGTYPE_MAP.containsKey(device.deviceModel)) {
|
||||||
|
builder.append(DEVICEMODEL_TO_THINGTYPE_MAP.get(device.deviceModel).getId());
|
||||||
|
} else {
|
||||||
|
builder.append("!UNSUPPORTED!");
|
||||||
|
}
|
||||||
|
builder.append(String.format("%n"));
|
||||||
|
|
||||||
|
builder.append(buildDeviceServices(device.deviceServiceIds));
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildDeviceServices(List<String> deviceServiceIds) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
List<String> existingServices = getAllBoschShcServices();
|
||||||
|
for (String serviceName : deviceServiceIds) {
|
||||||
|
builder.append(String.format(" service: %1s -> ", serviceName));
|
||||||
|
|
||||||
|
if (existingServices.stream().anyMatch(s -> s.equals(serviceName.toLowerCase()))) {
|
||||||
|
for (String existingService : existingServices) {
|
||||||
|
if (existingService.equals(serviceName.toLowerCase())) {
|
||||||
|
builder.append(existingService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder.append("!UNSUPPORTED!");
|
||||||
|
}
|
||||||
|
builder.append(String.format("%n"));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String buildBindingInfo() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(String.format("Bosch SHC Binding%n"));
|
||||||
|
Bundle bundle = FrameworkUtil.getBundle(getClass());
|
||||||
|
if (bundle != null) {
|
||||||
|
builder.append(String.format(" SymbolicName %1s%n", bundle.getSymbolicName()));
|
||||||
|
builder.append(String.format(" Version %1s%n", bundle.getVersion()));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String buildSupportedDeviceStatus() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(String.format("Supported Devices (%1d):%n", DEVICEMODEL_TO_THINGTYPE_MAP.size()));
|
||||||
|
for (Map.Entry<String, ThingTypeUID> entry : DEVICEMODEL_TO_THINGTYPE_MAP.entrySet()) {
|
||||||
|
builder.append(
|
||||||
|
String.format(" - %1s = %1s%n", entry.getKey(), DEVICEMODEL_TO_THINGTYPE_MAP.get(entry.getKey())));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String buildSupportedServiceStatus() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
List<String> supportedServices = getAllBoschShcServices();
|
||||||
|
builder.append(String.format("Supported Services (%1d):%n", supportedServices.size()));
|
||||||
|
for (String service : supportedServices) {
|
||||||
|
builder.append(String.format(" - %1s%n", service));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getUsages() {
|
||||||
|
return List.of(buildCommandUsage(SHOW_BINDINGINFO, "list detailed information about this binding"),
|
||||||
|
buildCommandUsage(SHOW_DEVICES, "list all devices supported by this binding"),
|
||||||
|
buildCommandUsage(SHOW_SERVICES, "list all services supported by this binding"),
|
||||||
|
buildCommandUsage(GET_DEVICES, "get all Bosch SHC devices"),
|
||||||
|
buildCommandUsage(GET_BRIDGEINFO, "get detailed information from Bosch SHC"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConsoleCommandCompleter getCompleter() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) {
|
||||||
|
if (cursorArgumentIndex <= 0) {
|
||||||
|
return SUBCMD_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -39,6 +39,7 @@ import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler;
|
|||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.PublicInformation;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Room;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Room;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedState;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedState;
|
||||||
@ -432,6 +433,23 @@ public class BridgeHandler extends BaseBridgeHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get public information from Bosch SHC.
|
||||||
|
*/
|
||||||
|
public PublicInformation getPublicInformation()
|
||||||
|
throws InterruptedException, BoschSHCException, ExecutionException, TimeoutException {
|
||||||
|
@Nullable
|
||||||
|
BoschHttpClient localHttpClient = this.httpClient;
|
||||||
|
if (localHttpClient == null) {
|
||||||
|
throw new BoschSHCException(HTTP_CLIENT_NOT_INITIALIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
String url = localHttpClient.getPublicInformationUrl();
|
||||||
|
Request request = localHttpClient.createRequest(url, GET);
|
||||||
|
|
||||||
|
return localHttpClient.sendRequest(request, PublicInformation.class, PublicInformation::isValid, null);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean registerDiscoveryListener(ThingDiscoveryService listener) {
|
public boolean registerDiscoveryListener(ThingDiscoveryService listener) {
|
||||||
if (thingDiscoveryService == null) {
|
if (thingDiscoveryService == null) {
|
||||||
thingDiscoveryService = listener;
|
thingDiscoveryService = listener;
|
||||||
@ -604,7 +622,7 @@ public class BridgeHandler extends BaseBridgeHandler {
|
|||||||
@Nullable
|
@Nullable
|
||||||
BoschHttpClient localHttpClient = this.httpClient;
|
BoschHttpClient localHttpClient = this.httpClient;
|
||||||
if (localHttpClient == null) {
|
if (localHttpClient == null) {
|
||||||
throw new BoschSHCException("HTTP client not initialized");
|
throw new BoschSHCException(HTTP_CLIENT_NOT_INITIALIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
String url = localHttpClient.getBoschSmartHomeUrl(String.format("devices/%s", deviceId));
|
String url = localHttpClient.getBoschSmartHomeUrl(String.format("devices/%s", deviceId));
|
||||||
@ -634,7 +652,7 @@ public class BridgeHandler extends BaseBridgeHandler {
|
|||||||
@Nullable
|
@Nullable
|
||||||
BoschHttpClient locaHttpClient = this.httpClient;
|
BoschHttpClient locaHttpClient = this.httpClient;
|
||||||
if (locaHttpClient == null) {
|
if (locaHttpClient == null) {
|
||||||
throw new BoschSHCException("HTTP client not initialized");
|
throw new BoschSHCException(HTTP_CLIENT_NOT_INITIALIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
String url = locaHttpClient.getBoschSmartHomeUrl(String.format("userdefinedstates/%s", stateId));
|
String url = locaHttpClient.getBoschSmartHomeUrl(String.format("userdefinedstates/%s", stateId));
|
||||||
|
@ -55,7 +55,7 @@ public class Device {
|
|||||||
public String status;
|
public String status;
|
||||||
public List<String> childDeviceIds;
|
public List<String> childDeviceIds;
|
||||||
|
|
||||||
public static Boolean isValid(Device obj) {
|
public static boolean isValid(Device obj) {
|
||||||
return obj != null && obj.id != null;
|
return obj != null && obj.id != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,4 +42,10 @@ public class PublicInformation {
|
|||||||
public List<String> apiVersions;
|
public List<String> apiVersions;
|
||||||
public String shcIpAddress;
|
public String shcIpAddress;
|
||||||
public String shcGeneration;
|
public String shcGeneration;
|
||||||
|
public SoftwareUpdateState softwareUpdateState;
|
||||||
|
|
||||||
|
public static boolean isValid(PublicInformation obj) {
|
||||||
|
return obj != null && obj.shcIpAddress != null && obj.shcGeneration != null && obj.apiVersions != null
|
||||||
|
&& SoftwareUpdateState.isValid(obj.softwareUpdateState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,17 +48,13 @@ public class Scenario extends BoschSHCServiceState {
|
|||||||
return scenario;
|
return scenario;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean isValid(Scenario[] scenarios) {
|
public static boolean isValid(Scenario[] scenarios) {
|
||||||
return Arrays.stream(scenarios).allMatch(scenario -> (scenario.id != null));
|
return Arrays.stream(scenarios).allMatch(scenario -> (scenario.id != null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
final StringBuilder sb = new StringBuilder("Scenario{");
|
return "Scenario{" + "name='" + name + "'" + ", id='" + id + "'" + ", lastTimeTriggered='" + lastTimeTriggered
|
||||||
sb.append("name='").append(name).append("'");
|
+ "'" + '}';
|
||||||
sb.append(", id='").append(id).append("'");
|
|
||||||
sb.append(", lastTimeTriggered='").append(lastTimeTriggered).append("'");
|
|
||||||
sb.append('}');
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.boschshc.internal.devices.bridge.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Software Update State is part of PublicInformation.
|
||||||
|
*
|
||||||
|
* @author Gerd Zanker - Initial contribution
|
||||||
|
*/
|
||||||
|
public class SoftwareUpdateState {
|
||||||
|
|
||||||
|
public String swUpdateState;
|
||||||
|
public String swInstalledVersion;
|
||||||
|
public String swUpdateAvailableVersion;
|
||||||
|
|
||||||
|
public static boolean isValid(SoftwareUpdateState obj) {
|
||||||
|
return obj != null && obj.swUpdateState != null && obj.swInstalledVersion != null
|
||||||
|
&& obj.swUpdateAvailableVersion != null;
|
||||||
|
}
|
||||||
|
}
|
@ -31,7 +31,7 @@ public class SubscribeResult {
|
|||||||
return this.jsonrpc;
|
return this.jsonrpc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean isValid(SubscribeResult obj) {
|
public static boolean isValid(SubscribeResult obj) {
|
||||||
return obj != null && obj.result != null && obj.jsonrpc != null;
|
return obj != null && obj.result != null && obj.jsonrpc != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ public class UserDefinedState extends BoschSHCServiceState {
|
|||||||
+ type + '\'' + '}';
|
+ type + '\'' + '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean isValid(UserDefinedState obj) {
|
public static boolean isValid(UserDefinedState obj) {
|
||||||
return obj != null && obj.id != null;
|
return obj != null && obj.id != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ public class ThingDiscoveryService extends AbstractThingHandlerDiscoveryService<
|
|||||||
BoschSHCBindingConstants.THING_TYPE_SMOKE_DETECTOR);
|
BoschSHCBindingConstants.THING_TYPE_SMOKE_DETECTOR);
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
protected static final Map<String, ThingTypeUID> DEVICEMODEL_TO_THINGTYPE_MAP = Map.ofEntries(
|
public static final Map<String, ThingTypeUID> DEVICEMODEL_TO_THINGTYPE_MAP = Map.ofEntries(
|
||||||
new AbstractMap.SimpleEntry<>("BBL", BoschSHCBindingConstants.THING_TYPE_SHUTTER_CONTROL),
|
new AbstractMap.SimpleEntry<>("BBL", BoschSHCBindingConstants.THING_TYPE_SHUTTER_CONTROL),
|
||||||
new AbstractMap.SimpleEntry<>("TWINGUARD", BoschSHCBindingConstants.THING_TYPE_TWINGUARD),
|
new AbstractMap.SimpleEntry<>("TWINGUARD", BoschSHCBindingConstants.THING_TYPE_TWINGUARD),
|
||||||
new AbstractMap.SimpleEntry<>("BSM", BoschSHCBindingConstants.THING_TYPE_INWALL_SWITCH),
|
new AbstractMap.SimpleEntry<>("BSM", BoschSHCBindingConstants.THING_TYPE_INWALL_SWITCH),
|
||||||
|
@ -0,0 +1,228 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.boschshc.internal.console;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.PublicInformation;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.SoftwareUpdateState;
|
||||||
|
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||||
|
import org.openhab.core.io.console.Console;
|
||||||
|
import org.openhab.core.thing.Bridge;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for Console command to list Bosch SHC devices and openhab support.
|
||||||
|
*
|
||||||
|
* @author Gerd Zanker - Initial contribution
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@NonNullByDefault
|
||||||
|
class BoschShcCommandExtensionTest {
|
||||||
|
|
||||||
|
private @NonNullByDefault({}) BoschShcCommandExtension fixture;
|
||||||
|
|
||||||
|
private @Mock @NonNullByDefault({}) ThingRegistry thingRegistry;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
fixture = new BoschShcCommandExtension(thingRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void execute() {
|
||||||
|
// only sanity checks, content is tested with the functions called by execute
|
||||||
|
Console consoleMock = mock(Console.class);
|
||||||
|
when(thingRegistry.getAll()).thenReturn(Collections.emptyList());
|
||||||
|
|
||||||
|
fixture.execute(new String[] {}, consoleMock);
|
||||||
|
verify(consoleMock, times(5)).printUsage(any());
|
||||||
|
fixture.execute(new String[] { "" }, consoleMock);
|
||||||
|
verify(consoleMock, times(10)).printUsage(any());
|
||||||
|
|
||||||
|
fixture.execute(new String[] { BoschShcCommandExtension.SHOW_BINDINGINFO }, consoleMock);
|
||||||
|
verify(consoleMock, atLeastOnce()).print(any());
|
||||||
|
fixture.execute(new String[] { BoschShcCommandExtension.SHOW_DEVICES }, consoleMock);
|
||||||
|
verify(consoleMock, atLeastOnce()).print(any());
|
||||||
|
fixture.execute(new String[] { BoschShcCommandExtension.SHOW_SERVICES }, consoleMock);
|
||||||
|
verify(consoleMock, atLeastOnce()).print(any());
|
||||||
|
|
||||||
|
fixture.execute(new String[] { BoschShcCommandExtension.GET_BRIDGEINFO }, consoleMock);
|
||||||
|
verify(consoleMock, atLeastOnce()).print(any());
|
||||||
|
fixture.execute(new String[] { BoschShcCommandExtension.GET_DEVICES }, consoleMock);
|
||||||
|
verify(consoleMock, atLeastOnce()).print(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getCompleter() {
|
||||||
|
assertThat(fixture.getCompleter(), is(fixture));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUsages() {
|
||||||
|
List<String> strings = fixture.getUsages();
|
||||||
|
assertThat(strings.size(), is(5));
|
||||||
|
assertThat(strings.get(0), is("boschshc showBindingInfo - list detailed information about this binding"));
|
||||||
|
assertThat(strings.get(1), is("boschshc showDevices - list all devices supported by this binding"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void complete() {
|
||||||
|
ArrayList<String> candidates = new ArrayList<>();
|
||||||
|
assertThat(fixture.complete(new String[] { "" }, 1, 0, candidates), is(false));
|
||||||
|
assertThat(fixture.complete(new String[] { "" }, 0, 0, candidates), is(true));
|
||||||
|
// for empty arguments, the completer suggest all usage commands
|
||||||
|
assertThat(candidates.size(), is(fixture.getUsages().size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void printBridgeInfo() throws BoschSHCException, ExecutionException, InterruptedException, TimeoutException {
|
||||||
|
// no bridge
|
||||||
|
when(thingRegistry.getAll()).thenReturn(Collections.emptyList());
|
||||||
|
assertThat(fixture.buildBridgeInfo(), is(""));
|
||||||
|
|
||||||
|
// one bridge
|
||||||
|
PublicInformation publicInformation = new PublicInformation();
|
||||||
|
publicInformation.shcGeneration = "Gen-T";
|
||||||
|
publicInformation.shcIpAddress = "1.2.3.4";
|
||||||
|
publicInformation.softwareUpdateState = new SoftwareUpdateState();
|
||||||
|
Bridge mockBridge = mock(Bridge.class);
|
||||||
|
when(mockBridge.getLabel()).thenReturn("TestLabel");
|
||||||
|
BridgeHandler mockBridgeHandler = mock(BridgeHandler.class);
|
||||||
|
when(mockBridgeHandler.getThing()).thenReturn(mockBridge);
|
||||||
|
when(mockBridgeHandler.getPublicInformation()).thenReturn(publicInformation);
|
||||||
|
Thing mockBridgeThing = mock(Thing.class);
|
||||||
|
when(mockBridgeThing.getHandler()).thenReturn(mockBridgeHandler);
|
||||||
|
when(thingRegistry.getAll()).thenReturn(Collections.singletonList(mockBridgeThing));
|
||||||
|
assertThat(fixture.buildBridgeInfo(),
|
||||||
|
allOf(containsString("Bridge: TestLabel"), containsString("access possible: false"),
|
||||||
|
containsString("SHC Generation: Gen-T"), containsString("IP Address: 1.2.3.4")));
|
||||||
|
|
||||||
|
// two bridges
|
||||||
|
PublicInformation publicInformation2 = new PublicInformation();
|
||||||
|
publicInformation2.shcGeneration = "Gen-U";
|
||||||
|
publicInformation2.shcIpAddress = "11.22.33.44";
|
||||||
|
publicInformation2.softwareUpdateState = new SoftwareUpdateState();
|
||||||
|
Bridge mockBridge2 = mock(Bridge.class);
|
||||||
|
when(mockBridge2.getLabel()).thenReturn("Bridge 2");
|
||||||
|
BridgeHandler mockBridgeHandler2 = mock(BridgeHandler.class);
|
||||||
|
when(mockBridgeHandler2.getThing()).thenReturn(mockBridge2);
|
||||||
|
when(mockBridgeHandler2.getPublicInformation()).thenReturn(publicInformation2);
|
||||||
|
Thing mockBridgeThing2 = mock(Thing.class);
|
||||||
|
when(mockBridgeThing2.getHandler()).thenReturn(mockBridgeHandler2);
|
||||||
|
when(thingRegistry.getAll()).thenReturn(Arrays.asList(mockBridgeThing, mockBridgeThing2));
|
||||||
|
assertThat(fixture.buildBridgeInfo(),
|
||||||
|
allOf(containsString("Bridge: TestLabel"), containsString("access possible: false"),
|
||||||
|
containsString("SHC Generation: Gen-T"), containsString("IP Address: 1.2.3.4"),
|
||||||
|
containsString("Bridge: Bridge 2"), containsString("access possible: false"),
|
||||||
|
containsString("SHC Generation: Gen-U"), containsString("IP Address: 11.22.33.44")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void printDeviceInfo() throws InterruptedException {
|
||||||
|
// no bridge
|
||||||
|
when(thingRegistry.getAll()).thenReturn(Collections.emptyList());
|
||||||
|
assertThat(fixture.buildDeviceInfo(), is(""));
|
||||||
|
|
||||||
|
// One bridge, No device
|
||||||
|
BridgeHandler mockBridgeHandler = mock(BridgeHandler.class);
|
||||||
|
Thing mockBridgeThing = mock(Thing.class);
|
||||||
|
when(mockBridgeThing.getLabel()).thenReturn("TestLabel");
|
||||||
|
when(mockBridgeThing.getHandler()).thenReturn(mockBridgeHandler);
|
||||||
|
when(thingRegistry.getAll()).thenReturn(Collections.singletonList(mockBridgeThing));
|
||||||
|
assertThat(fixture.buildDeviceInfo(), allOf(containsString("thing: TestLabel"), containsString("devices (0)")));
|
||||||
|
|
||||||
|
// One bridge, One UNsupported device
|
||||||
|
Device mockShcDevice = mock(Device.class);
|
||||||
|
mockShcDevice.deviceModel = "";
|
||||||
|
mockShcDevice.deviceServiceIds = Collections.emptyList();
|
||||||
|
when(mockBridgeHandler.getDevices()).thenReturn(List.of(mockShcDevice));
|
||||||
|
assertThat(fixture.buildDeviceInfo(), allOf(containsString("thing: TestLabel"), containsString("devices (1)"),
|
||||||
|
containsString("!UNSUPPORTED!")));
|
||||||
|
|
||||||
|
// One bridge, One supported device
|
||||||
|
mockShcDevice.deviceModel = "TWINGUARD";
|
||||||
|
mockShcDevice.deviceServiceIds = Collections.emptyList();
|
||||||
|
when(mockBridgeHandler.getDevices()).thenReturn(List.of(mockShcDevice));
|
||||||
|
assertThat(fixture.buildDeviceInfo(), allOf(containsString("thing: TestLabel"), containsString("devices (1)"),
|
||||||
|
containsString("TWINGUARD -> twinguard")));
|
||||||
|
|
||||||
|
// One bridge, One supported device with services
|
||||||
|
mockShcDevice.deviceModel = "TWINGUARD";
|
||||||
|
mockShcDevice.deviceServiceIds = List.of("unknownService", "batterylevel");
|
||||||
|
when(mockBridgeHandler.getDevices()).thenReturn(List.of(mockShcDevice));
|
||||||
|
assertThat(fixture.buildDeviceInfo(), allOf(containsString("thing: TestLabel"), containsString("devices (1)"),
|
||||||
|
containsString("TWINGUARD -> twinguard"), containsString("service: unknownService -> !UNSUPPORTED!"),
|
||||||
|
containsString("batterylevel -> batterylevel")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void printBindingInfo() {
|
||||||
|
assertThat(fixture.buildBindingInfo(), containsString("Bosch SHC Binding"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void printSupportedDevices() {
|
||||||
|
assertThat(fixture.buildSupportedDeviceStatus(),
|
||||||
|
allOf(containsString("Supported Devices"), containsString("BBL = boschshc:shutter-control")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void printSupportedServices() {
|
||||||
|
assertThat(fixture.buildSupportedServiceStatus(),
|
||||||
|
allOf(containsString("Supported Services"), containsString("airqualitylevel")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of services returned by getAllBoschShcServices() shall match
|
||||||
|
* the implemented services in org.openhab.bindings.boschshc.internal.services.
|
||||||
|
* Because reflection doesn't return all services classes during runtime
|
||||||
|
* this test supports consistency between the lists of services and the implemented services.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void getAllBoschShcServices() throws IOException {
|
||||||
|
List<String> services = Files
|
||||||
|
.walk(Paths.get("src/main/java/org/openhab/binding/boschshc/internal/services").toAbsolutePath(), 1)
|
||||||
|
.filter(Files::isDirectory).map(Path::getFileName).map(Path::toString)
|
||||||
|
// exclude folders which no service implementation
|
||||||
|
.filter(name -> !name.equals("dto")).filter(name -> !name.equals("services")).sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertThat(services, is(fixture.getAllBoschShcServices()));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user