- [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)
## 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 | ☑ | Current state of the switch. |
-| power-consumption | Number:Power | ☐ | Current power consumption (W) of the device. |
-| energy-consumption | Number:Energy | ☐ | Energy consumption of the device. |
+| Channel Type ID | Item Type | Writable | Description |
+| ------------------ | ------------- | :------: | ------------------------------------------------ |
+| power-switch | Switch | ☑ | Current state of the switch. |
+| power-consumption | Number:Power | ☐ | Current power consumption (W) of the device. |
+| energy-consumption | Number:Energy | ☐ | Cumulated energy consumption (Wh) of the device. |
-### TwinGuard smoke detector
+### 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 | ☑ | Current state of the switch. |
+| power-consumption | Number:Power | ☐ | Current power consumption (W) of the device. |
+| energy-consumption | Number:Energy | ☐ | Cumulated energy consumption (Wh) of the device. |
+
+### Twinguard smoke detector
The Twinguard smoke detector warns you in case of fire and constantly monitors the air.
| air-description | String | ☐ | Overall description of the air quality. |
| combined-rating | String | ☐ | Combined rating of the air quality. |
-### Door/Window contact
+### Door/Window Contact
Detects open windows and doors.
--- /dev/null
+/**
+ * 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);
+ }
+}
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
* 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.
*
* @return Unique id of the Bosch device.
*/
+ @Override
public @Nullable String getBoschID() {
if (config != null) {
return config.id;
* 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
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;
}
/**
* 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
* 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.
/**
* 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
*/
/**
* 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.
/**
* Sends a HTTP POST request with empty body.
- *
+ *
* @param <TService> Type of service.
* @param service Service implementing the action
*/
/**
* 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
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;
* @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)
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) {
*/
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.
* @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);
}
}
--- /dev/null
+/**
+ * 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);
+ }
+}
</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"/>
--- /dev/null
+/**
+ * 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();
+}
--- /dev/null
+/**
+ * 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());
+ }
+}
--- /dev/null
+/**
+ * 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));
+ }
+}
--- /dev/null
+/**
+ * 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());
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}