Skip to content

Commit

Permalink
[deconz] support for color control and smart plugs, fixes for firmwar…
Browse files Browse the repository at this point in the history
…e >2.05.80 (openhab#8304)

Signed-off-by: Jan N. Klug <[email protected]>
  • Loading branch information
J-N-K authored and andrewfg committed Aug 31, 2020
1 parent d1b8825 commit 405d2ff
Show file tree
Hide file tree
Showing 15 changed files with 181 additions and 41 deletions.
31 changes: 16 additions & 15 deletions bundles/org.openhab.binding.deconz/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,20 @@ These sensors are supported:
| Vibration Sensor | ZHAVibration | `vibrationsensor` |
| deCONZ Artificial Daylight Sensor | deCONZ specific: simulated sensor | `daylightsensor` |
| Carbon-Monoxide Sensor | ZHACarbonmonoxide | `carbonmonoxide` |
| Color Controller | ZBT-Remote-ALL-RGBW | `colorcontrol` |


Additionally lights, window coverings (blinds) and thermostats are supported:

| Device type | Resource Type | Thing type |
|--------------------------------------|----------------------------------------|----------------------|
| Dimmable Light | Dimmable light, Dimmable plug-in unit | `dimmablelight` |
| On/Off Light | On/Off light, On/Off plug-in unit | `onofflight` |
| Color Light (w/o temperature) | Color dimmable light | `colorlight` |
| Extended Color Light (w/temperature) | Extended color light | `extendedcolorlight` |
| Blind / Window Covering | Window covering device | `windowcovering` |
| Thermostat | ZHAThermostat | `thermostat` |
| Warning Device (Siren) | Warning device | `warningdevice` |
| Device type | Resource Type | Thing type |
|--------------------------------------|-----------------------------------------------|----------------------|
| Dimmable Light | Dimmable light, Dimmable plug-in unit | `dimmablelight` |
| On/Off Light | On/Off light, On/Off plug-in unit, Smart plug | `onofflight` |
| Color Light (w/o temperature) | Color dimmable light | `colorlight` |
| Extended Color Light (w/temperature) | Extended color light | `extendedcolorlight` |
| Blind / Window Covering | Window covering device | `windowcovering` |
| Thermostat | ZHAThermostat | `thermostat` |
| Warning Device (Siren) | Warning device | `warningdevice` |

## Discovery

Expand Down Expand Up @@ -122,7 +123,7 @@ The sensor devices support some of the following channels:
| consumption | Number:Energy | R | Current power usage in Watts/Hour | consumptionsensor |
| voltage | Number:ElectricPotential | R | Current voltage in V | some powersensors |
| current | Number:ElectricCurrent | R | Current current in mA | some powersensors |
| button | Number | R | Last pressed button id on a switch | switch |
| button | Number | R | Last pressed button id on a switch | switch, colorcontrol |
| gesture | Number | R | A gesture that was performed with the switch | switch |
| lightlux | Number:Illuminance | R | Current light illuminance in Lux | lightsensor |
| light_level | Number | R | Current light level | lightsensor |
Expand All @@ -142,7 +143,7 @@ The sensor devices support some of the following channels:
| battery_level | Number | R | Battery level (in %) | any battery-powered sensor |
| battery_low | Switch | R | Battery level low: `ON`; `OFF` | any battery-powered sensor |
| carbonmonoxide | Switch | R | `ON` = carbon monoxide detected | carbonmonoxide |

| color | Color | R | Color set by remote | colorcontrol |

**NOTE:** Beside other non mandatory channels, the `battery_level` and `battery_low` channels will be added to the Thing during runtime if the sensor is battery-powered.
The specification of your sensor depends on the deCONZ capabilities.
Expand All @@ -169,10 +170,10 @@ Other devices support

The dimmer switch additionally supports trigger channels.

| Channel Type ID | Description | Thing types |
|-----------------|--------------------------|-------------|
| buttonevent | Event for switch pressed | switch |
| gestureevent | Event for gestures | switch |
| Channel Type ID | Description | Thing types |
|-----------------|--------------------------|----------------------|
| buttonevent | Event for switch pressed | switch, colorcontrol |
| gestureevent | Event for gestures | switch |

**NOTE:** The `gestureevent` trigger channel is only available if the optional channel `gesture` is present.
Both will be added during runtime if supported by the switch.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class BindingConstants {
public static final ThingTypeUID THING_TYPE_POWER_SENSOR = new ThingTypeUID(BINDING_ID, "powersensor");
public static final ThingTypeUID THING_TYPE_CONSUMPTION_SENSOR = new ThingTypeUID(BINDING_ID, "consumptionsensor");
public static final ThingTypeUID THING_TYPE_DAYLIGHT_SENSOR = new ThingTypeUID(BINDING_ID, "daylightsensor");
public static final ThingTypeUID THING_TYPE_COLOR_CONTROL = new ThingTypeUID(BINDING_ID, "colorcontrol");
public static final ThingTypeUID THING_TYPE_SWITCH = new ThingTypeUID(BINDING_ID, "switch");
public static final ThingTypeUID THING_TYPE_LIGHT_SENSOR = new ThingTypeUID(BINDING_ID, "lightsensor");
public static final ThingTypeUID THING_TYPE_TEMPERATURE_SENSOR = new ThingTypeUID(BINDING_ID, "temperaturesensor");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@
*/
package org.openhab.binding.deconz.internal;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.smarthome.core.library.types.DateTimeType;

/**
* The {@link Util} class defines common utility methods
Expand Down Expand Up @@ -47,4 +53,21 @@ public static int kelvinToMired(int kelvinValue) {
public static int constrainToRange(int intValue, int min, int max) {
return Math.max(min, Math.min(intValue, max));
}

/**
* convert a timestamp string to a DateTimeType
*
* @param timestamp either in zoned date time or local date time format
* @return the corresponding DateTimeType
*/
public static DateTimeType convertTimestampToDateTime(String timestamp) {
if (timestamp.endsWith("Z")) {
return new DateTimeType(
ZonedDateTime.parse(timestamp));
} else {
return new DateTimeType(
ZonedDateTime.ofInstant(LocalDateTime.parse(timestamp, DateTimeFormatter.ISO_LOCAL_DATE_TIME),
ZoneOffset.UTC, ZoneId.systemDefault()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ private void addLight(String lightID, LightMessage light) {
switch (lightType) {
case ON_OFF_LIGHT:
case ON_OFF_PLUGIN_UNIT:
case SMART_PLUG:
thingTypeUID = THING_TYPE_ONOFF_LIGHT;
break;
case DIMMABLE_LIGHT:
Expand Down Expand Up @@ -158,7 +159,7 @@ private void addLight(String lightID, LightMessage light) {
default:
logger.debug(
"Found light: {} ({}), type {} but no thing type defined for that type. This should be reported.",
light.modelid, light.name, lightType);
light.modelid, light.name, light.type);
return;
}

Expand Down Expand Up @@ -191,7 +192,11 @@ private void addSensor(String sensorID, SensorMessage sensor) {
} else if (sensor.type.contains("Presence")) { // ZHAPresence, CLIPPrensence
thingTypeUID = THING_TYPE_PRESENCE_SENSOR;
} else if (sensor.type.contains("Switch")) { // ZHASwitch
thingTypeUID = THING_TYPE_SWITCH;
if (sensor.modelid.contains("RGBW")) {
thingTypeUID = THING_TYPE_COLOR_CONTROL;
} else {
thingTypeUID = THING_TYPE_SWITCH;
}
} else if (sensor.type.contains("LightLevel")) { // ZHALightLevel
thingTypeUID = THING_TYPE_LIGHT_SENSOR;
} else if (sensor.type.contains("ZHATemperature")) { // ZHATemperature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*/
package org.openhab.binding.deconz.internal.dto;

import java.util.Arrays;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

Expand Down Expand Up @@ -76,6 +78,8 @@ public class SensorState {
public @Nullable Integer valve;
/** deCONZ sends a last update string with every event. */
public @Nullable String lastupdated;
/** color controllers send xy values */
public double @Nullable [] xy;

@Override
public String toString() {
Expand All @@ -85,6 +89,7 @@ public String toString() {
+ ", carbonmonoxide=" + carbonmonoxide + ", pressure=" + pressure + ", presence=" + presence
+ ", power=" + power + ", battery=" + battery + ", consumption=" + consumption + ", voltage=" + voltage
+ ", current=" + current + ", status=" + status + ", buttonevent=" + buttonevent + ", gesture="
+ gesture + ", lastupdated='" + lastupdated + '\'' + '}';
+ gesture + ", valve=" + valve + ", lastupdated='" + lastupdated + '\'' + ", xy=" + Arrays.toString(xy)
+ '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,9 @@
*/
@NonNullByDefault
public class LightThingHandler extends DeconzBaseThingHandler<LightMessage> {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Stream
.of(THING_TYPE_COLOR_TEMPERATURE_LIGHT, THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_COLOR_LIGHT,
THING_TYPE_EXTENDED_COLOR_LIGHT, THING_TYPE_ONOFF_LIGHT, THING_TYPE_WINDOW_COVERING,
THING_TYPE_WARNING_DEVICE).collect(Collectors.toSet());
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Stream.of(THING_TYPE_COLOR_TEMPERATURE_LIGHT,
THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_COLOR_LIGHT, THING_TYPE_EXTENDED_COLOR_LIGHT, THING_TYPE_ONOFF_LIGHT,
THING_TYPE_WINDOW_COVERING, THING_TYPE_WARNING_DEVICE).collect(Collectors.toSet());

private static final double HUE_FACTOR = 65535 / 360.0;
private static final double BRIGHTNESS_FACTOR = 2.54;
Expand Down Expand Up @@ -396,6 +395,7 @@ private PercentType toPercentType(int val) {
scaledValue = scaledValue < 0 ? 0 : scaledValue;
scaledValue = scaledValue > 100 ? 100 : scaledValue;
}
logger.debug("val = '{}', scaledValue = '{}'", val, scaledValue);
return new PercentType(scaledValue);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@

import static org.openhab.binding.deconz.internal.BindingConstants.*;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
Expand All @@ -28,7 +23,6 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.types.DateTimeType;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.QuantityType;
Expand All @@ -41,6 +35,7 @@
import org.eclipse.smarthome.core.thing.type.ChannelKind;
import org.eclipse.smarthome.core.thing.type.ChannelTypeUID;
import org.eclipse.smarthome.core.types.Command;
import org.openhab.binding.deconz.internal.Util;
import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
import org.openhab.binding.deconz.internal.dto.SensorConfig;
import org.openhab.binding.deconz.internal.dto.SensorMessage;
Expand Down Expand Up @@ -190,12 +185,10 @@ protected void processStateResponse(@Nullable SensorMessage stateResponse) {
// "Last seen" is the last "ping" from the device, whereas "last update" is the last status changed.
// For example, for a fire sensor, the device pings regularly, without necessarily updating channels.
// So to monitor a sensor is still alive, the "last seen" is necessary.
if (stateResponse.lastseen != null && config.lastSeenPolling > 0) {
String lastSeen = stateResponse.lastseen;
if (lastSeen != null && config.lastSeenPolling > 0) {
createChannel(CHANNEL_LAST_SEEN, ChannelKind.STATE);
updateState(CHANNEL_LAST_SEEN,
new DateTimeType(ZonedDateTime.ofInstant(
LocalDateTime.parse(stateResponse.lastseen, DateTimeFormatter.ISO_LOCAL_DATE_TIME),
ZoneOffset.UTC, ZoneId.systemDefault())));
updateState(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime(lastSeen));
// Because "last seen" is never updated by the WebSocket API - if this is supported, then we have to
// manually poll it after the defined time (default is off)
if (config.lastSeenPolling > 0) {
Expand Down Expand Up @@ -266,10 +259,7 @@ protected void valueUpdated(String channelID, SensorState newState, boolean init
case CHANNEL_LAST_UPDATED:
String lastUpdated = newState.lastupdated;
if (lastUpdated != null && !"none".equals(lastUpdated)) {
updateState(channelID,
new DateTimeType(ZonedDateTime.ofInstant(
LocalDateTime.parse(lastUpdated, DateTimeFormatter.ISO_LOCAL_DATE_TIME),
ZoneOffset.UTC, ZoneId.systemDefault())));
updateState(channelID, Util.convertTimestampToDateTime(lastUpdated));
}
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.types.HSBType;
import org.eclipse.smarthome.core.library.types.OpenClosedType;
import org.eclipse.smarthome.core.library.types.QuantityType;
import org.eclipse.smarthome.core.library.types.StringType;
Expand Down Expand Up @@ -63,7 +65,7 @@ public class SensorThingHandler extends SensorBaseThingHandler {
THING_TYPE_HUMIDITY_SENSOR, THING_TYPE_PRESSURE_SENSOR, THING_TYPE_SWITCH,
THING_TYPE_OPENCLOSE_SENSOR, THING_TYPE_WATERLEAKAGE_SENSOR, THING_TYPE_FIRE_SENSOR,
THING_TYPE_ALARM_SENSOR, THING_TYPE_VIBRATION_SENSOR, THING_TYPE_BATTERY_SENSOR,
THING_TYPE_CARBONMONOXIDE_SENSOR).collect(Collectors.toSet()));
THING_TYPE_CARBONMONOXIDE_SENSOR, THING_TYPE_COLOR_CONTROL).collect(Collectors.toSet()));

private static final List<String> CONFIG_CHANNELS = Arrays.asList(CHANNEL_BATTERY_LEVEL, CHANNEL_BATTERY_LOW,
CHANNEL_TEMPERATURE);
Expand Down Expand Up @@ -137,6 +139,12 @@ protected void valueUpdated(String channelID, SensorState newState, boolean init
case CHANNEL_LIGHT_LUX:
updateQuantityTypeChannel(channelID, newState.lux, LUX);
break;
case CHANNEL_COLOR:
final double @Nullable [] xy = newState.xy;
if (xy != null && xy.length == 2) {
updateState(channelID, HSBType.fromXY((float) xy[0], (float) xy[1]));
}
break;
case CHANNEL_LIGHT_LEVEL:
updateDecimalTypeChannel(channelID, newState.lightlevel);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
package org.openhab.binding.deconz.internal.netutils;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.websocket.api.Session;
Expand Down Expand Up @@ -46,8 +46,8 @@ public class WebSocketConnection {

private final WebSocketClient client;
private final WebSocketConnectionListener connectionListener;
private final Map<String, WebSocketMessageListener> sensorListener = new HashMap<>();
private final Map<String, WebSocketMessageListener> lightListener = new HashMap<>();
private final Map<String, WebSocketMessageListener> sensorListener = new ConcurrentHashMap<>();
private final Map<String, WebSocketMessageListener> lightListener = new ConcurrentHashMap<>();
private final Gson gson;
private boolean connected = false;

Expand Down Expand Up @@ -110,21 +110,27 @@ public void onConnect(Session session) {
@SuppressWarnings("null")
@OnWebSocketMessage
public void onMessage(String message) {
logger.trace("Raw data received by websocket: {}", message);
DeconzBaseMessage changedMessage = gson.fromJson(message, DeconzBaseMessage.class);
switch (changedMessage.r) {
case "sensors":
WebSocketMessageListener listener = sensorListener.get(changedMessage.id);
if (listener != null) {
listener.messageReceived(changedMessage.id, gson.fromJson(message, SensorMessage.class));
} else {
logger.trace("Couldn't find sensor listener for id {}", changedMessage.id);
}
break;
case "lights":
listener = lightListener.get(changedMessage.id);
if (listener != null) {
listener.messageReceived(changedMessage.id, gson.fromJson(message, LightMessage.class));
} else {
logger.trace("Couldn't find light listener for id {}", changedMessage.id);
}
break;
default:
logger.debug("Unknown message type: {}", changedMessage.r);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
public enum LightType {
ON_OFF_LIGHT("On/Off light"),
ON_OFF_PLUGIN_UNIT("On/Off plug-in unit"),
SMART_PLUG("Smart plug"),
EXTENDED_COLOR_LIGHT("Extended color light"),
COLOR_LIGHT("Color light"),
COLOR_DIMMABLE_LIGHT("Color dimmable light"),
Expand Down
Loading

0 comments on commit 405d2ff

Please sign in to comment.