Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[deconz] support for color control and smart plugs, fixes for firmware >2.05.80 #8304

Merged
merged 7 commits into from
Aug 22, 2020
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
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