[deconz] fix lastSeenPolling and compile warnings (#8918)

Signed-off-by: Jan N. Klug <jan.n.klug@rub.de>
This commit is contained in:
J-N-K 2020-11-03 23:23:57 +01:00 committed by GitHub
parent aaa464a255
commit 1dccf67909
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 64 additions and 46 deletions

View File

@ -81,7 +81,7 @@ Auto-discovered things do not need to be configured.
All sensor-things have an additional `lastSeenPolling` parameter. All sensor-things have an additional `lastSeenPolling` parameter.
Due to limitations in the API of deCONZ, the `lastSeen` channel (available some sensors) is only available when using polling. Due to limitations in the API of deCONZ, the `lastSeen` channel (available some sensors) is only available when using polling.
Allowed values are all positive integers, the unit is minutes. Allowed values are all positive integers, the unit is minutes.
The default-value is `0`, which means "no polling at all". The default-value is `1440`, which means "once a day".
`dimmablelight`, `extendedcolorlight`, `colorlight` and `colortemperaturelight` have an additional optional parameter `transitiontime`. `dimmablelight`, `extendedcolorlight`, `colorlight` and `colortemperaturelight` have an additional optional parameter `transitiontime`.
The transition time is the time to move between two states and is configured in seconds. The transition time is the time to move between two states and is configured in seconds.

View File

@ -163,11 +163,11 @@ public class ThingDiscoveryService extends AbstractDiscoveryService implements D
properties.put(Thing.PROPERTY_VENDOR, light.manufacturername); properties.put(Thing.PROPERTY_VENDOR, light.manufacturername);
properties.put(Thing.PROPERTY_MODEL_ID, light.modelid); properties.put(Thing.PROPERTY_MODEL_ID, light.modelid);
if (light.ctmax != null && light.ctmin != null) { Integer ctmax = light.ctmax;
properties.put(PROPERTY_CT_MAX, Integer ctmin = light.ctmin;
Integer.toString(Util.constrainToRange(light.ctmax, ZCL_CT_MIN, ZCL_CT_MAX))); if (ctmax != null && ctmin != null) {
properties.put(PROPERTY_CT_MIN, properties.put(PROPERTY_CT_MAX, Integer.toString(Util.constrainToRange(ctmax, ZCL_CT_MIN, ZCL_CT_MAX)));
Integer.toString(Util.constrainToRange(light.ctmin, ZCL_CT_MIN, ZCL_CT_MAX))); properties.put(PROPERTY_CT_MIN, Integer.toString(Util.constrainToRange(ctmin, ZCL_CT_MIN, ZCL_CT_MAX)));
} }
switch (lightType) { switch (lightType) {

View File

@ -19,6 +19,7 @@ import java.util.concurrent.CompletionException;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
@ -119,7 +120,7 @@ public abstract class DeconzBaseThingHandler<T extends DeconzBaseMessage> extend
registerListener(); registerListener();
// get initial values // get initial values
requestState(); requestState(this::processStateResponse);
} else { } else {
// if the bridge is not ONLINE, we assume communication is not possible, so we unregister the listener and // if the bridge is not ONLINE, we assume communication is not possible, so we unregister the listener and
// set the thing status to OFFLINE // set the thing status to OFFLINE
@ -131,7 +132,7 @@ public abstract class DeconzBaseThingHandler<T extends DeconzBaseMessage> extend
protected abstract @Nullable T parseStateResponse(AsyncHttpClient.Result r); protected abstract @Nullable T parseStateResponse(AsyncHttpClient.Result r);
/** /**
* processes a newly received state response * processes a newly received (initial) state response
* *
* MUST set the thing status! * MUST set the thing status!
* *
@ -142,7 +143,7 @@ public abstract class DeconzBaseThingHandler<T extends DeconzBaseMessage> extend
/** /**
* Perform a request to the REST API for retrieving the full light state with all data and configuration. * Perform a request to the REST API for retrieving the full light state with all data and configuration.
*/ */
protected void requestState() { protected void requestState(Consumer<@Nullable T> processor) {
AsyncHttpClient asyncHttpClient = http; AsyncHttpClient asyncHttpClient = http;
if (asyncHttpClient == null) { if (asyncHttpClient == null) {
return; return;
@ -162,10 +163,11 @@ public abstract class DeconzBaseThingHandler<T extends DeconzBaseMessage> extend
} }
stopInitializationJob(); stopInitializationJob();
initializationJob = scheduler.schedule((Runnable) this::requestState, 10, TimeUnit.SECONDS); initializationJob = scheduler.schedule(() -> requestState(this::processStateResponse), 10,
TimeUnit.SECONDS);
return null; return null;
}).thenAccept(this::processStateResponse); }).thenAccept(processor);
} }
/** /**

View File

@ -91,8 +91,9 @@ public class GroupThingHandler extends DeconzBaseThingHandler<GroupMessage> {
case CHANNEL_COLOR: case CHANNEL_COLOR:
if (command instanceof HSBType) { if (command instanceof HSBType) {
HSBType hsbCommand = (HSBType) command; HSBType hsbCommand = (HSBType) command;
newGroupAction.bri = Util.fromPercentType(hsbCommand.getBrightness()); Integer bri = Util.fromPercentType(hsbCommand.getBrightness());
if (newGroupAction.bri > 0) { newGroupAction.bri = bri;
if (bri > 0) {
newGroupAction.hue = (int) (hsbCommand.getHue().doubleValue() * HUE_FACTOR); newGroupAction.hue = (int) (hsbCommand.getHue().doubleValue() * HUE_FACTOR);
newGroupAction.sat = Util.fromPercentType(hsbCommand.getSaturation()); newGroupAction.sat = Util.fromPercentType(hsbCommand.getSaturation());
} }
@ -118,7 +119,8 @@ public class GroupThingHandler extends DeconzBaseThingHandler<GroupMessage> {
return; return;
} }
if (newGroupAction.bri != null && newGroupAction.bri > 0) { Integer bri = newGroupAction.bri;
if (bri != null && bri > 0) {
newGroupAction.on = true; newGroupAction.on = true;
} }

View File

@ -228,16 +228,17 @@ public class LightThingHandler extends DeconzBaseThingHandler<LightMessage> {
return; return;
} }
if (newLightState.on != null && !newLightState.on) { Boolean newOn = newLightState.on;
if (newOn != null && !newOn) {
// if light shall be off, no other commands are allowed, so reset the new light state // if light shall be off, no other commands are allowed, so reset the new light state
newLightState.clear(); newLightState.clear();
newLightState.on = false; newLightState.on = false;
} }
sendCommand(newLightState, command, channelUID, () -> { sendCommand(newLightState, command, channelUID, () -> {
Integer transitionTime = newLightState.transitiontime;
lastCommandExpireTimestamp = System.currentTimeMillis() lastCommandExpireTimestamp = System.currentTimeMillis()
+ (newLightState.transitiontime != null ? newLightState.transitiontime + (transitionTime != null ? transitionTime : DEFAULT_COMMAND_EXPIRY_TIME);
: DEFAULT_COMMAND_EXPIRY_TIME);
lastCommand = newLightState; lastCommand = newLightState;
}); });
} }
@ -248,16 +249,18 @@ public class LightThingHandler extends DeconzBaseThingHandler<LightMessage> {
return null; return null;
} else if (r.getResponseCode() == 200) { } else if (r.getResponseCode() == 200) {
LightMessage lightMessage = gson.fromJson(r.getBody(), LightMessage.class); LightMessage lightMessage = gson.fromJson(r.getBody(), LightMessage.class);
if (lightMessage != null && needsPropertyUpdate) { if (needsPropertyUpdate) {
// if we did not receive an ctmin/ctmax, then we probably don't need it // if we did not receive an ctmin/ctmax, then we probably don't need it
needsPropertyUpdate = false; needsPropertyUpdate = false;
if (lightMessage.ctmin != null && lightMessage.ctmax != null) { Integer ctmax = lightMessage.ctmax;
Integer ctmin = lightMessage.ctmin;
if (ctmin != null && ctmax != null) {
Map<String, String> properties = new HashMap<>(thing.getProperties()); Map<String, String> properties = new HashMap<>(thing.getProperties());
properties.put(PROPERTY_CT_MAX, properties.put(PROPERTY_CT_MAX,
Integer.toString(Util.constrainToRange(lightMessage.ctmax, ZCL_CT_MIN, ZCL_CT_MAX))); Integer.toString(Util.constrainToRange(ctmax, ZCL_CT_MIN, ZCL_CT_MAX)));
properties.put(PROPERTY_CT_MIN, properties.put(PROPERTY_CT_MIN,
Integer.toString(Util.constrainToRange(lightMessage.ctmin, ZCL_CT_MIN, ZCL_CT_MAX))); Integer.toString(Util.constrainToRange(ctmin, ZCL_CT_MIN, ZCL_CT_MAX)));
updateProperties(properties); updateProperties(properties);
} }
} }
@ -278,6 +281,8 @@ public class LightThingHandler extends DeconzBaseThingHandler<LightMessage> {
private void valueUpdated(String channelId, LightState newState) { private void valueUpdated(String channelId, LightState newState) {
Integer bri = newState.bri; Integer bri = newState.bri;
Integer hue = newState.hue;
Integer sat = newState.sat;
Boolean on = newState.on; Boolean on = newState.on;
switch (channelId) { switch (channelId) {
@ -292,15 +297,13 @@ public class LightThingHandler extends DeconzBaseThingHandler<LightMessage> {
case CHANNEL_COLOR: case CHANNEL_COLOR:
if (on != null && on == false) { if (on != null && on == false) {
updateState(channelId, OnOffType.OFF); updateState(channelId, OnOffType.OFF);
} else if (bri != null && newState.colormode != null && newState.colormode.equals("xy")) { } else if (bri != null && "xy".equals(newState.colormode)) {
final double @Nullable [] xy = newState.xy; final double @Nullable [] xy = newState.xy;
if (xy != null && xy.length == 2) { if (xy != null && xy.length == 2) {
HSBType color = HSBType.fromXY((float) xy[0], (float) xy[1]); HSBType color = HSBType.fromXY((float) xy[0], (float) xy[1]);
updateState(channelId, new HSBType(color.getHue(), color.getSaturation(), toPercentType(bri))); updateState(channelId, new HSBType(color.getHue(), color.getSaturation(), toPercentType(bri)));
} }
} else if (bri != null && newState.hue != null && newState.sat != null) { } else if (bri != null && hue != null && sat != null) {
final Integer hue = newState.hue;
final Integer sat = newState.sat;
updateState(channelId, updateState(channelId,
new HSBType(new DecimalType(hue / HUE_FACTOR), toPercentType(sat), toPercentType(bri))); new HSBType(new DecimalType(hue / HUE_FACTOR), toPercentType(sat), toPercentType(bri)));
} }
@ -340,7 +343,7 @@ public class LightThingHandler extends DeconzBaseThingHandler<LightMessage> {
return; return;
} }
lightStateCache = lightState; lightStateCache = lightState;
if (lightState.reachable != null && lightState.reachable) { if (Boolean.TRUE.equals(lightState.reachable)) {
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
thing.getChannels().stream().map(c -> c.getUID().getId()).forEach(c -> valueUpdated(c, lightState)); thing.getChannels().stream().map(c -> c.getUID().getId()).forEach(c -> valueUpdated(c, lightState));
} else { } else {

View File

@ -164,23 +164,31 @@ public abstract class SensorBaseThingHandler extends DeconzBaseThingHandler<Sens
// "Last seen" is the last "ping" from the device, whereas "last update" is the last status changed. // "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. // 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. // So to monitor a sensor is still alive, the "last seen" is necessary.
// 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
String lastSeen = stateResponse.lastseen; String lastSeen = stateResponse.lastseen;
if (lastSeen != null && config.lastSeenPolling > 0) { if (lastSeen != null && config.lastSeenPolling > 0) {
createChannel(CHANNEL_LAST_SEEN, ChannelKind.STATE); createChannel(CHANNEL_LAST_SEEN, ChannelKind.STATE);
updateState(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime(lastSeen)); 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 lastSeenPollingJob = scheduler.schedule(() -> requestState(this::processLastSeen), config.lastSeenPolling,
// manually poll it after the defined time (default is off)
if (config.lastSeenPolling > 0) {
lastSeenPollingJob = scheduler.schedule((Runnable) this::requestState, config.lastSeenPolling,
TimeUnit.MINUTES); TimeUnit.MINUTES);
logger.trace("lastSeen polling enabled for thing {} with interval of {} minutes", thing.getUID(), logger.trace("lastSeen polling enabled for thing {} with interval of {} minutes", thing.getUID(),
config.lastSeenPolling); config.lastSeenPolling);
} }
}
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
} }
private void processLastSeen(@Nullable SensorMessage stateResponse) {
if (stateResponse == null) {
return;
}
String lastSeen = stateResponse.lastseen;
if (lastSeen != null) {
updateState(CHANNEL_LAST_SEEN, Util.convertTimestampToDateTime(lastSeen));
}
}
protected void createChannel(String channelId, ChannelKind kind) { protected void createChannel(String channelId, ChannelKind kind) {
ThingHandlerCallback callback = getCallback(); ThingHandlerCallback callback = getCallback();
if (callback != null) { if (callback != null) {

View File

@ -22,6 +22,8 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.deconz.internal.dto.SensorConfig; import org.openhab.binding.deconz.internal.dto.SensorConfig;
@ -125,7 +127,8 @@ public class SensorThermostatThingHandler extends SensorBaseThingHandler {
@Override @Override
protected void valueUpdated(ChannelUID channelUID, SensorConfig newConfig) { protected void valueUpdated(ChannelUID channelUID, SensorConfig newConfig) {
super.valueUpdated(channelUID, newConfig); super.valueUpdated(channelUID, newConfig);
String mode = newConfig.mode != null ? newConfig.mode.name() : ThermostatMode.UNKNOWN.name(); ThermostatMode thermostatMode = newConfig.mode;
String mode = thermostatMode != null ? thermostatMode.name() : ThermostatMode.UNKNOWN.name();
String channelID = channelUID.getId(); String channelID = channelUID.getId();
switch (channelID) { switch (channelID) {
case CHANNEL_HEATSETPOINT: case CHANNEL_HEATSETPOINT:
@ -135,9 +138,7 @@ public class SensorThermostatThingHandler extends SensorBaseThingHandler {
updateQuantityTypeChannel(channelID, newConfig.offset, CELSIUS, 1.0 / 100); updateQuantityTypeChannel(channelID, newConfig.offset, CELSIUS, 1.0 / 100);
break; break;
case CHANNEL_THERMOSTAT_MODE: case CHANNEL_THERMOSTAT_MODE:
if (mode != null) {
updateState(channelUID, new StringType(mode)); updateState(channelUID, new StringType(mode));
}
break; break;
} }
} }
@ -169,7 +170,13 @@ public class SensorThermostatThingHandler extends SensorBaseThingHandler {
if (command instanceof DecimalType) { if (command instanceof DecimalType) {
newTemperature = ((DecimalType) command).toBigDecimal(); newTemperature = ((DecimalType) command).toBigDecimal();
} else if (command instanceof QuantityType) { } else if (command instanceof QuantityType) {
newTemperature = ((QuantityType) command).toUnit(CELSIUS).toBigDecimal(); @SuppressWarnings("unchecked")
QuantityType<Temperature> temperatureCelsius = ((QuantityType<Temperature>) command).toUnit(CELSIUS);
if (temperatureCelsius != null) {
newTemperature = temperatureCelsius.toBigDecimal();
} else {
return null;
}
} else { } else {
return null; return null;
} }

View File

@ -34,8 +34,6 @@ import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelKind; import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType; import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -65,8 +63,6 @@ public class SensorThingHandler extends SensorBaseThingHandler {
private static final List<String> CONFIG_CHANNELS = Arrays.asList(CHANNEL_BATTERY_LEVEL, CHANNEL_BATTERY_LOW, private static final List<String> CONFIG_CHANNELS = Arrays.asList(CHANNEL_BATTERY_LEVEL, CHANNEL_BATTERY_LOW,
CHANNEL_TEMPERATURE); CHANNEL_TEMPERATURE);
private final Logger logger = LoggerFactory.getLogger(SensorThingHandler.class);
public SensorThingHandler(Thing thing, Gson gson) { public SensorThingHandler(Thing thing, Gson gson) {
super(thing, gson); super(thing, gson);
} }

View File

@ -23,6 +23,6 @@ import org.eclipse.jdt.annotation.Nullable;
@NonNullByDefault @NonNullByDefault
public class ThingConfig { public class ThingConfig {
public String id = ""; public String id = "";
public int lastSeenPolling = 0; public int lastSeenPolling = 1440;
public @Nullable Double transitiontime; public @Nullable Double transitiontime;
} }

View File

@ -42,8 +42,8 @@
<parameter name="lastSeenPolling" type="integer" min="0" unit="min"> <parameter name="lastSeenPolling" type="integer" min="0" unit="min">
<label>LastSeen Poll Interval</label> <label>LastSeen Poll Interval</label>
<description>Interval to poll the deCONZ Gateway for this sensor's "lastSeen" channel. Polling is disabled when set <description>Interval to poll the deCONZ Gateway for this sensor's "lastSeen" channel. Polling is disabled when set
to 0.</description> to 1440 (once per day).</description>
<default>0</default> <default>1440</default>
</parameter> </parameter>
</config-description> </config-description>