The following table lists the thing types of the supported LIFX devices:
-| Device Type | Thing Type |
-|------------------------------|--------------|
-| Original 1000 | colorlight |
-| Color 650 | colorlight |
-| Color 1000 | colorlight |
-| Color 1000 BR30 | colorlight |
-| LIFX A19 | colorlight |
-| LIFX BR30 | colorlight |
-| LIFX Candle | colorlight |
-| LIFX Clean | colorlight |
-| LIFX Downlight | colorlight |
-| LIFX GU10 | colorlight |
-| LIFX Mini Color | colorlight |
-| | |
-| LIFX+ A19 | colorirlight |
-| LIFX+ BR30 | colorirlight |
-| | |
-| LIFX Beam | colormzlight |
-| LIFX Z | colormzlight |
-| | |
-| LIFX Tile | tilelight |
-| | |
-| White 800 (Low Voltage) | whitelight |
-| White 800 (High Voltage) | whitelight |
-| White 900 BR30 (Low Voltage) | whitelight |
-| LIFX Candle Warm to White | whitelight |
-| LIFX Filament | whitelight |
-| LIFX Mini Day and Dusk | whitelight |
-| LIFX Mini White | whitelight |
+| Device Type | Thing Type |
+|------------------------------|---------------|
+| Original 1000 | colorlight |
+| Color 650 | colorlight |
+| Color 1000 | colorlight |
+| Color 1000 BR30 | colorlight |
+| LIFX A19 | colorlight |
+| LIFX BR30 | colorlight |
+| LIFX Candle | colorlight |
+| LIFX Downlight | colorlight |
+| LIFX GU10 | colorlight |
+| LIFX Mini Color | colorlight |
+| | |
+| LIFX Clean | colorhevlight |
+| | |
+| LIFX+ A19 | colorirlight |
+| LIFX+ BR30 | colorirlight |
+| | |
+| LIFX Beam | colormzlight |
+| LIFX Z | colormzlight |
+| | |
+| LIFX Tile | tilelight |
+| | |
+| White 800 (Low Voltage) | whitelight |
+| White 800 (High Voltage) | whitelight |
+| White 900 BR30 (Low Voltage) | whitelight |
+| LIFX Candle Warm to White | whitelight |
+| LIFX Filament | whitelight |
+| LIFX Mini Day and Dusk | whitelight |
+| LIFX Mini White | whitelight |
The thing type determines the capability of a device and with that the possible ways of interacting with it.
The following matrix lists the capabilities (channels) for each type:
-| Thing Type | On/Off | Brightness | Color | Color Zone | Color Temperature | Color Temperature Zone | Infrared | Tile Effects |
-|--------------|:------:|:----------:|:-----:|:----------:|:-----------------:|:----------------------:|:--------:|:------------:|
-| colorlight | X | | X | | X | | | |
-| colorirlight | X | | X | | X | | X | |
-| colormzlight | X | | X | X | X | X | | |
-| tilelight | X | X | X | | X | | | X |
-| whitelight | X | X | | | X | | | |
+| Thing Type | On/Off | Brightness | Color | Color Zone | Color Temperature | Color Temperature Zone | HEV Cycle | Infrared | Tile Effects |
+|---------------|:------:|:----------:|:-----:|:----------:|:-----------------:|:----------------------:|:---------:|:--------:|:------------:|
+| colorlight | X | | X | | X | | | | |
+| colorhevlight | X | | X | | X | | X | | |
+| colorirlight | X | | X | | X | | | X | |
+| colormzlight | X | | X | X | X | X | | | |
+| tilelight | X | X | X | | X | | | | X |
+| whitelight | X | X | | | X | | | | |
## Discovery
All devices support some of the following channels:
-| Channel Type ID | Item Type | Description | Thing Types |
-|-----------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------|
-| brightness | Dimmer | This channel supports adjusting the brightness value. | whitelight |
-| color | Color | This channel supports full color control with hue, saturation and brightness values. | colorlight, colorirlight, colormzlight, tile |
-| colorzone | Color | This channel supports full zone color control with hue, saturation and brightness values. | colormzlight |
-| effect | String | This channel represents a type of light effect (e.g. for tile light: off, morph, flame) | tilelight |
-| infrared | Dimmer | This channel supports adjusting the infrared value. *Note:* IR capable lights only activate their infrared LEDs when the brightness drops below a certain level. | colorirlight |
-| signalstrength | Number | This channel represents signal strength with values 0, 1, 2, 3 or 4; 0 being worst strength and 4 being best strength. | colorlight, colorirlight, colormzlight, whitelight, tile |
-| temperature | Dimmer | This channel supports adjusting the color temperature from cold (0%) to warm (100%). | colorlight, colorirlight, colormzlight, whitelight, tile |
-| temperaturezone | Dimmer | This channel supports adjusting the zone color temperature from cold (0%) to warm (100%). | colormzlight |
-
-The *color* and *brightness* channels have a "Power on brightness" configuration option that is used to determine the brightness when a light is switched on.
+| Channel Type ID | Item Type | Description | Thing Types |
+|-----------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
+| brightness | Dimmer | This channel supports adjusting the brightness value. | whitelight |
+| color | Color | This channel supports full color control with hue, saturation and brightness values. | colorlight, colorhevlight, colorirlight, colormzlight, tilelight |
+| colorzone | Color | This channel supports full zone color control with hue, saturation and brightness values. | colormzlight |
+| effect | String | This channel represents a type of light effect (e.g. for tile light: off, morph, flame) | tilelight |
+| hevcycle | Switch | This channel supports starting and stopping the HEV clean cycle. | colorhevlight |
+| infrared | Dimmer | This channel supports adjusting the infrared value. *Note:* IR capable lights only activate their infrared LEDs when the brightness drops below a certain level. | colorirlight |
+| signalstrength | Number | This channel represents signal strength with values 0, 1, 2, 3 or 4; 0 being worst strength and 4 being best strength. | colorlight, colorhevlight, colorirlight, colormzlight, tilelight, whitelight |
+| temperature | Dimmer | This channel supports adjusting the color temperature from cold (0%) to warm (100%). | colorlight, colorhevlight, colorirlight, colormzlight, tilelight, whitelight |
+| temperaturezone | Dimmer | This channel supports adjusting the zone color temperature from cold (0%) to warm (100%). | colormzlight |
+
+The *color* and *brightness* channels have a "Power On Brightness" configuration option that is used to determine the brightness when a light is switched on.
When it is left empty, the brightness of a light remains unchanged when a light is switched on or off.
-The *color* channels have a "Power on color" configuration option that is used to determine the hue, saturation, brightness levels when a light is switched on.
+The *color* channels have a "Power On Color" configuration option that is used to determine the hue, saturation, brightness levels when a light is switched on.
When it is left empty, the color of a light remains unchanged when a light is switched on or off.
Configuration options contains 3 comma separated values, where first value is hue (0-360), second saturation (0-100) and third brightness (0-100).
-If both "Power on brightness" and "Power on color" configuration options are defined, "Power on brightness" option overrides the brightness level defined on the "Power on color" configuration option.
+If both "Power on brightness" and "Power On Color" configuration options are defined, "Power on brightness" option overrides the brightness level defined on the "Power on color" configuration option.
-The *temperature* channels have a "Power on temperature" configuration option that is used to determine the color temperature when a light is switched on. When it is left empty, the color temperature of a light remains unchanged when a light is switched on or off.
+The *temperature* channels have a "Power On Temperature" configuration option that is used to determine the color temperature when a light is switched on. When it is left empty, the color temperature of a light remains unchanged when a light is switched on or off.
-MultiZone lights (*colormzlight*) have serveral channels (e.g. *colorzone0*, *temperaturezone0*, etc.) that allow for controlling specific zones of the light.
+MultiZone lights (*colormzlight*) have several channels (e.g. *colorzone0*, *temperaturezone0*, etc.) that allow for controlling specific zones of the light.
Changing the *color* and *temperature* channels will update the states of all zones.
The *color* and *temperature* channels of MultiZone lights always return the same state as *colorzone0*, *temperaturezone0*.
+The *hevcycle* channels have an optional "HEV Cycle Duration" configuration option that can be used to override the cycle duration configured in the light.
+
LIFX Tile (*tilelight*) supports special tile effects: morph and flame.
These effects are predefined to their appearance using LIFX application.
-Each effect has a separate speed configurable.
+Each effect has a separate speed configuration option.
## Full Example
In this example **living** is a Color 1000 light that has a *colorlight* thing type which supports *color* and *temperature* channels.
+The **desk** light is a LIFX Clean that has a *colorhevlight* thing type which supports *color*, *temperature* and *hevcycle* channels.
+
The **porch** light is a LIFX+ BR30 that has a *colorirlight* thing type which supports *color*, *temperature* and *infrared* channels.
The **ceiling** light is a LIFX Z with 2 strips (16 zones) that has a *colormzlight* thing type which supports *color*, *colorzone*, *temperature* and *temperaturezone* channels.
Type color : color [ powerOnBrightness=50 ]
}
+Thing lifx:colorhevlight:desk [ deviceId="D073D5A3A3A3" ] {
+ Channels:
+ Type hevcycle : hevcycle [ hevCycleDuration=3600 ]
+}
+
Thing lifx:colorirlight:porch [ deviceId="D073D5B2B2B2", host="10.120.130.4", fadetime=0 ] {
Channels:
Type color : color [ powerOnBrightness=75 ]
Dimmer Living2_Dimmer { channel="lifx:colorlight:living2:color" }
Dimmer Living2_Temperature { channel="lifx:colorlight:living2:temperature" }
+// Desk
+Color Desk_Color { channel="lifx:colorhevlight:desk:color" }
+Dimmer Desk_Temperature { channel="lifx:colorhevlight:desk:temperature" }
+Switch Desk_HEV_Cycle { channel="lifx:colorhevlight:desk:hevcycle" }
+
// Porch
Color Porch_Color { channel="lifx:colorirlight:porch:color" }
Dimmer Porch_Infrared { channel="lifx:colorirlight:porch:infrared" }
Slider item=Living2_Temperature
}
+ Frame label="Desk" {
+ Switch item=Desk_Color
+ Slider item=Desk_Color
+ Colorpicker item=Desk_Color
+ Slider item=Desk_Temperature
+ Switch item=Desk_HEV_Cycle
+ }
+
Frame label="Porch" {
Switch item=Porch_Color
Slider item=Porch_Color
package org.openhab.binding.lifx.internal;
import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.lifx.internal.fields.HSBK;
public static final String CHANNEL_COLOR = "color";
public static final String CHANNEL_COLOR_ZONE = "colorzone";
public static final String CHANNEL_EFFECT = "effect";
+ public static final String CHANNEL_HEV_CYCLE = "hevcycle";
public static final String CHANNEL_INFRARED = "infrared";
public static final String CHANNEL_SIGNAL_STRENGTH = "signalstrength";
public static final String CHANNEL_TEMPERATURE = "temperature";
public static final String CONFIG_PROPERTY_FADETIME = "fadetime";
// Config property for channel configuration
+ public static final String CONFIG_PROPERTY_HEV_CYCLE_DURATION = "hevCycleDuration";
+ public static final String CONFIG_PROPERTY_EFFECT_FLAME_SPEED = "effectFlameSpeed";
+ public static final String CONFIG_PROPERTY_EFFECT_MORPH_SPEED = "effectMorphSpeed";
public static final String CONFIG_PROPERTY_POWER_ON_BRIGHTNESS = "powerOnBrightness";
public static final String CONFIG_PROPERTY_POWER_ON_COLOR = "powerOnColor";
public static final String CONFIG_PROPERTY_POWER_ON_TEMPERATURE = "powerOnTemperature";
- public static final String CONFIG_PROPERTY_EFFECT_MORPH_SPEED = "effectMorphSpeed";
- public static final String CONFIG_PROPERTY_EFFECT_FLAME_SPEED = "effectFlameSpeed";
// Property keys
public static final String PROPERTY_HOST = "host";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_COLORLIGHT = new ThingTypeUID(BINDING_ID, "colorlight");
+ public static final ThingTypeUID THING_TYPE_COLORHEVLIGHT = new ThingTypeUID(BINDING_ID, "colorhevlight");
public static final ThingTypeUID THING_TYPE_COLORIRLIGHT = new ThingTypeUID(BINDING_ID, "colorirlight");
public static final ThingTypeUID THING_TYPE_COLORMZLIGHT = new ThingTypeUID(BINDING_ID, "colormzlight");
- public static final ThingTypeUID THING_TYPE_WHITELIGHT = new ThingTypeUID(BINDING_ID, "whitelight");
public static final ThingTypeUID THING_TYPE_TILELIGHT = new ThingTypeUID(BINDING_ID, "tilelight");
+ public static final ThingTypeUID THING_TYPE_WHITELIGHT = new ThingTypeUID(BINDING_ID, "whitelight");
- public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Stream.of(THING_TYPE_COLORLIGHT,
- THING_TYPE_COLORIRLIGHT, THING_TYPE_COLORMZLIGHT, THING_TYPE_WHITELIGHT, THING_TYPE_TILELIGHT)
- .collect(Collectors.toSet());
+ public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_COLORLIGHT,
+ THING_TYPE_COLORHEVLIGHT, THING_TYPE_COLORIRLIGHT, THING_TYPE_COLORMZLIGHT, THING_TYPE_TILELIGHT,
+ THING_TYPE_WHITELIGHT);
}
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.lifx.internal.LifxProduct.Features;
import org.openhab.binding.lifx.internal.dto.GetColorZonesRequest;
+import org.openhab.binding.lifx.internal.dto.GetHevCycleRequest;
import org.openhab.binding.lifx.internal.dto.GetLightInfraredRequest;
import org.openhab.binding.lifx.internal.dto.GetRequest;
import org.openhab.binding.lifx.internal.dto.GetTileEffectRequest;
import org.openhab.binding.lifx.internal.dto.GetWifiInfoRequest;
+import org.openhab.binding.lifx.internal.dto.HevCycleState;
import org.openhab.binding.lifx.internal.dto.Packet;
+import org.openhab.binding.lifx.internal.dto.StateHevCycleResponse;
import org.openhab.binding.lifx.internal.dto.StateLightInfraredResponse;
import org.openhab.binding.lifx.internal.dto.StateLightPowerResponse;
import org.openhab.binding.lifx.internal.dto.StateMultiZoneResponse;
private void sendLightStateRequests() {
communicationHandler.sendPacket(new GetRequest());
+ if (features.hasFeature(HEV)) {
+ communicationHandler.sendPacket(new GetHevCycleRequest());
+ }
if (features.hasFeature(INFRARED)) {
communicationHandler.sendPacket(new GetLightInfraredRequest());
}
handlePowerStatus((StatePowerResponse) packet);
} else if (packet instanceof StateLightPowerResponse) {
handleLightPowerStatus((StateLightPowerResponse) packet);
+ } else if (packet instanceof StateHevCycleResponse) {
+ handleHevCycleStatus((StateHevCycleResponse) packet);
} else if (packet instanceof StateLightInfraredResponse) {
handleInfraredStatus((StateLightInfraredResponse) packet);
} else if (packet instanceof StateMultiZoneResponse) {
handleMultiZoneStatus((StateMultiZoneResponse) packet);
- } else if (packet instanceof StateWifiInfoResponse) {
- handleWifiInfoStatus((StateWifiInfoResponse) packet);
} else if (packet instanceof StateTileEffectResponse) {
handleTileEffectStatus((StateTileEffectResponse) packet);
+ } else if (packet instanceof StateWifiInfoResponse) {
+ handleWifiInfoStatus((StateWifiInfoResponse) packet);
}
currentLightState.setOnline();
currentLightState.setPowerState(packet.getState());
}
+ private void handleHevCycleStatus(StateHevCycleResponse packet) {
+ HevCycleState hevCycleState = new HevCycleState(!packet.getRemaining().isZero(), packet.getDuration());
+ currentLightState.setHevCycleState(hevCycleState);
+ }
+
private void handleInfraredStatus(StateLightInfraredResponse packet) {
PercentType infrared = infraredToPercentType(packet.getInfrared());
currentLightState.setInfrared(infrared);
currentLightState.setColors(colors);
}
- private void handleWifiInfoStatus(StateWifiInfoResponse packet) {
- currentLightState.setSignalStrength(packet.getSignalStrength());
- }
-
private void handleTileEffectStatus(StateTileEffectResponse packet) {
currentLightState.setTileEffect(packet.getEffect());
}
+
+ private void handleWifiInfoStatus(StateWifiInfoResponse packet) {
+ currentLightState.setSignalStrength(packet.getSignalStrength());
+ }
}
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.lifx.internal.dto.Effect;
+import org.openhab.binding.lifx.internal.dto.HevCycleState;
import org.openhab.binding.lifx.internal.dto.PowerState;
import org.openhab.binding.lifx.internal.dto.SignalStrength;
import org.openhab.binding.lifx.internal.fields.HSBK;
public class LifxLightState {
private HSBK[] colors = new HSBK[] { new HSBK(DEFAULT_COLOR) };
+ private @Nullable HevCycleState hevCycleState;
private @Nullable PercentType infrared;
private @Nullable PowerState powerState;
private @Nullable SignalStrength signalStrength;
public void copy(LifxLightState other) {
this.powerState = other.getPowerState();
this.colors = other.getColors();
+ this.hevCycleState = other.getHevCycleState();
this.infrared = other.getInfrared();
this.signalStrength = other.getSignalStrength();
this.tileEffect = other.getTileEffect();
return colorsCopy;
}
+ public @Nullable HevCycleState getHevCycleState() {
+ return hevCycleState;
+ }
+
public @Nullable PercentType getInfrared() {
return infrared;
}
setColor(newColor, zoneIndex);
}
+ public void setHevCycleState(HevCycleState newHevCycleState) {
+ HevCycleState oldHevCycleState = this.hevCycleState;
+ this.hevCycleState = newHevCycleState;
+ updateLastChange();
+ listeners.forEach(listener -> listener.handleHevCycleStateChange(oldHevCycleState, newHevCycleState));
+ }
+
public void setInfrared(PercentType newInfrared) {
PercentType oldInfrared = this.infrared;
this.infrared = newInfrared;
import org.openhab.binding.lifx.internal.dto.ApplicationRequest;
import org.openhab.binding.lifx.internal.dto.Effect;
import org.openhab.binding.lifx.internal.dto.GetColorZonesRequest;
+import org.openhab.binding.lifx.internal.dto.GetHevCycleRequest;
import org.openhab.binding.lifx.internal.dto.GetLightInfraredRequest;
import org.openhab.binding.lifx.internal.dto.GetLightPowerRequest;
import org.openhab.binding.lifx.internal.dto.GetRequest;
+import org.openhab.binding.lifx.internal.dto.HevCycleState;
import org.openhab.binding.lifx.internal.dto.Packet;
import org.openhab.binding.lifx.internal.dto.PowerState;
import org.openhab.binding.lifx.internal.dto.SetColorRequest;
import org.openhab.binding.lifx.internal.dto.SetColorZonesRequest;
+import org.openhab.binding.lifx.internal.dto.SetHevCycleRequest;
import org.openhab.binding.lifx.internal.dto.SetLightInfraredRequest;
import org.openhab.binding.lifx.internal.dto.SetLightPowerRequest;
import org.openhab.binding.lifx.internal.dto.SetPowerRequest;
}
}
+ @Override
+ public void handleHevCycleStateChange(@Nullable HevCycleState oldHevCycleState, HevCycleState newHevCycleState) {
+ // The change should be ignored when both the old and new state are disabled regardless of the cycle duration
+ if (!newHevCycleState.equals(oldHevCycleState)
+ && (oldHevCycleState == null || oldHevCycleState.isEnable() || newHevCycleState.isEnable())) {
+ SetHevCycleRequest packet = new SetHevCycleRequest(newHevCycleState.isEnable(),
+ newHevCycleState.getDuration());
+ replacePacketsInMap(packet);
+ }
+ }
+
@Override
public void handleInfraredChange(@Nullable PercentType oldInfrared, PercentType newInfrared) {
PercentType infrared = pendingLightState.getInfrared();
@Override
public void handleTileEffectChange(@Nullable Effect oldEffect, Effect newEffect) {
- if (oldEffect == null || !oldEffect.equals(newEffect)) {
+ if (!newEffect.equals(oldEffect)) {
SetTileEffectRequest packet = new SetTileEffectRequest(newEffect);
replacePacketsInMap(packet);
}
getZonesIfZonesAreSet();
} else if (sentPacket instanceof SetColorZonesRequest) {
getZonesIfZonesAreSet();
+ } else if (sentPacket instanceof SetHevCycleRequest) {
+ scheduler.schedule(() -> {
+ GetHevCycleRequest hevCyclePacket = new GetHevCycleRequest();
+ communicationHandler.sendPacket(hevCyclePacket);
+ GetLightPowerRequest powerPacket = new GetLightPowerRequest();
+ communicationHandler.sendPacket(powerPacket);
+ }, 600, TimeUnit.MILLISECONDS);
} else if (sentPacket instanceof SetLightInfraredRequest) {
GetLightInfraredRequest infraredPacket = new GetLightInfraredRequest();
communicationHandler.sendPacket(infraredPacket);
public ThingTypeUID getThingTypeUID() {
if (hasFeature(COLOR)) {
- if (hasFeature(TILE_EFFECT)) {
- return LifxBindingConstants.THING_TYPE_TILELIGHT;
+ if (hasFeature(HEV)) {
+ return LifxBindingConstants.THING_TYPE_COLORHEVLIGHT;
} else if (hasFeature(INFRARED)) {
return LifxBindingConstants.THING_TYPE_COLORIRLIGHT;
} else if (hasFeature(MULTIZONE)) {
return LifxBindingConstants.THING_TYPE_COLORMZLIGHT;
+ } else if (hasFeature(TILE_EFFECT)) {
+ return LifxBindingConstants.THING_TYPE_TILELIGHT;
} else {
return LifxBindingConstants.THING_TYPE_COLORLIGHT;
}
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.dto;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @author Wouter Born - Initial contribution
+ */
+public class GetHevCycleConfigurationRequest extends Packet {
+
+ public static final int TYPE = 0x91;
+
+ public GetHevCycleConfigurationRequest() {
+ setTagged(false);
+ setAddressable(true);
+ setResponseRequired(true);
+ }
+
+ @Override
+ public int packetType() {
+ return TYPE;
+ }
+
+ @Override
+ protected int packetLength() {
+ return 0;
+ }
+
+ @Override
+ protected void parsePacket(ByteBuffer bytes) {
+ // do nothing
+ }
+
+ @Override
+ protected ByteBuffer packetBytes() {
+ return ByteBuffer.allocate(0);
+ }
+
+ @Override
+ public int[] expectedResponses() {
+ return new int[] { StateHevCycleConfigurationResponse.TYPE };
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.dto;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @author Wouter Born - Initial contribution
+ */
+public class GetHevCycleRequest extends Packet {
+
+ public static final int TYPE = 0x8E;
+
+ public GetHevCycleRequest() {
+ setTagged(false);
+ setAddressable(true);
+ setResponseRequired(true);
+ }
+
+ @Override
+ public int packetType() {
+ return TYPE;
+ }
+
+ @Override
+ protected int packetLength() {
+ return 0;
+ }
+
+ @Override
+ protected void parsePacket(ByteBuffer bytes) {
+ // do nothing
+ }
+
+ @Override
+ protected ByteBuffer packetBytes() {
+ return ByteBuffer.allocate(0);
+ }
+
+ @Override
+ public int[] expectedResponses() {
+ return new int[] { StateHevCycleResponse.TYPE };
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.dto;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @author Wouter Born - Initial contribution
+ */
+public class GetLastHevCycleResultRequest extends Packet {
+
+ public static final int TYPE = 0x94;
+
+ public GetLastHevCycleResultRequest() {
+ setTagged(false);
+ setAddressable(true);
+ setResponseRequired(true);
+ }
+
+ @Override
+ public int packetType() {
+ return TYPE;
+ }
+
+ @Override
+ protected int packetLength() {
+ return 0;
+ }
+
+ @Override
+ protected void parsePacket(ByteBuffer bytes) {
+ // do nothing
+ }
+
+ @Override
+ protected ByteBuffer packetBytes() {
+ return ByteBuffer.allocate(0);
+ }
+
+ @Override
+ public int[] expectedResponses() {
+ return new int[] { StateLastHevCycleResultResponse.TYPE };
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.dto;
+
+import java.time.Duration;
+
+/**
+ * The pending or current HEV cycle state.
+ *
+ * @author Wouter Born - Initial contribution
+ */
+public class HevCycleState {
+
+ public static final HevCycleState OFF = new HevCycleState(false);
+ public static final HevCycleState ON = new HevCycleState(true);
+
+ private boolean enable;
+ private Duration duration;
+
+ public HevCycleState(boolean enable) {
+ this.enable = enable;
+ this.duration = Duration.ZERO;
+ }
+
+ public HevCycleState(boolean enable, Duration duration) {
+ this.enable = enable;
+ this.duration = duration;
+ }
+
+ public boolean isEnable() {
+ return enable;
+ }
+
+ public Duration getDuration() {
+ return duration;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((duration == null) ? 0 : duration.hashCode());
+ result = prime * result + (enable ? 1231 : 1237);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ HevCycleState other = (HevCycleState) obj;
+ if (duration == null) {
+ if (other.duration != null) {
+ return false;
+ }
+ } else if (!duration.equals(other.duration)) {
+ return false;
+ }
+ if (enable != other.enable) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "HevCycleState [enable=" + enable + ", duration=" + duration + "]";
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.dto;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * @author Wouter Born - Initial contribution
+ */
+@NonNullByDefault
+public enum LightLastHevCycleResult {
+
+ SUCCESS(0),
+ BUSY(1),
+ INTERRUPTED_BY_RESET(2),
+ INTERRUPTED_BY_HOMEKIT(3),
+ INTERRUPTED_BY_LAN(4),
+ INTERRUPTED_BY_CLOUD(5),
+ NONE(255);
+
+ private final int type;
+
+ LightLastHevCycleResult(int type) {
+ this.type = type;
+ }
+
+ public static LightLastHevCycleResult fromValue(int type) {
+ Optional<LightLastHevCycleResult> result = Arrays.stream(values()).filter((value) -> value.type == type)
+ .findFirst();
+
+ if (!result.isPresent()) {
+ throw new IllegalArgumentException("Invalid LightLastHevCycleResult type: " + type);
+ }
+
+ return result.get();
+ }
+}
register(GetColorZonesRequest.class);
register(GetEchoRequest.class);
register(GetGroupRequest.class);
+ register(GetHevCycleConfigurationRequest.class);
+ register(GetHevCycleRequest.class);
register(GetHostFirmwareRequest.class);
register(GetHostInfoRequest.class);
register(GetInfoRequest.class);
register(GetLabelRequest.class);
+ register(GetLastHevCycleResultRequest.class);
register(GetLightInfraredRequest.class);
register(GetLightPowerRequest.class);
register(GetLocationRequest.class);
register(SetColorRequest.class);
register(SetColorZonesRequest.class);
register(SetDimAbsoluteRequest.class);
+ register(SetHevCycleRequest.class);
+ register(SetHevCycleConfigurationRequest.class);
register(SetLabelRequest.class);
register(SetLightInfraredRequest.class);
register(SetLightPowerRequest.class);
register(SetPowerRequest.class);
register(SetTagsRequest.class);
register(StateGroupResponse.class);
+ register(StateHevCycleConfigurationResponse.class);
+ register(StateHevCycleResponse.class);
register(StateHostFirmwareResponse.class);
register(StateHostInfoResponse.class);
register(StateInfoResponse.class);
register(StateLabelResponse.class);
+ register(StateLastHevCycleResultResponse.class);
register(StateLightInfraredResponse.class);
register(StateLightPowerResponse.class);
register(StateLocationResponse.class);
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.dto;
+
+import java.nio.ByteBuffer;
+import java.time.Duration;
+
+import org.openhab.binding.lifx.internal.fields.BoolIntField;
+import org.openhab.binding.lifx.internal.fields.Field;
+import org.openhab.binding.lifx.internal.fields.UInt32Field;
+
+/**
+ * @author Wouter Born - Initial contribution
+ */
+public class SetHevCycleConfigurationRequest extends Packet {
+
+ public static final int TYPE = 0x92;
+
+ public static final Field<Boolean> FIELD_INDICATION = new BoolIntField();
+ public static final Field<Long> FIELD_DURATION = new UInt32Field().little();
+
+ private boolean indication;
+ private long duration;
+
+ public boolean isIndication() {
+ return indication;
+ }
+
+ public Duration getDuration() {
+ return Duration.ofSeconds(duration);
+ }
+
+ public SetHevCycleConfigurationRequest() {
+ setTagged(false);
+ setAddressable(true);
+ setResponseRequired(true);
+ }
+
+ public SetHevCycleConfigurationRequest(boolean indication, Duration duration) {
+ this();
+ this.indication = indication;
+ this.duration = duration.toSeconds();
+ }
+
+ @Override
+ public int packetType() {
+ return TYPE;
+ }
+
+ @Override
+ protected int packetLength() {
+ return 5;
+ }
+
+ @Override
+ protected void parsePacket(ByteBuffer bytes) {
+ indication = FIELD_INDICATION.value(bytes);
+ duration = FIELD_DURATION.value(bytes);
+ }
+
+ @Override
+ protected ByteBuffer packetBytes() {
+ return ByteBuffer.allocate(packetLength()).put(FIELD_INDICATION.bytes(indication))
+ .put(FIELD_DURATION.bytes(duration));
+ }
+
+ @Override
+ public int[] expectedResponses() {
+ return new int[] { StateHevCycleConfigurationResponse.TYPE };
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.dto;
+
+import java.nio.ByteBuffer;
+import java.time.Duration;
+
+import org.openhab.binding.lifx.internal.fields.BoolIntField;
+import org.openhab.binding.lifx.internal.fields.Field;
+import org.openhab.binding.lifx.internal.fields.UInt32Field;
+
+/**
+ * @author Wouter Born - Initial contribution
+ */
+public class SetHevCycleRequest extends Packet {
+
+ public static final int TYPE = 0x8F;
+
+ public static final Field<Boolean> FIELD_ENABLE = new BoolIntField();
+ public static final Field<Long> FIELD_DURATION = new UInt32Field().little();
+
+ private boolean enable;
+ private long duration;
+
+ public boolean isEnable() {
+ return enable;
+ }
+
+ public Duration getDuration() {
+ return Duration.ofSeconds(duration);
+ }
+
+ public SetHevCycleRequest() {
+ setTagged(false);
+ setAddressable(true);
+ setResponseRequired(true);
+ }
+
+ public SetHevCycleRequest(boolean enable) {
+ this();
+ this.enable = enable;
+ }
+
+ public SetHevCycleRequest(boolean enable, Duration duration) {
+ this();
+ this.enable = enable;
+ this.duration = duration.toSeconds();
+ }
+
+ @Override
+ public int packetType() {
+ return TYPE;
+ }
+
+ @Override
+ protected int packetLength() {
+ return 5;
+ }
+
+ @Override
+ protected void parsePacket(ByteBuffer bytes) {
+ enable = FIELD_ENABLE.value(bytes);
+ duration = FIELD_DURATION.value(bytes);
+ }
+
+ @Override
+ protected ByteBuffer packetBytes() {
+ return ByteBuffer.allocate(packetLength()).put(FIELD_ENABLE.bytes(enable)).put(FIELD_DURATION.bytes(duration));
+ }
+
+ @Override
+ public int[] expectedResponses() {
+ return new int[] { StateHevCycleResponse.TYPE };
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.dto;
+
+import java.nio.ByteBuffer;
+import java.time.Duration;
+
+import org.openhab.binding.lifx.internal.fields.BoolIntField;
+import org.openhab.binding.lifx.internal.fields.Field;
+import org.openhab.binding.lifx.internal.fields.UInt32Field;
+
+/**
+ * @author Wouter Born - Initial contribution
+ */
+public class StateHevCycleConfigurationResponse extends Packet {
+
+ public static final int TYPE = 0x93;
+
+ public static final Field<Boolean> FIELD_INDICATION = new BoolIntField();
+ public static final Field<Long> FIELD_DURATION = new UInt32Field().little();
+
+ private boolean indication;
+ private long duration;
+
+ public boolean isIndication() {
+ return indication;
+ }
+
+ public Duration getDuration() {
+ return Duration.ofSeconds(duration);
+ }
+
+ @Override
+ public int packetType() {
+ return TYPE;
+ }
+
+ @Override
+ protected int packetLength() {
+ return 5;
+ }
+
+ @Override
+ protected void parsePacket(ByteBuffer bytes) {
+ indication = FIELD_INDICATION.value(bytes);
+ duration = FIELD_DURATION.value(bytes);
+ }
+
+ @Override
+ protected ByteBuffer packetBytes() {
+ return ByteBuffer.allocate(packetLength()).put(FIELD_INDICATION.bytes(indication))
+ .put(FIELD_DURATION.bytes(duration));
+ }
+
+ @Override
+ public int[] expectedResponses() {
+ return new int[] {};
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.dto;
+
+import java.nio.ByteBuffer;
+import java.time.Duration;
+
+import org.openhab.binding.lifx.internal.fields.BoolIntField;
+import org.openhab.binding.lifx.internal.fields.Field;
+import org.openhab.binding.lifx.internal.fields.UInt32Field;
+
+/**
+ * @author Wouter Born - Initial contribution
+ */
+public class StateHevCycleResponse extends Packet {
+
+ public static final int TYPE = 0x90;
+
+ public static final Field<Long> FIELD_DURATION = new UInt32Field().little();
+ public static final Field<Long> FIELD_REMAINING = new UInt32Field().little();
+ public static final Field<Boolean> FIELD_LAST_POWER = new BoolIntField();
+
+ private long duration;
+ private long remaining;
+ private boolean lastPower;
+
+ public Duration getDuration() {
+ return Duration.ofSeconds(duration);
+ }
+
+ public Duration getRemaining() {
+ return Duration.ofSeconds(remaining);
+ }
+
+ public boolean isLastPower() {
+ return lastPower;
+ }
+
+ @Override
+ public int packetType() {
+ return TYPE;
+ }
+
+ @Override
+ protected int packetLength() {
+ return 9;
+ }
+
+ @Override
+ protected void parsePacket(ByteBuffer bytes) {
+ duration = FIELD_DURATION.value(bytes);
+ remaining = FIELD_REMAINING.value(bytes);
+ lastPower = FIELD_LAST_POWER.value(bytes);
+ }
+
+ @Override
+ protected ByteBuffer packetBytes() {
+ return ByteBuffer.allocate(packetLength()).put(FIELD_DURATION.bytes(duration))
+ .put(FIELD_REMAINING.bytes(remaining)).put(FIELD_LAST_POWER.bytes(lastPower));
+ }
+
+ @Override
+ public int[] expectedResponses() {
+ return new int[] {};
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.dto;
+
+import java.nio.ByteBuffer;
+
+import org.openhab.binding.lifx.internal.fields.Field;
+import org.openhab.binding.lifx.internal.fields.UInt8Field;
+
+/**
+ * @author Wouter Born - Initial contribution
+ */
+public class StateLastHevCycleResultResponse extends Packet {
+
+ public static final int TYPE = 0x95;
+
+ public static final Field<Integer> FIELD_RESULT = new UInt8Field().little();
+
+ private int result;
+
+ public LightLastHevCycleResult getResult() {
+ return LightLastHevCycleResult.fromValue(result);
+ }
+
+ @Override
+ public int packetType() {
+ return TYPE;
+ }
+
+ @Override
+ protected int packetLength() {
+ return 1;
+ }
+
+ @Override
+ protected void parsePacket(ByteBuffer bytes) {
+ result = FIELD_RESULT.value(bytes);
+ }
+
+ @Override
+ protected ByteBuffer packetBytes() {
+ return ByteBuffer.allocate(packetLength()).put(FIELD_RESULT.bytes(result));
+ }
+
+ @Override
+ public int[] expectedResponses() {
+ return new int[] {};
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 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.lifx.internal.fields;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * @author Wouter Born - Initial contribution
+ */
+@NonNullByDefault
+public class BoolIntField extends Field<Boolean> {
+
+ public BoolIntField() {
+ super(1);
+ }
+
+ @Override
+ public int defaultLength() {
+ return 1;
+ }
+
+ @Override
+ public Boolean value(ByteBuffer bytes) {
+ return bytes.get() == 1;
+ }
+
+ @Override
+ public ByteBuffer bytesInternal(Boolean value) {
+ return ByteBuffer.allocate(1).put((byte) (value ? 1 : 0));
+ }
+}
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.lifx.internal.LifxProduct;
import org.openhab.binding.lifx.internal.LifxProduct.Features;
import org.openhab.binding.lifx.internal.dto.Effect;
+import org.openhab.binding.lifx.internal.dto.GetHevCycleRequest;
import org.openhab.binding.lifx.internal.dto.GetLightInfraredRequest;
import org.openhab.binding.lifx.internal.dto.GetLightPowerRequest;
import org.openhab.binding.lifx.internal.dto.GetRequest;
import org.openhab.binding.lifx.internal.dto.GetTileEffectRequest;
import org.openhab.binding.lifx.internal.dto.GetWifiInfoRequest;
+import org.openhab.binding.lifx.internal.dto.HevCycleState;
import org.openhab.binding.lifx.internal.dto.Packet;
import org.openhab.binding.lifx.internal.dto.PowerState;
import org.openhab.binding.lifx.internal.dto.SignalStrength;
private final LifxChannelFactory channelFactory;
private @NonNullByDefault({}) Features features;
+ private Duration hevCycleDuration = Duration.ZERO;
private @Nullable PercentType powerOnBrightness;
private @Nullable HSBType powerOnColor;
private @Nullable PercentType powerOnTemperature;
return updateColor;
}
+ @Override
+ public void setHevCycleState(HevCycleState hevCycleState) {
+ if (!isStateChangePending() || hevCycleState.equals(pendingLightState.getHevCycleState())) {
+ updateStateIfChanged(CHANNEL_HEV_CYCLE, OnOffType.from(hevCycleState.isEnable()));
+ }
+ super.setHevCycleState(hevCycleState);
+ }
+
@Override
public void setInfrared(PercentType infrared) {
if (!isStateChangePending() || infrared.equals(pendingLightState.getInfrared())) {
if (speed != null) {
effectFlameSpeed = speed;
}
+ hevCycleDuration = getHevCycleDuration();
+
channelStates.clear();
currentLightState = new CurrentLightState();
pendingLightState = new LifxLightState();
return speed == null ? null : Double.valueOf(speed.toString());
}
+ private Duration getHevCycleDuration() {
+ ChannelUID channelUID = new ChannelUID(getThing().getUID(), LifxBindingConstants.CHANNEL_HEV_CYCLE);
+ Channel channel = getThing().getChannel(channelUID.getId());
+
+ if (channel == null) {
+ return Duration.ZERO;
+ }
+
+ Configuration configuration = channel.getConfiguration();
+ Object duration = configuration.get(LifxBindingConstants.CONFIG_PROPERTY_HEV_CYCLE_DURATION);
+ return duration == null ? Duration.ZERO : Duration.ofSeconds(Integer.valueOf(duration.toString()));
+ }
+
private Features getFeatures() {
LifxProduct product = getProduct();
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
- channelStates.remove(channelUID.getId());
- switch (channelUID.getId()) {
- case CHANNEL_COLOR:
- case CHANNEL_BRIGHTNESS:
- sendPacket(new GetLightPowerRequest());
- sendPacket(new GetRequest());
- break;
- case CHANNEL_TEMPERATURE:
- sendPacket(new GetRequest());
- break;
- case CHANNEL_INFRARED:
- sendPacket(new GetLightInfraredRequest());
- break;
- case CHANNEL_SIGNAL_STRENGTH:
- sendPacket(new GetWifiInfoRequest());
- break;
- case CHANNEL_EFFECT:
- if (features.hasFeature(TILE_EFFECT)) {
- sendPacket(new GetTileEffectRequest());
- }
- break;
- default:
- break;
- }
+ handleRefreshCommand(channelUID);
} else {
- boolean supportedCommand = true;
- switch (channelUID.getId()) {
- case CHANNEL_COLOR:
- if (command instanceof HSBType) {
- handleHSBCommand((HSBType) command);
- } else if (command instanceof PercentType) {
- handlePercentCommand((PercentType) command);
- } else if (command instanceof OnOffType) {
- handleOnOffCommand((OnOffType) command);
- } else if (command instanceof IncreaseDecreaseType) {
- handleIncreaseDecreaseCommand((IncreaseDecreaseType) command);
- } else {
- supportedCommand = false;
- }
- break;
- case CHANNEL_BRIGHTNESS:
- if (command instanceof PercentType) {
- handlePercentCommand((PercentType) command);
- } else if (command instanceof OnOffType) {
- handleOnOffCommand((OnOffType) command);
- } else if (command instanceof IncreaseDecreaseType) {
- handleIncreaseDecreaseCommand((IncreaseDecreaseType) command);
- } else {
- supportedCommand = false;
- }
- break;
- case CHANNEL_TEMPERATURE:
- if (command instanceof PercentType) {
- handleTemperatureCommand((PercentType) command);
- } else if (command instanceof IncreaseDecreaseType) {
- handleIncreaseDecreaseTemperatureCommand((IncreaseDecreaseType) command);
- } else {
- supportedCommand = false;
- }
- break;
- case CHANNEL_INFRARED:
- if (command instanceof PercentType) {
- handleInfraredCommand((PercentType) command);
- } else if (command instanceof IncreaseDecreaseType) {
- handleIncreaseDecreaseInfraredCommand((IncreaseDecreaseType) command);
- } else {
- supportedCommand = false;
- }
- break;
- case CHANNEL_EFFECT:
- if (command instanceof StringType && features.hasFeature(TILE_EFFECT)) {
- handleTileEffectCommand((StringType) command);
- } else {
- supportedCommand = false;
- }
- break;
- default:
- try {
- if (channelUID.getId().startsWith(CHANNEL_COLOR_ZONE)) {
- int zoneIndex = Integer.parseInt(channelUID.getId().replace(CHANNEL_COLOR_ZONE, ""));
- if (command instanceof HSBType) {
- handleHSBCommand((HSBType) command, zoneIndex);
- } else if (command instanceof PercentType) {
- handlePercentCommand((PercentType) command, zoneIndex);
- } else if (command instanceof IncreaseDecreaseType) {
- handleIncreaseDecreaseCommand((IncreaseDecreaseType) command, zoneIndex);
- } else {
- supportedCommand = false;
- }
- } else if (channelUID.getId().startsWith(CHANNEL_TEMPERATURE_ZONE)) {
- int zoneIndex = Integer.parseInt(channelUID.getId().replace(CHANNEL_TEMPERATURE_ZONE, ""));
- if (command instanceof PercentType) {
- handleTemperatureCommand((PercentType) command, zoneIndex);
- } else if (command instanceof IncreaseDecreaseType) {
- handleIncreaseDecreaseTemperatureCommand((IncreaseDecreaseType) command, zoneIndex);
- } else {
- supportedCommand = false;
- }
- } else {
- supportedCommand = false;
- }
- } catch (NumberFormatException e) {
- logger.error("Failed to parse zone index for a command of a light ({}) : {}", logId,
- e.getMessage());
- supportedCommand = false;
- }
- break;
+ Runnable channelCommandRunnable = getChannelCommandRunnable(channelUID, command);
+ if (channelCommandRunnable == null) {
+ return;
+ }
+
+ String channelId = channelUID.getId();
+ boolean isHevCycleChannelCommand = CHANNEL_HEV_CYCLE.equals(channelId);
+ boolean isInfraredChannelCommand = CHANNEL_INFRARED.equals(channelId);
+ boolean waitForHevCycleDisabled = false;
+
+ if (getFeatures().hasFeature(HEV) && !isHevCycleChannelCommand) {
+ LifxLightState lightState = getLightStateForCommand();
+ HevCycleState currentHevCycleState = lightState.getHevCycleState();
+ if (currentHevCycleState == null || currentHevCycleState.isEnable()) {
+ lightState.setHevCycleState(HevCycleState.OFF);
+ lightState.setPowerState(PowerState.OFF);
+ waitForHevCycleDisabled = true;
+ }
}
- if (supportedCommand && !(command instanceof OnOffType) && !CHANNEL_INFRARED.equals(channelUID.getId())) {
- getLightStateForCommand().setPowerState(PowerState.ON);
+ Runnable compositeCommandsRunnable = () -> {
+ channelCommandRunnable.run();
+ if (!(command instanceof OnOffType) && !isHevCycleChannelCommand && !isInfraredChannelCommand) {
+ getLightStateForCommand().setPowerState(PowerState.ON);
+ }
+ };
+
+ if (waitForHevCycleDisabled) {
+ scheduler.schedule(compositeCommandsRunnable, 200, TimeUnit.MILLISECONDS);
+ } else {
+ compositeCommandsRunnable.run();
}
}
}
+ private @Nullable Runnable getChannelCommandRunnable(ChannelUID channelUID, Command command) {
+ switch (channelUID.getId()) {
+ case CHANNEL_BRIGHTNESS:
+ if (command instanceof PercentType) {
+ return () -> handlePercentCommand((PercentType) command);
+ } else if (command instanceof OnOffType) {
+ return () -> handleOnOffCommand((OnOffType) command);
+ } else if (command instanceof IncreaseDecreaseType) {
+ return () -> handleIncreaseDecreaseCommand((IncreaseDecreaseType) command);
+ } else {
+ return null;
+ }
+ case CHANNEL_COLOR:
+ if (command instanceof HSBType) {
+ return () -> handleHSBCommand((HSBType) command);
+ } else if (command instanceof PercentType) {
+ return () -> handlePercentCommand((PercentType) command);
+ } else if (command instanceof OnOffType) {
+ return () -> handleOnOffCommand((OnOffType) command);
+ } else if (command instanceof IncreaseDecreaseType) {
+ return () -> handleIncreaseDecreaseCommand((IncreaseDecreaseType) command);
+ } else {
+ return null;
+ }
+ case CHANNEL_EFFECT:
+ if (command instanceof StringType && features.hasFeature(TILE_EFFECT)) {
+ return () -> handleTileEffectCommand((StringType) command);
+ } else {
+ return null;
+ }
+ case CHANNEL_HEV_CYCLE:
+ if (command instanceof OnOffType) {
+ return () -> handleHevCycleCommand((OnOffType) command);
+ } else {
+ return null;
+ }
+ case CHANNEL_INFRARED:
+ if (command instanceof PercentType) {
+ return () -> handleInfraredCommand((PercentType) command);
+ } else if (command instanceof IncreaseDecreaseType) {
+ return () -> handleIncreaseDecreaseInfraredCommand((IncreaseDecreaseType) command);
+ } else {
+ return null;
+ }
+ case CHANNEL_TEMPERATURE:
+ if (command instanceof PercentType) {
+ return () -> handleTemperatureCommand((PercentType) command);
+ } else if (command instanceof IncreaseDecreaseType) {
+ return () -> handleIncreaseDecreaseTemperatureCommand((IncreaseDecreaseType) command);
+ } else {
+ return null;
+ }
+ default:
+ try {
+ if (channelUID.getId().startsWith(CHANNEL_COLOR_ZONE)) {
+ int zoneIndex = Integer.parseInt(channelUID.getId().replace(CHANNEL_COLOR_ZONE, ""));
+ if (command instanceof HSBType) {
+ return () -> handleHSBCommand((HSBType) command, zoneIndex);
+ } else if (command instanceof PercentType) {
+ return () -> handlePercentCommand((PercentType) command, zoneIndex);
+ } else if (command instanceof IncreaseDecreaseType) {
+ return () -> handleIncreaseDecreaseCommand((IncreaseDecreaseType) command, zoneIndex);
+ } else {
+ return null;
+ }
+ } else if (channelUID.getId().startsWith(CHANNEL_TEMPERATURE_ZONE)) {
+ int zoneIndex = Integer.parseInt(channelUID.getId().replace(CHANNEL_TEMPERATURE_ZONE, ""));
+ if (command instanceof PercentType) {
+ return () -> handleTemperatureCommand((PercentType) command, zoneIndex);
+ } else if (command instanceof IncreaseDecreaseType) {
+ return () -> handleIncreaseDecreaseTemperatureCommand((IncreaseDecreaseType) command,
+ zoneIndex);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ } catch (NumberFormatException e) {
+ logger.error("Failed to parse zone index for a command of a light ({}) : {}", logId,
+ e.getMessage());
+ return null;
+ }
+ }
+ }
+
private LifxLightState getLightStateForCommand() {
if (!isStateChangePending()) {
pendingLightState.copy(currentLightState);
return pendingLightState.getDurationSinceLastChange().minus(MAX_STATE_CHANGE_DURATION).isNegative();
}
+ private void handleRefreshCommand(ChannelUID channelUID) {
+ channelStates.remove(channelUID.getId());
+ switch (channelUID.getId()) {
+ case CHANNEL_COLOR:
+ case CHANNEL_BRIGHTNESS:
+ sendPacket(new GetLightPowerRequest());
+ sendPacket(new GetRequest());
+ break;
+ case CHANNEL_EFFECT:
+ if (features.hasFeature(TILE_EFFECT)) {
+ sendPacket(new GetTileEffectRequest());
+ }
+ break;
+ case CHANNEL_HEV_CYCLE:
+ sendPacket(new GetHevCycleRequest());
+ break;
+ case CHANNEL_INFRARED:
+ sendPacket(new GetLightInfraredRequest());
+ break;
+ case CHANNEL_SIGNAL_STRENGTH:
+ sendPacket(new GetWifiInfoRequest());
+ break;
+ case CHANNEL_TEMPERATURE:
+ sendPacket(new GetRequest());
+ break;
+ default:
+ break;
+ }
+ }
+
private void handleTemperatureCommand(PercentType temperature) {
HSBK newColor = getLightStateForCommand().getColor();
newColor.setSaturation(PercentType.ZERO);
handleTemperatureCommand(newTemperature, zoneIndex);
}
+ private void handleHevCycleCommand(OnOffType onOff) {
+ HevCycleState hevCycleState = new HevCycleState(onOff == OnOffType.ON, hevCycleDuration);
+ getLightStateForCommand().setHevCycleState(hevCycleState);
+ }
+
private void handleInfraredCommand(PercentType infrared) {
getLightStateForCommand().setInfrared(infrared);
}
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.lifx.internal.LifxLightState;
import org.openhab.binding.lifx.internal.dto.Effect;
+import org.openhab.binding.lifx.internal.dto.HevCycleState;
import org.openhab.binding.lifx.internal.dto.PowerState;
import org.openhab.binding.lifx.internal.dto.SignalStrength;
import org.openhab.binding.lifx.internal.fields.HSBK;
*/
void handlePowerStateChange(@Nullable PowerState oldPowerState, PowerState newPowerState);
+ /**
+ * Called when the HEV cycle state property changes.
+ *
+ * @param oldHevCycleState the old HEV cycle state value
+ * @param newHevCycleState the new HEV cycle state value
+ */
+ void handleHevCycleStateChange(@Nullable HevCycleState oldHevCycleState, HevCycleState newHevCycleState);
+
/**
* Called when the infrared property changes.
*
</parameter>
</config-description>
- <config-description uri="channel-type:lifx:temperature">
- <parameter name="powerOnTemperature" type="integer" min="0" max="100" required="false" unit="%">
- <label>Power On Temperature</label>
- <description>Color temperature level used when switching on the light. Use empty value to leave color temperature as
- is.</description>
- </parameter>
- </config-description>
-
<config-description uri="channel-type:lifx:effect">
<parameter name="effectMorphSpeed" type="decimal" min="0" max="120" step="0.1" required="false" unit="s">
- <label>Morph effect speed</label>
+ <label>Morph Effect Speed</label>
<description>Speed of the morph effect in seconds.</description>
<default>3</default>
</parameter>
<parameter name="effectFlameSpeed" type="decimal" min="0" max="120" step="0.1" required="false" unit="s">
- <label>Flame effect speed</label>
+ <label>Flame Effect Speed</label>
<description>Speed of the flame effect in seconds.</description>
<default>4</default>
</parameter>
</config-description>
+ <config-description uri="channel-type:lifx:hevcycle">
+ <parameter name="hevCycleDuration" type="integer" min="0" max="86400" step="1" required="false" unit="m">
+ <label>HEV Cycle Duration</label>
+ <description>HEV cycle duration in seconds. Use empty value for the cycle duration configured in the light.</description>
+ </parameter>
+ </config-description>
+
+ <config-description uri="channel-type:lifx:temperature">
+ <parameter name="powerOnTemperature" type="integer" min="0" max="100" required="false" unit="%">
+ <label>Power On Temperature</label>
+ <description>Color temperature level used when switching on the light. Use empty value to leave color temperature as
+ is.</description>
+ </parameter>
+ </config-description>
+
</config-description:config-descriptions>
# thing types
thing-type.lifx.colorlight.label = LIFX Color Light
+thing-type.lifx.colorhevlight.label = LIFX Color HEV Light
thing-type.lifx.colorirlight.label = LIFX Color IR Light
thing-type.lifx.colormzlight.label = LIFX Color MultiZone Light
+thing-type.lifx.tilelight.label = LIFX Tile Light
thing-type.lifx.whitelight.label = LIFX White Light
# thing type configuration
-thing-type.config.lifx.light.deviceId.label = LIFX device ID
+thing-type.config.lifx.light.deviceId.label = LIFX Device ID
thing-type.config.lifx.light.deviceId.description = Identifies the light, e.g. "D073D5A1A1A1"
thing-type.config.lifx.light.host.label = Host
thing-type.config.lifx.light.host.description = Hostname or IP address of the light. Use empty value for automatic discovery.
-thing-type.config.lifx.light.fadetime.label = Fade time
+thing-type.config.lifx.light.fadetime.label = Fade Time
thing-type.config.lifx.light.fadetime.description = The time to fade to the new color value (in ms).
# channel types
channel-type.lifx.brightness.description = Sets the brightness of the light
channel-type.lifx.color.label = Color
channel-type.lifx.color.description = Selects the color of the light
-channel-type.lifx.colorzone.label = Color zone {0}
+channel-type.lifx.colorzone.label = Color Zone {0}
channel-type.lifx.colorzone.description = Selects the zone {0} color of the light
-channel-type.lifx.infrared.label = Infrared
-channel-type.lifx.infrared.description = Sets the infrared of the light
-channel-type.lifx.temperature.label = Temperature
-channel-type.lifx.temperature.description = Sets the temperature of the light
-channel-type.lifx.temperaturezone.label = Temperature zone {0}
-channel-type.lifx.temperaturezone.description = Sets the zone {0} temperature of the light
channel-type.lifx.effect.label = Effect
channel-type.lifx.effect.description = Sets the effect of the light
channel-type.lifx.effect.state.option.off = Off
channel-type.lifx.effect.state.option.morph = Morph
channel-type.lifx.effect.state.option.flame = Flame
+channel-type.lifx.hevcycle.label = HEV Cycle
+channel-type.lifx.hevcycle.description = Controls the HEV clean cycle of the light
+channel-type.lifx.infrared.label = Infrared
+channel-type.lifx.infrared.description = Sets the infrared of the light
+channel-type.lifx.temperature.label = Temperature
+channel-type.lifx.temperature.description = Sets the temperature of the light
+channel-type.lifx.temperaturezone.label = Temperature Zone {0}
+channel-type.lifx.temperaturezone.description = Sets the zone {0} temperature of the light
# channel type configuration
-channel-type.config.lifx.brightness.powerOnBrightness.label = Power on brightness
+channel-type.config.lifx.brightness.powerOnBrightness.label = Power On Brightness
channel-type.config.lifx.brightness.powerOnBrightness.description = Brightness level used when switching on the light. Use empty value to leave brightness as is.
-channel-type.config.lifx.color.powerOnBrightness.label = Power on brightness
+channel-type.config.lifx.color.powerOnBrightness.label = Power On Brightness
channel-type.config.lifx.color.powerOnBrightness.description = Brightness level used when switching on the light. Use empty value to leave brightness as is.
-channel-type.config.lifx.color.powerOnColor.label = Power on color
+channel-type.config.lifx.color.powerOnColor.label = Power On Color
channel-type.config.lifx.color.powerOnColor.description = Color used when switching on the light. Use empty value to leave color as is.
-channel-type.config.lifx.temperature.powerOnTemperature.label = Power on color temperature
-channel-type.config.lifx.temperature.powerOnTemperature.description = Color temperature used when switching on the light. Use empty value to leave color temperature as is.
-channel-type.config.lifx.effect.effectMorphSpeed.label = Morph effect speed
+channel-type.config.lifx.effect.effectMorphSpeed.label = Morph Effect Speed
channel-type.config.lifx.effect.effectMorphSpeed.description = Speed of the morph effect in seconds.
-channel-type.config.lifx.effect.effectFlameSpeed.label = Flame effect speed
+channel-type.config.lifx.effect.effectFlameSpeed.label = Flame Effect Speed
channel-type.config.lifx.effect.effectFlameSpeed.description = Speed of the flame effect in seconds.
+channel-type.config.lifx.hevcycle.hevCycleDuration.label = HEV Cycle Duration
+channel-type.config.lifx.hevcycle.hevCycleDuration.description = HEV cycle duration in seconds. Use empty value for the cycle duration configured in the light.
+channel-type.config.lifx.temperature.powerOnTemperature.label = Power On Color Temperature
+channel-type.config.lifx.temperature.powerOnTemperature.description = Color temperature used when switching on the light. Use empty value to leave color temperature as is.
# thing types
thing-type.lifx.colorlight.label = LIFX Kleuren Lamp
+thing-type.lifx.colorhevlight.label = LIFX Kleuren HEV Lamp
thing-type.lifx.colorirlight.label = LIFX Kleuren IR Lamp
thing-type.lifx.colormzlight.label = LIFX Kleuren MultiZone Lamp
+thing-type.lifx.tilelight.label = LIFX Tegel Lamp
thing-type.lifx.whitelight.label = LIFX Wittinten Lamp
# thing type configuration
-thing-type.config.lifx.light.deviceId.label = LIFX apparaat ID
+thing-type.config.lifx.light.deviceId.label = LIFX Apparaat ID
thing-type.config.lifx.light.deviceId.description = Identificeert de lamp, bv. "D073D5A1A1A1"
thing-type.config.lifx.light.host.label = Host
-thing-type.config.lifx.light.host.description = Hostnaam of IP adres van de lamp. Gebruik lege waarde voor automatische detectie.
+thing-type.config.lifx.light.host.description = Hostnaam of IP adres van de lamp. Gebruik een lege waarde voor automatische detectie.
thing-type.config.lifx.light.fadetime.label = Vervagingsduur
thing-type.config.lifx.light.fadetime.description = De tijdsduur van het vervagen naar een nieuwe kleur (in ms).
channel-type.lifx.brightness.description = Bepaalt de helderheid van de lamp
channel-type.lifx.color.label = Kleur
channel-type.lifx.color.description = Bepaalt de kleur van de lamp
-channel-type.lifx.colorzone.label = Kleur zone {0}
+channel-type.lifx.colorzone.label = Kleur Zone {0}
channel-type.lifx.colorzone.description = Bepaalt de kleur van lampzone {0}
+channel-type.lifx.effect.label = Effect
+channel-type.lifx.effect.description = Bepaalt het lichteffect
+channel-type.lifx.effect.state.option.off = Uit
+channel-type.lifx.effect.state.option.morph = Morph
+channel-type.lifx.effect.state.option.flame = Vlam
+channel-type.lifx.hevcycle.label = HEV Cyclus
+channel-type.lifx.hevcycle.description = Bedient de HEV schoonmaakcylcus van de lamp
channel-type.lifx.infrared.label = Infrarood
channel-type.lifx.infrared.description = Bepaalt het infraroodniveau van de lamp
channel-type.lifx.temperature.label = Temperatuur
channel-type.lifx.temperature.description = Bepaalt de kleurtemperatuur van de lamp
-channel-type.lifx.temperaturezone.label = Temperatuur zone {0}
+channel-type.lifx.temperaturezone.label = Temperatuur Zone {0}
channel-type.lifx.temperaturezone.description = Bepaalt de kleurtemperatuur van lampzone {0}
-channel-type.lifx.effect.label = Effect
-channel-type.lifx.effect.description = Bepaalt het lichteffect
-channel-type.lifx.effect.state.option.off = Uit
-channel-type.lifx.effect.state.option.morph = Morph
-channel-type.lifx.effect.state.option.flame = Flamme
# channel type configuration
channel-type.config.lifx.brightness.powerOnBrightness.label = Inschakelhelderheid
channel-type.config.lifx.brightness.powerOnBrightness.description = Het helderheidsniveau bij inschakeling van de lamp. Gebruik een lege waarde om de helderheid ongewijzigd te laten.
-channel-type.config.lifx.effect.effectMorphSpeed.label = Morph-effect snelheid
+channel-type.config.lifx.color.powerOnBrightness.label = Inschakelhelderheid
+channel-type.config.lifx.color.powerOnBrightness.description = Het helderheidsniveau bij inschakeling van de lamp. Gebruik een lege waarde om de helderheid ongewijzigd te laten.
+channel-type.config.lifx.color.powerOnColor.label = Inschakelkleur
+channel-type.config.lifx.color.powerOnColor.description = De kleur die gebruikt wordt bij het inschakelen van de lamp. Gebruik een lege waarde om de kleur ongewijzigd te laten.
+channel-type.config.lifx.effect.effectMorphSpeed.label = Morph-effect Snelheid
channel-type.config.lifx.effect.effectMorphSpeed.description = De snelheid van het Morph-effect in seconden.
-channel-type.config.lifx.effect.effectFlameSpeed.label = Vlam-effect snelheid
+channel-type.config.lifx.effect.effectFlameSpeed.label = Vlam-effect Snelheid
channel-type.config.lifx.effect.effectFlameSpeed.description = De snelheid van het Vlam-effect in seconden.
+channel-type.config.lifx.hevcycle.hevCycleDuration.label = HEV Cycluslooptijd
+channel-type.config.lifx.hevcycle.hevCycleDuration.description = HEV cycluslooptijd in seconden. Gebruik een lege waarde om de in de lamp geconfigureerde cycluslooptijd te gebruiken.
+channel-type.config.lifx.temperature.powerOnTemperature.label = Inschakelkleurtemperatuur
+channel-type.config.lifx.temperature.powerOnTemperature.description = De kleurtemperatuur die gebruikt wordt bij het inschakelen van de lamp. Gebruik een lege waarde om de kleurtemperatuur ongewijzigd te laten.
</tags>
</channel-type>
+ <channel-type id="hevcycle">
+ <item-type>Switch</item-type>
+ <label>HEV Cycle</label>
+ <description>Controls the HEV clean cycle of the light</description>
+ <config-description-ref uri="channel-type:lifx:hevcycle"/>
+ </channel-type>
+
<channel-type id="infrared">
<item-type>Dimmer</item-type>
<label>Infrared</label>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="lifx"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+ <thing-type id="colorhevlight">
+ <label>LIFX Color HEV Light</label>
+ <channels>
+ <channel id="color" typeId="color"/>
+ <channel id="temperature" typeId="temperature"/>
+ <channel id="hevcycle" typeId="hevcycle"/>
+ <channel id="signalstrength" typeId="system.signal-strength"/>
+ </channels>
+ <representation-property>macAddress</representation-property>
+ <config-description-ref uri="thing-type:lifx:light"/>
+ </thing-type>
+
+</thing:thing-descriptions>