Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion bundles/org.openhab.binding.boschshc/README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ public BoschShcCommandExtension(final @Reference ThingRegistry thingRegistry) {
*/
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -61,8 +57,6 @@ protected void initializeServices() throws BoschSHCException {
super.initializeServices();

this.registerService(this.powerSwitchService, this::updateChannels, List.of(CHANNEL_POWER_SWITCH), true);
this.createService(PowerMeterService::new, this::updateChannels,
List.of(CHANNEL_POWER_CONSUMPTION, CHANNEL_ENERGY_CONSUMPTION), true);
}

@Override
Expand All @@ -79,19 +73,9 @@ public void handleCommand(ChannelUID channelUID, Command command) {
}

/**
* 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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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
Expand All @@ -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";
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

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;
Expand Down Expand Up @@ -57,30 +58,68 @@ protected BoschSHCDeviceHandler(Thing thing) {

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

/**
Expand Down
Loading