mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[boschshc] Add support for Light/Shutter Control II (#16400)
* [boschshc] Add support for Shutter Control II (#14562) * add new channel type for child protection Signed-off-by: David Pace <dev@davidpace.de> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
bd2f2d4cdb
commit
d3a107d9b7
@ -1,6 +1,6 @@
|
||||
# Bosch Smart Home Binding
|
||||
|
||||
Binding for the Bosch Smart Home.
|
||||
Binding for Bosch Smart Home devices.
|
||||
|
||||
- [Bosch Smart Home Binding](#bosch-smart-home-binding)
|
||||
- [Supported Things](#supported-things)
|
||||
@ -10,8 +10,10 @@ Binding for the Bosch Smart Home.
|
||||
- [Twinguard Smoke Detector](#twinguard-smoke-detector)
|
||||
- [Door/Window Contact](#door-window-contact)
|
||||
- [Door/Window Contact II](#door-window-contact-ii)
|
||||
- [Light Control II](#light-control-ii)
|
||||
- [Motion Detector](#motion-detector)
|
||||
- [Shutter Control](#shutter-control)
|
||||
- [Shutter Control II](#shutter-control-ii)
|
||||
- [Thermostat](#thermostat)
|
||||
- [Climate Control](#climate-control)
|
||||
- [Wall Thermostat](#wall-thermostat)
|
||||
@ -114,6 +116,22 @@ Detects open windows and doors and features an additional button.
|
||||
| bypass | Switch | ☐ | Indicates whether the device is currently bypassed. Possible values are `ON`,`OFF` and `UNDEF` if the bypass state cannot be determined. |
|
||||
| signal-strength | Number | ☐ | Communication quality between the device and the Smart Home Controller. Possible values range between 0 (unknown) and 4 (best signal strength). |
|
||||
|
||||
### Light Control II
|
||||
|
||||
This thing type is used if Light/Shutter Control II devices are configured as light controls.
|
||||
|
||||
**Thing Type ID**: `light-control-2`
|
||||
|
||||
| Channel Type ID | Item Type | Writable | Description |
|
||||
| ------------------ | ------------- | :------: | ------------------------------------------------------------- |
|
||||
| signal-strength | Number | ☐ | Communication quality between the device and the Smart Home Controller. Possible values range between 0 (unknown) and 4 (best signal strength). |
|
||||
| power-consumption | Number:Power | ☐ | Current power consumption (W) of the device. |
|
||||
| energy-consumption | Number:Energy | ☐ | Cumulated energy consumption (Wh) of the device. |
|
||||
| power-switch-1 | Switch | ☑ | Switches the light on or off (circuit 1). |
|
||||
| child-protection-1 | Switch | ☑ | Indicates whether the child protection is active (circuit 1). |
|
||||
| power-switch-2 | Switch | ☑ | Switches the light on or off (circuit 2). |
|
||||
| child-protection-2 | Switch | ☑ | Indicates whether the child protection is active (circuit 2). |
|
||||
|
||||
### Motion Detector
|
||||
|
||||
Detects every movement through an intelligent combination of passive infra-red technology and an additional temperature sensor.
|
||||
@ -137,6 +155,20 @@ Control of your shutter to take any position you desire.
|
||||
| --------------- | ------------- | :------: | ---------------------------------------- |
|
||||
| level | Rollershutter | ☑ | Current open ratio (0 to 100, Step 0.5). |
|
||||
|
||||
### Shutter Control II
|
||||
|
||||
This thing type is used if Light/Shutter Control II devices are configured as shutter controls.
|
||||
|
||||
**Thing Type ID**: `shutter-control-2`
|
||||
|
||||
| Channel Type ID | Item Type | Writable | Description |
|
||||
| ------------------ | ------------- | :------: | ------------------------------------------------- |
|
||||
| level | Rollershutter | ☑ | Current open ratio (0 to 100, Step 0.5). |
|
||||
| signal-strength | Number | ☐ | Communication quality between the device and the Smart Home Controller. Possible values range between 0 (unknown) and 4 (best signal strength). |
|
||||
| child-protection | Switch | ☑ | Indicates whether the child protection is active. |
|
||||
| power-consumption | Number:Power | ☐ | Current power consumption (W) of the device. |
|
||||
| energy-consumption | Number:Energy | ☐ | Cumulated energy consumption (Wh) of the device. |
|
||||
|
||||
### Thermostat
|
||||
|
||||
Radiator thermostat
|
||||
|
@ -79,10 +79,10 @@ public class BoschShcCommandExtension extends AbstractConsoleCommandExtension im
|
||||
*/
|
||||
List<String> getAllBoschShcServices() {
|
||||
return List.of("airqualitylevel", "batterylevel", "binaryswitch", "bypass", "cameranotification", "childlock",
|
||||
"communicationquality", "hsbcoloractuator", "humiditylevel", "illuminance", "intrusion", "keypad",
|
||||
"latestmotion", "multilevelswitch", "powermeter", "powerswitch", "privacymode", "roomclimatecontrol",
|
||||
"shuttercontact", "shuttercontrol", "silentmode", "smokedetectorcheck", "temperaturelevel", "userstate",
|
||||
"valvetappet");
|
||||
"childprotection", "communicationquality", "hsbcoloractuator", "humiditylevel", "illuminance",
|
||||
"intrusion", "keypad", "latestmotion", "multilevelswitch", "powermeter", "powerswitch", "privacymode",
|
||||
"roomclimatecontrol", "shuttercontact", "shuttercontrol", "silentmode", "smokedetectorcheck",
|
||||
"temperaturelevel", "userstate", "valvetappet");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -12,20 +12,16 @@
|
||||
*/
|
||||
package org.openhab.binding.boschshc.internal.devices;
|
||||
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.*;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_POWER_SWITCH;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
@ -61,8 +57,6 @@ public abstract class AbstractPowerSwitchHandler extends BoschSHCDeviceHandler {
|
||||
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
|
||||
@ -79,19 +73,9 @@ public abstract class AbstractPowerSwitchHandler extends BoschSHCDeviceHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the channels which are linked to the {@link PowerMeterService} of the device.
|
||||
* Updates the power switch channel when a new state is received.
|
||||
*
|
||||
* @param state Current state of {@link PowerMeterService}.
|
||||
*/
|
||||
private void updateChannels(PowerMeterServiceState state) {
|
||||
super.updateState(CHANNEL_POWER_CONSUMPTION, new QuantityType<>(state.powerConsumption, Units.WATT));
|
||||
super.updateState(CHANNEL_ENERGY_CONSUMPTION, new QuantityType<>(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}.
|
||||
* @param state the new {@link PowerSwitchService} state.
|
||||
*/
|
||||
private void updateChannels(PowerSwitchServiceState state) {
|
||||
State powerState = OnOffType.from(state.switchState.toString());
|
||||
|
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.CHANNEL_ENERGY_CONSUMPTION;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
||||
/**
|
||||
* Abstract handler implementation for devices providing a {@link PowerSwitchService} and a {@link PowerMeterService}.
|
||||
* <p>
|
||||
* Examples for such devices are smart plugs and in-wall switches.
|
||||
*
|
||||
* @author David Pace - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AbstractPowerSwitchHandlerWithPowerMeter extends AbstractPowerSwitchHandler {
|
||||
|
||||
protected AbstractPowerSwitchHandlerWithPowerMeter(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeServices() throws BoschSHCException {
|
||||
super.initializeServices();
|
||||
|
||||
this.createService(PowerMeterService::new, this::updateChannels,
|
||||
List.of(CHANNEL_POWER_CONSUMPTION, CHANNEL_ENERGY_CONSUMPTION), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<>(state.powerConsumption, Units.WATT));
|
||||
super.updateState(CHANNEL_ENERGY_CONSUMPTION, new QuantityType<>(state.energyConsumption, Units.WATT_HOUR));
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Utilities for handling parent/child relations in Bosch device IDs.
|
||||
*
|
||||
* @author David Pace - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class BoschDeviceIdUtils {
|
||||
|
||||
private static final String CHILD_ID_SEPARATOR = "#";
|
||||
|
||||
private BoschDeviceIdUtils() {
|
||||
// Utility Class
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given device ID is a child device ID.
|
||||
* <p>
|
||||
* Example for a parent device ID:
|
||||
*
|
||||
* <pre>
|
||||
* hdm:ZigBee:70ac08fffefead2d
|
||||
* </pre>
|
||||
*
|
||||
* Example for a child device ID:
|
||||
*
|
||||
* <pre>
|
||||
* hdm:ZigBee:70ac08fffefead2d#2
|
||||
* </pre>
|
||||
*
|
||||
* @param deviceId the Bosch device ID to check
|
||||
* @return <code>true</code> if the device ID contains a hash character, <code>false</code> otherwise
|
||||
*/
|
||||
public static boolean isChildDeviceId(String deviceId) {
|
||||
return deviceId.contains(CHILD_ID_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the given device ID is a child device ID, the parent device ID is derived by cutting off the part starting
|
||||
* from the hash character.
|
||||
*
|
||||
* @param deviceId a device ID
|
||||
* @return the parent device ID, if derivable. Otherwise the given ID is returned.
|
||||
*/
|
||||
public static String getParentDeviceId(String deviceId) {
|
||||
int hashIndex = deviceId.indexOf(CHILD_ID_SEPARATOR);
|
||||
if (hashIndex < 0) {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
return deviceId.substring(0, hashIndex);
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ public class BoschSHCBindingConstants {
|
||||
public static final ThingTypeUID THING_TYPE_WINDOW_CONTACT_2 = new ThingTypeUID(BINDING_ID, "window-contact-2");
|
||||
public static final ThingTypeUID THING_TYPE_MOTION_DETECTOR = new ThingTypeUID(BINDING_ID, "motion-detector");
|
||||
public static final ThingTypeUID THING_TYPE_SHUTTER_CONTROL = new ThingTypeUID(BINDING_ID, "shutter-control");
|
||||
public static final ThingTypeUID THING_TYPE_SHUTTER_CONTROL_2 = new ThingTypeUID(BINDING_ID, "shutter-control-2");
|
||||
public static final ThingTypeUID THING_TYPE_THERMOSTAT = new ThingTypeUID(BINDING_ID, "thermostat");
|
||||
public static final ThingTypeUID THING_TYPE_CLIMATE_CONTROL = new ThingTypeUID(BINDING_ID, "climate-control");
|
||||
public static final ThingTypeUID THING_TYPE_WALL_THERMOSTAT = new ThingTypeUID(BINDING_ID, "wall-thermostat");
|
||||
@ -51,9 +52,10 @@ public class BoschSHCBindingConstants {
|
||||
public static final ThingTypeUID THING_TYPE_SMOKE_DETECTOR = new ThingTypeUID(BINDING_ID, "smoke-detector");
|
||||
public static final ThingTypeUID THING_TYPE_UNIVERSAL_SWITCH = new ThingTypeUID(BINDING_ID, "universal-switch");
|
||||
public static final ThingTypeUID THING_TYPE_UNIVERSAL_SWITCH_2 = new ThingTypeUID(BINDING_ID, "universal-switch-2");
|
||||
public static final ThingTypeUID THING_TYPE_SMOKE_DETECTOR_2 = new ThingTypeUID(BINDING_ID, "smoke-detector-2");
|
||||
public static final ThingTypeUID THING_TYPE_LIGHT_CONTROL_2 = new ThingTypeUID(BINDING_ID, "light-control-2");
|
||||
|
||||
public static final ThingTypeUID THING_TYPE_USER_DEFINED_STATE = new ThingTypeUID(BINDING_ID, "user-defined-state");
|
||||
public static final ThingTypeUID THING_TYPE_SMOKE_DETECTOR_2 = new ThingTypeUID(BINDING_ID, "smoke-detector-2");
|
||||
|
||||
// List of all Channel IDs
|
||||
// Auto-generated from thing-types.xml via script, don't modify
|
||||
@ -76,6 +78,7 @@ public class BoschSHCBindingConstants {
|
||||
public static final String CHANNEL_VALVE_TAPPET_POSITION = "valve-tappet-position";
|
||||
public static final String CHANNEL_SETPOINT_TEMPERATURE = "setpoint-temperature";
|
||||
public static final String CHANNEL_CHILD_LOCK = "child-lock";
|
||||
public static final String CHANNEL_CHILD_PROTECTION = "child-protection";
|
||||
public static final String CHANNEL_PRIVACY_MODE = "privacy-mode";
|
||||
public static final String CHANNEL_CAMERA_NOTIFICATION = "camera-notification";
|
||||
public static final String CHANNEL_SYSTEM_AVAILABILITY = "system-availability";
|
||||
@ -99,6 +102,14 @@ public class BoschSHCBindingConstants {
|
||||
public static final String CHANNEL_KEY_EVENT_TYPE = "key-event-type";
|
||||
public static final String CHANNEL_KEY_EVENT_TIMESTAMP = "key-event-timestamp";
|
||||
|
||||
// numbered channels
|
||||
// the rationale for introducing numbered channels was discussed in
|
||||
// https://github.com/openhab/openhab-addons/pull/16400
|
||||
public static final String CHANNEL_POWER_SWITCH_1 = "power-switch-1";
|
||||
public static final String CHANNEL_POWER_SWITCH_2 = "power-switch-2";
|
||||
public static final String CHANNEL_CHILD_PROTECTION_1 = "child-protection-1";
|
||||
public static final String CHANNEL_CHILD_PROTECTION_2 = "child-protection-2";
|
||||
|
||||
public static final String CHANNEL_USER_DEFINED_STATE = "user-state";
|
||||
|
||||
// static device/service names
|
||||
|
@ -17,6 +17,7 @@ import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
@ -57,30 +58,68 @@ public abstract class BoschSHCDeviceHandler extends BoschSHCHandler {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
var config = this.config = getConfigAs(BoschSHCConfiguration.class);
|
||||
this.config = getConfigAs(BoschSHCConfiguration.class);
|
||||
|
||||
String deviceId = config.id;
|
||||
@Nullable
|
||||
Device deviceInfo = validateDeviceId(deviceId);
|
||||
if (deviceInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!processDeviceInfo(deviceInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the handler to process the device info that was obtained from a REST
|
||||
* call to the Smart Home Controller at <code>/devices/{deviceId}</code>.
|
||||
*
|
||||
* @param deviceInfo the device info obtained from the controller, guaranteed to be non-null
|
||||
* @return <code>true</code> if the device info is valid and the initialization should proceed, <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
protected boolean processDeviceInfo(Device deviceInfo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to obtain information about the device with the specified ID via a REST call.
|
||||
* <p>
|
||||
* If the REST call is successful, the device ID is considered to be valid and the resulting {@link Device} object
|
||||
* is returned.
|
||||
* <p>
|
||||
* If the device ID is not configured/empty or the REST call is not successful, the device ID is considered invalid
|
||||
* and <code>null</code> is returned.
|
||||
*
|
||||
* @param deviceId the device ID to check
|
||||
* @return the {@link Device} info object if the REST call was successful, <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
protected Device validateDeviceId(@Nullable String deviceId) {
|
||||
if (deviceId == null || deviceId.isBlank()) {
|
||||
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error.empty-device-id");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to get device info to make sure the device exists
|
||||
try {
|
||||
var bridgeHandler = this.getBridgeHandler();
|
||||
var info = bridgeHandler.getDeviceInfo(deviceId);
|
||||
logger.trace("Device initialized:\n{}", info);
|
||||
var deviceInfo = bridgeHandler.getDeviceInfo(deviceId);
|
||||
logger.trace("Device validated and initialized:\n{}", deviceInfo);
|
||||
return deviceInfo;
|
||||
} catch (TimeoutException | ExecutionException | BoschSHCException e) {
|
||||
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||
return;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
super.initialize();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,6 +49,7 @@ import com.google.gson.JsonElement;
|
||||
* @author Stefan Kästle - Initial contribution
|
||||
* @author Christian Oeing - refactorings of e.g. server registration
|
||||
* @author David Pace - Handler abstraction
|
||||
* @author David Pace - Support for child device updates
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class BoschSHCHandler extends BaseThingHandler {
|
||||
@ -154,7 +155,7 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
|
||||
* @param stateData Current state of device service. Serialized as JSON.
|
||||
*/
|
||||
public void processUpdate(String serviceName, @Nullable JsonElement stateData) {
|
||||
// Check services of device to correctly
|
||||
// Find service(s) with the specified name and propagate new state to them
|
||||
for (DeviceService<? extends BoschSHCServiceState> deviceService : this.services) {
|
||||
BoschSHCService<? extends BoschSHCServiceState> service = deviceService.service;
|
||||
if (serviceName.equals(service.getServiceName())) {
|
||||
@ -163,11 +164,23 @@ public abstract class BoschSHCHandler extends BaseThingHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an update for a logical child device.
|
||||
*
|
||||
* @param childDeviceId the ID of the logical child device
|
||||
* @param serviceName the name of the service this update is targeted at
|
||||
* @param stateData the new service state serialized as JSON
|
||||
*/
|
||||
public void processChildUpdate(String childDeviceId, String serviceName, @Nullable JsonElement stateData) {
|
||||
// default implementation is empty, subclasses may override
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to register all services of the device with
|
||||
* {@link #registerService(BoschSHCService, Consumer, Collection, boolean)}.
|
||||
*/
|
||||
protected void initializeServices() throws BoschSHCException {
|
||||
// default implementation is empty, subclasses may override
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,9 +17,11 @@ import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConst
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_CLIMATE_CONTROL;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_INTRUSION_DETECTION_SYSTEM;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_INWALL_SWITCH;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_LIGHT_CONTROL_2;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_MOTION_DETECTOR;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_SHC;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_SHUTTER_CONTROL;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_SHUTTER_CONTROL_2;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_SMART_BULB;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_SMART_PLUG_COMPACT;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_SMOKE_DETECTOR;
|
||||
@ -43,9 +45,11 @@ import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler;
|
||||
import org.openhab.binding.boschshc.internal.devices.camera.CameraHandler;
|
||||
import org.openhab.binding.boschshc.internal.devices.climatecontrol.ClimateControlHandler;
|
||||
import org.openhab.binding.boschshc.internal.devices.intrusion.IntrusionDetectionHandler;
|
||||
import org.openhab.binding.boschshc.internal.devices.lightcontrol.LightControl2Handler;
|
||||
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.ShutterControl2Handler;
|
||||
import org.openhab.binding.boschshc.internal.devices.shuttercontrol.ShutterControlHandler;
|
||||
import org.openhab.binding.boschshc.internal.devices.smartbulb.SmartBulbHandler;
|
||||
import org.openhab.binding.boschshc.internal.devices.smokedetector.SmokeDetector2Handler;
|
||||
@ -109,6 +113,7 @@ public class BoschSHCHandlerFactory extends BaseThingHandlerFactory {
|
||||
new ThingTypeHandlerMapping(THING_TYPE_WINDOW_CONTACT_2, WindowContact2Handler::new),
|
||||
new ThingTypeHandlerMapping(THING_TYPE_MOTION_DETECTOR, MotionDetectorHandler::new),
|
||||
new ThingTypeHandlerMapping(THING_TYPE_SHUTTER_CONTROL, ShutterControlHandler::new),
|
||||
new ThingTypeHandlerMapping(THING_TYPE_SHUTTER_CONTROL_2, ShutterControl2Handler::new),
|
||||
new ThingTypeHandlerMapping(THING_TYPE_THERMOSTAT, ThermostatHandler::new),
|
||||
new ThingTypeHandlerMapping(THING_TYPE_CLIMATE_CONTROL, ClimateControlHandler::new),
|
||||
new ThingTypeHandlerMapping(THING_TYPE_WALL_THERMOSTAT, WallThermostatHandler::new),
|
||||
@ -123,7 +128,8 @@ public class BoschSHCHandlerFactory extends BaseThingHandlerFactory {
|
||||
thing -> new UniversalSwitchHandler(thing, timeZoneProvider)),
|
||||
new ThingTypeHandlerMapping(THING_TYPE_UNIVERSAL_SWITCH_2,
|
||||
thing -> new UniversalSwitch2Handler(thing, timeZoneProvider)),
|
||||
new ThingTypeHandlerMapping(THING_TYPE_SMOKE_DETECTOR_2, SmokeDetector2Handler::new));
|
||||
new ThingTypeHandlerMapping(THING_TYPE_SMOKE_DETECTOR_2, SmokeDetector2Handler::new),
|
||||
new ThingTypeHandlerMapping(THING_TYPE_LIGHT_CONTROL_2, LightControl2Handler::new));
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
|
@ -111,7 +111,32 @@ public class BoschHttpClient extends HttpClient {
|
||||
* @return Bosch SHC URL for passed endpoint
|
||||
*/
|
||||
public String getBoschShcUrl(String endpoint) {
|
||||
return String.format("https://%s:8444/%s", this.ipAddress, endpoint);
|
||||
String url = String.format("https://%s:8444/%s", this.ipAddress, endpoint);
|
||||
return escapeURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs specific URL escaping required for certain Bosch SHC URLs.
|
||||
* <p>
|
||||
* In particular, hash characters in child device IDs must be escaped with <code>%23</code>.
|
||||
* <p>
|
||||
* Invalid example:
|
||||
*
|
||||
* <pre>
|
||||
* https://host:port/devices/hdm:ZigBee:70ac08fffe5294ea#3/services/PowerSwitch/state
|
||||
* </pre>
|
||||
*
|
||||
* Valid example:
|
||||
*
|
||||
* <pre>
|
||||
* https://host:port/devices/hdm:ZigBee:70ac08fffe5294ea%233/services/PowerSwitch/state
|
||||
* </pre>
|
||||
*
|
||||
* @param url the URL to be escaped
|
||||
* @return the escaped URL
|
||||
*/
|
||||
private String escapeURL(String url) {
|
||||
return url.replace("#", "%23");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,6 +34,7 @@ import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.openhab.binding.boschshc.internal.devices.BoschDeviceIdUtils;
|
||||
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
|
||||
import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||
@ -476,7 +477,7 @@ public class BridgeHandler extends BaseBridgeHandler {
|
||||
*
|
||||
* @param result Results from Long Polling
|
||||
*/
|
||||
private void handleLongPollResult(LongPollResult result) {
|
||||
void handleLongPollResult(LongPollResult result) {
|
||||
for (BoschSHCServiceState serviceState : result.result) {
|
||||
if (serviceState instanceof DeviceServiceData deviceServiceData) {
|
||||
handleDeviceServiceData(deviceServiceData);
|
||||
@ -562,12 +563,7 @@ public class BridgeHandler extends BaseBridgeHandler {
|
||||
*/
|
||||
private void forwardStateToHandlers(BoschSHCServiceState serviceData, JsonElement state, String updateDeviceId) {
|
||||
boolean handled = false;
|
||||
final String serviceId;
|
||||
if (serviceData instanceof UserDefinedState userState) {
|
||||
serviceId = userState.getId();
|
||||
} else {
|
||||
serviceId = ((DeviceServiceData) serviceData).id;
|
||||
}
|
||||
final String serviceId = getServiceId(serviceData);
|
||||
|
||||
Bridge bridge = this.getThing();
|
||||
for (Thing childThing : bridge.getThings()) {
|
||||
@ -578,13 +574,17 @@ public class BridgeHandler extends BaseBridgeHandler {
|
||||
@Nullable
|
||||
String deviceId = handler.getBoschID();
|
||||
|
||||
handled = true;
|
||||
logger.debug("Registered device: {} - looking for {}", deviceId, updateDeviceId);
|
||||
|
||||
if (deviceId != null && updateDeviceId.equals(deviceId)) {
|
||||
logger.debug("Found child: {} - calling processUpdate (id: {}) with {}", handler, serviceId, state);
|
||||
handler.processUpdate(serviceId, state);
|
||||
if (deviceId == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.trace("Checking device {}, looking for {}", deviceId, updateDeviceId);
|
||||
|
||||
// handled is a boolean latch that stays true once it becomes true
|
||||
// note that no short-circuiting operators are used, meaning that the method
|
||||
// calls will always be evaluated, even if the latch is already true
|
||||
handled |= notifyHandler(handler, deviceId, updateDeviceId, serviceId, state);
|
||||
handled |= notifyParentHandler(handler, deviceId, updateDeviceId, serviceId, state);
|
||||
} else {
|
||||
logger.warn("longPoll: child handler for {} does not implement Bosch SHC handler", baseHandler);
|
||||
}
|
||||
@ -595,6 +595,61 @@ public class BridgeHandler extends BaseBridgeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the given handler if its device ID exactly matches the device ID for which the update was received.
|
||||
*
|
||||
* @param handler the handler to be notified if applicable
|
||||
* @param deviceId the device ID associated with the handler
|
||||
* @param updateDeviceId the device ID for which the update was received
|
||||
* @param serviceId the ID of the service for which the update was received
|
||||
* @param state the received state object as JSON element
|
||||
*
|
||||
* @return <code>true</code> if the handler matched and was notified, <code>false</code> otherwise
|
||||
*/
|
||||
private boolean notifyHandler(BoschSHCHandler handler, String deviceId, String updateDeviceId, String serviceId,
|
||||
JsonElement state) {
|
||||
if (updateDeviceId.equals(deviceId)) {
|
||||
logger.debug("Found handler {}, calling processUpdate() for service {} with state {}", handler, serviceId,
|
||||
state);
|
||||
handler.processUpdate(serviceId, state);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If an update is received for a logical child device and the given handler is the parent device handler, the
|
||||
* parent handler is notified.
|
||||
*
|
||||
* @param handler the handler to be notified if applicable
|
||||
* @param deviceId the device ID associated with the handler
|
||||
* @param updateDeviceId the device ID for which the update was received
|
||||
* @param serviceId the ID of the service for which the update was received
|
||||
* @param state the received state object as JSON element
|
||||
* @return <code>true</code> if the given handler was the corresponding parent handler and was notified,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private boolean notifyParentHandler(BoschSHCHandler handler, String deviceId, String updateDeviceId,
|
||||
String serviceId, JsonElement state) {
|
||||
if (BoschDeviceIdUtils.isChildDeviceId(updateDeviceId)) {
|
||||
String parentDeviceId = BoschDeviceIdUtils.getParentDeviceId(updateDeviceId);
|
||||
if (parentDeviceId.equals(deviceId)) {
|
||||
logger.debug("Notifying parent handler {} about update for child device for service {} with state {}",
|
||||
handler, serviceId, state);
|
||||
handler.processChildUpdate(updateDeviceId, serviceId, state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getServiceId(BoschSHCServiceState serviceData) {
|
||||
if (serviceData instanceof UserDefinedState userState) {
|
||||
return userState.getId();
|
||||
}
|
||||
return ((DeviceServiceData) serviceData).id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bridge callback handler for the failures during long polls.
|
||||
*
|
||||
|
@ -18,23 +18,25 @@ import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Represents a single devices connected to the Bosch Smart Home Controller.
|
||||
*
|
||||
* Example from Json:
|
||||
*
|
||||
* <p>
|
||||
* Example JSON:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "@type":"device",
|
||||
* "rootDeviceId":"64-da-a0-02-14-9b",
|
||||
* "id":"hdm:HomeMaticIP:3014F711A00004953859F31B",
|
||||
* "deviceServiceIds":["PowerMeter","PowerSwitch","PowerSwitchProgram","Routing"],
|
||||
* "manufacturer":"BOSCH",
|
||||
* "roomId":"hz_3",
|
||||
* "deviceModel":"PSM",
|
||||
* "serial":"3014F711A00004953859F31B",
|
||||
* "profile":"GENERIC",
|
||||
* "name":"Coffee Machine",
|
||||
* "status":"AVAILABLE",
|
||||
* "childDeviceIds":[]
|
||||
* "@type": "device",
|
||||
* "rootDeviceId": "64-da-a0-02-14-9b",
|
||||
* "id": "hdm:HomeMaticIP:3014F711A00004953859F31B",
|
||||
* "deviceServiceIds": ["PowerMeter","PowerSwitch","PowerSwitchProgram","Routing"],
|
||||
* "manufacturer": "BOSCH",
|
||||
* "roomId": "hz_3",
|
||||
* "deviceModel": "PSM",
|
||||
* "serial": "3014F711A00004953859F31B",
|
||||
* "profile": "GENERIC",
|
||||
* "name": "Coffee Machine",
|
||||
* "status": "AVAILABLE",
|
||||
* "childDeviceIds": []
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Stefan Kästle - Initial contribution
|
||||
*/
|
||||
|
@ -18,25 +18,30 @@ import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
|
||||
|
||||
/**
|
||||
* Response of the Controller for a Long Poll API call.
|
||||
* <p>
|
||||
* Example JSON:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "result": [{
|
||||
* "path": "/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch",
|
||||
* "@type": "DeviceServiceData",
|
||||
* "id": "PowerSwitch",
|
||||
* "state": {
|
||||
* "@type": "powerSwitchState",
|
||||
* "switchState": "ON"
|
||||
* },
|
||||
* "deviceId": "hdm:HomeMaticIP:3014F711A0001916D859A8A9"
|
||||
* }],
|
||||
* "jsonrpc": "2.0"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Stefan Kästle - Initial contribution
|
||||
*/
|
||||
public class LongPollResult {
|
||||
|
||||
/**
|
||||
* {"result":[
|
||||
* ..{
|
||||
* ...."path":"/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch",
|
||||
* ...."@type":"DeviceServiceData",
|
||||
* ...."id":"PowerSwitch",
|
||||
* ...."state":{
|
||||
* ......"@type":"powerSwitchState",
|
||||
* ......"switchState":"ON"
|
||||
* ....},
|
||||
* ...."deviceId":"hdm:HomeMaticIP:3014F711A0001916D859A8A9"}
|
||||
* ],"jsonrpc":"2.0"}
|
||||
*/
|
||||
|
||||
public ArrayList<BoschSHCServiceState> result;
|
||||
|
||||
public String jsonrpc;
|
||||
}
|
||||
|
@ -0,0 +1,240 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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 static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION_1;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION_2;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_POWER_SWITCH_1;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_POWER_SWITCH_2;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_SIGNAL_STRENGTH;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.boschshc.internal.devices.BoschSHCDeviceHandler;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||
import org.openhab.binding.boschshc.internal.services.childprotection.ChildProtectionService;
|
||||
import org.openhab.binding.boschshc.internal.services.childprotection.dto.ChildProtectionServiceState;
|
||||
import org.openhab.binding.boschshc.internal.services.communicationquality.CommunicationQualityService;
|
||||
import org.openhab.binding.boschshc.internal.services.communicationquality.dto.CommunicationQualityServiceState;
|
||||
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.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* Handler for Light Control II devices.
|
||||
* <p>
|
||||
* This implementation handles both common channels and specific channels of the
|
||||
* two logical child devices.
|
||||
*
|
||||
* @author David Pace - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LightControl2Handler extends BoschSHCDeviceHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(LightControl2Handler.class);
|
||||
|
||||
private @Nullable String childDeviceId1;
|
||||
private @Nullable String childDeviceId2;
|
||||
|
||||
private PowerSwitchService lightSwitchCircuit1PowerSwitchService;
|
||||
private PowerSwitchService lightSwitchCircuit2PowerSwitchService;
|
||||
|
||||
private ChildProtectionService lightSwitchCircuit1ChildProtectionService;
|
||||
private ChildProtectionService lightSwitchCircuit2ChildProtectionService;
|
||||
|
||||
public LightControl2Handler(Thing thing) {
|
||||
super(thing);
|
||||
|
||||
lightSwitchCircuit1PowerSwitchService = new PowerSwitchService();
|
||||
lightSwitchCircuit2PowerSwitchService = new PowerSwitchService();
|
||||
|
||||
lightSwitchCircuit1ChildProtectionService = new ChildProtectionService();
|
||||
lightSwitchCircuit2ChildProtectionService = new ChildProtectionService();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processDeviceInfo(Device deviceInfo) {
|
||||
super.processDeviceInfo(deviceInfo);
|
||||
|
||||
logger.debug("Initializing child devices of Light Control II, child device IDs from device info: {}",
|
||||
deviceInfo.childDeviceIds);
|
||||
|
||||
if (deviceInfo.childDeviceIds == null || deviceInfo.childDeviceIds.size() != 2) {
|
||||
updateStatusChildDeviceIDsNotObtainable();
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> childDeviceIds = new ArrayList<>(deviceInfo.childDeviceIds);
|
||||
// since we were not sure whether the child device ID order is always the same,
|
||||
// we ensure a deterministic order by sorting the child IDs
|
||||
// see https://github.com/openhab/openhab-addons/pull/16400#discussion_r1497762612
|
||||
Collections.sort(childDeviceIds);
|
||||
|
||||
logger.trace("Child device IDs for Light Control II after sorting: {}", childDeviceIds);
|
||||
|
||||
if (validateDeviceId(childDeviceIds.get(0)) == null || validateDeviceId(childDeviceIds.get(1)) == null) {
|
||||
updateStatusChildDeviceIDsNotObtainable();
|
||||
return false;
|
||||
}
|
||||
|
||||
childDeviceId1 = childDeviceIds.get(0);
|
||||
childDeviceId2 = childDeviceIds.get(1);
|
||||
|
||||
logger.debug("Child device IDs for Light Control II configured successfully.");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateStatusChildDeviceIDsNotObtainable() {
|
||||
super.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error.child-device-ids-not-obtainable");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeServices() throws BoschSHCException {
|
||||
super.initializeServices();
|
||||
|
||||
createService(CommunicationQualityService::new, this::updateChannels, List.of(CHANNEL_SIGNAL_STRENGTH), true);
|
||||
createService(PowerMeterService::new, this::updateChannels,
|
||||
List.of(CHANNEL_POWER_CONSUMPTION, CHANNEL_ENERGY_CONSUMPTION), true);
|
||||
|
||||
// local variable required to ensure non-nullness, member can theoretically be modified
|
||||
String lChildDeviceId1 = childDeviceId1;
|
||||
if (lChildDeviceId1 == null) {
|
||||
throw new BoschSHCException("Child device ID 1 is not set for thing " + getThing().getUID());
|
||||
}
|
||||
|
||||
// local variable required to ensure non-nullness, member can theoretically be modified
|
||||
String lChildDeviceId2 = childDeviceId2;
|
||||
if (lChildDeviceId2 == null) {
|
||||
throw new BoschSHCException("Child device ID 2 is not set for thing " + getThing().getUID());
|
||||
}
|
||||
|
||||
lightSwitchCircuit1PowerSwitchService.initialize(getBridgeHandler(), lChildDeviceId1,
|
||||
state -> updatePowerSwitchChannel(state, CHANNEL_POWER_SWITCH_1));
|
||||
lightSwitchCircuit2PowerSwitchService.initialize(getBridgeHandler(), lChildDeviceId2,
|
||||
state -> updatePowerSwitchChannel(state, CHANNEL_POWER_SWITCH_2));
|
||||
|
||||
lightSwitchCircuit1ChildProtectionService.initialize(getBridgeHandler(), lChildDeviceId1,
|
||||
state -> updateChildProtectionChannel(state, CHANNEL_CHILD_PROTECTION_1));
|
||||
lightSwitchCircuit2ChildProtectionService.initialize(getBridgeHandler(), lChildDeviceId2,
|
||||
state -> updateChildProtectionChannel(state, CHANNEL_CHILD_PROTECTION_2));
|
||||
}
|
||||
|
||||
private void updateChannels(CommunicationQualityServiceState communicationQualityServiceState) {
|
||||
updateState(CHANNEL_SIGNAL_STRENGTH, communicationQualityServiceState.quality.toSystemSignalStrength());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<>(state.powerConsumption, Units.WATT));
|
||||
super.updateState(CHANNEL_ENERGY_CONSUMPTION, new QuantityType<>(state.energyConsumption, Units.WATT_HOUR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processChildUpdate(String childDeviceId, String serviceName, @Nullable JsonElement stateData) {
|
||||
super.processChildUpdate(childDeviceId, serviceName, stateData);
|
||||
|
||||
if (PowerSwitchService.POWER_SWITCH_SERVICE_NAME.equals(serviceName)) {
|
||||
if (childDeviceId.equals(childDeviceId1)) {
|
||||
lightSwitchCircuit1PowerSwitchService.onStateUpdate(stateData);
|
||||
} else if (childDeviceId.equals(childDeviceId2)) {
|
||||
lightSwitchCircuit2PowerSwitchService.onStateUpdate(stateData);
|
||||
}
|
||||
} else if (ChildProtectionService.CHILD_PROTECTION_SERVICE_NAME.equals(serviceName)) {
|
||||
if (childDeviceId.equals(childDeviceId1)) {
|
||||
lightSwitchCircuit1ChildProtectionService.onStateUpdate(stateData);
|
||||
} else if (childDeviceId.equals(childDeviceId2)) {
|
||||
lightSwitchCircuit2ChildProtectionService.onStateUpdate(stateData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the power switch channel for one of the child devices.
|
||||
*
|
||||
* @param state the new {@link PowerSwitchServiceState}
|
||||
* @param channelId the power switch channel ID associated with the child device
|
||||
*/
|
||||
private void updatePowerSwitchChannel(PowerSwitchServiceState state, String channelId) {
|
||||
State powerState = OnOffType.from(state.switchState.toString());
|
||||
super.updateState(channelId, powerState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the child protection channel for one of the child devices.
|
||||
*
|
||||
* @param state the new {@link ChildProtectionServiceState}
|
||||
* @param channelId the child protection channel ID associated with the child
|
||||
* device
|
||||
*/
|
||||
private void updateChildProtectionChannel(ChildProtectionServiceState state, String channelId) {
|
||||
super.updateState(channelId, OnOffType.from(state.childLockActive));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
super.handleCommand(channelUID, command);
|
||||
|
||||
if (CHANNEL_POWER_SWITCH_1.equals(channelUID.getId()) && (command instanceof OnOffType onOffCommand)) {
|
||||
updatePowerSwitchState(onOffCommand, lightSwitchCircuit1PowerSwitchService);
|
||||
} else if (CHANNEL_POWER_SWITCH_2.equals(channelUID.getId()) && (command instanceof OnOffType onOffCommand)) {
|
||||
updatePowerSwitchState(onOffCommand, lightSwitchCircuit2PowerSwitchService);
|
||||
} else if (CHANNEL_CHILD_PROTECTION_1.equals(channelUID.getId())
|
||||
&& (command instanceof OnOffType onOffCommand)) {
|
||||
updateChildProtectionState(onOffCommand, lightSwitchCircuit1ChildProtectionService);
|
||||
} else if (CHANNEL_CHILD_PROTECTION_2.equals(channelUID.getId())
|
||||
&& (command instanceof OnOffType onOffCommand)) {
|
||||
updateChildProtectionState(onOffCommand, lightSwitchCircuit2ChildProtectionService);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePowerSwitchState(OnOffType command, PowerSwitchService powerSwitchService) {
|
||||
PowerSwitchServiceState state = new PowerSwitchServiceState();
|
||||
state.switchState = PowerSwitchState.valueOf(command.toFullString());
|
||||
this.updateServiceState(powerSwitchService, state);
|
||||
}
|
||||
|
||||
private void updateChildProtectionState(OnOffType onOffCommand, ChildProtectionService childProtectionService) {
|
||||
ChildProtectionServiceState childProtectionServiceState = new ChildProtectionServiceState();
|
||||
childProtectionServiceState.childLockActive = onOffCommand == OnOffType.ON;
|
||||
updateServiceState(childProtectionService, childProtectionServiceState);
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
package org.openhab.binding.boschshc.internal.devices.lightcontrol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.boschshc.internal.devices.AbstractPowerSwitchHandler;
|
||||
import org.openhab.binding.boschshc.internal.devices.AbstractPowerSwitchHandlerWithPowerMeter;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
||||
/**
|
||||
@ -22,7 +22,7 @@ import org.openhab.core.thing.Thing;
|
||||
* @author Stefan Kästle - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LightControlHandler extends AbstractPowerSwitchHandler {
|
||||
public class LightControlHandler extends AbstractPowerSwitchHandlerWithPowerMeter {
|
||||
|
||||
public LightControlHandler(Thing thing) {
|
||||
super(thing);
|
||||
|
@ -13,7 +13,7 @@
|
||||
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.binding.boschshc.internal.devices.AbstractPowerSwitchHandlerWithPowerMeter;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
||||
/**
|
||||
@ -22,7 +22,7 @@ import org.openhab.core.thing.Thing;
|
||||
* @author David Pace - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugHandler extends AbstractPowerSwitchHandler {
|
||||
public class PlugHandler extends AbstractPowerSwitchHandlerWithPowerMeter {
|
||||
|
||||
public PlugHandler(Thing thing) {
|
||||
super(thing);
|
||||
|
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.shuttercontrol;
|
||||
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION;
|
||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_SIGNAL_STRENGTH;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
|
||||
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||
import org.openhab.binding.boschshc.internal.services.childprotection.ChildProtectionService;
|
||||
import org.openhab.binding.boschshc.internal.services.childprotection.dto.ChildProtectionServiceState;
|
||||
import org.openhab.binding.boschshc.internal.services.communicationquality.CommunicationQualityService;
|
||||
import org.openhab.binding.boschshc.internal.services.communicationquality.dto.CommunicationQualityServiceState;
|
||||
import org.openhab.binding.boschshc.internal.services.powermeter.PowerMeterService;
|
||||
import org.openhab.binding.boschshc.internal.services.powermeter.dto.PowerMeterServiceState;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Handler for second generation shutter controls.
|
||||
* <p>
|
||||
* This handler is used if Shutter/Light Control II devices are configured as shutter controls.
|
||||
*
|
||||
* @author David Pace - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ShutterControl2Handler extends ShutterControlHandler {
|
||||
|
||||
private final ChildProtectionService childProtectionService;
|
||||
|
||||
public ShutterControl2Handler(Thing thing) {
|
||||
super(thing);
|
||||
this.childProtectionService = new ChildProtectionService();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeServices() throws BoschSHCException {
|
||||
super.initializeServices();
|
||||
|
||||
createService(CommunicationQualityService::new, this::updateChannels, List.of(CHANNEL_SIGNAL_STRENGTH), true);
|
||||
registerService(childProtectionService, this::updateChannels, List.of(CHANNEL_CHILD_PROTECTION), true);
|
||||
createService(PowerMeterService::new, this::updateChannels,
|
||||
List.of(CHANNEL_POWER_CONSUMPTION, CHANNEL_ENERGY_CONSUMPTION), true);
|
||||
}
|
||||
|
||||
private void updateChannels(CommunicationQualityServiceState communicationQualityServiceState) {
|
||||
updateState(CHANNEL_SIGNAL_STRENGTH, communicationQualityServiceState.quality.toSystemSignalStrength());
|
||||
}
|
||||
|
||||
private void updateChannels(ChildProtectionServiceState childProtectionServiceState) {
|
||||
updateState(CHANNEL_CHILD_PROTECTION, OnOffType.from(childProtectionServiceState.childLockActive));
|
||||
}
|
||||
|
||||
private void updateChannels(PowerMeterServiceState state) {
|
||||
super.updateState(CHANNEL_POWER_CONSUMPTION, new QuantityType<>(state.powerConsumption, Units.WATT));
|
||||
super.updateState(CHANNEL_ENERGY_CONSUMPTION, new QuantityType<>(state.energyConsumption, Units.WATT_HOUR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
super.handleCommand(channelUID, command);
|
||||
|
||||
if (BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION.equals(channelUID.getId())
|
||||
&& (command instanceof OnOffType onOffCommand)) {
|
||||
updateChildProtectionState(onOffCommand);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChildProtectionState(OnOffType onOffCommand) {
|
||||
ChildProtectionServiceState childProtectionServiceState = new ChildProtectionServiceState();
|
||||
childProtectionServiceState.childLockActive = onOffCommand == OnOffType.ON;
|
||||
updateServiceState(childProtectionService, childProtectionServiceState);
|
||||
}
|
||||
}
|
@ -56,6 +56,11 @@ public class ThingDiscoveryService extends AbstractThingHandlerDiscoveryService<
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ThingDiscoveryService.class);
|
||||
|
||||
/**
|
||||
* Device model representing logical child devices of Light Control II
|
||||
*/
|
||||
static final String DEVICE_MODEL_LIGHT_CONTROL_CHILD_DEVICE = "MICROMODULE_LIGHT_ATTACHED";
|
||||
|
||||
protected static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(
|
||||
BoschSHCBindingConstants.THING_TYPE_INWALL_SWITCH, BoschSHCBindingConstants.THING_TYPE_TWINGUARD,
|
||||
BoschSHCBindingConstants.THING_TYPE_WINDOW_CONTACT, BoschSHCBindingConstants.THING_TYPE_WINDOW_CONTACT_2,
|
||||
@ -89,7 +94,10 @@ public class ThingDiscoveryService extends AbstractThingHandlerDiscoveryService<
|
||||
new AbstractMap.SimpleEntry<>("TRV", BoschSHCBindingConstants.THING_TYPE_THERMOSTAT),
|
||||
new AbstractMap.SimpleEntry<>("WRC2", BoschSHCBindingConstants.THING_TYPE_UNIVERSAL_SWITCH),
|
||||
new AbstractMap.SimpleEntry<>("SWITCH2", BoschSHCBindingConstants.THING_TYPE_UNIVERSAL_SWITCH_2),
|
||||
new AbstractMap.SimpleEntry<>("SMOKE_DETECTOR2", BoschSHCBindingConstants.THING_TYPE_SMOKE_DETECTOR_2)
|
||||
new AbstractMap.SimpleEntry<>("SMOKE_DETECTOR2", BoschSHCBindingConstants.THING_TYPE_SMOKE_DETECTOR_2),
|
||||
new AbstractMap.SimpleEntry<>("MICROMODULE_SHUTTER", BoschSHCBindingConstants.THING_TYPE_SHUTTER_CONTROL_2),
|
||||
new AbstractMap.SimpleEntry<>("MICROMODULE_AWNING", BoschSHCBindingConstants.THING_TYPE_SHUTTER_CONTROL_2),
|
||||
new AbstractMap.SimpleEntry<>("MICROMODULE_LIGHT_CONTROL", BoschSHCBindingConstants.THING_TYPE_LIGHT_CONTROL_2)
|
||||
// Future Extension: map deviceModel names to BoschSHC Thing Types when they are supported
|
||||
// new AbstractMap.SimpleEntry<>("SMOKE_DETECTION_SYSTEM", BoschSHCBindingConstants.),
|
||||
// new AbstractMap.SimpleEntry<>("PRESENCE_SIMULATION_SERVICE", BoschSHCBindingConstants.),
|
||||
@ -219,13 +227,15 @@ public class ThingDiscoveryService extends AbstractThingHandlerDiscoveryService<
|
||||
|
||||
logger.trace("- got thingTypeID '{}' for deviceModel '{}'", thingTypeUID.getId(), device.deviceModel);
|
||||
|
||||
ThingUID thingUID = new ThingUID(thingTypeUID, thingHandler.getThing().getUID(), device.id.replace(':', '_'));
|
||||
ThingUID thingUID = new ThingUID(thingTypeUID, thingHandler.getThing().getUID(),
|
||||
buildCompliantThingID(device.id));
|
||||
|
||||
logger.trace("- got thingUID '{}' for device: '{}'", thingUID, device);
|
||||
|
||||
DiscoveryResultBuilder discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
|
||||
.withProperty("id", device.id).withLabel(getNiceName(device.name, roomName));
|
||||
discoveryResult.withBridge(thingHandler.getThing().getUID());
|
||||
|
||||
if (!roomName.isEmpty()) {
|
||||
discoveryResult.withProperty("Location", roomName);
|
||||
}
|
||||
@ -235,6 +245,18 @@ public class ThingDiscoveryService extends AbstractThingHandlerDiscoveryService<
|
||||
thingUID, thingTypeUID, device.id, device.deviceModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a Bosch device ID to an openHAB-compliant thing ID.
|
||||
* <p>
|
||||
* Characters that are not allowed in thing IDs are replaced by underscores.
|
||||
*
|
||||
* @param deviceId the Bosch device ID
|
||||
* @return the translated openHAB-compliant thing ID
|
||||
*/
|
||||
private String buildCompliantThingID(String deviceId) {
|
||||
return deviceId.replace(':', '_').replace('#', '_');
|
||||
}
|
||||
|
||||
private String getNiceName(String name, String roomName) {
|
||||
if (!name.startsWith("-")) {
|
||||
return name;
|
||||
@ -268,6 +290,15 @@ public class ThingDiscoveryService extends AbstractThingHandlerDiscoveryService<
|
||||
if (thingTypeId != null) {
|
||||
return new ThingTypeUID(BoschSHCBindingConstants.BINDING_ID, thingTypeId.getId());
|
||||
}
|
||||
|
||||
if (DEVICE_MODEL_LIGHT_CONTROL_CHILD_DEVICE.equals(device.deviceModel)) {
|
||||
// Light Control II exposes a parent device and two child devices.
|
||||
// We only add one thing for the parent device and the child devices are logically included.
|
||||
// Therefore we do not need to add separate things for the child devices and need to suppress the
|
||||
// log entry about the unknown device model.
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug("Unknown deviceModel '{}'! Please create a support request issue for this unknown device model.",
|
||||
device.deviceModel);
|
||||
return null;
|
||||
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.boschshc.internal.services.childprotection;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.boschshc.internal.services.BoschSHCService;
|
||||
import org.openhab.binding.boschshc.internal.services.childprotection.dto.ChildProtectionServiceState;
|
||||
|
||||
/**
|
||||
* Service to activate and deactivate child protection.
|
||||
*
|
||||
* @author David Pace - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ChildProtectionService extends BoschSHCService<ChildProtectionServiceState> {
|
||||
|
||||
public static final String CHILD_PROTECTION_SERVICE_NAME = "ChildProtection";
|
||||
|
||||
public ChildProtectionService() {
|
||||
super(CHILD_PROTECTION_SERVICE_NAME, ChildProtectionServiceState.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.boschshc.internal.services.childprotection.dto;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
|
||||
|
||||
/**
|
||||
* State of the child protection service.
|
||||
*
|
||||
* @author David Pace - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ChildProtectionServiceState extends BoschSHCServiceState {
|
||||
|
||||
public ChildProtectionServiceState() {
|
||||
super("ChildProtectionState");
|
||||
}
|
||||
|
||||
public boolean childLockActive;
|
||||
}
|
@ -24,7 +24,9 @@ import org.openhab.binding.boschshc.internal.services.powerswitch.dto.PowerSwitc
|
||||
@NonNullByDefault
|
||||
public class PowerSwitchService extends BoschSHCService<PowerSwitchServiceState> {
|
||||
|
||||
public static final String POWER_SWITCH_SERVICE_NAME = "PowerSwitch";
|
||||
|
||||
public PowerSwitchService() {
|
||||
super("PowerSwitch", PowerSwitchServiceState.class);
|
||||
super(POWER_SWITCH_SERVICE_NAME, PowerSwitchServiceState.class);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:boschshc:bridge">
|
||||
<parameter name="ipAddress" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
@ -15,16 +16,19 @@
|
||||
<description>The system password of the Bosch Smart Home Controller necessary for pairing.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="thing-type:boschshc:device">
|
||||
<parameter name="id" type="text" required="true">
|
||||
<label>Device ID</label>
|
||||
<description>Unique ID of the device.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="thing-type:boschshc:user-defined-state">
|
||||
<parameter name="id" type="text" required="true">
|
||||
<label>State ID</label>
|
||||
<description>Unique ID of the state.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
||||
|
@ -11,6 +11,8 @@ thing-type.boschshc.in-wall-switch.label = In-wall Switch
|
||||
thing-type.boschshc.in-wall-switch.description = A simple light control.
|
||||
thing-type.boschshc.intrusion-detection-system.label = Intrusion Detection System
|
||||
thing-type.boschshc.intrusion-detection-system.description = Allows to retrieve and control the state of the intrusion detection alarm system.
|
||||
thing-type.boschshc.light-control-2.label = Light Control II
|
||||
thing-type.boschshc.light-control-2.description = Advanced light control with two switch circuits.
|
||||
thing-type.boschshc.motion-detector.label = Motion Detector
|
||||
thing-type.boschshc.motion-detector.description = Detects every movement through an intelligent combination of passive infra-red technology and an additional temperature sensor.
|
||||
thing-type.boschshc.security-camera-360.label = Security Camera 360
|
||||
@ -19,6 +21,8 @@ thing-type.boschshc.security-camera-eyes.label = Security Camera Eyes
|
||||
thing-type.boschshc.security-camera-eyes.description = Outdoor security camera with motion detection and light.
|
||||
thing-type.boschshc.shc.label = Smart Home Controller
|
||||
thing-type.boschshc.shc.description = The Bosch Smart Home Bridge representing the Bosch Smart Home Controller.
|
||||
thing-type.boschshc.shutter-control-2.label = Shutter Control II
|
||||
thing-type.boschshc.shutter-control-2.description = Second generation shutter control.
|
||||
thing-type.boschshc.shutter-control.label = Shutter Control
|
||||
thing-type.boschshc.shutter-control.description = Control of your shutter to take any position you desire.
|
||||
thing-type.boschshc.smart-bulb.label = Smart Bulb
|
||||
@ -87,6 +91,8 @@ channel-type.boschshc.camera-notification.state.option.ON = Enabled
|
||||
channel-type.boschshc.camera-notification.state.option.OFF = Disabled
|
||||
channel-type.boschshc.child-lock.label = Child Lock
|
||||
channel-type.boschshc.child-lock.description = Enables or disables the child lock on the device.
|
||||
channel-type.boschshc.child-protection.label = Child Protection
|
||||
channel-type.boschshc.child-protection.description = Enables or disables the child protection on the device.
|
||||
channel-type.boschshc.combined-rating.label = Combined Rating
|
||||
channel-type.boschshc.combined-rating.description = Combined rating of the air quality.
|
||||
channel-type.boschshc.combined-rating.state.option.GOOD = Good Quality
|
||||
@ -185,3 +191,4 @@ offline.conf-error.empty-device-id = No device ID set.
|
||||
offline.conf-error.invalid-device-id = Device ID is invalid.
|
||||
offline.conf-error.empty-state-id = No ID set.
|
||||
offline.conf-error.invalid-state-id = ID is invalid.
|
||||
offline.conf-error.child-device-ids-not-obtainable = Could not obtain child device IDs.
|
||||
|
@ -163,6 +163,48 @@
|
||||
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="shutter-control-2">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="shc"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Shutter Control II</label>
|
||||
<description>Second generation shutter control.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="level" typeId="level"/>
|
||||
<channel id="signal-strength" typeId="system.signal-strength"/>
|
||||
<channel id="child-protection" typeId="child-protection"/>
|
||||
<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="light-control-2">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="shc"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Light Control II</label>
|
||||
<description>Advanced light control with two switch circuits.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="signal-strength" typeId="system.signal-strength"/>
|
||||
<channel id="power-consumption" typeId="power-consumption"/>
|
||||
<channel id="energy-consumption" typeId="energy-consumption"/>
|
||||
<channel id="power-switch-1" typeId="system.power"/>
|
||||
<channel id="child-protection-1" typeId="child-protection"/>
|
||||
<channel id="power-switch-2" typeId="system.power"/>
|
||||
<channel id="child-protection-2" typeId="child-protection"/>
|
||||
</channels>
|
||||
|
||||
<config-description-ref uri="thing-type:boschshc:device"/>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="thermostat">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="shc"/>
|
||||
@ -703,4 +745,10 @@
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="child-protection">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Child Protection</label>
|
||||
<description>Enables or disables the child protection on the device.</description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
@ -13,6 +13,7 @@
|
||||
package org.openhab.binding.boschshc.internal.devices;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
|
||||
/**
|
||||
@ -26,6 +27,13 @@ import org.openhab.core.config.core.Configuration;
|
||||
public abstract class AbstractBoschSHCDeviceHandlerTest<T extends BoschSHCDeviceHandler>
|
||||
extends AbstractBoschSHCHandlerTest<T> {
|
||||
|
||||
@Override
|
||||
protected void configureDevice(Device device) {
|
||||
super.configureDevice(device);
|
||||
|
||||
device.id = getDeviceID();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Configuration getConfiguration() {
|
||||
Configuration configuration = super.getConfiguration();
|
||||
|
@ -12,8 +12,13 @@
|
||||
*/
|
||||
package org.openhab.binding.boschshc.internal.devices;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@ -25,6 +30,7 @@ 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.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
@ -58,6 +64,8 @@ public abstract class AbstractBoschSHCHandlerTest<T extends BoschSHCHandler> {
|
||||
|
||||
private @Mock @NonNullByDefault({}) ThingHandlerCallback callback;
|
||||
|
||||
private @NonNullByDefault({}) Device device;
|
||||
|
||||
protected AbstractBoschSHCHandlerTest() {
|
||||
this.fixture = createFixture();
|
||||
}
|
||||
@ -72,6 +80,10 @@ public abstract class AbstractBoschSHCHandlerTest<T extends BoschSHCHandler> {
|
||||
when(bridge.getHandler()).thenReturn(bridgeHandler);
|
||||
lenient().when(thing.getConfiguration()).thenReturn(getConfiguration());
|
||||
|
||||
device = new Device();
|
||||
configureDevice(device);
|
||||
lenient().when(bridgeHandler.getDeviceInfo(anyString())).thenReturn(device);
|
||||
|
||||
fixture.initialize();
|
||||
}
|
||||
|
||||
@ -107,6 +119,14 @@ public abstract class AbstractBoschSHCHandlerTest<T extends BoschSHCHandler> {
|
||||
return callback;
|
||||
}
|
||||
|
||||
protected Device getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
protected void configureDevice(Device device) {
|
||||
// abstract implementation is empty, subclasses may override
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitialize() {
|
||||
ThingStatusInfo expectedStatusInfo = new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
|
||||
|
@ -12,28 +12,26 @@
|
||||
*/
|
||||
package org.openhab.binding.boschshc.internal.devices;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.measure.quantity.Energy;
|
||||
import javax.measure.quantity.Power;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
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.powermeter.dto.PowerMeterServiceState;
|
||||
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.types.RefreshType;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
@ -52,10 +50,6 @@ public abstract class AbstractPowerSwitchHandlerTest<T extends AbstractPowerSwit
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<PowerSwitchServiceState> serviceStateCaptor;
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<QuantityType<Power>> powerCaptor;
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<QuantityType<Energy>> energyCaptor;
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
|
||||
@ -65,12 +59,6 @@ public abstract class AbstractPowerSwitchHandlerTest<T extends AbstractPowerSwit
|
||||
powerSwitchServiceState.switchState = PowerSwitchState.ON;
|
||||
lenient().when(bridgeHandler.getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class)))
|
||||
.thenReturn(powerSwitchServiceState);
|
||||
|
||||
PowerMeterServiceState powerMeterServiceState = new PowerMeterServiceState();
|
||||
powerMeterServiceState.powerConsumption = 12.34d;
|
||||
powerMeterServiceState.energyConsumption = 56.78d;
|
||||
lenient().when(bridgeHandler.getState(anyString(), eq("PowerMeter"), same(PowerMeterServiceState.class)))
|
||||
.thenReturn(powerMeterServiceState);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -101,47 +89,9 @@ public abstract class AbstractPowerSwitchHandlerTest<T extends AbstractPowerSwit
|
||||
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateChannelPowerMeterServiceState() {
|
||||
JsonElement jsonObject = JsonParser.parseString("""
|
||||
{
|
||||
"@type": "powerMeterState",
|
||||
"powerConsumption": "23",
|
||||
"energyConsumption": 42
|
||||
}\
|
||||
""");
|
||||
getFixture().processUpdate("PowerMeter", jsonObject);
|
||||
|
||||
verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)),
|
||||
powerCaptor.capture());
|
||||
QuantityType<Power> powerValue = powerCaptor.getValue();
|
||||
assertEquals(23, powerValue.intValue());
|
||||
|
||||
verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)),
|
||||
energyCaptor.capture());
|
||||
QuantityType<Energy> energyValue = energyCaptor.getValue();
|
||||
assertEquals(42, energyValue.intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleCommandRefreshPowerSwitchChannel() {
|
||||
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), RefreshType.REFRESH);
|
||||
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleCommandRefreshPowerConsumptionChannel() {
|
||||
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION),
|
||||
RefreshType.REFRESH);
|
||||
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION),
|
||||
new QuantityType<>(12.34d, Units.WATT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleCommandRefreshEnergyConsumptionChannel() {
|
||||
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION),
|
||||
RefreshType.REFRESH);
|
||||
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION),
|
||||
new QuantityType<>(56.78d, Units.WATT_HOUR));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.measure.quantity.Energy;
|
||||
import javax.measure.quantity.Power;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
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.powermeter.dto.PowerMeterServiceState;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* Abstract unit test implementation for power switch handler with power meter support.
|
||||
*
|
||||
* @author David Pace - Initial contribution
|
||||
*
|
||||
* @param <T> type of the handler to be tested
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AbstractPowerSwitchHandlerWithPowerMeterTest<T extends AbstractPowerSwitchHandlerWithPowerMeter>
|
||||
extends AbstractPowerSwitchHandlerTest<T> {
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<QuantityType<Power>> powerCaptor;
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<QuantityType<Energy>> energyCaptor;
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
|
||||
super.beforeEach();
|
||||
|
||||
PowerMeterServiceState powerMeterServiceState = new PowerMeterServiceState();
|
||||
powerMeterServiceState.powerConsumption = 12.34d;
|
||||
powerMeterServiceState.energyConsumption = 56.78d;
|
||||
lenient().when(bridgeHandler.getState(anyString(), eq("PowerMeter"), same(PowerMeterServiceState.class)))
|
||||
.thenReturn(powerMeterServiceState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateChannelPowerMeterServiceState() {
|
||||
JsonElement jsonObject = JsonParser.parseString("""
|
||||
{
|
||||
"@type": "powerMeterState",
|
||||
"powerConsumption": "23",
|
||||
"energyConsumption": 42
|
||||
}\
|
||||
""");
|
||||
getFixture().processUpdate("PowerMeter", jsonObject);
|
||||
|
||||
verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)),
|
||||
powerCaptor.capture());
|
||||
QuantityType<Power> powerValue = powerCaptor.getValue();
|
||||
assertEquals(23, powerValue.intValue());
|
||||
|
||||
verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)),
|
||||
energyCaptor.capture());
|
||||
QuantityType<Energy> energyValue = energyCaptor.getValue();
|
||||
assertEquals(42, energyValue.intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleCommandRefreshPowerConsumptionChannel() {
|
||||
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION),
|
||||
RefreshType.REFRESH);
|
||||
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION),
|
||||
new QuantityType<>(12.34d, Units.WATT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleCommandRefreshEnergyConsumptionChannel() {
|
||||
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION),
|
||||
RefreshType.REFRESH);
|
||||
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION),
|
||||
new QuantityType<>(56.78d, Units.WATT_HOUR));
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link BoschDeviceIdUtils}.
|
||||
*
|
||||
* @author David Pace - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class BoschDeviceIdUtilsTest {
|
||||
|
||||
@Test
|
||||
void testIsChildDeviceId() {
|
||||
assertFalse(BoschDeviceIdUtils.isChildDeviceId("hdm:ZigBee:70ac08fffe5294ea"));
|
||||
assertTrue(BoschDeviceIdUtils.isChildDeviceId("hdm:ZigBee:70ac08fffe5294ea#3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetParentDeviceId() {
|
||||
assertEquals("hdm:ZigBee:70ac08fffe5294ea",
|
||||
BoschDeviceIdUtils.getParentDeviceId("hdm:ZigBee:70ac08fffe5294ea#3"));
|
||||
assertEquals("hdm:ZigBee:70ac08fffe5294ea",
|
||||
BoschDeviceIdUtils.getParentDeviceId("hdm:ZigBee:70ac08fffe5294ea"));
|
||||
}
|
||||
}
|
@ -101,6 +101,13 @@ class BoschHttpClientTest {
|
||||
httpClient.getServiceStateUrl("testService", "testDevice", UserStateServiceState.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getServiceStateUrlForChildDevice() {
|
||||
assertEquals(
|
||||
"https://127.0.0.1:8444/smarthome/devices/hdm:ZigBee:70ac08fffe5294ea%233/services/PowerSwitch/state",
|
||||
httpClient.getServiceStateUrl("PowerSwitch", "hdm:ZigBee:70ac08fffe5294ea#3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAccessPossible() throws InterruptedException {
|
||||
assertFalse(httpClient.isAccessPossible());
|
||||
|
@ -12,14 +12,27 @@
|
||||
*/
|
||||
package org.openhab.binding.boschshc.internal.devices.bridge;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.contains;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -38,10 +51,12 @@ import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceTest;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Faults;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedState;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedStateTest;
|
||||
@ -62,6 +77,9 @@ import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||
import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link BridgeHandler}.
|
||||
*
|
||||
@ -77,6 +95,11 @@ class BridgeHandlerTest {
|
||||
|
||||
private @NonNullByDefault({}) ThingHandlerCallback thingHandlerCallback;
|
||||
|
||||
/**
|
||||
* A mocked bridge instance
|
||||
*/
|
||||
private @NonNullByDefault({}) Bridge thing;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() throws IOException {
|
||||
Path mavenTargetFolder = Paths.get("target");
|
||||
@ -102,7 +125,7 @@ class BridgeHandlerTest {
|
||||
properties.put("password", "test");
|
||||
bridgeConfiguration.setProperties(properties);
|
||||
|
||||
Thing thing = mock(Bridge.class);
|
||||
thing = mock(Bridge.class);
|
||||
when(thing.getConfiguration()).thenReturn(bridgeConfiguration);
|
||||
// this calls initialize() as well
|
||||
fixture.thingUpdated(thing);
|
||||
@ -502,4 +525,129 @@ class BridgeHandlerTest {
|
||||
void afterEach() throws Exception {
|
||||
fixture.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleLongPollResultNoDeviceId() {
|
||||
List<Thing> things = new ArrayList<Thing>();
|
||||
when(thing.getThings()).thenReturn(things);
|
||||
|
||||
Thing thing = mock(Thing.class);
|
||||
things.add(thing);
|
||||
|
||||
BoschSHCHandler thingHandler = mock(BoschSHCHandler.class);
|
||||
when(thing.getHandler()).thenReturn(thingHandler);
|
||||
|
||||
String json = """
|
||||
{
|
||||
"result": [{
|
||||
"path": "/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch",
|
||||
"@type": "DeviceServiceData",
|
||||
"id": "PowerSwitch",
|
||||
"state": {
|
||||
"@type": "powerSwitchState",
|
||||
"switchState": "ON"
|
||||
},
|
||||
"deviceId": "hdm:HomeMaticIP:3014F711A0001916D859A8A9"
|
||||
}],
|
||||
"jsonrpc": "2.0"
|
||||
}
|
||||
""";
|
||||
LongPollResult longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(json, LongPollResult.class);
|
||||
assertNotNull(longPollResult);
|
||||
|
||||
fixture.handleLongPollResult(longPollResult);
|
||||
|
||||
verify(thingHandler).getBoschID();
|
||||
verifyNoMoreInteractions(thingHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleLongPollResult() {
|
||||
List<Thing> things = new ArrayList<Thing>();
|
||||
when(thing.getThings()).thenReturn(things);
|
||||
|
||||
Thing thing = mock(Thing.class);
|
||||
things.add(thing);
|
||||
|
||||
BoschSHCHandler thingHandler = mock(BoschSHCHandler.class);
|
||||
when(thing.getHandler()).thenReturn(thingHandler);
|
||||
|
||||
when(thingHandler.getBoschID()).thenReturn("hdm:HomeMaticIP:3014F711A0001916D859A8A9");
|
||||
|
||||
String json = """
|
||||
{
|
||||
"result": [{
|
||||
"path": "/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch",
|
||||
"@type": "DeviceServiceData",
|
||||
"id": "PowerSwitch",
|
||||
"state": {
|
||||
"@type": "powerSwitchState",
|
||||
"switchState": "ON"
|
||||
},
|
||||
"deviceId": "hdm:HomeMaticIP:3014F711A0001916D859A8A9"
|
||||
}],
|
||||
"jsonrpc": "2.0"
|
||||
}
|
||||
""";
|
||||
LongPollResult longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(json, LongPollResult.class);
|
||||
assertNotNull(longPollResult);
|
||||
|
||||
fixture.handleLongPollResult(longPollResult);
|
||||
|
||||
verify(thingHandler).getBoschID();
|
||||
|
||||
JsonElement expectedState = JsonParser.parseString("""
|
||||
{
|
||||
"@type": "powerSwitchState",
|
||||
"switchState": "ON"
|
||||
}
|
||||
""");
|
||||
|
||||
verify(thingHandler).processUpdate("PowerSwitch", expectedState);
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleLongPollResultHandleChildUpdate() {
|
||||
List<Thing> things = new ArrayList<Thing>();
|
||||
when(thing.getThings()).thenReturn(things);
|
||||
|
||||
Thing thing = mock(Thing.class);
|
||||
things.add(thing);
|
||||
|
||||
BoschSHCHandler thingHandler = mock(BoschSHCHandler.class);
|
||||
when(thing.getHandler()).thenReturn(thingHandler);
|
||||
|
||||
when(thingHandler.getBoschID()).thenReturn("hdm:ZigBee:70ac08fffefead2d");
|
||||
|
||||
String json = """
|
||||
{
|
||||
"result": [{
|
||||
"path": "/devices/hdm:ZigBee:70ac08fffefead2d#3/services/PowerSwitch",
|
||||
"@type": "DeviceServiceData",
|
||||
"id": "PowerSwitch",
|
||||
"state": {
|
||||
"@type": "powerSwitchState",
|
||||
"switchState": "ON"
|
||||
},
|
||||
"deviceId": "hdm:ZigBee:70ac08fffefead2d#3"
|
||||
}],
|
||||
"jsonrpc": "2.0"
|
||||
}
|
||||
""";
|
||||
LongPollResult longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(json, LongPollResult.class);
|
||||
assertNotNull(longPollResult);
|
||||
|
||||
fixture.handleLongPollResult(longPollResult);
|
||||
|
||||
verify(thingHandler).getBoschID();
|
||||
|
||||
JsonElement expectedState = JsonParser.parseString("""
|
||||
{
|
||||
"@type": "powerSwitchState",
|
||||
"switchState": "ON"
|
||||
}
|
||||
""");
|
||||
|
||||
verify(thingHandler).processChildUpdate("hdm:ZigBee:70ac08fffefead2d#3", "PowerSwitch", expectedState);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,284 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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 static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.measure.quantity.Energy;
|
||||
import javax.measure.quantity.Power;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCDeviceHandlerTest;
|
||||
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
|
||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||
import org.openhab.binding.boschshc.internal.services.childprotection.dto.ChildProtectionServiceState;
|
||||
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.DecimalType;
|
||||
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.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link LightControl2Handler}.
|
||||
*
|
||||
* @author David Pace - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class LightControl2HandlerTest extends AbstractBoschSHCDeviceHandlerTest<LightControl2Handler> {
|
||||
|
||||
private static final String CHILD_DEVICE_ID_1 = "hdm:ZigBee:70ac08fffefead2d#2";
|
||||
private static final String CHILD_DEVICE_ID_2 = "hdm:ZigBee:70ac08fffefead2d#3";
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<QuantityType<Power>> powerCaptor;
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<QuantityType<Energy>> energyCaptor;
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<ChildProtectionServiceState> childProtectionServiceStateCaptor;
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<PowerSwitchServiceState> powerSwitchStateCaptor;
|
||||
|
||||
@Override
|
||||
protected LightControl2Handler createFixture() {
|
||||
return new LightControl2Handler(getThing());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingTypeUID getThingTypeUID() {
|
||||
return BoschSHCBindingConstants.THING_TYPE_LIGHT_CONTROL_2;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDeviceID() {
|
||||
return "hdm:ZigBee:70ac08fcfefa5197";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureDevice(Device device) {
|
||||
super.configureDevice(device);
|
||||
|
||||
// order is reversed to test child ID sorting during initialization
|
||||
device.childDeviceIds = List.of(CHILD_DEVICE_ID_2, CHILD_DEVICE_ID_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateChannelCommunicationQualityService() {
|
||||
String json = """
|
||||
{
|
||||
"@type": "communicationQualityState",
|
||||
"quality": "UNKNOWN"
|
||||
}
|
||||
""";
|
||||
JsonElement jsonObject = JsonParser.parseString(json);
|
||||
|
||||
getFixture().processUpdate("CommunicationQuality", jsonObject);
|
||||
verify(getCallback()).stateUpdated(
|
||||
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SIGNAL_STRENGTH),
|
||||
new DecimalType(0));
|
||||
|
||||
json = """
|
||||
{
|
||||
"@type": "communicationQualityState",
|
||||
"quality": "GOOD"
|
||||
}
|
||||
""";
|
||||
jsonObject = JsonParser.parseString(json);
|
||||
|
||||
getFixture().processUpdate("CommunicationQuality", jsonObject);
|
||||
verify(getCallback()).stateUpdated(
|
||||
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SIGNAL_STRENGTH),
|
||||
new DecimalType(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateChannelPowerMeterServiceState() {
|
||||
JsonElement jsonObject = JsonParser.parseString("""
|
||||
{
|
||||
"@type": "powerMeterState",
|
||||
"powerConsumption": "23",
|
||||
"energyConsumption": 42
|
||||
}\
|
||||
""");
|
||||
getFixture().processUpdate("PowerMeter", jsonObject);
|
||||
|
||||
verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)),
|
||||
powerCaptor.capture());
|
||||
QuantityType<Power> powerValue = powerCaptor.getValue();
|
||||
assertEquals(23, powerValue.intValue());
|
||||
|
||||
verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)),
|
||||
energyCaptor.capture());
|
||||
QuantityType<Energy> energyValue = energyCaptor.getValue();
|
||||
assertEquals(42, energyValue.intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleCommandPowerSwitchChannelChildDevice1()
|
||||
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
|
||||
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH_1), OnOffType.ON);
|
||||
verify(getBridgeHandler()).putState(eq(CHILD_DEVICE_ID_1), eq("PowerSwitch"), powerSwitchStateCaptor.capture());
|
||||
PowerSwitchServiceState state = powerSwitchStateCaptor.getValue();
|
||||
assertSame(PowerSwitchState.ON, state.switchState);
|
||||
|
||||
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH_1), OnOffType.OFF);
|
||||
verify(getBridgeHandler(), times(2)).putState(eq(CHILD_DEVICE_ID_1), eq("PowerSwitch"),
|
||||
powerSwitchStateCaptor.capture());
|
||||
state = powerSwitchStateCaptor.getValue();
|
||||
assertSame(PowerSwitchState.OFF, state.switchState);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleCommandPowerSwitchChannelChildDevice2()
|
||||
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
|
||||
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH_2), OnOffType.ON);
|
||||
verify(getBridgeHandler()).putState(eq(CHILD_DEVICE_ID_2), eq("PowerSwitch"), powerSwitchStateCaptor.capture());
|
||||
PowerSwitchServiceState state = powerSwitchStateCaptor.getValue();
|
||||
assertSame(PowerSwitchState.ON, state.switchState);
|
||||
|
||||
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH_2), OnOffType.OFF);
|
||||
verify(getBridgeHandler(), times(2)).putState(eq(CHILD_DEVICE_ID_2), eq("PowerSwitch"),
|
||||
powerSwitchStateCaptor.capture());
|
||||
state = powerSwitchStateCaptor.getValue();
|
||||
assertSame(PowerSwitchState.OFF, state.switchState);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateChannelPowerSwitchStateChildDevice1() {
|
||||
JsonElement jsonObject = JsonParser
|
||||
.parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"ON\"\n" + "}");
|
||||
getFixture().processChildUpdate(CHILD_DEVICE_ID_1, PowerSwitchService.POWER_SWITCH_SERVICE_NAME, jsonObject);
|
||||
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH_1),
|
||||
OnOffType.ON);
|
||||
|
||||
jsonObject = JsonParser
|
||||
.parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"OFF\"\n" + "}");
|
||||
getFixture().processChildUpdate(CHILD_DEVICE_ID_1, PowerSwitchService.POWER_SWITCH_SERVICE_NAME, jsonObject);
|
||||
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH_1),
|
||||
OnOffType.OFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateChannelPowerSwitchStateChildDevice2() {
|
||||
JsonElement jsonObject = JsonParser
|
||||
.parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"ON\"\n" + "}");
|
||||
getFixture().processChildUpdate(CHILD_DEVICE_ID_2, PowerSwitchService.POWER_SWITCH_SERVICE_NAME, jsonObject);
|
||||
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH_2),
|
||||
OnOffType.ON);
|
||||
|
||||
jsonObject = JsonParser
|
||||
.parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"OFF\"\n" + "}");
|
||||
getFixture().processChildUpdate(CHILD_DEVICE_ID_2, PowerSwitchService.POWER_SWITCH_SERVICE_NAME, jsonObject);
|
||||
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH_2),
|
||||
OnOffType.OFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateChannelsChildProtectionServiceChildDevice1() {
|
||||
String json = """
|
||||
{
|
||||
"@type": "ChildProtectionState",
|
||||
"childLockActive": true
|
||||
}
|
||||
""";
|
||||
JsonElement jsonObject = JsonParser.parseString(json);
|
||||
|
||||
getFixture().processChildUpdate(CHILD_DEVICE_ID_1, "ChildProtection", jsonObject);
|
||||
verify(getCallback()).stateUpdated(
|
||||
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION_1), OnOffType.ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateChannelsChildProtectionServiceChildDevice2() {
|
||||
String json = """
|
||||
{
|
||||
"@type": "ChildProtectionState",
|
||||
"childLockActive": true
|
||||
}
|
||||
""";
|
||||
JsonElement jsonObject = JsonParser.parseString(json);
|
||||
|
||||
getFixture().processChildUpdate(CHILD_DEVICE_ID_2, "ChildProtection", jsonObject);
|
||||
verify(getCallback()).stateUpdated(
|
||||
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION_2), OnOffType.ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleCommandChildProtectionServiceChildDevice1()
|
||||
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
|
||||
getFixture().handleCommand(
|
||||
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION_1), OnOffType.ON);
|
||||
verify(getBridgeHandler()).putState(eq(CHILD_DEVICE_ID_1), eq("ChildProtection"),
|
||||
childProtectionServiceStateCaptor.capture());
|
||||
ChildProtectionServiceState state = childProtectionServiceStateCaptor.getValue();
|
||||
assertTrue(state.childLockActive);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleCommandChildProtectionServiceChildDevice2()
|
||||
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
|
||||
getFixture().handleCommand(
|
||||
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION_2), OnOffType.ON);
|
||||
verify(getBridgeHandler()).putState(eq(CHILD_DEVICE_ID_2), eq("ChildProtection"),
|
||||
childProtectionServiceStateCaptor.capture());
|
||||
ChildProtectionServiceState state = childProtectionServiceStateCaptor.getValue();
|
||||
assertTrue(state.childLockActive);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInitializeNoChildIDsInDeviceInfo() {
|
||||
getDevice().childDeviceIds = null;
|
||||
|
||||
getFixture().initialize();
|
||||
|
||||
verify(getCallback()).statusUpdated(same(getThing()),
|
||||
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
|
||||
&& status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInitializeServicesNoChildIDsInDeviceInfo() {
|
||||
getDevice().childDeviceIds = null;
|
||||
|
||||
LightControl2Handler lFixture = new LightControl2Handler(getThing());
|
||||
lFixture.setCallback(getCallback());
|
||||
|
||||
// this call will return before reaching initializeServices()
|
||||
lFixture.initialize();
|
||||
|
||||
assertThrows(BoschSHCException.class, () -> lFixture.initializeServices());
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
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.AbstractPowerSwitchHandlerWithPowerMeterTest;
|
||||
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
@ -24,7 +24,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LightControlHandlerTest extends AbstractPowerSwitchHandlerTest<LightControlHandler> {
|
||||
class LightControlHandlerTest extends AbstractPowerSwitchHandlerWithPowerMeterTest<LightControlHandler> {
|
||||
|
||||
@Override
|
||||
protected ThingTypeUID getThingTypeUID() {
|
||||
|
@ -13,7 +13,7 @@
|
||||
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.AbstractPowerSwitchHandlerWithPowerMeterTest;
|
||||
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
@ -24,7 +24,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugHandlerTest extends AbstractPowerSwitchHandlerTest<PlugHandler> {
|
||||
class PlugHandlerTest extends AbstractPowerSwitchHandlerWithPowerMeterTest<PlugHandler> {
|
||||
|
||||
@Override
|
||||
protected PlugHandler createFixture() {
|
||||
|
@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.shuttercontrol;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.measure.quantity.Energy;
|
||||
import javax.measure.quantity.Power;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
|
||||
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||
import org.openhab.binding.boschshc.internal.services.childprotection.dto.ChildProtectionServiceState;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
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 com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ShutterControl2Handler}
|
||||
*
|
||||
* @author David Pace - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class ShutterControl2HandlerTest extends ShutterControlHandlerTest {
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<QuantityType<Power>> powerCaptor;
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<QuantityType<Energy>> energyCaptor;
|
||||
|
||||
private @Captor @NonNullByDefault({}) ArgumentCaptor<ChildProtectionServiceState> childProtectionServiceStateCaptor;
|
||||
|
||||
@Override
|
||||
protected ShutterControlHandler createFixture() {
|
||||
return new ShutterControl2Handler(getThing());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingTypeUID getThingTypeUID() {
|
||||
return BoschSHCBindingConstants.THING_TYPE_SHUTTER_CONTROL_2;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateChannelsCommunicationQualityService() {
|
||||
String json = """
|
||||
{
|
||||
"@type": "communicationQualityState",
|
||||
"quality": "UNKNOWN"
|
||||
}
|
||||
""";
|
||||
JsonElement jsonObject = JsonParser.parseString(json);
|
||||
|
||||
getFixture().processUpdate("CommunicationQuality", jsonObject);
|
||||
verify(getCallback()).stateUpdated(
|
||||
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SIGNAL_STRENGTH),
|
||||
new DecimalType(0));
|
||||
|
||||
json = """
|
||||
{
|
||||
"@type": "communicationQualityState",
|
||||
"quality": "GOOD"
|
||||
}
|
||||
""";
|
||||
jsonObject = JsonParser.parseString(json);
|
||||
|
||||
getFixture().processUpdate("CommunicationQuality", jsonObject);
|
||||
verify(getCallback()).stateUpdated(
|
||||
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SIGNAL_STRENGTH),
|
||||
new DecimalType(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateChannelsChildProtectionService() {
|
||||
String json = """
|
||||
{
|
||||
"@type": "ChildProtectionState",
|
||||
"childLockActive": true
|
||||
}
|
||||
""";
|
||||
JsonElement jsonObject = JsonParser.parseString(json);
|
||||
|
||||
getFixture().processUpdate("ChildProtection", jsonObject);
|
||||
verify(getCallback()).stateUpdated(
|
||||
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION), OnOffType.ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateChannelPowerMeterServiceState() {
|
||||
JsonElement jsonObject = JsonParser.parseString("""
|
||||
{
|
||||
"@type": "powerMeterState",
|
||||
"powerConsumption": "23",
|
||||
"energyConsumption": 42
|
||||
}\
|
||||
""");
|
||||
getFixture().processUpdate("PowerMeter", jsonObject);
|
||||
|
||||
verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)),
|
||||
powerCaptor.capture());
|
||||
QuantityType<Power> powerValue = powerCaptor.getValue();
|
||||
assertEquals(23, powerValue.intValue());
|
||||
|
||||
verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)),
|
||||
energyCaptor.capture());
|
||||
QuantityType<Energy> energyValue = energyCaptor.getValue();
|
||||
assertEquals(42, energyValue.intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleCommandChildProtection()
|
||||
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
|
||||
getFixture().handleCommand(
|
||||
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION), OnOffType.ON);
|
||||
verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("ChildProtection"),
|
||||
childProtectionServiceStateCaptor.capture());
|
||||
ChildProtectionServiceState state = childProtectionServiceStateCaptor.getValue();
|
||||
assertTrue(state.childLockActive);
|
||||
}
|
||||
}
|
@ -13,10 +13,16 @@
|
||||
package org.openhab.binding.boschshc.internal.discovery;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
@ -41,7 +47,7 @@ import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
|
||||
/**
|
||||
* ThingDiscoveryService Tester.
|
||||
* Unit tests for {@link ThingDiscoveryService}.
|
||||
*
|
||||
* @author Gerd Zanker - Initial contribution
|
||||
*/
|
||||
@ -261,4 +267,12 @@ class ThingDiscoveryServiceTest {
|
||||
// two calls for the two devices expected
|
||||
verify(discoveryListener, times(2)).thingDiscovered(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getThingTypeUIDLightControl2ChildDevice() {
|
||||
Device device = new Device();
|
||||
device.deviceModel = ThingDiscoveryService.DEVICE_MODEL_LIGHT_CONTROL_CHILD_DEVICE;
|
||||
|
||||
assertThat(fixture.getThingTypeUID(device), is(nullValue()));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user