]> git.basschouten.com Git - openhab-addons.git/commitdiff
[boschshc] Support for Compact Smart Plugs (#13528) (#13533)
authorDavid Pace <dev@davidpace.de>
Mon, 31 Oct 2022 16:21:25 +0000 (17:21 +0100)
committerGitHub <noreply@github.com>
Mon, 31 Oct 2022 16:21:25 +0000 (17:21 +0100)
* add thing definition with ID "smart-plug-compact"
* add constant for thing type UID
* extract abstract implementation for devices with power switch and
energy monitoring
* let in-wall switch handler and smart plug handler extend the abstract
implementation
* register new handler
* add method with boolean parameter to fetch initial state actively
* make BoschSHCDeviceHandler abstract
* add documentation
* add unit tests

closes #13528

Signed-off-by: David Pace <dev@davidpace.de>
14 files changed:
bundles/org.openhab.binding.boschshc/README.md
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCDeviceHandler.java
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandler.java
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandlerFactory.java
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/lightcontrol/LightControlHandler.java
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/plug/PlugHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.boschshc/src/main/resources/OH-INF/thing/thing-types.xml
bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCDeviceHandlerTest.java [new file with mode: 0644]
bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java [new file with mode: 0644]
bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSHCHandlerTest.java [new file with mode: 0644]
bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/lightcontrol/LightControlHandlerTest.java [new file with mode: 0644]
bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/plug/PlugHandlerTest.java [new file with mode: 0644]

index 9331afa9fd0c6d8a5208b82e598b4a1ce6b39d9f..979f54659e49664a4d892ab888b43872a9817674 100644 (file)
@@ -4,9 +4,10 @@ Binding for the Bosch Smart Home.
 
 - [Bosch Smart Home Binding](#bosch-smart-home-binding)
   - [Supported Things](#supported-things)
-    - [In-Wall switches & Smart Plugs](#in-wall-switches-smart-plugs)
-    - [TwinGuard smoke detector](#twinguard-smoke-detector)
-    - [Door/Window contact](#door-window-contact)
+    - [In-Wall Switch](#in-wall-switch)
+    - [Compact Smart Plug](#compact-smart-plug)
+    - [Twinguard Smoke Detector](#twinguard-smoke-detector)
+    - [Door/Window Contact](#door-window-contact)
     - [Motion Detector](#motion-detector)
     - [Shutter Control](#shutter-control)
     - [Thermostat](#thermostat)
@@ -24,19 +25,31 @@ Binding for the Bosch Smart Home.
 
 ## Supported Things
 
-### In-Wall switches & Smart Plugs
+### In-Wall Switch
 
 A simple light control.
 
 **Thing Type ID**: `in-wall-switch`
 
-| Channel Type ID    | Item Type     | Writable | Description                                  |
-| ------------------ | ------------- | :------: | -------------------------------------------- |
-| power-switch       | Switch        | &#9745;  | Current state of the switch.                 |
-| power-consumption  | Number:Power  | &#9744;  | Current power consumption (W) of the device. |
-| energy-consumption | Number:Energy | &#9744;  | Energy consumption of the device.            |
+| Channel Type ID    | Item Type     | Writable | Description                                      |
+| ------------------ | ------------- | :------: | ------------------------------------------------ |
+| power-switch       | Switch        | &#9745;  | Current state of the switch.                     |
+| power-consumption  | Number:Power  | &#9744;  | Current power consumption (W) of the device.     |
+| energy-consumption | Number:Energy | &#9744;  | Cumulated energy consumption (Wh) of the device. |
 
-### TwinGuard smoke detector
+### Compact Smart Plug
+
+A compact smart plug with energy monitoring capabilities.
+
+**Thing Type ID**: `smart-plug-compact`
+
+| Channel Type ID    | Item Type     | Writable | Description                                      |
+| ------------------ | ------------- | :------: | ------------------------------------------------ |
+| power-switch       | Switch        | &#9745;  | Current state of the switch.                     |
+| power-consumption  | Number:Power  | &#9744;  | Current power consumption (W) of the device.     |
+| energy-consumption | Number:Energy | &#9744;  | Cumulated energy consumption (Wh) of the device. |
+
+### Twinguard smoke detector
 
 The Twinguard smoke detector warns you in case of fire and constantly monitors the air.
 
@@ -53,7 +66,7 @@ The Twinguard smoke detector warns you in case of fire and constantly monitors t
 | air-description    | String               | &#9744;  | Overall description of the air quality.                                                           |
 | combined-rating    | String               | &#9744;  | Combined rating of the air quality.                                                               |
 
-### Door/Window contact
+### Door/Window Contact
 
 Detects open windows and doors.
 
diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandler.java
new file mode 100644 (file)
index 0000000..21503db
--- /dev/null
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.boschshc.internal.devices;
+
+import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.*;
+
+import java.util.List;
+
+import javax.measure.quantity.Energy;
+import javax.measure.quantity.Power;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+import org.openhab.binding.boschshc.internal.services.powerswitch.PowerSwitchService;
+import org.openhab.binding.boschshc.internal.services.powerswitch.PowerSwitchState;
+import org.openhab.binding.boschshc.internal.services.powerswitch.dto.PowerSwitchServiceState;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.State;
+
+/**
+ * Abstract handler implementation for devices with power switches and energy monitoring.
+ * <p>
+ * This implementation provides the functionality to
+ * <ul>
+ * <li>Switch the device on and off using the <code>PowerSwitch</code> service</li>
+ * <li>Measuring the current power consumption and the overall energy consumption using the <code>PowerMeter</code>
+ * service</li>
+ * </ul>
+ *
+ * @author David Pace - Initial contribution (extracted from LightControlHandler)
+ */
+@NonNullByDefault
+public abstract class AbstractPowerSwitchHandler extends BoschSHCDeviceHandler {
+
+    /**
+     * Service for switching the device on and off
+     */
+    private final PowerSwitchService powerSwitchService;
+
+    protected AbstractPowerSwitchHandler(Thing thing) {
+        super(thing);
+        this.powerSwitchService = new PowerSwitchService();
+    }
+
+    @Override
+    protected void initializeServices() throws BoschSHCException {
+        super.initializeServices();
+
+        this.registerService(this.powerSwitchService, this::updateChannels, List.of(CHANNEL_POWER_SWITCH), true);
+        this.createService(PowerMeterService::new, this::updateChannels,
+                List.of(CHANNEL_POWER_CONSUMPTION, CHANNEL_ENERGY_CONSUMPTION), true);
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        super.handleCommand(channelUID, command);
+
+        switch (channelUID.getId()) {
+            case CHANNEL_POWER_SWITCH:
+                if (command instanceof OnOffType) {
+                    updatePowerSwitchState((OnOffType) command);
+                }
+                break;
+        }
+    }
+
+    /**
+     * Updates the channels which are linked to the {@link PowerMeterService} of the device.
+     *
+     * @param state Current state of {@link PowerMeterService}.
+     */
+    private void updateChannels(PowerMeterServiceState state) {
+        super.updateState(CHANNEL_POWER_CONSUMPTION, new QuantityType<Power>(state.powerConsumption, Units.WATT));
+        super.updateState(CHANNEL_ENERGY_CONSUMPTION,
+                new QuantityType<Energy>(state.energyConsumption, Units.WATT_HOUR));
+    }
+
+    /**
+     * Updates the channels which are linked to the {@link PowerSwitchService} of the device.
+     *
+     * @param state Current state of {@link PowerSwitchService}.
+     */
+    private void updateChannels(PowerSwitchServiceState state) {
+        State powerState = OnOffType.from(state.switchState.toString());
+        super.updateState(CHANNEL_POWER_SWITCH, powerState);
+    }
+
+    private void updatePowerSwitchState(OnOffType command) {
+        PowerSwitchServiceState state = new PowerSwitchServiceState();
+        state.switchState = PowerSwitchState.valueOf(command.toFullString());
+        this.updateServiceState(this.powerSwitchService, state);
+    }
+}
index 6b3cf4e3eb5c65569de726d2e5702923843d40a9..7097deba80a60e6998aa0b976c60c03c56d2cf10 100644 (file)
@@ -44,6 +44,7 @@ public class BoschSHCBindingConstants {
     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");
+    public static final ThingTypeUID THING_TYPE_SMART_PLUG_COMPACT = new ThingTypeUID(BINDING_ID, "smart-plug-compact");
 
     // List of all Channel IDs
     // Auto-generated from thing-types.xml via script, don't modify
index a7e1fc88fbb6815ff141de1559cbac5b0414532e..7479efd09b4088245c09fc866e433130c32d2a5e 100644 (file)
@@ -28,19 +28,19 @@ import org.openhab.core.thing.ThingStatusDetail;
  * The device ID of physical devices has to be configured in the thing configuration.
  * <p>
  * Examples for physical device IDs are:
- * 
+ *
  * <pre>
  * hdm:Cameras:d20354de-44b5-3acc-924c-24c98d59da42
  * hdm:ZigBee:000d6f0016d1cdae
  * </pre>
- * 
+ *
  * @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 {
+public abstract class BoschSHCDeviceHandler extends BoschSHCHandler {
 
     /**
      * Bosch SHC configuration loaded from openHAB configuration.
@@ -85,6 +85,7 @@ public class BoschSHCDeviceHandler extends BoschSHCHandler {
      *
      * @return Unique id of the Bosch device.
      */
+    @Override
     public @Nullable String getBoschID() {
         if (config != null) {
             return config.id;
index 5fbbe50a65341c9bc395afa7981680705bcb0773..9e3d7983699878b088f40cb62c88ea2a7d26799e 100644 (file)
@@ -100,14 +100,14 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
      * Returns the unique id of the Bosch device or service.
      * <p>
      * For physical devices, the ID looks like
-     * 
+     *
      * <pre>
      * hdm:Cameras:d20354de-44b5-3acc-924c-24c98d59da42
      * hdm:ZigBee:000d6f0016d1c087
      * </pre>
-     * 
+     *
      * For virtual devices / services, static IDs like the following are used:
-     * 
+     *
      * <pre>
      * ventilationService
      * smokeDetectionSystem
@@ -241,8 +241,29 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
     protected <TService extends BoschSHCService<TState>, TState extends BoschSHCServiceState> TService createService(
             Supplier<TService> newService, Consumer<TState> stateUpdateListener, Collection<String> affectedChannels)
             throws BoschSHCException {
+        return createService(newService, stateUpdateListener, affectedChannels, false);
+    }
+
+    /**
+     * Creates and registers a new service for this device.
+     *
+     * @param <TService> Type of service.
+     * @param <TState> Type of service state.
+     * @param newService Supplier function to create a new instance of the service.
+     * @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.
+     * @return Instance of registered service.
+     * @throws BoschSHCException
+     */
+    protected <TService extends BoschSHCService<TState>, TState extends BoschSHCServiceState> TService createService(
+            Supplier<TService> newService, Consumer<TState> stateUpdateListener, Collection<String> affectedChannels,
+            boolean shouldFetchInitialState) throws BoschSHCException {
         TService service = newService.get();
-        this.registerService(service, stateUpdateListener, affectedChannels);
+        this.registerService(service, stateUpdateListener, affectedChannels, shouldFetchInitialState);
         return service;
     }
 
@@ -296,7 +317,7 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
     /**
      * 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 <TService> Type of the service for which the state should be obtained
      * @param <TState> Type of the objects to serialize and deserialize the service state
      * @param service Service for which the state should be requested
@@ -325,7 +346,7 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
      * Registers a write-only service that does not receive states from the bridge.
      * <p>
      * Examples for such services are the actions of the intrusion detection service.
-     * 
+     *
      * @param <TService> Type of service.
      * @param service Service to register.
      * @throws BoschSHCException If no device ID is set.
@@ -340,7 +361,7 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
 
     /**
      * 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
      */
@@ -404,7 +425,7 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
     /**
      * Requests a service to refresh its state.
      * Sets the device offline if request fails.
-     * 
+     *
      * @param <TService> Type of service.
      * @param <TState> Type of service state.
      * @param service Service to refresh state for.
@@ -438,7 +459,7 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
 
     /**
      * Sends a HTTP POST request with empty body.
-     * 
+     *
      * @param <TService> Type of service.
      * @param service Service implementing the action
      */
@@ -457,7 +478,7 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
 
     /**
      * Sends a HTTP POST request with the given request body.
-     * 
+     *
      * @param <TService> Type of service.
      * @param <TState> Type of the request to be sent.
      * @param service Service implementing the action
index b686acb5172faae2edd0ebbcb691e41f2f40e275..81dffc59f60c52e8993ee80f47eabbb258fd5d8e 100644 (file)
@@ -26,6 +26,7 @@ import org.openhab.binding.boschshc.internal.devices.climatecontrol.ClimateContr
 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.plug.PlugHandler;
 import org.openhab.binding.boschshc.internal.devices.shuttercontrol.ShutterControlHandler;
 import org.openhab.binding.boschshc.internal.devices.thermostat.ThermostatHandler;
 import org.openhab.binding.boschshc.internal.devices.twinguard.TwinguardHandler;
@@ -47,7 +48,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
+ * @author David Pace - Added cameras, intrusion detection system and smart plugs
  */
 @NonNullByDefault
 @Component(configurationPid = "binding.boschshc", service = ThingHandlerFactory.class)
@@ -75,7 +76,8 @@ public class BoschSHCHandlerFactory extends BaseThingHandlerFactory {
             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_INTRUSION_DETECTION_SYSTEM, IntrusionDetectionHandler::new));
+            new ThingTypeHandlerMapping(THING_TYPE_INTRUSION_DETECTION_SYSTEM, IntrusionDetectionHandler::new),
+            new ThingTypeHandlerMapping(THING_TYPE_SMART_PLUG_COMPACT, PlugHandler::new));
 
     @Override
     public boolean supportsThingType(ThingTypeUID thingTypeUID) {
index 2dde138cee857fba0d24d9b1c6b90d0d99b2ddc6..7c651baba83a02a14f8b32e5f6ca6e0372240d76 100644 (file)
  */
 package org.openhab.binding.boschshc.internal.devices.lightcontrol;
 
-import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.*;
-
-import java.util.List;
-
-import javax.measure.quantity.Energy;
-import javax.measure.quantity.Power;
-
 import org.eclipse.jdt.annotation.NonNullByDefault;
-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;
-import org.openhab.binding.boschshc.internal.services.powerswitch.PowerSwitchService;
-import org.openhab.binding.boschshc.internal.services.powerswitch.PowerSwitchState;
-import org.openhab.binding.boschshc.internal.services.powerswitch.dto.PowerSwitchServiceState;
-import org.openhab.core.library.types.OnOffType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.ChannelUID;
+import org.openhab.binding.boschshc.internal.devices.AbstractPowerSwitchHandler;
 import org.openhab.core.thing.Thing;
-import org.openhab.core.types.Command;
-import org.openhab.core.types.State;
 
 /**
  * A simple light control.
@@ -41,61 +22,9 @@ import org.openhab.core.types.State;
  * @author Stefan Kästle - Initial contribution
  */
 @NonNullByDefault
-public class LightControlHandler extends BoschSHCDeviceHandler {
-
-    private final PowerSwitchService powerSwitchService;
+public class LightControlHandler extends AbstractPowerSwitchHandler {
 
     public LightControlHandler(Thing thing) {
         super(thing);
-        this.powerSwitchService = new PowerSwitchService();
-    }
-
-    @Override
-    protected void initializeServices() throws BoschSHCException {
-        super.initializeServices();
-
-        this.registerService(this.powerSwitchService, this::updateChannels, List.of(CHANNEL_POWER_SWITCH));
-        this.createService(PowerMeterService::new, this::updateChannels,
-                List.of(CHANNEL_POWER_CONSUMPTION, CHANNEL_ENERGY_CONSUMPTION));
-    }
-
-    @Override
-    public void handleCommand(ChannelUID channelUID, Command command) {
-        super.handleCommand(channelUID, command);
-
-        switch (channelUID.getId()) {
-            case CHANNEL_POWER_SWITCH:
-                if (command instanceof OnOffType) {
-                    updatePowerSwitchState((OnOffType) command);
-                }
-                break;
-        }
-    }
-
-    /**
-     * Updates the channels which are linked to the {@link PowerMeterService} of the device.
-     * 
-     * @param state Current state of {@link PowerMeterService}.
-     */
-    private void updateChannels(PowerMeterServiceState state) {
-        super.updateState(CHANNEL_POWER_CONSUMPTION, new QuantityType<Power>(state.powerConsumption, Units.WATT));
-        super.updateState(CHANNEL_ENERGY_CONSUMPTION,
-                new QuantityType<Energy>(state.energyConsumption, Units.WATT_HOUR));
-    }
-
-    /**
-     * Updates the channels which are linked to the {@link PowerSwitchService} of the device.
-     * 
-     * @param state Current state of {@link PowerSwitchService}.
-     */
-    private void updateChannels(PowerSwitchServiceState state) {
-        State powerState = OnOffType.from(state.switchState.toString());
-        super.updateState(CHANNEL_POWER_SWITCH, powerState);
-    }
-
-    private void updatePowerSwitchState(OnOffType command) {
-        PowerSwitchServiceState state = new PowerSwitchServiceState();
-        state.switchState = PowerSwitchState.valueOf(command.toFullString());
-        this.updateServiceState(this.powerSwitchService, state);
     }
 }
diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/plug/PlugHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/plug/PlugHandler.java
new file mode 100644 (file)
index 0000000..927e678
--- /dev/null
@@ -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.devices.plug;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.boschshc.internal.devices.AbstractPowerSwitchHandler;
+import org.openhab.core.thing.Thing;
+
+/**
+ * A handler for compact smart plugs.
+ *
+ * @author David Pace - Initial contribution
+ */
+@NonNullByDefault
+public class PlugHandler extends AbstractPowerSwitchHandler {
+
+    public PlugHandler(Thing thing) {
+        super(thing);
+    }
+}
index 34c55fc801c580e99db0ecfea6d7d28140516d2a..12c1dda89eacb36b142fe494f19149e47c1c7d76 100644 (file)
 
        </thing-type>
 
+       <thing-type id="smart-plug-compact">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="shc"/>
+               </supported-bridge-type-refs>
+
+               <label>Compact Smart Plug</label>
+               <description>A compact smart plug with energy monitoring capabilities.</description>
+
+               <channels>
+                       <channel id="power-switch" typeId="system.power"/>
+                       <channel id="power-consumption" typeId="power-consumption"/>
+                       <channel id="energy-consumption" typeId="energy-consumption"/>
+               </channels>
+
+               <config-description-ref uri="thing-type:boschshc:device"/>
+
+       </thing-type>
+
        <thing-type id="twinguard">
                <supported-bridge-type-refs>
                        <bridge-type-ref id="shc"/>
diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCDeviceHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCDeviceHandlerTest.java
new file mode 100644 (file)
index 0000000..f5f9608
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * 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 org.openhab.core.config.core.Configuration;
+
+/**
+ * Abstract unit test implementation for device handlers.
+ *
+ * @author David Pace - Initial contribution
+ *
+ * @param <T> type of the device handler to be tested
+ */
+public abstract class AbstractBoschSHCDeviceHandlerTest<T extends BoschSHCDeviceHandler>
+        extends AbstractSHCHandlerTest<T> {
+
+    @Override
+    protected Configuration getConfiguration() {
+        Configuration configuration = super.getConfiguration();
+        configuration.put("id", getDeviceID());
+        return configuration;
+    }
+
+    protected abstract String getDeviceID();
+}
diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java
new file mode 100644 (file)
index 0000000..38432d5
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.boschshc.internal.devices;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import javax.measure.quantity.Energy;
+import javax.measure.quantity.Power;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
+import org.openhab.binding.boschshc.internal.services.powerswitch.PowerSwitchState;
+import org.openhab.binding.boschshc.internal.services.powerswitch.dto.PowerSwitchServiceState;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+/**
+ * Abstract unit test implementation for devices with power switches and energy monitoring.
+ *
+ * @author David Pace - Initial contribution
+ *
+ * @param <T> type of the handler to be tested
+ */
+public abstract class AbstractPowerSwitchHandlerTest<T extends AbstractPowerSwitchHandler>
+        extends AbstractBoschSHCDeviceHandlerTest<T> {
+
+    @Captor
+    private ArgumentCaptor<PowerSwitchServiceState> serviceStateCaptor;
+
+    @Captor
+    private ArgumentCaptor<QuantityType<Power>> powerCaptor;
+
+    @Captor
+    private ArgumentCaptor<QuantityType<Energy>> energyCaptor;
+
+    @Test
+    public void testHandleCommand()
+            throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
+
+        when(getThing().getUID()).thenReturn(new ThingUID("boschshc", "abcdef"));
+
+        getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_POWER_SWITCH),
+                OnOffType.ON);
+        verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("PowerSwitch"), serviceStateCaptor.capture());
+        PowerSwitchServiceState state = serviceStateCaptor.getValue();
+        assertSame(PowerSwitchState.ON, state.switchState);
+
+        getFixture().handleCommand(new ChannelUID(new ThingUID(getThingTypeUID(), "abcdef"),
+                BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF);
+        verify(getBridgeHandler(), times(2)).putState(eq(getDeviceID()), eq("PowerSwitch"),
+                serviceStateCaptor.capture());
+        state = serviceStateCaptor.getValue();
+        assertSame(PowerSwitchState.OFF, state.switchState);
+    }
+
+    protected abstract ThingTypeUID getThingTypeUID();
+
+    @Test
+    public void testUpdateChannel_PowerSwitchState() {
+        when(getThing().getUID()).thenReturn(new ThingUID("boschshc", "abcdef"));
+
+        JsonElement jsonObject = JsonParser
+                .parseString("{\n" + "  \"@type\": \"powerSwitchState\",\n" + "  \"switchState\": \"ON\"\n" + "}");
+        getFixture().processUpdate("PowerSwitch", jsonObject);
+        verify(getCallback()).stateUpdated(
+                new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);
+
+        jsonObject = JsonParser
+                .parseString("{\n" + "  \"@type\": \"powerSwitchState\",\n" + "  \"switchState\": \"OFF\"\n" + "}");
+        getFixture().processUpdate("PowerSwitch", jsonObject);
+        verify(getCallback()).stateUpdated(
+                new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF);
+    }
+
+    @Test
+    public void testUpdateChannel_PowerMeterServiceState() {
+        when(getThing().getUID()).thenReturn(new ThingUID("boschshc", "abcdef"));
+
+        JsonElement jsonObject = JsonParser.parseString("{\n" + "  \"@type\": \"powerMeterState\",\n"
+                + "  \"powerConsumption\": \"23\",\n" + "  \"energyConsumption\": 42\n" + "}");
+        getFixture().processUpdate("PowerMeter", jsonObject);
+
+        verify(getCallback()).stateUpdated(
+                eq(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)),
+                powerCaptor.capture());
+        QuantityType<Power> powerValue = powerCaptor.getValue();
+        assertEquals(23, powerValue.intValue());
+
+        verify(getCallback()).stateUpdated(
+                eq(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)),
+                energyCaptor.capture());
+        QuantityType<Energy> energyValue = energyCaptor.getValue();
+        assertEquals(42, energyValue.intValue());
+    }
+}
diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSHCHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSHCHandlerTest.java
new file mode 100644 (file)
index 0000000..872ca53
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.boschshc.internal.devices;
+
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingStatusInfo;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandlerCallback;
+
+/**
+ * Abstract unit test implementation for all types of handlers.
+ *
+ * @author David Pace - Initial contribution
+ *
+ * @param <T> type of the handler to be tested
+ */
+@ExtendWith(MockitoExtension.class)
+public abstract class AbstractSHCHandlerTest<T extends BoschSHCHandler> {
+
+    private T fixture;
+
+    @Mock
+    private Thing thing;
+
+    @Mock
+    private Bridge bridge;
+
+    @Mock
+    private BridgeHandler bridgeHandler;
+
+    @Mock
+    private ThingHandlerCallback callback;
+
+    @BeforeEach
+    public void beforeEach() {
+        fixture = createFixture();
+        when(thing.getBridgeUID()).thenReturn(new ThingUID("boschshc", "shc", "myBridgeUID"));
+        when(callback.getBridge(any())).thenReturn(bridge);
+        fixture.setCallback(callback);
+        when(bridge.getHandler()).thenReturn(bridgeHandler);
+        when(thing.getConfiguration()).thenReturn(getConfiguration());
+
+        fixture.initialize();
+    }
+
+    protected abstract T createFixture();
+
+    protected T getFixture() {
+        return fixture;
+    }
+
+    protected Configuration getConfiguration() {
+        return new Configuration();
+    }
+
+    protected Thing getThing() {
+        return thing;
+    }
+
+    public BridgeHandler getBridgeHandler() {
+        return bridgeHandler;
+    }
+
+    public ThingHandlerCallback getCallback() {
+        return callback;
+    }
+
+    @Test
+    public void testInitialize() {
+        ThingStatusInfo expectedStatusInfo = new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
+        verify(callback).statusUpdated(same(thing), eq(expectedStatusInfo));
+    }
+}
diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/lightcontrol/LightControlHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/lightcontrol/LightControlHandlerTest.java
new file mode 100644 (file)
index 0000000..94ae904
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.boschshc.internal.devices.lightcontrol;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.boschshc.internal.devices.AbstractPowerSwitchHandlerTest;
+import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * Unit tests for {@link LightControlHandler}.
+ *
+ * @author David Pace - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class LightControlHandlerTest extends AbstractPowerSwitchHandlerTest<LightControlHandler> {
+
+    @Override
+    protected ThingTypeUID getThingTypeUID() {
+        return BoschSHCBindingConstants.THING_TYPE_INWALL_SWITCH;
+    }
+
+    @Override
+    protected String getDeviceID() {
+        return "hdm:ZigBee:50325ffffe61d7b9c6e";
+    }
+
+    @Override
+    protected LightControlHandler createFixture() {
+        return new LightControlHandler(getThing());
+    }
+}
diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/plug/PlugHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/plug/PlugHandlerTest.java
new file mode 100644 (file)
index 0000000..0c28591
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.boschshc.internal.devices.plug;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.boschshc.internal.devices.AbstractPowerSwitchHandlerTest;
+import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * Unit tests for {@link PlugHandler}.
+ *
+ * @author David Pace - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class PlugHandlerTest extends AbstractPowerSwitchHandlerTest<PlugHandler> {
+
+    @Override
+    protected PlugHandler createFixture() {
+        return new PlugHandler(getThing());
+    }
+
+    @Override
+    protected String getDeviceID() {
+        return "hdm:ZigBee:50325ffffe61d7b9c6e";
+    }
+
+    @Override
+    protected ThingTypeUID getThingTypeUID() {
+        return BoschSHCBindingConstants.THING_TYPE_SMART_PLUG_COMPACT;
+    }
+}