[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)
- [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.

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_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

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.
* <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;

View 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

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.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) {

View File

@ -12,28 +12,9 @@
*/
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);
}
}

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 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"/>

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