diff --git a/bundles/org.openhab.binding.boschshc/README.md b/bundles/org.openhab.binding.boschshc/README.md index 979f54659..f5ba3b261 100644 --- a/bundles/org.openhab.binding.boschshc/README.md +++ b/bundles/org.openhab.binding.boschshc/README.md @@ -49,7 +49,7 @@ A compact smart plug with energy monitoring capabilities. | power-consumption | Number:Power | ☐ | Current power consumption (W) of the device. | | energy-consumption | Number:Energy | ☐ | Cumulated energy consumption (Wh) of the device. | -### Twinguard smoke detector +### Twinguard Smoke Detector The Twinguard smoke detector warns you in case of fire and constantly monitors the air. @@ -65,6 +65,8 @@ The Twinguard smoke detector warns you in case of fire and constantly monitors t | purity-rating | String | ☐ | Rating of current measured purity. | | air-description | String | ☐ | Overall description of the air quality. | | combined-rating | String | ☐ | Combined rating of the air quality. | +| battery-level | Number | ☐ | Current battery level percentage as integer number. Bosch-specific battery levels are mapped to numbers as follows: `OK`: 100, `LOW_BATTERY`: 10, `CRITICAL_LOW`: 1, `CRITICALLY_LOW_BATTERY`: 1, `NOT_AVAILABLE`: `UNDEF`. | +| low-battery | Switch | ☐ | Indicates whether the battery is low (`ON`) or OK (`OFF`). | ### Door/Window Contact @@ -75,6 +77,8 @@ Detects open windows and doors. | Channel Type ID | Item Type | Writable | Description | | --------------- | --------- | :------: | ---------------------------- | | contact | Contact | ☐ | Contact state of the device. | +| battery-level | Number | ☐ | Current battery level percentage as integer number. Bosch-specific battery levels are mapped to numbers as follows: `OK`: 100, `LOW_BATTERY`: 10, `CRITICAL_LOW`: 1, `CRITICALLY_LOW_BATTERY`: 1, `NOT_AVAILABLE`: `UNDEF`. | +| low-battery | Switch | ☐ | Indicates whether the battery is low (`ON`) or OK (`OFF`). | ### Motion Detector @@ -85,6 +89,8 @@ Detects every movement through an intelligent combination of passive infra-red t | Channel Type ID | Item Type | Writable | Description | | --------------- | --------- | :------: | ------------------------------ | | latest-motion | DateTime | ☐ | The date of the latest motion. | +| battery-level | Number | ☐ | Current battery level percentage as integer number. Bosch-specific battery levels are mapped to numbers as follows: `OK`: 100, `LOW_BATTERY`: 10, `CRITICAL_LOW`: 1, `CRITICALLY_LOW_BATTERY`: 1, `NOT_AVAILABLE`: `UNDEF`. | +| low-battery | Switch | ☐ | Indicates whether the battery is low (`ON`) or OK (`OFF`). | ### Shutter Control @@ -107,6 +113,8 @@ Radiator thermostat | temperature | Number:Temperature | ☐ | Current measured temperature. | | valve-tappet-position | Number:Dimensionless | ☐ | Current open ratio of valve tappet (0 to 100). | | child-lock | Switch | ☑ | Indicates if child lock is active. | +| battery-level | Number | ☐ | Current battery level percentage as integer number. Bosch-specific battery levels are mapped to numbers as follows: `OK`: 100, `LOW_BATTERY`: 10, `CRITICAL_LOW`: 1, `CRITICALLY_LOW_BATTERY`: 1, `NOT_AVAILABLE`: `UNDEF`. | +| low-battery | Switch | ☐ | Indicates whether the battery is low (`ON`) or OK (`OFF`). | ### Climate Control @@ -129,6 +137,8 @@ Display of the current room temperature as well as the relative humidity in the | --------------- | -------------------- | :------: | ------------------------------------- | | temperature | Number:Temperature | ☐ | Current measured temperature. | | humidity | Number:Dimensionless | ☐ | Current measured humidity (0 to 100). | +| battery-level | Number | ☐ | Current battery level percentage as integer number. Bosch-specific battery levels are mapped to numbers as follows: `OK`: 100, `LOW_BATTERY`: 10, `CRITICAL_LOW`: 1, `CRITICALLY_LOW_BATTERY`: 1, `NOT_AVAILABLE`: `UNDEF`. | +| low-battery | Switch | ☐ | Indicates whether the battery is low (`ON`) or OK (`OFF`). | ### Security Camera 360 diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandler.java new file mode 100644 index 000000000..4e87b1084 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandler.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2010-2022 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; + +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.*; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.batterylevel.BatteryLevel; +import org.openhab.binding.boschshc.internal.services.batterylevel.BatteryLevelService; +import org.openhab.core.thing.Thing; + +/** + * Abstract implementation for battery-powered devices. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class AbstractBatteryPoweredDeviceHandler extends BoschSHCDeviceHandler { + + /** + * Service to monitor the battery level of the device + */ + private final BatteryLevelService batteryLevelService; + + public AbstractBatteryPoweredDeviceHandler(Thing thing) { + super(thing); + this.batteryLevelService = new BatteryLevelService(); + } + + @Override + protected void initializeServices() throws BoschSHCException { + super.initializeServices(); + + registerService(batteryLevelService, this::updateChannels, List.of(CHANNEL_BATTERY_LEVEL, CHANNEL_LOW_BATTERY), + true); + } + + private void updateChannels(DeviceServiceData deviceServiceData) { + BatteryLevel batteryLevel = BatteryLevel.fromDeviceServiceData(deviceServiceData); + super.updateState(CHANNEL_BATTERY_LEVEL, batteryLevel.toState()); + super.updateState(CHANNEL_LOW_BATTERY, batteryLevel.toLowBatteryState()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java index 7097deba8..56f61ce5c 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java @@ -74,6 +74,8 @@ public class BoschSHCBindingConstants { public static final String CHANNEL_ARM_ACTION = "arm-action"; public static final String CHANNEL_DISARM_ACTION = "disarm-action"; public static final String CHANNEL_MUTE_ACTION = "mute-action"; + public static final String CHANNEL_BATTERY_LEVEL = "battery-level"; + public static final String CHANNEL_LOW_BATTERY = "low-battery"; // static device/service names public static final String SERVICE_INTRUSION_DETECTION = "intrusionDetectionSystem"; diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandler.java index 9e3d79836..a4c8e5966 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandler.java @@ -161,7 +161,7 @@ public abstract class BoschSHCHandler extends BaseThingHandler { * @param serviceName Name of service the update came from. * @param stateData Current state of device service. Serialized as JSON. */ - public void processUpdate(String serviceName, JsonElement stateData) { + public void processUpdate(String serviceName, @Nullable JsonElement stateData) { // Check services of device to correctly for (DeviceService deviceService : this.services) { BoschSHCService service = deviceService.service; diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BoschHttpClient.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BoschHttpClient.java index fd372f5f9..7c214a7de 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BoschHttpClient.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BoschHttpClient.java @@ -77,7 +77,7 @@ public class BoschHttpClient extends HttpClient { /** * Returns the pairing URL for the Bosch SHC clients, using port 8443. * See https://github.com/BoschSmartHome/bosch-shc-api-docs/blob/master/postman/README.md - * + * * @return URL for pairing */ public String getPairingUrl() { @@ -86,7 +86,7 @@ public class BoschHttpClient extends HttpClient { /** * Returns a Bosch SHC URL for the endpoint, using port 8444. - * + * * @param endpoint a endpoint, see https://apidocs.bosch-smarthome.com/local/index.html * @return Bosch SHC URL for passed endpoint */ @@ -96,7 +96,7 @@ public class BoschHttpClient extends HttpClient { /** * Returns a SmartHome URL for the endpoint - shortcut of {@link BoschSslUtil::getBoschShcUrl()} - * + * * @param endpoint a endpoint, see https://apidocs.bosch-smarthome.com/local/index.html * @return SmartHome URL for passed endpoint */ @@ -105,15 +105,41 @@ public class BoschHttpClient extends HttpClient { } /** - * Returns a device & service URL. + * Returns a URL to get or put a service state. + *

