[boschshc] Support for Compact Smart Plugs (#13528) (#13533)

* 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>
This commit is contained in:
David Pace 2022-10-31 17:21:25 +01:00 committed by GitHub
parent e528564bb6
commit 71da06dac7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 559 additions and 99 deletions

View File

@ -4,9 +4,10 @@ Binding for the Bosch Smart Home.
- [Bosch Smart Home Binding](#bosch-smart-home-binding) - [Bosch Smart Home Binding](#bosch-smart-home-binding)
- [Supported Things](#supported-things) - [Supported Things](#supported-things)
- [In-Wall switches & Smart Plugs](#in-wall-switches-smart-plugs) - [In-Wall Switch](#in-wall-switch)
- [TwinGuard smoke detector](#twinguard-smoke-detector) - [Compact Smart Plug](#compact-smart-plug)
- [Door/Window contact](#door-window-contact) - [Twinguard Smoke Detector](#twinguard-smoke-detector)
- [Door/Window Contact](#door-window-contact)
- [Motion Detector](#motion-detector) - [Motion Detector](#motion-detector)
- [Shutter Control](#shutter-control) - [Shutter Control](#shutter-control)
- [Thermostat](#thermostat) - [Thermostat](#thermostat)
@ -24,19 +25,31 @@ Binding for the Bosch Smart Home.
## Supported Things ## Supported Things
### In-Wall switches & Smart Plugs ### In-Wall Switch
A simple light control. A simple light control.
**Thing Type ID**: `in-wall-switch` **Thing Type ID**: `in-wall-switch`
| Channel Type ID | Item Type | Writable | Description | | Channel Type ID | Item Type | Writable | Description |
| ------------------ | ------------- | :------: | -------------------------------------------- | | ------------------ | ------------- | :------: | ------------------------------------------------ |
| power-switch | Switch | &#9745; | Current state of the switch. | | power-switch | Switch | &#9745; | Current state of the switch. |
| power-consumption | Number:Power | &#9744; | Current power consumption (W) of the device. | | power-consumption | Number:Power | &#9744; | Current power consumption (W) of the device. |
| energy-consumption | Number:Energy | &#9744; | Energy consumption 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. 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. | | air-description | String | &#9744; | Overall description of the air quality. |
| combined-rating | String | &#9744; | Combined rating 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. Detects open windows and doors.

View File

@ -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);
}
}

View 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_CAMERA_EYES = new ThingTypeUID(BINDING_ID, "security-camera-eyes");
public static final ThingTypeUID THING_TYPE_INTRUSION_DETECTION_SYSTEM = new ThingTypeUID(BINDING_ID, public static final ThingTypeUID THING_TYPE_INTRUSION_DETECTION_SYSTEM = new ThingTypeUID(BINDING_ID,
"intrusion-detection-system"); "intrusion-detection-system");
public static final ThingTypeUID THING_TYPE_SMART_PLUG_COMPACT = new ThingTypeUID(BINDING_ID, "smart-plug-compact");
// List of all Channel IDs // List of all Channel IDs
// Auto-generated from thing-types.xml via script, don't modify // Auto-generated from thing-types.xml via script, don't modify

View 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. * The device ID of physical devices has to be configured in the thing configuration.
* <p> * <p>
* Examples for physical device IDs are: * Examples for physical device IDs are:
* *
* <pre> * <pre>
* hdm:Cameras:d20354de-44b5-3acc-924c-24c98d59da42 * hdm:Cameras:d20354de-44b5-3acc-924c-24c98d59da42
* hdm:ZigBee:000d6f0016d1cdae * hdm:ZigBee:000d6f0016d1cdae
* </pre> * </pre>
* *
* @author Stefan Kästle - Initial contribution * @author Stefan Kästle - Initial contribution
* @author Christian Oeing - refactorings of e.g. server registration * @author Christian Oeing - refactorings of e.g. server registration
* @author David Pace - Handler abstraction * @author David Pace - Handler abstraction
* *
*/ */
@NonNullByDefault @NonNullByDefault
public class BoschSHCDeviceHandler extends BoschSHCHandler { public abstract class BoschSHCDeviceHandler extends BoschSHCHandler {
/** /**
* Bosch SHC configuration loaded from openHAB configuration. * Bosch SHC configuration loaded from openHAB configuration.
@ -85,6 +85,7 @@ public class BoschSHCDeviceHandler extends BoschSHCHandler {
* *
* @return Unique id of the Bosch device. * @return Unique id of the Bosch device.
*/ */
@Override
public @Nullable String getBoschID() { public @Nullable String getBoschID() {
if (config != null) { if (config != null) {
return config.id; return config.id;

View File

@ -100,14 +100,14 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
* Returns the unique id of the Bosch device or service. * Returns the unique id of the Bosch device or service.
* <p> * <p>
* For physical devices, the ID looks like * For physical devices, the ID looks like
* *
* <pre> * <pre>
* hdm:Cameras:d20354de-44b5-3acc-924c-24c98d59da42 * hdm:Cameras:d20354de-44b5-3acc-924c-24c98d59da42
* hdm:ZigBee:000d6f0016d1c087 * hdm:ZigBee:000d6f0016d1c087
* </pre> * </pre>
* *
* For virtual devices / services, static IDs like the following are used: * For virtual devices / services, static IDs like the following are used:
* *
* <pre> * <pre>
* ventilationService * ventilationService
* smokeDetectionSystem * smokeDetectionSystem
@ -241,8 +241,29 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
protected <TService extends BoschSHCService<TState>, TState extends BoschSHCServiceState> TService createService( protected <TService extends BoschSHCService<TState>, TState extends BoschSHCServiceState> TService createService(
Supplier<TService> newService, Consumer<TState> stateUpdateListener, Collection<String> affectedChannels) Supplier<TService> newService, Consumer<TState> stateUpdateListener, Collection<String> affectedChannels)
throws BoschSHCException { 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(); TService service = newService.get();
this.registerService(service, stateUpdateListener, affectedChannels); this.registerService(service, stateUpdateListener, affectedChannels, shouldFetchInitialState);
return service; 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 * 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. * status updates for the given service.
* *
* @param <TService> Type of the service for which the state should be obtained * @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 <TState> Type of the objects to serialize and deserialize the service state
* @param service Service for which the state should be requested * @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. * Registers a write-only service that does not receive states from the bridge.
* <p> * <p>
* Examples for such services are the actions of the intrusion detection service. * Examples for such services are the actions of the intrusion detection service.
* *
* @param <TService> Type of service. * @param <TService> Type of service.
* @param service Service to register. * @param service Service to register.
* @throws BoschSHCException If no device ID is set. * @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. * 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 * @return the Bosch ID, if present
* @throws BoschSHCException if no Bosch ID is set * @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. * Requests a service to refresh its state.
* Sets the device offline if request fails. * Sets the device offline if request fails.
* *
* @param <TService> Type of service. * @param <TService> Type of service.
* @param <TState> Type of service state. * @param <TState> Type of service state.
* @param service Service to refresh state for. * @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. * Sends a HTTP POST request with empty body.
* *
* @param <TService> Type of service. * @param <TService> Type of service.
* @param service Service implementing the action * @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. * Sends a HTTP POST request with the given request body.
* *
* @param <TService> Type of service. * @param <TService> Type of service.
* @param <TState> Type of the request to be sent. * @param <TState> Type of the request to be sent.
* @param service Service implementing the action * @param service Service implementing the action

View 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.intrusion.IntrusionDetectionHandler;
import org.openhab.binding.boschshc.internal.devices.lightcontrol.LightControlHandler; 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.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.shuttercontrol.ShutterControlHandler;
import org.openhab.binding.boschshc.internal.devices.thermostat.ThermostatHandler; import org.openhab.binding.boschshc.internal.devices.thermostat.ThermostatHandler;
import org.openhab.binding.boschshc.internal.devices.twinguard.TwinguardHandler; 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 Stefan Kästle - Initial contribution
* @author Christian Oeing - Added Shutter Control and ThermostatHandler; refactored handler mapping * @author Christian Oeing - Added Shutter Control and ThermostatHandler; refactored handler mapping
* @author Christian Oeing - Added WallThermostatHandler * @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 @NonNullByDefault
@Component(configurationPid = "binding.boschshc", service = ThingHandlerFactory.class) @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_WALL_THERMOSTAT, WallThermostatHandler::new),
new ThingTypeHandlerMapping(THING_TYPE_CAMERA_360, CameraHandler::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)); new ThingTypeHandlerMapping(THING_TYPE_INTRUSION_DETECTION_SYSTEM, IntrusionDetectionHandler::new),
new ThingTypeHandlerMapping(THING_TYPE_SMART_PLUG_COMPACT, PlugHandler::new));
@Override @Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) { public boolean supportsThingType(ThingTypeUID thingTypeUID) {

View File

@ -12,28 +12,9 @@
*/ */
package org.openhab.binding.boschshc.internal.devices.lightcontrol; 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.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler; import org.openhab.binding.boschshc.internal.devices.AbstractPowerSwitchHandler;
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.thing.Thing;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
/** /**
* A simple light control. * A simple light control.
@ -41,61 +22,9 @@ import org.openhab.core.types.State;
* @author Stefan Kästle - Initial contribution * @author Stefan Kästle - Initial contribution
*/ */
@NonNullByDefault @NonNullByDefault
public class LightControlHandler extends BoschSHCDeviceHandler { public class LightControlHandler extends AbstractPowerSwitchHandler {
private final PowerSwitchService powerSwitchService;
public LightControlHandler(Thing thing) { public LightControlHandler(Thing thing) {
super(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);
} }
} }

View File

@ -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);
}
}

View File

@ -30,6 +30,24 @@
</thing-type> </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"> <thing-type id="twinguard">
<supported-bridge-type-refs> <supported-bridge-type-refs>
<bridge-type-ref id="shc"/> <bridge-type-ref id="shc"/>

View File

@ -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();
}

View File

@ -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());
}
}

View File

@ -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));
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}