[deconz] Cross update color & color temperature channels (#18482)
* [deconz] improve color / color temperature handling Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>pull/18674/head
parent
fcec2e1720
commit
7749bbbbed
|
@ -54,6 +54,7 @@ import org.openhab.core.types.CommandOption;
|
|||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.StateDescriptionFragment;
|
||||
import org.openhab.core.types.StateDescriptionFragmentBuilder;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.openhab.core.util.ColorUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -255,6 +256,11 @@ public class LightThingHandler extends DeconzBaseThingHandler {
|
|||
if (miredQuantity != null) {
|
||||
newLightState.ct = constrainToRange(miredQuantity.intValue(), ctMin, ctMax);
|
||||
newLightState.on = true;
|
||||
Double transitiontime = config.transitiontime;
|
||||
if (transitiontime != null) {
|
||||
// value is in 1/10 seconds
|
||||
newLightState.transitiontime = (int) Math.round(10 * transitiontime);
|
||||
}
|
||||
}
|
||||
}
|
||||
case CHANNEL_POSITION -> {
|
||||
|
@ -396,18 +402,8 @@ public class LightThingHandler extends DeconzBaseThingHandler {
|
|||
case CHANNEL_SWITCH, CHANNEL_LOCK -> updateSwitchChannel(channelUID, on);
|
||||
case CHANNEL_COLOR -> updateColorChannel(channelUID, newState);
|
||||
case CHANNEL_BRIGHTNESS -> updatePercentTypeChannel(channelUID, newState.bri, newState.on);
|
||||
case CHANNEL_COLOR_TEMPERATURE -> {
|
||||
Integer ct = newState.ct;
|
||||
if (ct != null && ct >= ctMin && ct <= ctMax) {
|
||||
updateState(channelUID, new DecimalType(miredToKelvin(ct)));
|
||||
}
|
||||
}
|
||||
case CHANNEL_POSITION -> {
|
||||
Integer lift = newState.lift;
|
||||
if (lift != null) {
|
||||
updateState(channelUID, new PercentType(lift));
|
||||
}
|
||||
}
|
||||
case CHANNEL_COLOR_TEMPERATURE -> updateColorTemperatureChannel(channelUID, newState);
|
||||
case CHANNEL_POSITION -> updatePosition(channelUID, newState);
|
||||
case CHANNEL_EFFECT -> updateStringChannel(channelUID, newState.effect);
|
||||
case CHANNEL_EFFECT_SPEED -> updateDecimalTypeChannel(channelUID, newState.effectSpeed);
|
||||
}
|
||||
|
@ -443,26 +439,85 @@ public class LightThingHandler extends DeconzBaseThingHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the given {@link ChannelUID} depending on the given {@link LightState}. If the 'colorMode' is "xy"
|
||||
* then update the channel with an {@link HSBType} built from the given CIE XY co-ordinates and brightness,
|
||||
* otherwise if the 'colorMode' is not "ct" then update the channel with an {@link HSBType} from the given hue,
|
||||
* saturation and brightness. In either case if the 'on' field is false then the brightness is set to zero.
|
||||
* Furthermore if the color channel has been updated then cross-update the color temperature channel (if any)
|
||||
* to {@link UnDefType.UNDEF} as well.
|
||||
*
|
||||
* @param channelUID the UID of the channel being updated.
|
||||
* @param newState the new {@link LightState}
|
||||
*/
|
||||
private void updateColorChannel(ChannelUID channelUID, LightState newState) {
|
||||
Boolean on = newState.on;
|
||||
Integer bri = newState.bri;
|
||||
Integer hue = newState.hue;
|
||||
Integer sat = newState.sat;
|
||||
|
||||
boolean ctChannelUpdate = false;
|
||||
if (on != null && !on) {
|
||||
updateState(channelUID, OnOffType.OFF);
|
||||
} else if (bri != null && "xy".equals(newState.colormode)) {
|
||||
bri = 0;
|
||||
}
|
||||
|
||||
if (bri != null && "xy".equals(newState.colormode)) {
|
||||
final double @Nullable [] xy = newState.xy;
|
||||
if (xy != null && xy.length == 2) {
|
||||
double[] xyY = new double[3];
|
||||
xyY[0] = xy[0];
|
||||
xyY[1] = xy[1];
|
||||
xyY[2] = ((double) bri) / BRIGHTNESS_MAX;
|
||||
updateState(channelUID, ColorUtil.xyToHsb(xyY));
|
||||
HSBType hsX = ColorUtil.xyToHsb(xy);
|
||||
HSBType hsb = new HSBType(hsX.getHue(), hsX.getSaturation(), toPercentType(bri));
|
||||
logger.trace("updateColorChannel(xy) channelUID:{}, hsb:{}", channelUID, hsb);
|
||||
updateState(channelUID, hsb);
|
||||
ctChannelUpdate = true;
|
||||
}
|
||||
} else if (bri != null && hue != null && sat != null) {
|
||||
updateState(channelUID,
|
||||
new HSBType(new DecimalType(hue / HUE_FACTOR), toPercentType(sat), toPercentType(bri)));
|
||||
} else if (bri != null && !"ct".equals(newState.colormode) && hue != null && sat != null) {
|
||||
HSBType hsb = new HSBType(new DecimalType(hue / HUE_FACTOR), toPercentType(sat), toPercentType(bri));
|
||||
logger.trace("updateColorChannel(hsb) channelUID:{}, hsb:{}", channelUID, hsb);
|
||||
updateState(channelUID, hsb);
|
||||
ctChannelUpdate = true;
|
||||
}
|
||||
|
||||
// cross-update the color temperature channel (if any)
|
||||
if (ctChannelUpdate && thing.getChannel(CHANNEL_COLOR_TEMPERATURE) instanceof Channel ctChannel) {
|
||||
logger.trace("updateColorTemperatureChannel() channelUID:{}, ct:UNDEF", ctChannel.getUID());
|
||||
updateState(ctChannel.getUID(), UnDefType.UNDEF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the given {@link ChannelUID} depending on the given {@link LightState}. If the 'colorMode' is "ct" and
|
||||
* there is a 'ct' value (in mired) then convert it to Kelvin and update the channel. If the color temperature
|
||||
* channel state has been updated then cross-update the color channel (if any) state to an {@link HSBType} that
|
||||
* matches the given Kelvin value on the "Planckian Locus" on the CIE color chart as well.
|
||||
*
|
||||
* @param channelUID the UID of the channel being updated.
|
||||
* @param newState the new {@link LightState}
|
||||
*/
|
||||
private void updateColorTemperatureChannel(ChannelUID channelUID, LightState newState) {
|
||||
Integer ct = newState.ct;
|
||||
String colorMode = newState.colormode;
|
||||
if ((colorMode == null || "ct".equals(colorMode)) && ct != null && ct >= ctMin && ct <= ctMax) {
|
||||
int kelvin = miredToKelvin(ct);
|
||||
logger.trace("updateColorTemperatureChannel() channelUID:{}, kelvin:{}", channelUID, kelvin);
|
||||
updateState(channelUID, QuantityType.valueOf(kelvin, Units.KELVIN));
|
||||
|
||||
// cross-update the color channel (if any)
|
||||
if (thing.getChannel(CHANNEL_COLOR) instanceof Channel colChannel) {
|
||||
int brightness = !Boolean.TRUE.equals(newState.on) ? 0 : newState.bri instanceof Integer bri ? bri : -1;
|
||||
if (brightness >= 0) {
|
||||
HSBType hsX = ColorUtil.xyToHsb(ColorUtil.kelvinToXY(kelvin));
|
||||
HSBType hsb = new HSBType(hsX.getHue(), hsX.getSaturation(), toPercentType(brightness));
|
||||
logger.trace("updateColorChannel() channelUID:{}, hsb:{}", colChannel.getUID(), hsb);
|
||||
updateState(colChannel.getUID(), hsb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePosition(ChannelUID channelUID, LightState newState) {
|
||||
Integer lift = newState.lift;
|
||||
if (lift != null) {
|
||||
updateState(channelUID, new PercentType(lift));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
</supported-bridge-type-refs>
|
||||
<label>Light Group</label>
|
||||
<category>Lightbulb</category>
|
||||
<semantic-equipment-tag>Zone</semantic-equipment-tag>
|
||||
<channels>
|
||||
<channel typeId="all_on" id="all_on"/>
|
||||
<channel typeId="any_on" id="any_on"/>
|
||||
|
|
|
@ -69,9 +69,15 @@
|
|||
<semantic-equipment-tag>LightSource</semantic-equipment-tag>
|
||||
|
||||
<channels>
|
||||
<channel typeId="system.brightness" id="brightness"/>
|
||||
<channel typeId="ontime" id="ontime"/>
|
||||
<channel typeId="alert" id="alert"/>
|
||||
<channel typeId="system.brightness" id="brightness">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
<channel typeId="ontime" id="ontime">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
<channel typeId="alert" id="alert">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
|
@ -93,10 +99,18 @@
|
|||
<semantic-equipment-tag>LightSource</semantic-equipment-tag>
|
||||
|
||||
<channels>
|
||||
<channel typeId="system.brightness" id="brightness"/>
|
||||
<channel typeId="system.color-temperature-abs" id="color_temperature"/>
|
||||
<channel typeId="ontime" id="ontime"/>
|
||||
<channel typeId="alert" id="alert"/>
|
||||
<channel typeId="system.brightness" id="brightness">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
<channel typeId="system.color-temperature-abs" id="color_temperature">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
<channel typeId="ontime" id="ontime">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
<channel typeId="alert" id="alert">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
|
@ -118,9 +132,15 @@
|
|||
<semantic-equipment-tag>LightSource</semantic-equipment-tag>
|
||||
|
||||
<channels>
|
||||
<channel typeId="system.color" id="color"/>
|
||||
<channel typeId="ontime" id="ontime"/>
|
||||
<channel typeId="alert" id="alert"/>
|
||||
<channel typeId="system.color" id="color">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
<channel typeId="ontime" id="ontime">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
<channel typeId="alert" id="alert">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
|
@ -142,10 +162,18 @@
|
|||
<semantic-equipment-tag>LightSource</semantic-equipment-tag>
|
||||
|
||||
<channels>
|
||||
<channel typeId="system.color" id="color"/>
|
||||
<channel typeId="system.color-temperature-abs" id="color_temperature"/>
|
||||
<channel typeId="ontime" id="ontime"/>
|
||||
<channel typeId="alert" id="alert"/>
|
||||
<channel typeId="system.color" id="color">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
<channel typeId="system.color-temperature-abs" id="color_temperature">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
<channel typeId="ontime" id="ontime">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
<channel typeId="alert" id="alert">
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
|
@ -181,7 +209,7 @@
|
|||
<category>Rollershutter</category>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
<tag>OpenState</tag>
|
||||
<tag>OpenLevel</tag>
|
||||
</tags>
|
||||
<state pattern="%.1f %%"/>
|
||||
</channel-type>
|
||||
|
@ -197,7 +225,7 @@
|
|||
<item-type>String</item-type>
|
||||
<label>Effect Channel</label>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>Control</tag>
|
||||
<tag>Mode</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
@ -216,6 +244,10 @@
|
|||
<item-type>String</item-type>
|
||||
<label>Alert</label>
|
||||
<category>Alarm</category>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
<tag>Mode</tag>
|
||||
</tags>
|
||||
<command>
|
||||
<options>
|
||||
<option value="none">No Alarm</option>
|
||||
|
@ -228,6 +260,10 @@
|
|||
<channel-type id="lock">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Lock</label>
|
||||
<tags>
|
||||
<tag>Switch</tag>
|
||||
<tag>OpenState</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
<label>Air Quality</label>
|
||||
<description>Current air quality level based on volatile organic compounds (VOCs) measurement. Example: good or poor,
|
||||
...</description>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>VOC</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -17,6 +21,10 @@
|
|||
<label>Air Quality (ppb)</label>
|
||||
<description>Current air quality based on measurements of volatile organic compounds (VOCs). The measured value is
|
||||
specified in ppb (parts per billion).</description>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>VOC</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -24,6 +32,9 @@
|
|||
<item-type>Switch</item-type>
|
||||
<label>Alarm</label>
|
||||
<description>Alarm was triggered.</description>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -46,6 +57,10 @@
|
|||
<item-type>Switch</item-type>
|
||||
<label>Carbon-monoxide</label>
|
||||
<description>Carbon-monoxide was detected.</description>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
<tag>CO</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -54,6 +69,10 @@
|
|||
<label>Consumption</label>
|
||||
<description>Current consumption</description>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -62,6 +81,10 @@
|
|||
<label>Current</label>
|
||||
<description>Current current</description>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Current</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -82,12 +105,20 @@
|
|||
<channel-type id="externalwindowopen">
|
||||
<item-type>Contact</item-type>
|
||||
<label>External Window Open</label>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>OpenState</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fire">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Fire</label>
|
||||
<description>A fire was detected.</description>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
<tag>Smoke</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -122,6 +153,10 @@
|
|||
<label>Target Temperature</label>
|
||||
<description>Target temperature</description>
|
||||
<category>Heating</category>
|
||||
<tags>
|
||||
<tag>Setpoint</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state pattern="%.1f %unit%" step="0.5" max="28" min="6"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -130,6 +165,10 @@
|
|||
<label>Humidity</label>
|
||||
<description>Current humidity</description>
|
||||
<category>Humidity</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Humidity</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -152,6 +191,10 @@
|
|||
<channel-type id="light">
|
||||
<item-type>String</item-type>
|
||||
<label>Lightlevel</label>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>Illuminance</tag>
|
||||
</tags>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="daylight">Daylight</option>
|
||||
|
@ -165,6 +208,10 @@
|
|||
<item-type>Number:Illuminance</item-type>
|
||||
<label>Illuminance</label>
|
||||
<description>Current light illuminance</description>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Illuminance</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -172,6 +219,10 @@
|
|||
<item-type>Number</item-type>
|
||||
<label>Light Level</label>
|
||||
<description>Current light level.</description>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Illuminance</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%d"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -180,6 +231,10 @@
|
|||
<label>Locked</label>
|
||||
<description>Status of this thermostat's child lock.</description>
|
||||
<category>Lock</category>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>OpenState</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="mode">
|
||||
|
@ -187,6 +242,10 @@
|
|||
<label>Mode</label>
|
||||
<description>Current mode</description>
|
||||
<category>Heating</category>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
<tag>Mode</tag>
|
||||
</tags>
|
||||
<state>
|
||||
<options>
|
||||
<option value="AUTO">auto</option>
|
||||
|
@ -200,6 +259,10 @@
|
|||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Moisture</label>
|
||||
<description>Current moisture</description>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Moisture</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -213,6 +276,10 @@
|
|||
<channel-type id="on">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Heater State</label>
|
||||
<tags>
|
||||
<tag>Switch</tag>
|
||||
<tag>Heating</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -220,6 +287,10 @@
|
|||
<item-type>Contact</item-type>
|
||||
<label>Open/Close</label>
|
||||
<description>Open/Close detected</description>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>OpenState</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -246,6 +317,10 @@
|
|||
<label>Power</label>
|
||||
<description>Current power usage</description>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Power</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -254,6 +329,10 @@
|
|||
<label>Pressure</label>
|
||||
<description>Current pressure</description>
|
||||
<category>Pressure</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Pressure</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -261,6 +340,10 @@
|
|||
<item-type>Switch</item-type>
|
||||
<label>Tampered</label>
|
||||
<description>A zone is being tampered.</description>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
<tag>Tampered</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -269,6 +352,10 @@
|
|||
<label>Temperature</label>
|
||||
<description>Current temperature</description>
|
||||
<category>Temperature</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -289,6 +376,10 @@
|
|||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Valve position</label>
|
||||
<description>Current valve position</description>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>OpenLevel</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -296,6 +387,10 @@
|
|||
<item-type>Switch</item-type>
|
||||
<label>Vibration</label>
|
||||
<description>Vibration was detected.</description>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>Vibration</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -311,6 +406,10 @@
|
|||
<label>Voltage</label>
|
||||
<description>Current voltage</description>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Voltage</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
|
@ -318,12 +417,20 @@
|
|||
<item-type>Switch</item-type>
|
||||
<label>Water Leakage</label>
|
||||
<description>Water leakage detected</description>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
<tag>Water</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windowopen">
|
||||
<item-type>Contact</item-type>
|
||||
<label>Window Open</label>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>OpenState</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<bridge-type id="deconz">
|
||||
<label>deCONZ</label>
|
||||
<description>A running deCONZ software instance.</description>
|
||||
<semantic-equipment-tag>WebService</semantic-equipment-tag>
|
||||
<semantic-equipment-tag>Application</semantic-equipment-tag>
|
||||
<representation-property>UDN</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:deconz:bridge"/>
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.eclipse.jdt.annotation.Nullable;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
@ -37,8 +38,10 @@ import org.openhab.binding.deconz.internal.types.LightType;
|
|||
import org.openhab.binding.deconz.internal.types.LightTypeDeserializer;
|
||||
import org.openhab.binding.deconz.internal.types.ThermostatMode;
|
||||
import org.openhab.binding.deconz.internal.types.ThermostatModeGsonTypeAdapter;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
@ -46,6 +49,7 @@ import org.openhab.core.thing.ThingUID;
|
|||
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
@ -64,6 +68,22 @@ public class LightsTest {
|
|||
private @Mock @NonNullByDefault({}) DeconzDynamicStateDescriptionProvider stateDescriptionProvider;
|
||||
private @Mock @NonNullByDefault({}) DeconzDynamicCommandDescriptionProvider commandDescriptionProvider;
|
||||
|
||||
/**
|
||||
* Custom Mockito {@link ArgumentMatcher} to compare the closeness of two {@link HSBType} values.
|
||||
*/
|
||||
private static class CloseToHSBType implements ArgumentMatcher<HSBType> {
|
||||
private HSBType target;
|
||||
|
||||
public CloseToHSBType(HSBType target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HSBType source) {
|
||||
return source.closeTo(target, 0.02);
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void initialize() {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
|
@ -72,6 +92,75 @@ public class LightsTest {
|
|||
gson = gsonBuilder.create();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extColorTemperatureLightUpdateHSBTest() throws IOException {
|
||||
LightMessage lightMessage = DeconzTest.getObjectFromJson("extended_hsb.json", LightMessage.class, gson);
|
||||
assertNotNull(lightMessage);
|
||||
|
||||
ThingUID thingUID = new ThingUID("deconz", "light");
|
||||
ChannelUID channelUIDColor = new ChannelUID(thingUID, CHANNEL_COLOR);
|
||||
ChannelUID channelUIDCt = new ChannelUID(thingUID, CHANNEL_COLOR_TEMPERATURE);
|
||||
|
||||
Thing light = ThingBuilder.create(THING_TYPE_COLOR_TEMPERATURE_LIGHT, thingUID)
|
||||
.withProperties(Map.of(PROPERTY_THING_TYPE_VERSION, "1"))
|
||||
.withChannel(ChannelBuilder.create(channelUIDColor, "Color").build())
|
||||
.withChannel(ChannelBuilder.create(channelUIDCt, "Number").build()).build();
|
||||
LightThingHandler lightThingHandler = new LightThingHandler(light, gson, stateDescriptionProvider,
|
||||
commandDescriptionProvider);
|
||||
lightThingHandler.setCallback(thingHandlerCallback);
|
||||
|
||||
lightThingHandler.messageReceived(lightMessage);
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDColor), eq(new HSBType("0,50,100")));
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDCt), eq(UnDefType.UNDEF));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extColorTemperatureLightUpdateXYTest() throws IOException {
|
||||
LightMessage lightMessage = DeconzTest.getObjectFromJson("extended_xy.json", LightMessage.class, gson);
|
||||
assertNotNull(lightMessage);
|
||||
|
||||
ThingUID thingUID = new ThingUID("deconz", "light");
|
||||
ChannelUID channelUIDColor = new ChannelUID(thingUID, CHANNEL_COLOR);
|
||||
ChannelUID channelUIDCt = new ChannelUID(thingUID, CHANNEL_COLOR_TEMPERATURE);
|
||||
|
||||
Thing light = ThingBuilder.create(THING_TYPE_COLOR_TEMPERATURE_LIGHT, thingUID)
|
||||
.withProperties(Map.of(PROPERTY_THING_TYPE_VERSION, "1"))
|
||||
.withChannel(ChannelBuilder.create(channelUIDColor, "Color").build())
|
||||
.withChannel(ChannelBuilder.create(channelUIDCt, "Number").build()).build();
|
||||
LightThingHandler lightThingHandler = new LightThingHandler(light, gson, stateDescriptionProvider,
|
||||
commandDescriptionProvider);
|
||||
lightThingHandler.setCallback(thingHandlerCallback);
|
||||
|
||||
lightThingHandler.messageReceived(lightMessage);
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDColor),
|
||||
argThat(new CloseToHSBType(new HSBType("357,100,50"))));
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDCt), eq(UnDefType.UNDEF));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extColorTemperatureLightUpdateCTTest() throws IOException {
|
||||
LightMessage lightMessage = DeconzTest.getObjectFromJson("extended_ct.json", LightMessage.class, gson);
|
||||
assertNotNull(lightMessage);
|
||||
|
||||
ThingUID thingUID = new ThingUID("deconz", "light");
|
||||
ChannelUID channelUIDColor = new ChannelUID(thingUID, CHANNEL_COLOR);
|
||||
ChannelUID channelUIDCt = new ChannelUID(thingUID, CHANNEL_COLOR_TEMPERATURE);
|
||||
|
||||
Thing light = ThingBuilder.create(THING_TYPE_COLOR_TEMPERATURE_LIGHT, thingUID)
|
||||
.withProperties(Map.of(PROPERTY_THING_TYPE_VERSION, "1"))
|
||||
.withChannel(ChannelBuilder.create(channelUIDColor, "Color").build())
|
||||
.withChannel(ChannelBuilder.create(channelUIDCt, "Number").build()).build();
|
||||
LightThingHandler lightThingHandler = new LightThingHandler(light, gson, stateDescriptionProvider,
|
||||
commandDescriptionProvider);
|
||||
lightThingHandler.setCallback(thingHandlerCallback);
|
||||
|
||||
lightThingHandler.messageReceived(lightMessage);
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDColor),
|
||||
argThat(new CloseToHSBType(new HSBType("43,26,50"))));
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDCt),
|
||||
eq(QuantityType.valueOf(4000, Units.KELVIN)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void colorTemperatureLightUpdateTest() throws IOException {
|
||||
LightMessage lightMessage = DeconzTest.getObjectFromJson("colortemperature.json", LightMessage.class, gson);
|
||||
|
@ -91,7 +180,32 @@ public class LightsTest {
|
|||
|
||||
lightThingHandler.messageReceived(lightMessage);
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDBri), eq(new PercentType("21")));
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDCt), eq(new DecimalType("2500")));
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDCt),
|
||||
eq(QuantityType.valueOf(2500, Units.KELVIN)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void colorTemperatureSparseLightUpdateTest() throws IOException {
|
||||
LightMessage lightMessage = DeconzTest.getObjectFromJson("colortemperature-sparse.json", LightMessage.class,
|
||||
gson);
|
||||
assertNotNull(lightMessage);
|
||||
|
||||
ThingUID thingUID = new ThingUID("deconz", "light");
|
||||
ChannelUID channelUIDBri = new ChannelUID(thingUID, CHANNEL_BRIGHTNESS);
|
||||
ChannelUID channelUIDCt = new ChannelUID(thingUID, CHANNEL_COLOR_TEMPERATURE);
|
||||
|
||||
Thing light = ThingBuilder.create(THING_TYPE_COLOR_TEMPERATURE_LIGHT, thingUID)
|
||||
.withProperties(Map.of(PROPERTY_THING_TYPE_VERSION, "1"))
|
||||
.withChannel(ChannelBuilder.create(channelUIDBri, "Dimmer").build())
|
||||
.withChannel(ChannelBuilder.create(channelUIDCt, "Number").build()).build();
|
||||
LightThingHandler lightThingHandler = new LightThingHandler(light, gson, stateDescriptionProvider,
|
||||
commandDescriptionProvider);
|
||||
lightThingHandler.setCallback(thingHandlerCallback);
|
||||
|
||||
lightThingHandler.messageReceived(lightMessage);
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDBri), eq(new PercentType("21")));
|
||||
Mockito.verify(thingHandlerCallback).stateUpdated(eq(channelUIDCt),
|
||||
eq(QuantityType.valueOf(2500, Units.KELVIN)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"e": "changed",
|
||||
"id": "3",
|
||||
"r": "lights",
|
||||
"state": {
|
||||
"alert": null,
|
||||
"bri": 51,
|
||||
"ct": 400,
|
||||
"on": true,
|
||||
"reachable": true
|
||||
},
|
||||
"t": "event",
|
||||
"uniqueid": "00:0b:57:ff:fe:eb:2f:84-01"
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"e": "changed",
|
||||
"id": "11",
|
||||
"r": "lights",
|
||||
"state": {
|
||||
"alert": "none",
|
||||
"bri": 127,
|
||||
"colormode": "ct",
|
||||
"ct": 250,
|
||||
"effect": "none",
|
||||
"hue": 0,
|
||||
"on": true,
|
||||
"reachable": true,
|
||||
"sat": 125,
|
||||
"xy": [
|
||||
0.6981,
|
||||
0.298
|
||||
]
|
||||
},
|
||||
"t": "event",
|
||||
"uniqueid": "a4:c1:38:89:fb:f3:c5:a3-0b"
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"e": "changed",
|
||||
"id": "11",
|
||||
"r": "lights",
|
||||
"state": {
|
||||
"alert": "none",
|
||||
"bri": 254,
|
||||
"colormode": "hs",
|
||||
"ct": 250,
|
||||
"effect": "none",
|
||||
"hue": 0,
|
||||
"on": true,
|
||||
"reachable": true,
|
||||
"sat": 125,
|
||||
"xy": [
|
||||
0.6981,
|
||||
0.298
|
||||
]
|
||||
},
|
||||
"t": "event",
|
||||
"uniqueid": "a4:c1:38:89:fb:f3:c5:a3-0b"
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"e": "changed",
|
||||
"id": "11",
|
||||
"r": "lights",
|
||||
"state": {
|
||||
"alert": "none",
|
||||
"bri": 127,
|
||||
"colormode": "xy",
|
||||
"ct": 250,
|
||||
"effect": "none",
|
||||
"hue": 0,
|
||||
"on": true,
|
||||
"reachable": true,
|
||||
"sat": 125,
|
||||
"xy": [
|
||||
0.6981,
|
||||
0.298
|
||||
]
|
||||
},
|
||||
"t": "event",
|
||||
"uniqueid": "a4:c1:38:89:fb:f3:c5:a3-0b"
|
||||
}
|
Loading…
Reference in New Issue