Skip to content

Commit

Permalink
[boschshc] Support for Universal Switch I + II (#16274)
Browse files Browse the repository at this point in the history
* [boschshc] Support for Universal Switch I + II

- add thing type and channel type definitions
- re-generate i18n file
- add constants
- add model classes and enums
- implement service and handlers
- register handlers in factory
- register devices in discovery

closes #16244

Signed-off-by: David Pace <dev@davidpace.de>
  • Loading branch information
david-pace committed Jan 18, 2024
1 parent bcdb664 commit 15642af
Show file tree
Hide file tree
Showing 15 changed files with 542 additions and 9 deletions.
27 changes: 27 additions & 0 deletions bundles/org.openhab.binding.boschshc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Binding for the Bosch Smart Home.
- [Smart Bulb](#smart-bulb)
- [Smoke Detector](#smoke-detector)
- [User-defined States](#user-defined-states)
- [Universal Switch](#universal-switch)
- [Universal Switch II](#universal-switch-ii)
- [Limitations](#limitations)
- [Discovery](#discovery)
- [Bridge Configuration](#bridge-configuration)
Expand Down Expand Up @@ -246,6 +248,31 @@ Individual states can be activated/deactivated and can be used as triggers, cond
|-----------------|-----------| :------: |--------------------------------------------|
| user-state | Switch | &#9745; | Switches the User-defined state on or off. |

### Universal Switch

A universally configurable switch with two buttons.

**Thing Type ID**: `universal-switch`

| Channel Type ID | Item Type | Writable | Description |
| ------------------- | -------------------- | :------: | ----------------------------------------- |
| key-code | Number:Dimensionless | &#9744; | Integer code of the key that was pressed. |
| key-name | String | &#9744; | Name of a key pressed on a device. Possible values for Universal Switch: `LOWER_BUTTON`, `UPPER_BUTTON`. |
| key-event-type | String | &#9744; | Indicates how the key was pressed. Possible values are `PRESS_SHORT`, `PRESS_LONG` and `PRESS_LONG_RELEASED`. |
| key-event-timestamp | DateTime | &#9744; | Timestamp indicating when the key was pressed. |

### Universal Switch II

A universally configurable switch with four buttons.

**Thing Type ID**: `universal-switch-2`

| Channel Type ID | Item Type | Writable | Description |
| ------------------- | -------------------- | :------: | ----------------------------------------- |
| key-code | Number:Dimensionless | &#9744; | Integer code of the key that was pressed. |
| key-name | String | &#9744; | Name of the key that was pressed. Possible values for Universal Switch II: `LOWER_LEFT_BUTTON`, `LOWER_RIGHT_BUTTON`, `UPPER_LEFT_BUTTON`, `UPPER_RIGHT_BUTTON`. |
| key-event-type | String | &#9744; | Indicates how the key was pressed. Possible values are `PRESS_SHORT`, `PRESS_LONG` and `PRESS_LONG_RELEASED`. |
| key-event-timestamp | DateTime | &#9744; | Timestamp indicating when the key was pressed. |

## Limitations

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class BoschSHCBindingConstants {
public static final ThingTypeUID THING_TYPE_SMART_PLUG_COMPACT = new ThingTypeUID(BINDING_ID, "smart-plug-compact");
public static final ThingTypeUID THING_TYPE_SMART_BULB = new ThingTypeUID(BINDING_ID, "smart-bulb");
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_USER_DEFINED_STATE = new ThingTypeUID(BINDING_ID, "user-defined-state");

Expand Down Expand Up @@ -91,6 +93,10 @@ public class BoschSHCBindingConstants {
public static final String CHANNEL_ILLUMINANCE = "illuminance";
public static final String CHANNEL_BYPASS_STATE = "bypass-state";
public static final String CHANNEL_SIGNAL_STRENGTH = "signal-strength";
public static final String CHANNEL_KEY_CODE = "key-code";
public static final String CHANNEL_KEY_NAME = "key-name";
public static final String CHANNEL_KEY_EVENT_TYPE = "key-event-type";
public static final String CHANNEL_KEY_EVENT_TIMESTAMP = "key-event-timestamp";

public static final String CHANNEL_USER_DEFINED_STATE = "user-state";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,25 @@
*/
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.THING_TYPE_CAMERA_360;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_CAMERA_EYES;
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_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_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;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_THERMOSTAT;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_TWINGUARD;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_UNIVERSAL_SWITCH;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_UNIVERSAL_SWITCH_2;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_USER_DEFINED_STATE;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_WALL_THERMOSTAT;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_WINDOW_CONTACT;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.THING_TYPE_WINDOW_CONTACT_2;

import java.util.Collection;
import java.util.List;
Expand All @@ -32,18 +50,23 @@
import org.openhab.binding.boschshc.internal.devices.smokedetector.SmokeDetectorHandler;
import org.openhab.binding.boschshc.internal.devices.thermostat.ThermostatHandler;
import org.openhab.binding.boschshc.internal.devices.twinguard.TwinguardHandler;
import org.openhab.binding.boschshc.internal.devices.universalswitch.UniversalSwitch2Handler;
import org.openhab.binding.boschshc.internal.devices.universalswitch.UniversalSwitchHandler;
import org.openhab.binding.boschshc.internal.devices.userdefinedstate.UserStateHandler;
import org.openhab.binding.boschshc.internal.devices.wallthermostat.WallThermostatHandler;
import org.openhab.binding.boschshc.internal.devices.windowcontact.WindowContact2Handler;
import org.openhab.binding.boschshc.internal.devices.windowcontact.WindowContactHandler;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

/**
* The {@link BoschSHCHandlerFactory} is responsible for creating things and
Expand All @@ -59,6 +82,13 @@
@Component(configurationPid = "binding.boschshc", service = ThingHandlerFactory.class)
public class BoschSHCHandlerFactory extends BaseThingHandlerFactory {

private TimeZoneProvider timeZoneProvider;

@Activate
public BoschSHCHandlerFactory(final @Reference TimeZoneProvider timeZoneProvider) {
this.timeZoneProvider = timeZoneProvider;
}

private static class ThingTypeHandlerMapping {
public ThingTypeUID thingTypeUID;
public Function<Thing, BaseThingHandler> handlerSupplier;
Expand All @@ -69,7 +99,7 @@ public ThingTypeHandlerMapping(ThingTypeUID thingTypeUID, Function<Thing, BaseTh
}
}

private static final Collection<ThingTypeHandlerMapping> SUPPORTED_THING_TYPES = List.of(
private final Collection<ThingTypeHandlerMapping> supportedThingTypes = List.of(
new ThingTypeHandlerMapping(THING_TYPE_SHC, thing -> new BridgeHandler((Bridge) thing)),
new ThingTypeHandlerMapping(THING_TYPE_INWALL_SWITCH, LightControlHandler::new),
new ThingTypeHandlerMapping(THING_TYPE_TWINGUARD, TwinguardHandler::new),
Expand All @@ -86,19 +116,23 @@ public ThingTypeHandlerMapping(ThingTypeUID thingTypeUID, Function<Thing, BaseTh
new ThingTypeHandlerMapping(THING_TYPE_SMART_PLUG_COMPACT, PlugHandler::new),
new ThingTypeHandlerMapping(THING_TYPE_SMART_BULB, SmartBulbHandler::new),
new ThingTypeHandlerMapping(THING_TYPE_SMOKE_DETECTOR, SmokeDetectorHandler::new),
new ThingTypeHandlerMapping(THING_TYPE_USER_DEFINED_STATE, UserStateHandler::new));
new ThingTypeHandlerMapping(THING_TYPE_USER_DEFINED_STATE, UserStateHandler::new),
new ThingTypeHandlerMapping(THING_TYPE_UNIVERSAL_SWITCH,
thing -> new UniversalSwitchHandler(thing, timeZoneProvider)),
new ThingTypeHandlerMapping(THING_TYPE_UNIVERSAL_SWITCH_2,
thing -> new UniversalSwitch2Handler(thing, timeZoneProvider)));

@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES.stream().anyMatch(mapping -> mapping.thingTypeUID.equals(thingTypeUID));
return supportedThingTypes.stream().anyMatch(mapping -> mapping.thingTypeUID.equals(thingTypeUID));
}

@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();

// Search for mapping for thing type and return handler for it if found. Otherwise return null.
return SUPPORTED_THING_TYPES.stream().filter(mapping -> mapping.thingTypeUID.equals(thingTypeUID)).findFirst()
return supportedThingTypes.stream().filter(mapping -> mapping.thingTypeUID.equals(thingTypeUID)).findFirst()
.<@Nullable BaseThingHandler> map(mapping -> mapping.handlerSupplier.apply(thing)).orElse(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* 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.universalswitch;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.thing.Thing;

/**
* Handler for a universally configurable switch with four buttons.
*
* @author David Pace - Initial contribution
*
*/
@NonNullByDefault
public class UniversalSwitch2Handler extends UniversalSwitchHandler {

public UniversalSwitch2Handler(Thing thing, TimeZoneProvider timeZoneProvider) {
super(thing, timeZoneProvider);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* 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.universalswitch;

import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_KEY_CODE;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_KEY_EVENT_TIMESTAMP;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_KEY_EVENT_TYPE;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_KEY_NAME;

import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.List;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandler;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.services.keypad.KeypadService;
import org.openhab.binding.boschshc.internal.services.keypad.dto.KeypadServiceState;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Thing;

/**
* Handler for a universally configurable switch with two buttons.
*
* @author David Pace - Initial contribution
*
*/
@NonNullByDefault
public class UniversalSwitchHandler extends AbstractBatteryPoweredDeviceHandler {

private TimeZoneProvider timeZoneProvider;

public UniversalSwitchHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
super(thing);
this.timeZoneProvider = timeZoneProvider;
}

@Override
protected void initializeServices() throws BoschSHCException {
super.initializeServices();

createService(KeypadService::new, this::updateChannels,
List.of(CHANNEL_KEY_CODE, CHANNEL_KEY_NAME, CHANNEL_KEY_EVENT_TYPE, CHANNEL_KEY_EVENT_TIMESTAMP));
}

private void updateChannels(KeypadServiceState keypadServiceState) {
updateState(CHANNEL_KEY_CODE, new DecimalType(keypadServiceState.keyCode));
updateState(CHANNEL_KEY_NAME, new StringType(keypadServiceState.keyName.toString()));
updateState(CHANNEL_KEY_EVENT_TYPE, new StringType(keypadServiceState.eventType.toString()));

Instant instant = Instant.ofEpochMilli(keypadServiceState.eventTimestamp);
updateState(CHANNEL_KEY_EVENT_TIMESTAMP,
new DateTimeType(ZonedDateTime.ofInstant(instant, timeZoneProvider.getTimeZone())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ public class ThingDiscoveryService extends AbstractThingHandlerDiscoveryService<
new AbstractMap.SimpleEntry<>("LEDVANCE_LIGHT", BoschSHCBindingConstants.THING_TYPE_SMART_BULB),
new AbstractMap.SimpleEntry<>("SWD", BoschSHCBindingConstants.THING_TYPE_WINDOW_CONTACT),
new AbstractMap.SimpleEntry<>("SWD2", BoschSHCBindingConstants.THING_TYPE_WINDOW_CONTACT_2),
new AbstractMap.SimpleEntry<>("TRV", BoschSHCBindingConstants.THING_TYPE_THERMOSTAT)
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)
// 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.),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* 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.keypad;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.boschshc.internal.services.BoschSHCService;
import org.openhab.binding.boschshc.internal.services.keypad.dto.KeypadServiceState;

/**
* Service for the keypads for Universal Switches.
*
* @author David Pace - Initial contribution
*
*/
@NonNullByDefault
public class KeypadService extends BoschSHCService<KeypadServiceState> {

public KeypadService() {
super("Keypad", KeypadServiceState.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* 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.keypad.dto;

/**
* Event types of keys/buttons pressed on Universal Switches.
*
* @author David Pace - Initial contribution
*
*/
public enum KeyEventType {
PRESS_SHORT,
PRESS_LONG,
PRESS_LONG_RELEASED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* 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.keypad.dto;

/**
* Key names of keys/buttons pressed on Universal Switches.
*
* @author David Pace - Initial contribution
*
*/
public enum KeyName {
LOWER_BUTTON,
UPPER_BUTTON,
LOWER_LEFT_BUTTON,
LOWER_RIGHT_BUTTON,
UPPER_LEFT_BUTTON,
UPPER_RIGHT_BUTTON
}

0 comments on commit 15642af

Please sign in to comment.