+ * Example: + * + *

+     * https://localhost:8444/smarthome/devices/hdm:ZigBee:000d6f0016d1cdae/services/AirQualityLevel/state
+     * 
+ * * see https://apidocs.bosch-smarthome.com/local/index.html - * + * * @param serviceName the name of the service * @param deviceId the device identifier - * @return SmartHome URL for passed endpoint + * @return a URL to get or put a service state + */ + public String getServiceStateUrl(String serviceName, String deviceId) { + return this.getBoschSmartHomeUrl(String.format("devices/%s/services/%s/state", deviceId, serviceName)); + } + + /** + * Returns a URL to get general information about a service. + *

+ * Example: + * + *

+     * https://localhost:8444/smarthome/devices/hdm:ZigBee:000d6f0016d1cdae/services/BatteryLevel
+     * 
+ * + * In some cases this URL has to be used to get the service state, for example for battery levels. + * + * @param serviceName the name of the service + * @param deviceId the device identifier + * @return a URL to retrieve general service information */ public String getServiceUrl(String serviceName, String deviceId) { - return this.getBoschSmartHomeUrl(String.format("devices/%s/services/%s/state", deviceId, serviceName)); + return this.getBoschSmartHomeUrl(String.format("devices/%s/services/%s", deviceId, serviceName)); } /** @@ -177,7 +203,7 @@ public class BoschHttpClient extends HttpClient { /** * Pairs this client with the Bosch SHC. * Press pairing button on the Bosch Smart Home Controller! - * + * * @return true if pairing was successful, otherwise false * @throws InterruptedException in case of an interrupt */ @@ -228,7 +254,7 @@ public class BoschHttpClient extends HttpClient { /** * Creates a HTTP request. - * + * * @param url for the HTTP request * @param method for the HTTP request * @return created HTTP request instance @@ -239,7 +265,7 @@ public class BoschHttpClient extends HttpClient { /** * Creates a HTTP request. - * + * * @param url for the HTTP request * @param method for the HTTP request * @param content for the HTTP request @@ -265,7 +291,7 @@ public class BoschHttpClient extends HttpClient { /** * Sends a request and expects a response of the specified type. - * + * * @param request Request to send * @param responseContentClass Type of expected response * @param contentValidator Checks if the parsed response is valid diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java index a7a134ed0..a7f3e6bab 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java @@ -30,7 +30,7 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.util.ssl.SslContextFactory; 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.DeviceStatusUpdate; +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.Room; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; @@ -51,6 +51,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; +import com.google.gson.JsonElement; import com.google.gson.reflect.TypeToken; /** @@ -299,52 +300,96 @@ public class BridgeHandler extends BaseBridgeHandler { /** * Bridge callback handler for the results of long polls. * - * It will check the result and - * forward the received to the bosch thing handlers. + * It will check the results and + * forward the received states to the Bosch thing handlers. * * @param result Results from Long Polling */ private void handleLongPollResult(LongPollResult result) { - for (DeviceStatusUpdate update : result.result) { - if (update != null && update.state != null) { - logger.debug("Got update for service {} of type {}: {}", update.id, update.type, update.state); + for (DeviceServiceData deviceServiceData : result.result) { + handleDeviceServiceData(deviceServiceData); + } + } - var updateDeviceId = update.deviceId; - if (updateDeviceId == null) { - continue; - } + /** + * Processes a single long poll result. + * + * @param deviceServiceData object representing a single long poll result + */ + private void handleDeviceServiceData(@Nullable DeviceServiceData deviceServiceData) { + if (deviceServiceData != null) { + JsonElement state = obtainState(deviceServiceData); - logger.debug("Got update for device {}", updateDeviceId); + logger.debug("Got update for service {} of type {}: {}", deviceServiceData.id, deviceServiceData.type, + state); - boolean handled = false; - - Bridge bridge = this.getThing(); - for (Thing childThing : bridge.getThings()) { - // All children of this should implement BoschSHCHandler - @Nullable - ThingHandler baseHandler = childThing.getHandler(); - if (baseHandler != null && baseHandler instanceof BoschSHCHandler) { - BoschSHCHandler handler = (BoschSHCHandler) baseHandler; - @Nullable - String deviceId = handler.getBoschID(); - - handled = true; - logger.debug("Registered device: {} - looking for {}", deviceId, updateDeviceId); - - if (deviceId != null && updateDeviceId.equals(deviceId)) { - logger.debug("Found child: {} - calling processUpdate (id: {}) with {}", handler, update.id, - update.state); - handler.processUpdate(update.id, update.state); - } - } else { - logger.warn("longPoll: child handler for {} does not implement Bosch SHC handler", baseHandler); - } - } - - if (!handled) { - logger.debug("Could not find a thing for device ID: {}", updateDeviceId); - } + var updateDeviceId = deviceServiceData.deviceId; + if (updateDeviceId == null || state == null) { + return; } + + logger.debug("Got update for device {}", updateDeviceId); + + forwardStateToHandlers(deviceServiceData, state, updateDeviceId); + } + } + + /** + * Extracts the actual state object from the given {@link DeviceServiceData} instance. + *

+ * In some special cases like the BatteryLevel service the {@link DeviceServiceData} object itself + * contains the state. + * In all other cases, the state is contained in a sub-object named state. + * + * @param deviceServiceData the {@link DeviceServiceData} object from which the state should be obtained + * @return the state sub-object or the {@link DeviceServiceData} object itself + */ + @Nullable + private JsonElement obtainState(DeviceServiceData deviceServiceData) { + // the battery level service receives no individual state object but rather requires the DeviceServiceData + // structure + if ("BatteryLevel".equals(deviceServiceData.id)) { + return gson.toJsonTree(deviceServiceData); + } + + return deviceServiceData.state; + } + + /** + * Tries to find handlers for the device with the given ID and forwards the received state to the handlers. + * + * @param deviceServiceData object representing updates received in long poll results + * @param state the received state object as JSON element + * @param updateDeviceId the ID of the device for which the state update was received + */ + private void forwardStateToHandlers(DeviceServiceData deviceServiceData, JsonElement state, String updateDeviceId) { + boolean handled = false; + + Bridge bridge = this.getThing(); + for (Thing childThing : bridge.getThings()) { + // All children of this should implement BoschSHCHandler + @Nullable + ThingHandler baseHandler = childThing.getHandler(); + if (baseHandler != null && baseHandler instanceof BoschSHCHandler) { + BoschSHCHandler handler = (BoschSHCHandler) baseHandler; + @Nullable + String deviceId = handler.getBoschID(); + + handled = true; + logger.debug("Registered device: {} - looking for {}", deviceId, updateDeviceId); + + if (deviceId != null && updateDeviceId.equals(deviceId)) { + logger.debug("Found child: {} - calling processUpdate (id: {}) with {}", handler, + deviceServiceData.id, state); + handler.processUpdate(deviceServiceData.id, state); + } + } else { + logger.warn("longPoll: child handler for {} does not implement Bosch SHC handler", baseHandler); + } + } + + if (!handled) { + logger.debug("Could not find a thing for device ID: {}", updateDeviceId); } } @@ -447,7 +492,7 @@ public class BridgeHandler extends BaseBridgeHandler { * Query the Bosch Smart Home Controller for the state of the given device. *

* The URL used for retrieving the state has the following structure: - * + * *

      * https://{IP}:8444/smarthome/devices/{deviceId}/services/{serviceName}/state
      * 
@@ -470,14 +515,14 @@ public class BridgeHandler extends BaseBridgeHandler { return null; } - String url = httpClient.getServiceUrl(stateName, deviceId); + String url = httpClient.getServiceStateUrl(stateName, deviceId); logger.debug("getState(): Requesting \"{}\" from Bosch: {} via {}", stateName, deviceId, url); return getState(httpClient, url, stateClass); } /** * Queries the Bosch Smart Home Controller for the state using an explicit endpoint. - * + * * @param Type to which the resulting JSON should be deserialized to * @param endpoint The destination endpoint part of the URL * @param stateClass Class to convert the resulting JSON to @@ -503,7 +548,7 @@ public class BridgeHandler extends BaseBridgeHandler { /** * Sends a HTTP GET request in order to retrieve a state from the Bosch Smart Home Controller. - * + * * @param Type to which the resulting JSON should be deserialized to * @param httpClient HTTP client used for sending the request * @param url URL at which the state should be retrieved @@ -566,7 +611,7 @@ public class BridgeHandler extends BaseBridgeHandler { } // Create request - String url = httpClient.getServiceUrl(serviceName, deviceId); + String url = httpClient.getServiceStateUrl(serviceName, deviceId); Request request = httpClient.createRequest(url, PUT, state); // Send request @@ -575,7 +620,7 @@ public class BridgeHandler extends BaseBridgeHandler { /** * Sends a HTTP POST request without a request body to the given endpoint. - * + * * @param endpoint The destination endpoint part of the URL * @return the HTTP response * @throws InterruptedException @@ -589,7 +634,7 @@ public class BridgeHandler extends BaseBridgeHandler { /** * Sends a HTTP POST request with a request body to the given endpoint. - * + * * @param Type of the request * @param endpoint The destination endpoint part of the URL * @param requestBody object representing the request body to be sent, may be null @@ -611,4 +656,18 @@ public class BridgeHandler extends BaseBridgeHandler { Request request = httpClient.createRequest(url, POST, requestBody); return request.send(); } + + public @Nullable DeviceServiceData getServiceData(String deviceId, String serviceName) + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + @Nullable + BoschHttpClient httpClient = this.httpClient; + if (httpClient == null) { + logger.warn("HttpClient not initialized"); + return null; + } + + String url = httpClient.getServiceUrl(serviceName, deviceId); + logger.debug("getState(): Requesting \"{}\" from Bosch: {} via {}", serviceName, deviceId, url); + return getState(httpClient, url, DeviceServiceData.class); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceStatusUpdate.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceServiceData.java similarity index 68% rename from bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceStatusUpdate.java rename to bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceServiceData.java index 0da4e2434..82f5d495e 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceStatusUpdate.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceServiceData.java @@ -13,9 +13,9 @@ package org.openhab.binding.boschshc.internal.devices.bridge.dto; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState; import com.google.gson.JsonElement; -import com.google.gson.annotations.SerializedName; /** * Represents a device status update as represented by the Smart Home @@ -24,18 +24,13 @@ import com.google.gson.annotations.SerializedName; * @author Stefan Kästle - Initial contribution * @author Christian Oeing - refactorings of e.g. server registration */ -public class DeviceStatusUpdate { +public class DeviceServiceData extends BoschSHCServiceState { + /** * Url path of the service the update came from. */ public String path; - /** - * The type of message. - */ - @SerializedName("@type") - public String type; - /** * Name of service the update came from. */ @@ -44,15 +39,26 @@ public class DeviceStatusUpdate { /** * Current state of device. Serialized as JSON. */ - public JsonElement state; + public @Nullable JsonElement state; /** * Id of device the update is for. */ public @Nullable String deviceId; + /** + * An optional object containing information about device faults. + *

+ * Example: low battery warnings are stored as faults with category WARNING + */ + public @Nullable Faults faults; + + public DeviceServiceData() { + super("DeviceServiceData"); + } + @Override public String toString() { - return this.deviceId + "state: " + this.type; + return this.deviceId + " state: " + this.type; } } diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/Fault.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/Fault.java new file mode 100644 index 000000000..070c8f806 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/Fault.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2010-2022 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; + +/** + * A fault entry containing a category and a type. + *

+ * Example JSON: + * + *

+ * {
+ *   "type":"LOW_BATTERY",
+ *   "category":"WARNING"
+ * }
+ * 
+ * + * @author David Pace - Initial contribution + * + */ +public class Fault { + public String type; + public String category; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/Faults.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/Faults.java new file mode 100644 index 000000000..3e3df0d0c --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/Faults.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2010-2022 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; + +import java.util.List; + +/** + * A container object for faults that can be contained in {@link DeviceServiceData}. + *

+ * Example JSON: + * + *

+ * "faults": {
+ *   "entries": [
+ *     {
+ *       "type":"LOW_BATTERY",
+ *       "category":"WARNING"
+ *     }
+ *   ]
+   }
+ * 
+ * + * @author David Pace - Initial contribution + * + */ +public class Faults { + + public List entries; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResult.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResult.java index ec56a13ca..ea830b35f 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResult.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResult.java @@ -35,6 +35,6 @@ public class LongPollResult { * ],"jsonrpc":"2.0"} */ - public ArrayList result; + public ArrayList result; public String jsonrpc; } diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandler.java index f4a088cec..64b16712e 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandler.java @@ -17,7 +17,7 @@ import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConst import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; +import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandler; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.latestmotion.LatestMotionService; import org.openhab.binding.boschshc.internal.services.latestmotion.dto.LatestMotionServiceState; @@ -32,7 +32,7 @@ import org.openhab.core.thing.Thing; * @author Christian Oeing - Use service instead of custom logic */ @NonNullByDefault -public class MotionDetectorHandler extends BoschSHCDeviceHandler { +public class MotionDetectorHandler extends AbstractBatteryPoweredDeviceHandler { public MotionDetectorHandler(Thing thing) { super(thing); diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandler.java index ff00373be..b0f956a65 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandler.java @@ -12,14 +12,12 @@ */ package org.openhab.binding.boschshc.internal.devices.thermostat; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_CHILD_LOCK; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_TEMPERATURE; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_VALVE_TAPPET_POSITION; +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.*; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; +import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandler; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.childlock.ChildLockService; import org.openhab.binding.boschshc.internal.services.childlock.dto.ChildLockServiceState; @@ -33,11 +31,11 @@ import org.openhab.core.types.Command; /** * Handler for a thermostat device. - * + * * @author Christian Oeing - Initial contribution */ @NonNullByDefault -public final class ThermostatHandler extends BoschSHCDeviceHandler { +public final class ThermostatHandler extends AbstractBatteryPoweredDeviceHandler { private ChildLockService childLockService; @@ -48,6 +46,8 @@ public final class ThermostatHandler extends BoschSHCDeviceHandler { @Override protected void initializeServices() throws BoschSHCException { + super.initializeServices(); + this.createService(TemperatureLevelService::new, this::updateChannels, List.of(CHANNEL_TEMPERATURE)); this.createService(ValveTappetService::new, this::updateChannels, List.of(CHANNEL_VALVE_TAPPET_POSITION)); this.registerService(this.childLockService, this::updateChannels, List.of(CHANNEL_CHILD_LOCK)); @@ -67,7 +67,7 @@ public final class ThermostatHandler extends BoschSHCDeviceHandler { /** * Updates the channels which are linked to the {@link TemperatureLevelService} * of the device. - * + * * @param state Current state of {@link TemperatureLevelService}. */ private void updateChannels(TemperatureLevelServiceState state) { @@ -77,7 +77,7 @@ public final class ThermostatHandler extends BoschSHCDeviceHandler { /** * Updates the channels which are linked to the {@link ValveTappetService} of * the device. - * + * * @param state Current state of {@link ValveTappetService}. */ private void updateChannels(ValveTappetServiceState state) { @@ -87,7 +87,7 @@ public final class ThermostatHandler extends BoschSHCDeviceHandler { /** * Updates the channels which are linked to the {@link ChildLockService} of the * device. - * + * * @param state Current state of {@link ChildLockService}. */ private void updateChannels(ChildLockServiceState state) { diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandler.java index d155c1896..a4b0f3ac8 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandler.java @@ -12,14 +12,7 @@ */ package org.openhab.binding.boschshc.internal.devices.twinguard; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_AIR_DESCRIPTION; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_COMBINED_RATING; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_HUMIDITY; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_HUMIDITY_RATING; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_PURITY; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_PURITY_RATING; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_TEMPERATURE; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_TEMPERATURE_RATING; +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.*; import java.util.List; @@ -27,7 +20,7 @@ import javax.measure.quantity.Dimensionless; import javax.measure.quantity.Temperature; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; +import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandler; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.airqualitylevel.AirQualityLevelService; import org.openhab.binding.boschshc.internal.services.airqualitylevel.dto.AirQualityLevelServiceState; @@ -44,7 +37,7 @@ import org.openhab.core.thing.Thing; * @author Christian Oeing - Use service instead of custom logic */ @NonNullByDefault -public class TwinguardHandler extends BoschSHCDeviceHandler { +public class TwinguardHandler extends AbstractBatteryPoweredDeviceHandler { public TwinguardHandler(Thing thing) { super(thing); diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandler.java index 411c52ebb..96de7355b 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandler.java @@ -12,13 +12,12 @@ */ package org.openhab.binding.boschshc.internal.devices.wallthermostat; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_HUMIDITY; -import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_TEMPERATURE; +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.*; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; +import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandler; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.humiditylevel.HumidityLevelService; import org.openhab.binding.boschshc.internal.services.humiditylevel.dto.HumidityLevelServiceState; @@ -28,11 +27,11 @@ import org.openhab.core.thing.Thing; /** * Handler for a wall thermostat device. - * + * * @author Christian Oeing - Initial contribution */ @NonNullByDefault -public final class WallThermostatHandler extends BoschSHCDeviceHandler { +public final class WallThermostatHandler extends AbstractBatteryPoweredDeviceHandler { public WallThermostatHandler(Thing thing) { super(thing); @@ -40,13 +39,15 @@ public final class WallThermostatHandler extends BoschSHCDeviceHandler { @Override protected void initializeServices() throws BoschSHCException { + super.initializeServices(); + this.createService(TemperatureLevelService::new, this::updateChannels, List.of(CHANNEL_TEMPERATURE)); this.createService(HumidityLevelService::new, this::updateChannels, List.of(CHANNEL_HUMIDITY)); } /** * Updates the channels which are linked to the {@link TemperatureLevelService} of the device. - * + * * @param state Current state of {@link TemperatureLevelService}. */ private void updateChannels(TemperatureLevelServiceState state) { @@ -55,7 +56,7 @@ public final class WallThermostatHandler extends BoschSHCDeviceHandler { /** * Updates the channels which are linked to the {@link HumidityLevelService} of the device. - * + * * @param state Current state of {@link HumidityLevelService}. */ private void updateChannels(HumidityLevelServiceState state) { diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandler.java index 08a34099c..1e79dea1e 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandler.java @@ -17,7 +17,7 @@ import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConst import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; +import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandler; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.shuttercontact.ShutterContactService; import org.openhab.binding.boschshc.internal.services.shuttercontact.ShutterContactState; @@ -32,7 +32,7 @@ import org.openhab.core.types.State; * @author Stefan Kästle - Initial contribution */ @NonNullByDefault -public class WindowContactHandler extends BoschSHCDeviceHandler { +public class WindowContactHandler extends AbstractBatteryPoweredDeviceHandler { public WindowContactHandler(Thing thing) { super(thing); @@ -40,6 +40,8 @@ public class WindowContactHandler extends BoschSHCDeviceHandler { @Override protected void initializeServices() throws BoschSHCException { + super.initializeServices(); + this.createService(ShutterContactService::new, this::updateChannels, List.of(CHANNEL_CONTACT)); } diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/BoschSHCService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/BoschSHCService.java index d3a0db77d..f0b7bf768 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/BoschSHCService.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/BoschSHCService.java @@ -32,17 +32,17 @@ import com.google.gson.JsonElement; * same endpoint. *

* The endpoints of this service have the following URL structure: - * + * *

  * https://{IP}:8444/smarthome/devices/{deviceId}/services/{serviceName}/state
  * 
- * + * * The HTTP client of the bridge will use GET requests to retrieve the state and PUT requests * to set the state. *

* The services of the devices and their official APIs can be found * here. - * + * * @author Christian Oeing - Initial contribution * @author David Pace - Service abstraction */ @@ -63,7 +63,7 @@ public abstract class BoschSHCService exten /** * Constructor - * + * * @param serviceName Unique name of the service. * @param stateClass State class that this service uses for data transfers * from/to the device. @@ -75,7 +75,7 @@ public abstract class BoschSHCService exten /** * Initializes the service - * + * * @param bridgeHandler Bridge to use for communication from/to the device * @param deviceId Id of device this service is for * @param stateUpdateListener Function to call when a state update was received @@ -89,7 +89,7 @@ public abstract class BoschSHCService exten /** * Returns the class of the state this service provides. - * + * * @return Class of the state this service provides. */ public Class getStateClass() { @@ -98,7 +98,7 @@ public abstract class BoschSHCService exten /** * Requests the current state of the service and updates it. - * + * * @throws ExecutionException * @throws TimeoutException * @throws InterruptedException @@ -114,7 +114,7 @@ public abstract class BoschSHCService exten /** * Requests the current state of the device with the specified id. - * + * * @return Current state of the device. * @throws ExecutionException * @throws TimeoutException @@ -136,7 +136,7 @@ public abstract class BoschSHCService exten /** * Sets the state of the device with the specified id. - * + * * @param state State to set. * @throws InterruptedException * @throws ExecutionException @@ -156,10 +156,10 @@ public abstract class BoschSHCService exten /** * A state update was received from the bridge - * + * * @param stateData Current state of service. Serialized as JSON. */ - public void onStateUpdate(JsonElement stateData) { + public void onStateUpdate(@Nullable JsonElement stateData) { @Nullable TState state = BoschSHCServiceState.fromJson(stateData, this.stateClass); if (state == null) { @@ -171,7 +171,7 @@ public abstract class BoschSHCService exten /** * A state update was received from the bridge. - * + * * @param state Current state of service as an instance of the state class. */ private void onStateUpdate(TState state) { @@ -184,7 +184,7 @@ public abstract class BoschSHCService exten /** * Allows a service to handle a command and create a new state out of it. * The new state still has to be set via setState. - * + * * @param command Command to handle * @throws BoschSHCException If service can not handle command */ diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevel.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevel.java new file mode 100644 index 000000000..de5803260 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevel.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2010-2022 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.services.batterylevel; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.Fault; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.Faults; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * Possible battery levels. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public enum BatteryLevel { + OK, + LOW_BATTERY, + CRITICAL_LOW, + CRITICALLY_LOW_BATTERY, + NOT_AVAILABLE; + + /** + * Derives a battery level by analyzing the fault elements in the given device service data. + *

+ * Note that no fault elements are present when the battery level is OK. + * + * @param deviceServiceData a device service data model + * @return the derived battery level + */ + public static BatteryLevel fromDeviceServiceData(DeviceServiceData deviceServiceData) { + Faults faults = deviceServiceData.faults; + if (faults == null || faults.entries == null || faults.entries.isEmpty()) { + return OK; + } + + for (Fault faultEntry : faults.entries) { + if ("warning".equalsIgnoreCase(faultEntry.category)) { + BatteryLevel batteryLevelState = BatteryLevel.get(faultEntry.type); + if (batteryLevelState != null) { + return batteryLevelState; + } + } + } + + return OK; + } + + /** + * Returns the corresponding battery level for the given string or null if no state matches. + * + * @param identifier the battery level identifier + * + * @return the matching battery level or null + */ + public static @Nullable BatteryLevel get(String identifier) { + for (BatteryLevel batteryLevelState : values()) { + if (batteryLevelState.toString().equalsIgnoreCase(identifier)) { + return batteryLevelState; + } + } + + return null; + } + + /** + * Transforms a Bosch-specific battery level to a percentage for the system.battery-level channel. + * + * @return a percentage between 0 and 100 as integer + */ + public State toState() { + switch (this) { + case LOW_BATTERY: + return new DecimalType(10); + case CRITICAL_LOW: + case CRITICALLY_LOW_BATTERY: + return new DecimalType(1); + case NOT_AVAILABLE: + return UnDefType.UNDEF; + default: + return new DecimalType(100); + } + } + + /** + * Transforms a Bosch-specific battery level to an ON/OFF state for the + * system.low-battery channel. + *

+ * If the result is ON, the battery is low; if the result is OFF the battery level is OK. + * + * @return + */ + public OnOffType toLowBatteryState() { + switch (this) { + case LOW_BATTERY: + case CRITICAL_LOW: + case CRITICALLY_LOW_BATTERY: + return OnOffType.ON; + default: + return OnOffType.OFF; + } + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelService.java new file mode 100644 index 000000000..a4a6b4dd8 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelService.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2022 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.services.batterylevel; + +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.bridge.BridgeHandler; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.BoschSHCService; + +/** + * Service to retrieve battery levels. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class BatteryLevelService extends BoschSHCService { + + public BatteryLevelService() { + super("BatteryLevel", DeviceServiceData.class); + } + + @Override + public @Nullable DeviceServiceData getState() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + + String deviceId = getDeviceId(); + if (deviceId == null) { + return null; + } + + BridgeHandler bridgeHandler = getBridgeHandler(); + if (bridgeHandler == null) { + return null; + } + return bridgeHandler.getServiceData(deviceId, getServiceName()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceState.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceState.java index 81d37ae92..190b7ceb7 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceState.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceState.java @@ -22,7 +22,7 @@ import com.google.gson.annotations.SerializedName; /** * Base Bosch Smart Home Controller service state. - * + * * @author Christian Oeing - Initial contribution */ public class BoschSHCServiceState { @@ -40,7 +40,7 @@ public class BoschSHCServiceState { private @Nullable String stateType = null; @SerializedName("@type") - private final String type; + public final String type; protected BoschSHCServiceState(String type) { this.type = type; diff --git a/bundles/org.openhab.binding.boschshc/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.boschshc/src/main/resources/OH-INF/thing/thing-types.xml index 12c1dda89..5f3095f76 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.boschshc/src/main/resources/OH-INF/thing/thing-types.xml @@ -53,7 +53,7 @@ - + The Twinguard smoke detector warns you in case of fire and constantly monitors the air. @@ -65,6 +65,8 @@ + + @@ -81,6 +83,8 @@ + + @@ -98,6 +102,8 @@ + + @@ -132,6 +138,8 @@ + + @@ -167,6 +175,8 @@ + + diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java new file mode 100644 index 000000000..6f6707e4d --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2010-2022 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; + +import static org.mockito.Mockito.verify; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.UnDefType; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Abstract test implementation for battery-powered devices. + * + * @author David Pace - Initial contribution + * + * @param type of the battery-powered device to be tested + */ +@NonNullByDefault +public abstract class AbstractBatteryPoweredDeviceHandlerTest + extends AbstractBoschSHCDeviceHandlerTest { + + @Test + public void testProcessUpdate_BatteryLevel_LowBattery() { + JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" + + " \"faults\":{ \n" + " \"entries\":[\n" + " {\n" + + " \"type\":\"LOW_BATTERY\",\n" + " \"category\":\"WARNING\"\n" + " }\n" + + " ]\n" + " }\n" + "}"); + getFixture().processUpdate("BatteryLevel", deviceServiceData); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), + new DecimalType(10)); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.ON); + } + + @Test + public void testProcessUpdate_BatteryLevel_CriticalLow() { + JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" + + " \"faults\":{ \n" + " \"entries\":[\n" + " {\n" + + " \"type\":\"CRITICAL_LOW\",\n" + " \"category\":\"WARNING\"\n" + + " }\n" + " ]\n" + " }\n" + "}"); + getFixture().processUpdate("BatteryLevel", deviceServiceData); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), + new DecimalType(1)); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.ON); + } + + @Test + public void testProcessUpdate_BatteryLevel_CriticallyLowBattery() { + JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" + + " \"faults\":{ \n" + " \"entries\":[\n" + " {\n" + + " \"type\":\"CRITICALLY_LOW_BATTERY\",\n" + " \"category\":\"WARNING\"\n" + + " }\n" + " ]\n" + " }\n" + "}"); + getFixture().processUpdate("BatteryLevel", deviceServiceData); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), + new DecimalType(1)); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.ON); + } + + @Test + public void testProcessUpdate_BatteryLevel_OK() { + JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\" }"); + getFixture().processUpdate("BatteryLevel", deviceServiceData); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), + new DecimalType(100)); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF); + } + + @Test + public void testProcessUpdate_BatteryLevel_NotAvailable() { + JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" + + " \"faults\":{ \n" + " \"entries\":[\n" + " {\n" + + " \"type\":\"NOT_AVAILABLE\",\n" + " \"category\":\"WARNING\"\n" + + " }\n" + " ]\n" + " }\n" + "}"); + getFixture().processUpdate("BatteryLevel", deviceServiceData); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), UnDefType.UNDEF); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java index 38432d552..f3bd2ecda 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java @@ -31,7 +31,6 @@ import org.openhab.binding.boschshc.internal.services.powerswitch.dto.PowerSwitc import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.thing.ChannelUID; -import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import com.google.gson.JsonElement; @@ -60,8 +59,6 @@ public abstract class AbstractPowerSwitchHandlerTest { @BeforeEach public void beforeEach() { fixture = createFixture(); + lenient().when(thing.getUID()).thenReturn(getThingUID()); when(thing.getBridgeUID()).thenReturn(new ThingUID("boschshc", "shc", "myBridgeUID")); when(callback.getBridge(any())).thenReturn(bridge); fixture.setCallback(callback); @@ -72,6 +74,12 @@ public abstract class AbstractSHCHandlerTest { return fixture; } + protected ThingUID getThingUID() { + return new ThingUID(getThingTypeUID(), "abcdef"); + } + + protected abstract ThingTypeUID getThingTypeUID(); + protected Configuration getConfiguration() { return new Configuration(); } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BoschHttpClientTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BoschHttpClientTest.java index 9877caeb6..48ab25dda 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BoschHttpClientTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BoschHttpClientTest.java @@ -71,10 +71,16 @@ class BoschHttpClientTest { @Test void getServiceUrl() { - assertEquals("https://127.0.0.1:8444/smarthome/devices/testDevice/services/testService/state", + assertEquals("https://127.0.0.1:8444/smarthome/devices/testDevice/services/testService", httpClient.getServiceUrl("testService", "testDevice")); } + @Test + void getServiceStateUrl() { + assertEquals("https://127.0.0.1:8444/smarthome/devices/testDevice/services/testService/state", + httpClient.getServiceStateUrl("testService", "testDevice")); + } + @Test void isAccessPossible() throws InterruptedException { assertFalse(httpClient.isAccessPossible()); diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java new file mode 100644 index 000000000..e35d145a7 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2022 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.motiondetector; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.thing.ThingTypeUID; + +/** + * Unit Tests for {@link MotionDetectorHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class MotionDetectorHandlerTest extends AbstractBatteryPoweredDeviceHandlerTest { + + @Override + protected MotionDetectorHandler createFixture() { + return new MotionDetectorHandler(getThing()); + } + + @Override + protected String getDeviceID() { + return "hdm:ZigBee:000d6f0012fd2571"; + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_MOTION_DETECTOR; + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandlerTest.java new file mode 100644 index 000000000..73b9b201f --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandlerTest.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2022 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.thermostat; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.thing.ThingTypeUID; + +/** + * Unit Tests for {@link ThermostatHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class ThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTest { + + @Override + protected ThermostatHandler createFixture() { + return new ThermostatHandler(getThing()); + } + + @Override + protected String getDeviceID() { + return "hdm:ZigBee:000d6f0017f1ace2"; + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_THERMOSTAT; + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java new file mode 100644 index 000000000..0b22f87c0 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2022 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.twinguard; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.thing.ThingTypeUID; + +/** + * Unit Tests for {@link TwinguardHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class TwinguardHandlerTest extends AbstractBatteryPoweredDeviceHandlerTest { + + @Override + protected TwinguardHandler createFixture() { + return new TwinguardHandler(getThing()); + } + + @Override + protected String getDeviceID() { + return "hdm:ZigBee:000d6f0016d1a193"; + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_TWINGUARD; + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java new file mode 100644 index 000000000..8f1c2a67b --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2022 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.wallthermostat; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.thing.ThingTypeUID; + +/** + * Unit Tests for {@link WallThermostatHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class WallThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTest { + + @Override + protected WallThermostatHandler createFixture() { + return new WallThermostatHandler(getThing()); + } + + @Override + protected String getDeviceID() { + return "hdm:ZigBee:000d6f0016d1a193"; + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_WALL_THERMOSTAT; + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java new file mode 100644 index 000000000..9acd2cc1f --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2022 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.windowcontact; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.thing.ThingTypeUID; + +/** + * Unit Tests for {@link WindowContactHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class WindowContactHandlerTest extends AbstractBatteryPoweredDeviceHandlerTest { + + @Override + protected WindowContactHandler createFixture() { + return new WindowContactHandler(getThing()); + } + + @Override + protected String getDeviceID() { + return "hdm:HomeMaticIP:3014D711A000009D545DEB39D"; + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_WINDOW_CONTACT; + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java new file mode 100644 index 000000000..b788074d3 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2010-2022 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.services.batterylevel; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; + +import org.junit.jupiter.api.Test; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.Fault; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.Faults; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.types.UnDefType; + +/** + * Unit tests for {@link BatteryLevel}. + * + * @author David Pace - Initial contribution + * + */ +class BatteryLevelTest { + + @Test + void testGet() { + assertSame(BatteryLevel.OK, BatteryLevel.get("OK")); + assertSame(BatteryLevel.OK, BatteryLevel.get("ok")); + assertSame(BatteryLevel.LOW_BATTERY, BatteryLevel.get("LOW_BATTERY")); + assertSame(BatteryLevel.LOW_BATTERY, BatteryLevel.get("low_battery")); + assertSame(BatteryLevel.CRITICAL_LOW, BatteryLevel.get("CRITICAL_LOW")); + assertSame(BatteryLevel.CRITICAL_LOW, BatteryLevel.get("critical_low")); + assertSame(BatteryLevel.CRITICALLY_LOW_BATTERY, BatteryLevel.get("CRITICALLY_LOW_BATTERY")); + assertSame(BatteryLevel.CRITICALLY_LOW_BATTERY, BatteryLevel.get("critically_low_battery")); + assertSame(BatteryLevel.NOT_AVAILABLE, BatteryLevel.get("NOT_AVAILABLE")); + assertSame(BatteryLevel.NOT_AVAILABLE, BatteryLevel.get("not_available")); + assertNull(BatteryLevel.get("foo")); + } + + @Test + void testFromDeviceServiceData() { + DeviceServiceData deviceServiceData = new DeviceServiceData(); + assertSame(BatteryLevel.OK, BatteryLevel.fromDeviceServiceData(deviceServiceData)); + + Faults faults = new Faults(); + deviceServiceData.faults = faults; + assertSame(BatteryLevel.OK, BatteryLevel.fromDeviceServiceData(deviceServiceData)); + + ArrayList entries = new ArrayList<>(); + faults.entries = entries; + assertSame(BatteryLevel.OK, BatteryLevel.fromDeviceServiceData(deviceServiceData)); + + Fault fault = new Fault(); + entries.add(fault); + assertSame(BatteryLevel.OK, BatteryLevel.fromDeviceServiceData(deviceServiceData)); + + fault.category = "WARNING"; + fault.type = "LOW_BATTERY"; + assertSame(BatteryLevel.LOW_BATTERY, BatteryLevel.fromDeviceServiceData(deviceServiceData)); + + fault.type = "CRITICAL_LOW"; + assertSame(BatteryLevel.CRITICAL_LOW, BatteryLevel.fromDeviceServiceData(deviceServiceData)); + + fault.type = "CRITICALLY_LOW_BATTERY"; + assertSame(BatteryLevel.CRITICALLY_LOW_BATTERY, BatteryLevel.fromDeviceServiceData(deviceServiceData)); + + fault.type = "FOO"; + assertSame(BatteryLevel.OK, BatteryLevel.fromDeviceServiceData(deviceServiceData)); + } + + @Test + void testToState() { + assertEquals(new DecimalType(100), BatteryLevel.OK.toState()); + assertEquals(new DecimalType(10), BatteryLevel.LOW_BATTERY.toState()); + assertEquals(new DecimalType(1), BatteryLevel.CRITICAL_LOW.toState()); + assertEquals(new DecimalType(1), BatteryLevel.CRITICALLY_LOW_BATTERY.toState()); + assertEquals(UnDefType.UNDEF, BatteryLevel.NOT_AVAILABLE.toState()); + } + + @Test + void testToLowBatteryState() { + assertEquals(OnOffType.OFF, BatteryLevel.OK.toLowBatteryState()); + assertEquals(OnOffType.ON, BatteryLevel.LOW_BATTERY.toLowBatteryState()); + assertEquals(OnOffType.ON, BatteryLevel.CRITICAL_LOW.toLowBatteryState()); + assertEquals(OnOffType.ON, BatteryLevel.CRITICALLY_LOW_BATTERY.toLowBatteryState()); + assertEquals(OnOffType.OFF, BatteryLevel.NOT_AVAILABLE.toLowBatteryState()); + } +}