From c16e9df7a0fc3c81a11ac700ab4910c8c29f057f Mon Sep 17 00:00:00 2001 From: David Pace Date: Sun, 22 May 2022 21:45:55 +0200 Subject: [PATCH] [boschshc] Support for Bosch Intrusion Detection System (#12700) (#12758) * [boschshc] Support for Bosch Intrusion Detection System (#12700) * Add thing and channel definitions for intrusion detection system * Extract handler abstraction for devices with non-configurable device IDs * Extract service abstractions for write-only services (i.e. services that can not receive states from the bridge) * Add handler, services and DTO implementations for the intrusion detection system * Add detailed Javadocs * Generalize mechanism to actively fetch initial states for certain services * Add unit tests * Add documentation closes #12700 * [boschshc] Documentation and formatting enhancements Signed-off-by: David Pace --- .../org.openhab.binding.boschshc/README.md | 17 ++ .../devices/BoschSHCBindingConstants.java | 13 ++ .../devices/BoschSHCDeviceHandler.java | 95 +++++++++ .../internal/devices/BoschSHCHandler.java | 187 ++++++++++++++---- .../devices/BoschSHCHandlerFactory.java | 5 +- .../devices/bridge/BridgeHandler.java | 119 +++++++++-- .../devices/camera/CameraHandler.java | 64 +----- .../climatecontrol/ClimateControlHandler.java | 4 +- .../intrusion/IntrusionDetectionHandler.java | 159 +++++++++++++++ .../lightcontrol/LightControlHandler.java | 4 +- .../motiondetector/MotionDetectorHandler.java | 4 +- .../shuttercontrol/ShutterControlHandler.java | 4 +- .../devices/thermostat/ThermostatHandler.java | 4 +- .../devices/twinguard/TwinguardHandler.java | 4 +- .../wallthermostat/WallThermostatHandler.java | 4 +- .../windowcontact/WindowContactHandler.java | 4 +- .../services/AbstractBoschSHCService.java | 72 +++++++ .../AbstractStatelessBoschSHCService.java | 69 +++++++ ...atelessBoschSHCServiceWithRequestBody.java | 60 ++++++ .../internal/services/BoschSHCService.java | 61 +++--- .../services/BoschSHCSystemService.java | 80 ++++++++ ...IntrusionDetectionControlStateService.java | 31 +++ .../IntrusionDetectionSystemStateService.java | 33 ++++ .../intrusion/SurveillanceAlarmService.java | 31 +++ .../actions/arm/ArmActionService.java | 32 +++ .../actions/arm/dto/ArmActionRequest.java | 33 ++++ .../actions/disarm/DisarmActionService.java | 33 ++++ .../actions/mute/MuteActionService.java | 33 ++++ .../dto/ActiveConfigurationProfileData.java | 27 +++ .../services/intrusion/dto/AlarmState.java | 30 +++ .../intrusion/dto/AlarmStateData.java | 47 +++++ .../services/intrusion/dto/ArmingState.java | 28 +++ .../intrusion/dto/ArmingStateData.java | 37 ++++ .../dto/IntrusionDetectionControlState.java | 70 +++++++ .../dto/IntrusionDetectionSystemState.java | 70 +++++++ .../intrusion/dto/SurveillanceAlarmState.java | 56 ++++++ .../dto/SystemAvailabilityStateData.java | 34 ++++ .../resources/OH-INF/thing/thing-types.xml | 83 ++++++++ .../devices/bridge/BridgeHandlerTest.java | 72 +++++++ ...usionDetectionControlStateServiceTest.java | 111 +++++++++++ ...rusionDetectionSystemStateServiceTest.java | 68 +++++++ 41 files changed, 1828 insertions(+), 164 deletions(-) create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCDeviceHandler.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandler.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractBoschSHCService.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractStatelessBoschSHCService.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractStatelessBoschSHCServiceWithRequestBody.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/BoschSHCSystemService.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateService.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateService.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/SurveillanceAlarmService.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/arm/ArmActionService.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/arm/dto/ArmActionRequest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/disarm/DisarmActionService.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/mute/MuteActionService.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ActiveConfigurationProfileData.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/AlarmState.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/AlarmStateData.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ArmingState.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ArmingStateData.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/IntrusionDetectionControlState.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/IntrusionDetectionSystemState.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/SurveillanceAlarmState.java create mode 100644 bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/SystemAvailabilityStateData.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java diff --git a/bundles/org.openhab.binding.boschshc/README.md b/bundles/org.openhab.binding.boschshc/README.md index d8dbbe840..9331afa9f 100644 --- a/bundles/org.openhab.binding.boschshc/README.md +++ b/bundles/org.openhab.binding.boschshc/README.md @@ -14,6 +14,7 @@ Binding for the Bosch Smart Home. - [Wall Thermostat](#wall-thermostat) - [Security Camera 360](#security-camera-360) - [Security Camera Eyes](#security-camera-eyes) + - [Intrusion Detection System](#intrusion-detection-system) - [Limitations](#limitations) - [Discovery](#discovery) - [Bridge Configuration](#bridge-configuration) @@ -138,6 +139,22 @@ Outdoor security camera with motion detection and light. | privacy-mode | Switch | ☑ | If privacy mode is enabled, the camera is disabled and vice versa. | | camera-notification | Switch | ☑ | Enables or disables notifications for the camera. | +### Intrusion Detection System + +Allows to retrieve notifications in case of intrusions. The system can be armed and disarmed and alarms can be muted. + +**Thing Type ID**: `intrusion-detection-system` + +| Channel Type ID | Item Type | Writable | Description | +| ---------------------------- | -------------------- | :------: | -------------------------------------------------------------- | +| system-availability | Switch | ☐ | Indicates whether the intrusion detection system is available. | +| arming-state | String | ☐ | Read-only channel to retrieve the current arming state. Possible values are `SYSTEM_ARMING`, `SYSTEM_ARMED` and `SYSTEM_DISARMED`. | +| alarm-state | String | ☐ | Read-only channel to retrieve the current alarm state. Possible values are `ALARM_OFF`, `PRE_ALARM`, `ALARM_ON`, `ALARM_MUTED` and `UNKNOWN`. | +| active-configuration-profile | String | ☐ | The name of the active configuration profile used for the intrusion detection system. | +| arm-action | String | ☑ | Arms the intrusion detection system using the given profile ID (default is "0"). | +| disarm-action | Switch | ☑ | Disarms the intrusion detection system when an ON command is received. | +| mute-action | Switch | ☑ | Mutes the alarm when an ON command is received. | + ## Limitations - Discovery of Things 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 a942c5205..6b3cf4e3e 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 @@ -22,6 +22,7 @@ import org.openhab.core.thing.ThingTypeUID; * @author Stefan Kästle - Initial contribution * @author Christian Oeing - added Shutter Control, ThermostatHandler * @author Christian Oeing - Added WallThermostatHandler + * @author David Pace - Added cameras and intrusion detection system */ @NonNullByDefault public class BoschSHCBindingConstants { @@ -41,6 +42,8 @@ public class BoschSHCBindingConstants { public static final ThingTypeUID THING_TYPE_WALL_THERMOSTAT = new ThingTypeUID(BINDING_ID, "wall-thermostat"); public static final ThingTypeUID THING_TYPE_CAMERA_360 = new ThingTypeUID(BINDING_ID, "security-camera-360"); public static final ThingTypeUID THING_TYPE_CAMERA_EYES = new ThingTypeUID(BINDING_ID, "security-camera-eyes"); + public static final ThingTypeUID THING_TYPE_INTRUSION_DETECTION_SYSTEM = new ThingTypeUID(BINDING_ID, + "intrusion-detection-system"); // List of all Channel IDs // Auto-generated from thing-types.xml via script, don't modify @@ -63,4 +66,14 @@ public class BoschSHCBindingConstants { public static final String CHANNEL_CHILD_LOCK = "child-lock"; public static final String CHANNEL_PRIVACY_MODE = "privacy-mode"; public static final String CHANNEL_CAMERA_NOTIFICATION = "camera-notification"; + public static final String CHANNEL_SYSTEM_AVAILABILITY = "system-availability"; + public static final String CHANNEL_ARMING_STATE = "arming-state"; + public static final String CHANNEL_ALARM_STATE = "alarm-state"; + public static final String CHANNEL_ACTIVE_CONFIGURATION_PROFILE = "active-configuration-profile"; + 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"; + + // 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/BoschSHCDeviceHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCDeviceHandler.java new file mode 100644 index 000000000..a7e1fc88f --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCDeviceHandler.java @@ -0,0 +1,95 @@ +/** + * 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 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.exceptions.BoschSHCException; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; + +/** + * Handler for physical Bosch devices with configurable IDs (as opposed to system services, which have static IDs). + *

+ * The device ID of physical devices has to be configured in the thing configuration. + *

+ * Examples for physical device IDs are: + * + *

+ * hdm:Cameras:d20354de-44b5-3acc-924c-24c98d59da42
+ * hdm:ZigBee:000d6f0016d1cdae
+ * 
+ * + * @author Stefan Kästle - Initial contribution + * @author Christian Oeing - refactorings of e.g. server registration + * @author David Pace - Handler abstraction + * + */ +@NonNullByDefault +public class BoschSHCDeviceHandler extends BoschSHCHandler { + + /** + * Bosch SHC configuration loaded from openHAB configuration. + */ + private @Nullable BoschSHCConfiguration config; + + public BoschSHCDeviceHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + + var config = this.config = getConfigAs(BoschSHCConfiguration.class); + + String deviceId = config.id; + if (deviceId == null || deviceId.isBlank()) { + this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/offline.conf-error.empty-device-id"); + return; + } + + // Try to get device info to make sure the device exists + try { + var bridgeHandler = this.getBridgeHandler(); + var info = bridgeHandler.getDeviceInfo(deviceId); + logger.trace("Device initialized:\n{}", info); + } catch (TimeoutException | ExecutionException | BoschSHCException e) { + this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + return; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + return; + } + + super.initialize(); + } + + /** + * Returns the unique id of the Bosch device. + * + * @return Unique id of the Bosch device. + */ + public @Nullable String getBoschID() { + if (config != null) { + return config.id; + } + + return null; + } +} 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 c88199deb..5fbbe50a6 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 @@ -24,6 +24,9 @@ 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.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.AbstractBoschSHCService; +import org.openhab.binding.boschshc.internal.services.AbstractStatelessBoschSHCService; +import org.openhab.binding.boschshc.internal.services.AbstractStatelessBoschSHCServiceWithRequestBody; import org.openhab.binding.boschshc.internal.services.BoschSHCService; import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState; import org.openhab.core.thing.Bridge; @@ -42,10 +45,11 @@ import com.google.gson.JsonElement; /** * The {@link BoschSHCHandler} represents Bosch Things. Each type of device - * inherits from this abstract thing handler. + * or system service inherits from this abstract thing handler. * * @author Stefan Kästle - Initial contribution * @author Christian Oeing - refactorings of e.g. server registration + * @author David Pace - Handler abstraction */ @NonNullByDefault public abstract class BoschSHCHandler extends BaseThingHandler { @@ -83,33 +87,36 @@ public abstract class BoschSHCHandler extends BaseThingHandler { protected final Logger logger = LoggerFactory.getLogger(getClass()); - /** - * Bosch SHC configuration loaded from openHAB configuration. - */ - private @Nullable BoschSHCConfiguration config; - /** * Services of the device. */ private List> services = new ArrayList<>(); - public BoschSHCHandler(Thing thing) { + protected BoschSHCHandler(Thing thing) { super(thing); } /** - * Returns the unique id of the Bosch device. + * Returns the unique id of the Bosch device or service. + *

+ * For physical devices, the ID looks like + * + *

+     * hdm:Cameras:d20354de-44b5-3acc-924c-24c98d59da42
+     * hdm:ZigBee:000d6f0016d1c087
+     * 
+ * + * For virtual devices / services, static IDs like the following are used: + * + *
+     * ventilationService
+     * smokeDetectionSystem
+     * intrusionDetectionSystem
+     * 
* - * @return Unique id of the Bosch device. + * @return Unique ID of the Bosch device or service. */ - public @Nullable String getBoschID() { - BoschSHCConfiguration config = this.config; - if (config != null) { - return config.id; - } else { - return null; - } - } + public abstract @Nullable String getBoschID(); /** * Initializes this handler. Use this method to register all services of the device with @@ -117,24 +124,6 @@ public abstract class BoschSHCHandler extends BaseThingHandler { */ @Override public void initialize() { - var config = this.config = getConfigAs(BoschSHCConfiguration.class); - - String deviceId = config.id; - if (deviceId == null || deviceId.isEmpty()) { - this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "@text/offline.conf-error.empty-device-id"); - return; - } - - // Try to get device info to make sure the device exists - try { - var bridgeHandler = this.getBridgeHandler(); - var info = bridgeHandler.getDeviceInfo(deviceId); - logger.trace("Device initialized:\n{}", info.toString()); - } catch (InterruptedException | TimeoutException | ExecutionException | BoschSHCException e) { - this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); - return; - } // Initialize device services try { @@ -273,16 +262,95 @@ public abstract class BoschSHCHandler extends BaseThingHandler { protected , TState extends BoschSHCServiceState> void registerService( TService service, Consumer stateUpdateListener, Collection affectedChannels) throws BoschSHCException { - BridgeHandler bridgeHandler = this.getBridgeHandler(); + registerService(service, stateUpdateListener, affectedChannels, false); + } + /** + * Registers a service for this device. + * + * @param Type of service. + * @param Type of service state. + * @param service Service to register. + * @param stateUpdateListener Function to call when a state update was received + * from the device. + * @param affectedChannels Channels which are affected by the state of this + * service. + * @param shouldFetchInitialState indicates whether the initial state should be actively requested from the device + * or service. Useful if state updates are not included in long poll results. + * @throws BoschSHCException If bridge for handler is not set or an invalid bridge is set. + * @throws BoschSHCException If no device id is set. + */ + protected , TState extends BoschSHCServiceState> void registerService( + TService service, Consumer stateUpdateListener, Collection affectedChannels, + boolean shouldFetchInitialState) throws BoschSHCException { + + String deviceId = verifyBoschID(); + service.initialize(getBridgeHandler(), deviceId, stateUpdateListener); + this.registerService(service, affectedChannels); + + if (shouldFetchInitialState) { + fetchInitialState(service, stateUpdateListener); + } + } + + /** + * Actively requests the initial state for the given service. This is required if long poll results do not contain + * status updates for the given service. + * + * @param Type of the service for which the state should be obtained + * @param Type of the objects to serialize and deserialize the service state + * @param service Service for which the state should be requested + * @param stateUpdateListener Function to process the obtained state + */ + private , TState extends BoschSHCServiceState> void fetchInitialState( + TService service, Consumer stateUpdateListener) { + + try { + @Nullable + TState serviceState = service.getState(); + if (serviceState != null) { + stateUpdateListener.accept(serviceState); + } + } catch (TimeoutException | ExecutionException | BoschSHCException e) { + logger.debug("Could not retrieve the initial state for service {} of device {}", service.getServiceName(), + getBoschID()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.debug("Could not retrieve the initial state for service {} of device {}", service.getServiceName(), + getBoschID()); + } + } + + /** + * Registers a write-only service that does not receive states from the bridge. + *

+ * Examples for such services are the actions of the intrusion detection service. + * + * @param Type of service. + * @param service Service to register. + * @throws BoschSHCException If no device ID is set. + */ + protected void registerStatelessService(TService service) + throws BoschSHCException { + + String deviceId = verifyBoschID(); + service.initialize(getBridgeHandler(), deviceId); + // do not register in service list because the service can not receive state updates + } + + /** + * Verifies that a Bosch device or service ID is set and throws an exception if this is not the case. + * + * @return the Bosch ID, if present + * @throws BoschSHCException if no Bosch ID is set + */ + private String verifyBoschID() throws BoschSHCException { String deviceId = this.getBoschID(); if (deviceId == null) { throw new BoschSHCException( String.format("Could not register service for %s, no device id set", this.getThing())); } - - service.initialize(bridgeHandler, deviceId, stateUpdateListener); - this.registerService(service, affectedChannels); + return deviceId; } /** @@ -367,4 +435,45 @@ public abstract class BoschSHCHandler extends BaseThingHandler { Collection affectedChannels) { this.services.add(new DeviceService(service, affectedChannels)); } + + /** + * Sends a HTTP POST request with empty body. + * + * @param Type of service. + * @param service Service implementing the action + */ + protected void postAction(TService service) { + try { + service.postAction(); + } catch (ExecutionException | TimeoutException e) { + this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, + String.format("Error while triggering action %s", service.getEndpoint())); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, + String.format("Error while triggering action %s", service.getEndpoint())); + } + } + + /** + * Sends a HTTP POST request with the given request body. + * + * @param Type of service. + * @param Type of the request to be sent. + * @param service Service implementing the action + * @param request Request object to be serialized to JSON + */ + protected , TState extends BoschSHCServiceState> void postAction( + TService service, TState request) { + try { + service.postAction(request); + } catch (ExecutionException | TimeoutException e) { + this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, + String.format("Error while triggering action %s", service.getEndpoint())); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, + String.format("Error while triggering action %s", service.getEndpoint())); + } + } } diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandlerFactory.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandlerFactory.java index 0bfb53c6e..b686acb51 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandlerFactory.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandlerFactory.java @@ -23,6 +23,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler; import org.openhab.binding.boschshc.internal.devices.camera.CameraHandler; import org.openhab.binding.boschshc.internal.devices.climatecontrol.ClimateControlHandler; +import org.openhab.binding.boschshc.internal.devices.intrusion.IntrusionDetectionHandler; import org.openhab.binding.boschshc.internal.devices.lightcontrol.LightControlHandler; import org.openhab.binding.boschshc.internal.devices.motiondetector.MotionDetectorHandler; import org.openhab.binding.boschshc.internal.devices.shuttercontrol.ShutterControlHandler; @@ -46,6 +47,7 @@ import org.osgi.service.component.annotations.Component; * @author Stefan Kästle - Initial contribution * @author Christian Oeing - Added Shutter Control and ThermostatHandler; refactored handler mapping * @author Christian Oeing - Added WallThermostatHandler + * @author David Pace - Added cameras and intrusion detection system */ @NonNullByDefault @Component(configurationPid = "binding.boschshc", service = ThingHandlerFactory.class) @@ -72,7 +74,8 @@ public class BoschSHCHandlerFactory extends BaseThingHandlerFactory { new ThingTypeHandlerMapping(THING_TYPE_CLIMATE_CONTROL, ClimateControlHandler::new), new ThingTypeHandlerMapping(THING_TYPE_WALL_THERMOSTAT, WallThermostatHandler::new), new ThingTypeHandlerMapping(THING_TYPE_CAMERA_360, CameraHandler::new), - new ThingTypeHandlerMapping(THING_TYPE_CAMERA_EYES, CameraHandler::new)); + new ThingTypeHandlerMapping(THING_TYPE_CAMERA_EYES, CameraHandler::new), + new ThingTypeHandlerMapping(THING_TYPE_INTRUSION_DETECTION_SYSTEM, IntrusionDetectionHandler::new)); @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { 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 508de1d20..a7a134ed0 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 @@ -59,6 +59,7 @@ import com.google.gson.reflect.TypeToken; * @author Stefan Kästle - Initial contribution * @author Gerd Zanker - added HttpClient with pairing support * @author Christian Oeing - refactorings of e.g. server registration + * @author David Pace - Added support for custom endpoints and HTTP POST requests */ @NonNullByDefault public class BridgeHandler extends BaseBridgeHandler { @@ -75,7 +76,13 @@ public class BridgeHandler extends BaseBridgeHandler { */ private final LongPolling longPolling; - private @Nullable BoschHttpClient httpClient; + /** + * HTTP client for all communications to and from the bridge. + *

+ * This member is package-protected to enable mocking in unit tests. + */ + /* package */ @Nullable + BoschHttpClient httpClient; private @Nullable ScheduledFuture scheduledPairing; @@ -300,14 +307,14 @@ public class BridgeHandler extends BaseBridgeHandler { private void handleLongPollResult(LongPollResult result) { for (DeviceStatusUpdate update : result.result) { if (update != null && update.state != null) { - logger.debug("Got update of type {}: {}", update.type, update.state); + logger.debug("Got update for service {} of type {}: {}", update.id, update.type, update.state); var updateDeviceId = update.deviceId; if (updateDeviceId == null) { continue; } - logger.debug("Got update for {}", updateDeviceId); + logger.debug("Got update for device {}", updateDeviceId); boolean handled = false; @@ -437,11 +444,18 @@ public class BridgeHandler extends BaseBridgeHandler { } /** - * Query the Bosch Smart Home Controller for the state of the given thing. + * 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
+     * 
* * @param deviceId Id of device to get state for * @param stateName Name of the state to query * @param stateClass Class to convert the resulting JSON to + * @return the deserialized state object, may be null * @throws ExecutionException * @throws TimeoutException * @throws InterruptedException @@ -457,26 +471,68 @@ public class BridgeHandler extends BaseBridgeHandler { } String url = httpClient.getServiceUrl(stateName, deviceId); - Request request = httpClient.createRequest(url, GET).header("Accept", "application/json"); + logger.debug("getState(): Requesting \"{}\" from Bosch: {} via {}", stateName, deviceId, url); + return getState(httpClient, url, stateClass); + } - logger.debug("refreshState: Requesting \"{}\" from Bosch: {} via {}", stateName, deviceId, url); + /** + * 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 + * @return the deserialized state object, may be null + * @throws InterruptedException + * @throws TimeoutException + * @throws ExecutionException + * @throws BoschSHCException + */ + public @Nullable T getState(String endpoint, Class stateClass) + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + @Nullable + BoschHttpClient httpClient = this.httpClient; + if (httpClient == null) { + logger.warn("HttpClient not initialized"); + return null; + } + + String url = httpClient.getBoschSmartHomeUrl(endpoint); + logger.debug("getState(): Requesting from Bosch: {}", url); + return getState(httpClient, url, stateClass); + } + + /** + * 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 + * @param stateClass Class to convert the resulting JSON to + * @return the deserialized state object, may be null + * @throws InterruptedException + * @throws TimeoutException + * @throws ExecutionException + * @throws BoschSHCException + */ + protected @Nullable T getState(BoschHttpClient httpClient, String url, + Class stateClass) throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = httpClient.createRequest(url, GET).header("Accept", "application/json"); ContentResponse contentResponse = request.send(); String content = contentResponse.getContentAsString(); - logger.debug("refreshState: Request complete: [{}] - return code: {}", content, contentResponse.getStatus()); + logger.debug("getState(): Request complete: [{}] - return code: {}", content, contentResponse.getStatus()); int statusCode = contentResponse.getStatus(); if (statusCode != 200) { JsonRestExceptionResponse errorResponse = gson.fromJson(content, JsonRestExceptionResponse.class); if (errorResponse != null) { - throw new BoschSHCException(String.format( - "State request for service %s of device %s failed with status code %d and error code %s", - stateName, deviceId, errorResponse.statusCode, errorResponse.errorCode)); + throw new BoschSHCException( + String.format("State request with URL %s failed with status code %d and error code %s", url, + errorResponse.statusCode, errorResponse.errorCode)); } else { throw new BoschSHCException( - String.format("State request for service %s of device %s failed with status code %d", stateName, - deviceId, statusCode)); + String.format("State request with URL %s failed with status code %d", url, statusCode)); } } @@ -516,4 +572,43 @@ public class BridgeHandler extends BaseBridgeHandler { // Send request return request.send(); } + + /** + * 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 + * @throws TimeoutException + * @throws ExecutionException + */ + public @Nullable Response postAction(String endpoint) + throws InterruptedException, TimeoutException, ExecutionException { + return postAction(endpoint, null); + } + + /** + * 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 + * @return the HTTP response + * @throws InterruptedException + * @throws TimeoutException + * @throws ExecutionException + */ + public @Nullable Response postAction(String endpoint, @Nullable T requestBody) + throws InterruptedException, TimeoutException, ExecutionException { + @Nullable + BoschHttpClient httpClient = this.httpClient; + if (httpClient == null) { + logger.warn("HttpClient not initialized"); + return null; + } + + String url = httpClient.getBoschSmartHomeUrl(endpoint); + Request request = httpClient.createRequest(url, POST, requestBody); + return request.send(); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandler.java index 06eb5f0a1..b50086de8 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandler.java @@ -16,12 +16,9 @@ import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConst import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_PRIVACY_MODE; import java.util.List; -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.BoschSHCHandler; +import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.cameranotification.CameraNotificationService; import org.openhab.binding.boschshc.internal.services.cameranotification.CameraNotificationState; @@ -54,7 +51,7 @@ import org.openhab.core.types.Command; * */ @NonNullByDefault -public class CameraHandler extends BoschSHCHandler { +public class CameraHandler extends BoschSHCDeviceHandler { private PrivacyModeService privacyModeService; private CameraNotificationService cameraNotificationService; @@ -69,60 +66,9 @@ public class CameraHandler extends BoschSHCHandler { protected void initializeServices() throws BoschSHCException { super.initializeServices(); - this.registerService(this.privacyModeService, this::updateChannels, List.of(CHANNEL_PRIVACY_MODE)); - this.registerService(this.cameraNotificationService, this::updateChannels, - List.of(CHANNEL_CAMERA_NOTIFICATION)); - } - - @Override - public void initialize() { - super.initialize(); - requestInitialStates(); - } - - /** - * Requests the initial states for relevant services. - *

- * If this is not done, items associated with the corresponding channels with stay in an uninitialized state - * (null). - * This in turn leads to events not being fired properly when switches are used in the UI. - *

- * Unfortunately the long poll results do not contain camera-related updates, so this is the current approach - * to get the initial states. - */ - private void requestInitialStates() { - requestInitialPrivacyState(); - requestInitialNotificationState(); - } - - private void requestInitialPrivacyState() { - try { - @Nullable - PrivacyModeServiceState serviceState = privacyModeService.getState(); - if (serviceState != null) { - super.updateState(CHANNEL_PRIVACY_MODE, serviceState.value.toOnOffType()); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - logger.debug("Could not retrieve the initial privacy state of camera {}", getBoschID()); - } catch (TimeoutException | ExecutionException | BoschSHCException e) { - logger.debug("Could not retrieve the initial privacy state of camera {}", getBoschID()); - } - } - - private void requestInitialNotificationState() { - try { - @Nullable - CameraNotificationServiceState serviceState = cameraNotificationService.getState(); - if (serviceState != null) { - super.updateState(CHANNEL_CAMERA_NOTIFICATION, serviceState.value.toOnOffType()); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - logger.debug("Could not retrieve the initial notification state of camera {}", getBoschID()); - } catch (TimeoutException | ExecutionException | BoschSHCException e) { - logger.debug("Could not retrieve the initial notification state of camera {}", getBoschID()); - } + this.registerService(this.privacyModeService, this::updateChannels, List.of(CHANNEL_PRIVACY_MODE), true); + this.registerService(this.cameraNotificationService, this::updateChannels, List.of(CHANNEL_CAMERA_NOTIFICATION), + true); } @Override diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandler.java index ee56dea36..904dbc8db 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandler.java @@ -18,7 +18,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.BoschSHCHandler; +import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.roomclimatecontrol.RoomClimateControlService; import org.openhab.binding.boschshc.internal.services.roomclimatecontrol.dto.RoomClimateControlServiceState; @@ -36,7 +36,7 @@ import org.openhab.core.types.Command; * @author Christian Oeing - Initial contribution */ @NonNullByDefault -public final class ClimateControlHandler extends BoschSHCHandler { +public final class ClimateControlHandler extends BoschSHCDeviceHandler { private RoomClimateControlService roomClimateControlService; diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandler.java new file mode 100644 index 000000000..1855381d4 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandler.java @@ -0,0 +1,159 @@ +/** + * 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.intrusion; + +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_ACTIVE_CONFIGURATION_PROFILE; +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_ALARM_STATE; +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_ARMING_STATE; +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_ARM_ACTION; +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_DISARM_ACTION; +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_MUTE_ACTION; +import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_SYSTEM_AVAILABILITY; + +import java.util.List; + +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.BoschSHCHandler; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.intrusion.IntrusionDetectionControlStateService; +import org.openhab.binding.boschshc.internal.services.intrusion.IntrusionDetectionSystemStateService; +import org.openhab.binding.boschshc.internal.services.intrusion.SurveillanceAlarmService; +import org.openhab.binding.boschshc.internal.services.intrusion.actions.arm.ArmActionService; +import org.openhab.binding.boschshc.internal.services.intrusion.actions.arm.dto.ArmActionRequest; +import org.openhab.binding.boschshc.internal.services.intrusion.actions.disarm.DisarmActionService; +import org.openhab.binding.boschshc.internal.services.intrusion.actions.mute.MuteActionService; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDetectionControlState; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDetectionSystemState; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.SurveillanceAlarmState; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.types.Command; + +/** + * Handler for the intrusion detection alarm system. + *

+ * It supports + *

    + *
  • Obtaining the current intrusion detection system state
  • + *
  • Receiving updates related to the detection control state
  • + *
  • Receiving updates related to surveillance alarm events
  • + *
  • Arming the system
  • + *
  • Disarming the system
  • + *
  • Muting the alarm
  • + *
+ * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class IntrusionDetectionHandler extends BoschSHCHandler { + + private IntrusionDetectionSystemStateService intrusionDetectionSystemStateService; + private IntrusionDetectionControlStateService intrusionDetectionControlStateService; + private SurveillanceAlarmService surveillanceAlarmService; + private ArmActionService armActionService; + private DisarmActionService disarmActionService; + private MuteActionService muteActionService; + + public IntrusionDetectionHandler(Thing thing) { + super(thing); + this.intrusionDetectionSystemStateService = new IntrusionDetectionSystemStateService(); + this.intrusionDetectionControlStateService = new IntrusionDetectionControlStateService(); + this.surveillanceAlarmService = new SurveillanceAlarmService(); + this.armActionService = new ArmActionService(); + this.disarmActionService = new DisarmActionService(); + this.muteActionService = new MuteActionService(); + } + + @Override + public @Nullable String getBoschID() { + return BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION; + } + + @Override + protected void initializeServices() throws BoschSHCException { + super.initializeServices(); + + this.registerService(intrusionDetectionSystemStateService, this::updateChannels, + List.of(CHANNEL_SYSTEM_AVAILABILITY, CHANNEL_ARMING_STATE, CHANNEL_ALARM_STATE, + CHANNEL_ACTIVE_CONFIGURATION_PROFILE), + true); + this.registerService(intrusionDetectionControlStateService, this::updateChannels, + List.of(CHANNEL_ARMING_STATE)); + this.registerService(surveillanceAlarmService, this::updateChannels, List.of(CHANNEL_ALARM_STATE)); + this.registerStatelessService(armActionService); + this.registerStatelessService(disarmActionService); + this.registerStatelessService(muteActionService); + } + + private void updateChannels(IntrusionDetectionSystemState systemState) { + super.updateState(CHANNEL_SYSTEM_AVAILABILITY, OnOffType.from(systemState.systemAvailability.available)); + super.updateState(CHANNEL_ARMING_STATE, new StringType(systemState.armingState.state.toString())); + super.updateState(CHANNEL_ALARM_STATE, new StringType(systemState.alarmState.value.toString())); + super.updateState(CHANNEL_ACTIVE_CONFIGURATION_PROFILE, + new StringType(systemState.activeConfigurationProfile.profileId)); + } + + private void updateChannels(IntrusionDetectionControlState controlState) { + super.updateState(CHANNEL_ARMING_STATE, new StringType(controlState.value.toString())); + } + + private void updateChannels(SurveillanceAlarmState surveillanceAlarmState) { + super.updateState(CHANNEL_ALARM_STATE, new StringType(surveillanceAlarmState.value.toString())); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + super.handleCommand(channelUID, command); + + switch (channelUID.getId()) { + case CHANNEL_ARM_ACTION: + if (command instanceof StringType) { + armIntrusionDetectionSystem((StringType) command); + } + break; + case CHANNEL_DISARM_ACTION: + if (command instanceof OnOffType) { + disarmIntrusionDetectionSystem((OnOffType) command); + } + break; + case CHANNEL_MUTE_ACTION: + if (command instanceof OnOffType) { + muteIntrusionDetectionSystem((OnOffType) command); + } + break; + } + } + + private void armIntrusionDetectionSystem(StringType profileIdCommand) { + ArmActionRequest armActionRequest = new ArmActionRequest(); + armActionRequest.profileId = profileIdCommand.toFullString(); + postAction(armActionService, armActionRequest); + } + + private void disarmIntrusionDetectionSystem(OnOffType command) { + if (command == OnOffType.ON) { + postAction(disarmActionService); + } + } + + private void muteIntrusionDetectionSystem(OnOffType command) { + if (command == OnOffType.ON) { + postAction(muteActionService); + } + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/lightcontrol/LightControlHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/lightcontrol/LightControlHandler.java index 038e24009..2dde138ce 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/lightcontrol/LightControlHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/lightcontrol/LightControlHandler.java @@ -20,7 +20,7 @@ import javax.measure.quantity.Energy; import javax.measure.quantity.Power; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler; +import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.powermeter.PowerMeterService; import org.openhab.binding.boschshc.internal.services.powermeter.dto.PowerMeterServiceState; @@ -41,7 +41,7 @@ import org.openhab.core.types.State; * @author Stefan Kästle - Initial contribution */ @NonNullByDefault -public class LightControlHandler extends BoschSHCHandler { +public class LightControlHandler extends BoschSHCDeviceHandler { private final PowerSwitchService powerSwitchService; 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 ef4c309c2..f4a088cec 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.BoschSHCHandler; +import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; 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 BoschSHCHandler { +public class MotionDetectorHandler extends BoschSHCDeviceHandler { public MotionDetectorHandler(Thing thing) { super(thing); diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandler.java index 45ae948a8..f852200d1 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandler.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.BoschSHCHandler; +import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.shuttercontrol.OperationState; import org.openhab.binding.boschshc.internal.services.shuttercontrol.ShutterControlService; @@ -35,7 +35,7 @@ import org.openhab.core.types.Command; * @author Christian Oeing - Initial contribution */ @NonNullByDefault -public class ShutterControlHandler extends BoschSHCHandler { +public class ShutterControlHandler extends BoschSHCDeviceHandler { /** * Utility functions to convert data between Bosch things and openHAB items */ 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 33de80c48..ff00373be 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 @@ -19,7 +19,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.BoschSHCHandler; +import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; 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; @@ -37,7 +37,7 @@ import org.openhab.core.types.Command; * @author Christian Oeing - Initial contribution */ @NonNullByDefault -public final class ThermostatHandler extends BoschSHCHandler { +public final class ThermostatHandler extends BoschSHCDeviceHandler { private ChildLockService childLockService; 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 fd5894c9e..d155c1896 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 @@ -27,7 +27,7 @@ import javax.measure.quantity.Dimensionless; import javax.measure.quantity.Temperature; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler; +import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; 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 +44,7 @@ import org.openhab.core.thing.Thing; * @author Christian Oeing - Use service instead of custom logic */ @NonNullByDefault -public class TwinguardHandler extends BoschSHCHandler { +public class TwinguardHandler extends BoschSHCDeviceHandler { 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 a3a084a67..411c52ebb 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 @@ -18,7 +18,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.BoschSHCHandler; +import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; 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; @@ -32,7 +32,7 @@ import org.openhab.core.thing.Thing; * @author Christian Oeing - Initial contribution */ @NonNullByDefault -public final class WallThermostatHandler extends BoschSHCHandler { +public final class WallThermostatHandler extends BoschSHCDeviceHandler { public WallThermostatHandler(Thing thing) { super(thing); 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 1013de755..08a34099c 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.BoschSHCHandler; +import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; 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 BoschSHCHandler { +public class WindowContactHandler extends BoschSHCDeviceHandler { public WindowContactHandler(Thing thing) { super(thing); diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractBoschSHCService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractBoschSHCService.java new file mode 100644 index 000000000..c7b3e5069 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractBoschSHCService.java @@ -0,0 +1,72 @@ +/** + * 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler; + +/** + * Base class for Bosch Smart Home services containing what all services have in common. + *

+ * The services of the devices and their official APIs can be found + * here. + * + * @author Christian Oeing - Initial contribution + * @author David Pace - Service abstraction + */ +@NonNullByDefault +public abstract class AbstractBoschSHCService { + + /** + * Unique service name + */ + private final String serviceName; + + /** + * Bridge to use for communication from/to the device + */ + private @Nullable BridgeHandler bridgeHandler; + + /** + * Id of device the service belongs to + */ + private @Nullable String deviceId; + + protected AbstractBoschSHCService(String serviceName) { + this.serviceName = serviceName; + } + + /** + * Initializes the service + * + * @param bridgeHandler Bridge to use for communication from/to the device + * @param deviceId Id of device this service is for + */ + public void initialize(BridgeHandler bridgeHandler, String deviceId) { + this.bridgeHandler = bridgeHandler; + this.deviceId = deviceId; + } + + public String getServiceName() { + return serviceName; + } + + protected @Nullable BridgeHandler getBridgeHandler() { + return bridgeHandler; + } + + protected @Nullable String getDeviceId() { + return deviceId; + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractStatelessBoschSHCService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractStatelessBoschSHCService.java new file mode 100644 index 000000000..debdd1f32 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractStatelessBoschSHCService.java @@ -0,0 +1,69 @@ +/** + * 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; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler; + +/** + * Abstract implementation for services that only allow setting states via HTTP POST requests. + * State-less services can not receive any states from the bridge. + *

+ * This implementation does not support request bodies when submitting the POST request. + * Request bodies are supported by the subclass {@link AbstractStatelessBoschSHCServiceWithRequestBody}. + *

+ * Examples for this kind of service are the following actions of the intrusion detection system: + * + *

+ * /intrusion/actions/arm
+ * /intrusion/actions/disarm
+ * /intrusion/actions/mute
+ * 
+ *

+ * The services of the devices and their official APIs can be found + * here. + * + * @author David Pace - Initial contribution + */ +@NonNullByDefault +public abstract class AbstractStatelessBoschSHCService extends AbstractBoschSHCService { + + private String endpoint; + + protected AbstractStatelessBoschSHCService(String serviceName, String endpoint) { + super(serviceName); + this.endpoint = endpoint; + } + + /** + * Sends a HTTP POST request without request body to the endpoint specified by {@link #endpoint}. + * + * @throws InterruptedException + * @throws TimeoutException + * @throws ExecutionException + */ + public void postAction() throws InterruptedException, TimeoutException, ExecutionException { + BridgeHandler bridgeHandler = getBridgeHandler(); + if (bridgeHandler == null) + return; + + bridgeHandler.postAction(endpoint); + } + + public String getEndpoint() { + return endpoint; + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractStatelessBoschSHCServiceWithRequestBody.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractStatelessBoschSHCServiceWithRequestBody.java new file mode 100644 index 000000000..e69f7f5b6 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/AbstractStatelessBoschSHCServiceWithRequestBody.java @@ -0,0 +1,60 @@ +/** + * 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; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler; +import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState; + +/** + * Abstract implementation for services that allow setting states via HTTP POST requests containing a JSON request body. + * State-less services can not receive any states from the bridge. + *

+ * An example of such a service is the arm action of the intrusion detection system. + *

+ * The services of the devices and their official APIs can be found + * here. + * + * @param Type to represent JSON requests sent by this service + * + * @author David Pace - Initial contribution + */ +@NonNullByDefault +public abstract class AbstractStatelessBoschSHCServiceWithRequestBody + extends AbstractStatelessBoschSHCService { + + protected AbstractStatelessBoschSHCServiceWithRequestBody(String serviceName, String endpoint) { + super(serviceName, endpoint); + } + + /** + * Sends a HTTP POST request containing the serialized request body to the endpoint specified by + * {@link #getEndpoint()}. + * + * @param request a JSON object representing the request body + * @throws InterruptedException + * @throws TimeoutException + * @throws ExecutionException + */ + public void postAction(TRequest request) throws InterruptedException, TimeoutException, ExecutionException { + BridgeHandler bridgeHandler = getBridgeHandler(); + if (bridgeHandler == null) { + return; + } + + bridgeHandler.postAction(getEndpoint(), request); + } +} 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 4082816f9..d3a0db77d 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 @@ -28,37 +28,34 @@ import org.slf4j.LoggerFactory; import com.google.gson.JsonElement; /** - * Base class of a service of a Bosch Smart Home device. The services of the - * devices and their official APIs can be found here: - * https://apidocs.bosch-smarthome.com/local/ + * Abstract implementation of a service that supports reading and writing its state using the same JSON message and the + * 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 */ @NonNullByDefault -public abstract class BoschSHCService { +public abstract class BoschSHCService extends AbstractBoschSHCService { protected final Logger logger = LoggerFactory.getLogger(BoschSHCService.class); - /** - * Unique service name - */ - private final String serviceName; - /** * Class of service state */ private final Class stateClass; - /** - * Bridge to use for communication from/to the device - */ - private @Nullable BridgeHandler bridgeHandler; - - /** - * Id of device the service belongs to - */ - private @Nullable String deviceId; - /** * Function to call after receiving state updates from the device */ @@ -72,7 +69,7 @@ public abstract class BoschSHCService { * from/to the device. */ protected BoschSHCService(String serviceName, Class stateClass) { - this.serviceName = serviceName; + super(serviceName); this.stateClass = stateClass; } @@ -86,20 +83,10 @@ public abstract class BoschSHCService { */ public void initialize(BridgeHandler bridgeHandler, String deviceId, @Nullable Consumer stateUpdateListener) { - this.bridgeHandler = bridgeHandler; - this.deviceId = deviceId; + super.initialize(bridgeHandler, deviceId); this.stateUpdateListener = stateUpdateListener; } - /** - * Returns the unique name of this service. - * - * @return Unique name of the service. - */ - public String getServiceName() { - return this.serviceName; - } - /** * Returns the class of the state this service provides. * @@ -136,15 +123,15 @@ public abstract class BoschSHCService { */ public @Nullable TState getState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { - String deviceId = this.deviceId; + String deviceId = getDeviceId(); if (deviceId == null) { return null; } - BridgeHandler bridgeHandler = this.bridgeHandler; + BridgeHandler bridgeHandler = getBridgeHandler(); if (bridgeHandler == null) { return null; } - return bridgeHandler.getState(deviceId, this.serviceName, this.stateClass); + return bridgeHandler.getState(deviceId, getServiceName(), getStateClass()); } /** @@ -156,15 +143,15 @@ public abstract class BoschSHCService { * @throws TimeoutException */ public void setState(TState state) throws InterruptedException, TimeoutException, ExecutionException { - String deviceId = this.deviceId; + String deviceId = getDeviceId(); if (deviceId == null) { return; } - BridgeHandler bridgeHandler = this.bridgeHandler; + BridgeHandler bridgeHandler = getBridgeHandler(); if (bridgeHandler == null) { return; } - bridgeHandler.putState(deviceId, this.serviceName, state); + bridgeHandler.putState(deviceId, getServiceName(), state); } /** diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/BoschSHCSystemService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/BoschSHCSystemService.java new file mode 100644 index 000000000..338e44da6 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/BoschSHCSystemService.java @@ -0,0 +1,80 @@ +/** + * 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; + +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.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState; + +/** + * Abstract implementation of a system service that does not represent a physical device. + * Examples for system services are the intrusion detection system and the water detection system. + *

+ * The endpoints to retrieve system states are different from the ones for physical devices, i.e. they do not follow the + * pattern + * + *

+ * https://{IP}:8444/smarthome/devices/{deviceId}/services/{serviceName}/state
+ * 
+ * + * Instead, system services have endpoints like + * + *
+ * /intrusion/states/system
+ * 
+ * + *

+ * The services of the devices and their official APIs can be found + * here. + * + * @param type used for representing the service state + * + * @author David Pace - Initial contribution + */ +@NonNullByDefault +public abstract class BoschSHCSystemService extends BoschSHCService { + + private String endpoint; + + /** + * Constructs a system service instance. + * + * @param serviceName name of the service, such as intrusionDetectionService + * @param stateClass the class representing states of the system + * @param endpoint the part of the URL after https://{IP}:8444/smarthome/, e.g. + * intrusion/states/system + */ + protected BoschSHCSystemService(String serviceName, Class stateClass, String endpoint) { + super(serviceName, stateClass); + this.endpoint = endpoint; + } + + /** + * Uses the endpoint directly instead of constructing a device-specific URL. + */ + @Override + public @Nullable TState getState() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + + BridgeHandler bridgeHandler = getBridgeHandler(); + if (bridgeHandler == null) { + return null; + } + return bridgeHandler.getState(this.endpoint, getStateClass()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateService.java new file mode 100644 index 000000000..199dae8ab --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateService.java @@ -0,0 +1,31 @@ +/** + * 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.intrusion; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.services.BoschSHCService; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDetectionControlState; + +/** + * Allows to retrieve the control state of the intrusion detection system. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class IntrusionDetectionControlStateService extends BoschSHCService { + + public IntrusionDetectionControlStateService() { + super("IntrusionDetectionControl", IntrusionDetectionControlState.class); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateService.java new file mode 100644 index 000000000..d220ca8b9 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateService.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.services.intrusion; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.services.BoschSHCSystemService; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDetectionSystemState; + +/** + * Allows to retrieve the system state of the intrusion detection system. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class IntrusionDetectionSystemStateService extends BoschSHCSystemService { + + public IntrusionDetectionSystemStateService() { + super(BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION, IntrusionDetectionSystemState.class, + "intrusion/states/system"); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/SurveillanceAlarmService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/SurveillanceAlarmService.java new file mode 100644 index 000000000..9946fa0af --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/SurveillanceAlarmService.java @@ -0,0 +1,31 @@ +/** + * 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.intrusion; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.services.BoschSHCService; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.SurveillanceAlarmState; + +/** + * Service to handle intrusion detection events. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class SurveillanceAlarmService extends BoschSHCService { + + public SurveillanceAlarmService() { + super("SurveillanceAlarm", SurveillanceAlarmState.class); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/arm/ArmActionService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/arm/ArmActionService.java new file mode 100644 index 000000000..3a37af1d7 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/arm/ArmActionService.java @@ -0,0 +1,32 @@ +/** + * 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.intrusion.actions.arm; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.services.AbstractStatelessBoschSHCServiceWithRequestBody; +import org.openhab.binding.boschshc.internal.services.intrusion.actions.arm.dto.ArmActionRequest; + +/** + * Service to arm the intrusion detection system using a specified profile. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class ArmActionService extends AbstractStatelessBoschSHCServiceWithRequestBody { + + public ArmActionService() { + super(BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION, "intrusion/actions/arm"); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/arm/dto/ArmActionRequest.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/arm/dto/ArmActionRequest.java new file mode 100644 index 000000000..645059b1b --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/arm/dto/ArmActionRequest.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.services.intrusion.actions.arm.dto; + +import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState; + +/** + * DTO for arming the intrusion detection system using a specified profile. + * + * @author David Pace - Initial contribution + * + */ +public class ArmActionRequest extends BoschSHCServiceState { + + public ArmActionRequest() { + super("armRequest"); + } + + /** + * The ID of the profile to be used for arming the system. + */ + public String profileId; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/disarm/DisarmActionService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/disarm/DisarmActionService.java new file mode 100644 index 000000000..8d76a0406 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/disarm/DisarmActionService.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.services.intrusion.actions.disarm; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.services.AbstractStatelessBoschSHCService; + +/** + * Service for disarming the intrusion detection system. + *

+ * This service does not require a DTO because it uses a simple HTTP POST request without a request body. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class DisarmActionService extends AbstractStatelessBoschSHCService { + + public DisarmActionService() { + super(BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION, "intrusion/actions/disarm"); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/mute/MuteActionService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/mute/MuteActionService.java new file mode 100644 index 000000000..7f893078d --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/actions/mute/MuteActionService.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.services.intrusion.actions.mute; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.services.AbstractStatelessBoschSHCService; + +/** + * Service to mute the intrusion detection system. + *

+ * This service does not require a DTO because it uses a simple HTTP POST request without a request body. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class MuteActionService extends AbstractStatelessBoschSHCService { + + public MuteActionService() { + super(BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION, "intrusion/actions/mute"); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ActiveConfigurationProfileData.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ActiveConfigurationProfileData.java new file mode 100644 index 000000000..48a7cd645 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ActiveConfigurationProfileData.java @@ -0,0 +1,27 @@ +/** + * 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.intrusion.dto; + +/** + * Represents the active configuration profile of the intrusion detection system. + * + * @author David Pace - Initial contribution + * + */ +public class ActiveConfigurationProfileData { + + /** + * The ID of the active configuration profile (default: "0") + */ + public String profileId; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/AlarmState.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/AlarmState.java new file mode 100644 index 000000000..0f493d432 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/AlarmState.java @@ -0,0 +1,30 @@ +/** + * 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.intrusion.dto; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Possible alarm states of the intrusion detection system. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public enum AlarmState { + ALARM_OFF, + PRE_ALARM, + ALARM_ON, + ALARM_MUTED, + UNKNOWN; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/AlarmStateData.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/AlarmStateData.java new file mode 100644 index 000000000..b1bcb62a1 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/AlarmStateData.java @@ -0,0 +1,47 @@ +/** + * 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.intrusion.dto; + +/** + * DTO for the alarm state of the intrusion detection system. + *

+ * Example data: + * + *

+ * "alarmState": {
+ *   "value": "ALARM_OFF",
+ *   "incidents": [
+ *     {
+ *       "id": "string",
+ *       "triggerName": "string",
+ *       "type": "SYSTEM_ARMED",
+ *       "time": 0,
+ *       "location": "string",
+ *       "locationId": "string"
+ *     }
+ *   ],
+ *   "deleted": true,
+ *   "id": "string"
+ * }
+ * 
+ * + *

+ * Note: Incidents are not supported yet as they do not seem to be included in the responses from the bridge. + * + * @author David Pace - Initial contribution + * + */ +public class AlarmStateData { + + public AlarmState value; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ArmingState.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ArmingState.java new file mode 100644 index 000000000..86bde20dc --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ArmingState.java @@ -0,0 +1,28 @@ +/** + * 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.intrusion.dto; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Possible arming state values of the intrusion detection system. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public enum ArmingState { + SYSTEM_ARMED, + SYSTEM_ARMING, + SYSTEM_DISARMED; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ArmingStateData.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ArmingStateData.java new file mode 100644 index 000000000..b1139ec20 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/ArmingStateData.java @@ -0,0 +1,37 @@ +/** + * 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.intrusion.dto; + +/** + * DTO for the arming state of the intrusion detection system. + *

+ * Example data: + * + *

+ * "armingState": {
+ *   "remainingTimeUntilArmed": 0,
+ *   "state": "SYSTEM_ARMING",
+ *   "deleted": true,
+ *   "id": "string"
+ * }
+ * 
+ * + * @author David Pace - Initial contribution + * + */ +public class ArmingStateData { + + public long remainingTimeUntilArmed; + + public ArmingState state; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/IntrusionDetectionControlState.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/IntrusionDetectionControlState.java new file mode 100644 index 000000000..a774fbb6a --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/IntrusionDetectionControlState.java @@ -0,0 +1,70 @@ +/** + * 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.intrusion.dto; + +import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState; + +/** + * A state which is periodically sent by the intrusion detection control state while arming. + *

+ * Example data: + * + *

+ * {
+ *   "@type": "intrusionDetectionControlState",
+ *   "activeProfile": "0",
+ *   "alarmActivationDelayTime": 30,
+ *   "actuators": [
+ *     {
+ *       "readonly": false,
+ *       "active": true,
+ *       "id": "intrusion:video"
+ *     },
+ *     {
+ *       "readonly": false,
+ *       "active": false,
+ *       "id": "intrusion:siren"
+ *     }
+ *   ],
+ *   "remainingTimeUntilArmed": 29559,
+ *   "armActivationDelayTime": 30,
+ *   "triggers": [
+ *     {
+ *       "readonly": false,
+ *       "active": true,
+ *       "id": "hdm:ZigBee:000d6f0012f02378"
+ *     }
+ *   ],
+ *   "value": "SYSTEM_ARMING"
+ * }
+ * 
+ * + * @author David Pace - Initial contribution + * + */ +public class IntrusionDetectionControlState extends BoschSHCServiceState { + + public IntrusionDetectionControlState() { + super("intrusionDetectionControlState"); + } + + public String activeProfile; + + public int alarmActivationDelayTime; + + public long remainingTimeUntilArmed; + + public int armActivationDelayTime; + + public ArmingState value; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/IntrusionDetectionSystemState.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/IntrusionDetectionSystemState.java new file mode 100644 index 000000000..b89cff03b --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/IntrusionDetectionSystemState.java @@ -0,0 +1,70 @@ +/** + * 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.intrusion.dto; + +import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState; + +/** + * Represents the state of the intrusion detection system as reported by the Bosch Smart Home Controller. + *

+ * Example data: + * + *

+ * {
+ *     "@type": "systemState",
+ *     "systemAvailability": {
+ *         "@type": "systemAvailabilityState",
+ *         "available": true,
+ *         "deleted": false
+ *     },
+ *     "armingState": {
+ *         "@type": "armingState",
+ *         "state": "SYSTEM_DISARMED",
+ *         "deleted": false
+ *     },
+ *     "alarmState": {
+ *         "@type": "alarmState",
+ *         "value": "ALARM_OFF",
+ *         "incidents": [],
+ *         "deleted": false
+ *     },
+ *     "activeConfigurationProfile": {
+ *         "@type": "activeConfigurationProfile",
+ *         "deleted": false
+ *     },
+ *     "securityGapState": {
+ *         "@type": "securityGapState",
+ *         "securityGaps": [],
+ *         "deleted": false
+ *     },
+ *     "deleted": false
+ * }
+ * 
+ * + * @author David Pace - Initial contribution + * + */ +public class IntrusionDetectionSystemState extends BoschSHCServiceState { + + public IntrusionDetectionSystemState() { + super("systemState"); + } + + public SystemAvailabilityStateData systemAvailability; + + public ArmingStateData armingState; + + public AlarmStateData alarmState; + + public ActiveConfigurationProfileData activeConfigurationProfile; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/SurveillanceAlarmState.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/SurveillanceAlarmState.java new file mode 100644 index 000000000..0ed52381a --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/SurveillanceAlarmState.java @@ -0,0 +1,56 @@ +/** + * 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.intrusion.dto; + +import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState; + +/** + * A state containing information about intrusion detection events. + *

+ * Example data: + * + *

+ * {
+ *   "@type": "surveillanceAlarmState",
+ *   "incidents": [
+ *     {
+ *       "triggerName": "Motion Detector",
+ *       "locationId": "hz_5",
+ *       "location": "Living Room",
+ *       "id": "hdm:ZigBee:000d6f0012f02342",
+ *       "time": 1652615755336,
+ *       "type": "INTRUSION"
+ *     }
+ *   ],
+ *   "value": "ALARM_ON"
+ * }
+ * 
+ * + *

+ * Note: This state is not documented in the official Bosch API docs. + * The type of the incidents seems to be very similar to IncidentType documented for + * the system state. However, the type enum seems to be slightly different (INTRUSION instead of + * INTRUSION_DETECTED). + * For this reason incidents are not modeled in this state object for now. + * + * @author David Pace - Initial contribution + * + */ +public class SurveillanceAlarmState extends BoschSHCServiceState { + + public SurveillanceAlarmState() { + super("surveillanceAlarmState"); + } + + public AlarmState value; +} diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/SystemAvailabilityStateData.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/SystemAvailabilityStateData.java new file mode 100644 index 000000000..2d79bda1d --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/intrusion/dto/SystemAvailabilityStateData.java @@ -0,0 +1,34 @@ +/** + * 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.intrusion.dto; + +/** + * DTO for the availability state of the intrusion detection system. + *

+ * Example data: + * + *

+ * {
+ *   "@type": "systemAvailabilityState",
+ *   "available": true,
+ *   "deleted": false
+ * }
+ * 
+ * + * @author David Pace - Initial contribution + * + */ +public class SystemAvailabilityStateData { + + public boolean available; +} 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 a50b94e45..34c55fc80 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 @@ -189,6 +189,89 @@ + + + + + + + Allows to retrieve and control the state of the intrusion detection alarm system. + + + + + + + + + + + + + + + Switch + + Indicates whether the intrusion detection system is available. + + + + + String + + The arming state of the intrusion detection system. Possible values are SYSTEM_ARMING, SYSTEM_ARMED and + SYSTEM_DISARMED. This channel is read-only. Use the arm-action and disarm-action channels to arm and disarm the + system. + + + + + + + + + + + String + + The alarm state of the intrusion detection system. Possible values are ALARM_OFF, PRE_ALARM, ALARM_ON, + ALARM_MUTED and UNKNOWN. + + + + + + + + + + + + + String + + The name of the active configuration profile used for the intrusion detection system. + + + + + String + + Arms the intrusion detection system using the given profile ID. + + + + Switch + + Disarms the intrusion detection system when an ON command is received. + + + + Switch + + Mutes the alarm when an ON command is received. + + Switch diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java new file mode 100644 index 000000000..20507b8f0 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java @@ -0,0 +1,72 @@ +/** + * 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; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.http.HttpMethod; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openhab.binding.boschshc.internal.services.intrusion.actions.arm.dto.ArmActionRequest; +import org.openhab.core.thing.Bridge; + +/** + * Unit tests for the {@link BridgeHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +class BridgeHandlerTest { + + @Nullable + private BridgeHandler fixture; + + @Nullable + private BoschHttpClient httpClient; + + @BeforeEach + void beforeEach() { + Bridge bridge = mock(Bridge.class); + fixture = new BridgeHandler(bridge); + httpClient = mock(BoschHttpClient.class); + fixture.httpClient = httpClient; + } + + @Test + void postAction() throws InterruptedException, TimeoutException, ExecutionException { + String endpoint = "/intrusion/actions/arm"; + String url = "https://127.0.0.1:8444/smarthome/intrusion/actions/arm"; + when(httpClient.getBoschSmartHomeUrl(endpoint)).thenReturn(url); + Request mockRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), any(), any())).thenReturn(mockRequest); + ArmActionRequest request = new ArmActionRequest(); + request.profileId = "0"; + + fixture.postAction(endpoint, request); + verify(httpClient).createRequest(eq(url), same(HttpMethod.POST), same(request)); + verify(mockRequest).send(); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java new file mode 100644 index 000000000..422758ca6 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java @@ -0,0 +1,111 @@ +/** + * 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.intrusion; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +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.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDetectionControlState; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Unit tests for {@link IntrusionDetectionControlStateService}. + * + * @author David Pace - Initial contribution + * + */ +@ExtendWith(MockitoExtension.class) +class IntrusionDetectionControlStateServiceTest { + + private IntrusionDetectionControlStateService fixture; + + @Mock + private BridgeHandler bridgeHandler; + + @Mock + private Consumer consumer; + + @Mock + private IntrusionDetectionControlState testState; + + @BeforeEach + void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + fixture = new IntrusionDetectionControlStateService(); + fixture.initialize(bridgeHandler, BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION, consumer); + } + + @Test + void getServiceName() { + assertEquals("IntrusionDetectionControl", fixture.getServiceName()); + } + + @Test + void getStateClass() { + assertSame(IntrusionDetectionControlState.class, fixture.getStateClass()); + } + + @Test + void getState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(bridgeHandler.getState(anyString(), anyString(), any())).thenReturn(testState); + IntrusionDetectionControlState state = fixture.getState(); + verify(bridgeHandler).getState(BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION, + "IntrusionDetectionControl", IntrusionDetectionControlState.class); + assertSame(testState, state); + } + + @Test + void setState() throws InterruptedException, TimeoutException, ExecutionException { + fixture.setState(testState); + verify(bridgeHandler).putState(BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION, + "IntrusionDetectionControl", testState); + } + + @Test + void onStateUpdate() { + final String json = "{\n" + "\"@type\": \"intrusionDetectionControlState\",\n" + "\"activeProfile\": \"0\",\n" + + "\"alarmActivationDelayTime\": 30,\n" + "\"actuators\": [\n" + "{\n" + "\"readonly\": false,\n" + + "\"active\": true,\n" + "\"id\": \"intrusion:video\"\n" + "},\n" + "{\n" + "\"readonly\": false,\n" + + "\"active\": false,\n" + "\"id\": \"intrusion:siren\"\n" + "}\n" + "],\n" + + "\"remainingTimeUntilArmed\": 28959,\n" + "\"armActivationDelayTime\": 30,\n" + "\"triggers\": [\n" + + "{\n" + "\"readonly\": false,\n" + "\"active\": true,\n" + "\"id\": \"hdm:ZigBee:000d6f0422f42378\"\n" + + "}\n" + "],\n" + "\"value\": \"SYSTEM_ARMING\"\n" + "}"; + JsonElement jsonElement = JsonParser.parseString(json); + fixture.onStateUpdate(jsonElement); + verify(consumer).accept(any()); + } + + @Test + void refreshState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(bridgeHandler.getState(anyString(), anyString(), any())).thenReturn(testState); + fixture.refreshState(); + verify(consumer).accept(testState); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java new file mode 100644 index 000000000..86229ed31 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java @@ -0,0 +1,68 @@ +/** + * 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.intrusion; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +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.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDetectionSystemState; + +/** + * Unit tests for {@link IntrusionDetectionSystemStateService}. + * + * @author David Pace - Initial contribution + * + */ +@ExtendWith(MockitoExtension.class) +class IntrusionDetectionSystemStateServiceTest { + + private IntrusionDetectionSystemStateService fixture; + + @Mock + private BridgeHandler bridgeHandler; + + @Mock + private Consumer consumer; + + @Mock + private IntrusionDetectionSystemState testState; + + @BeforeEach + void beforeEach() { + fixture = new IntrusionDetectionSystemStateService(); + fixture.initialize(bridgeHandler, BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION, consumer); + } + + @Test + void getState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(bridgeHandler.getState(anyString(), any())).thenReturn(testState); + IntrusionDetectionSystemState state = fixture.getState(); + verify(bridgeHandler).getState("intrusion/states/system", IntrusionDetectionSystemState.class); + assertSame(testState, state); + } +}