]> git.basschouten.com Git - openhab-addons.git/commitdiff
[lifx] Support HEV clean cycle (#11262)
authorWouter Born <github@maindrain.net>
Sun, 19 Sep 2021 09:45:06 +0000 (11:45 +0200)
committerGitHub <noreply@github.com>
Sun, 19 Sep 2021 09:45:06 +0000 (11:45 +0200)
* Implement HEV packets
* Add colorhevlight thing type with a hevcycle channel
* Update documentation

Signed-off-by: Wouter Born <github@maindrain.net>
25 files changed:
bundles/org.openhab.binding.lifx/README.md
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxBindingConstants.java
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightCurrentStateUpdater.java
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightState.java
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightStateChanger.java
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxProduct.java
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHevCycleConfigurationRequest.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHevCycleRequest.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLastHevCycleResultRequest.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/HevCycleState.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/LightLastHevCycleResult.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/PacketFactory.java
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetHevCycleConfigurationRequest.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetHevCycleRequest.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHevCycleConfigurationResponse.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHevCycleResponse.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLastHevCycleResultResponse.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/BoolIntField.java [new file with mode: 0644]
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/handler/LifxLightHandler.java
bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxLightStateListener.java
bundles/org.openhab.binding.lifx/src/main/resources/OH-INF/config/config.xml
bundles/org.openhab.binding.lifx/src/main/resources/OH-INF/i18n/lifx.properties
bundles/org.openhab.binding.lifx/src/main/resources/OH-INF/i18n/lifx_nl.properties
bundles/org.openhab.binding.lifx/src/main/resources/OH-INF/thing/channel.xml
bundles/org.openhab.binding.lifx/src/main/resources/OH-INF/thing/colorhevlight.xml [new file with mode: 0644]

index c3cdc9ac16d806fe517ed6981e4518e9b5d7e687..ab5d4f0c7c67fc4c089ffcaa3e09f978bd5a99cd 100644 (file)
@@ -9,46 +9,48 @@ All LIFX lights are directly connected to the WLAN and the binding communicates
 
 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
 
@@ -83,39 +85,44 @@ Thing lifx:colorirlight:porch [ host="10.120.130.4", fadetime=0 ]
 
 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.
@@ -136,6 +143,11 @@ Thing lifx:colorlight:living2 [ deviceId="D073D5A2A2A2" ] {
         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 ]
@@ -169,6 +181,11 @@ Switch Living2_Switch { channel="lifx:colorlight:living2:color" }
 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" }
@@ -208,6 +225,14 @@ sitemap demo label="Main Menu"
         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
index 7478fd52db23c44d56b78acc9dc7bfc639cb5463..1274479961de9339bc3dbe39b7dbd091666229ef 100644 (file)
@@ -13,8 +13,6 @@
 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;
@@ -55,6 +53,7 @@ public class LifxBindingConstants {
     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";
@@ -80,11 +79,12 @@ public class LifxBindingConstants {
     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";
@@ -100,12 +100,13 @@ public class LifxBindingConstants {
 
     // 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);
 }
index ef6ad12b69152530a9410c9c39e443a7ba24703d..5e42fb2fd946a7d446bb6884e6d8694cdb83f2ad 100644 (file)
@@ -25,11 +25,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 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;
@@ -133,6 +136,9 @@ public class LifxLightCurrentStateUpdater {
     private void sendLightStateRequests() {
         communicationHandler.sendPacket(new GetRequest());
 
+        if (features.hasFeature(HEV)) {
+            communicationHandler.sendPacket(new GetHevCycleRequest());
+        }
         if (features.hasFeature(INFRARED)) {
             communicationHandler.sendPacket(new GetLightInfraredRequest());
         }
@@ -157,14 +163,16 @@ public class LifxLightCurrentStateUpdater {
                 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();
@@ -192,6 +200,11 @@ public class LifxLightCurrentStateUpdater {
         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);
@@ -209,11 +222,11 @@ public class LifxLightCurrentStateUpdater {
         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());
+    }
 }
index b3bbefaf201a732406e1499d0d13023e29d09eb5..0d52ba0c757ad0f229215a027d9092d071cfe277 100644 (file)
@@ -23,6 +23,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
 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;
@@ -40,6 +41,7 @@ import org.openhab.core.library.types.PercentType;
 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;
@@ -51,6 +53,7 @@ public class LifxLightState {
     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();
@@ -76,6 +79,10 @@ public class LifxLightState {
         return colorsCopy;
     }
 
+    public @Nullable HevCycleState getHevCycleState() {
+        return hevCycleState;
+    }
+
     public @Nullable PercentType getInfrared() {
         return infrared;
     }
@@ -158,6 +165,13 @@ public class LifxLightState {
         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;
index dbccb2113b5d7f91627dcee6cf266fbca6d8331f..2a7bc6b569faa0f241ec8ec69eb6d1a8be455b00 100644 (file)
@@ -34,13 +34,16 @@ import org.openhab.binding.lifx.internal.dto.AcknowledgementResponse;
 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;
@@ -308,6 +311,17 @@ public class LifxLightStateChanger implements LifxLightStateListener {
         }
     }
 
+    @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();
@@ -325,7 +339,7 @@ public class LifxLightStateChanger implements LifxLightStateListener {
 
     @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);
         }
@@ -360,6 +374,13 @@ public class LifxLightStateChanger implements LifxLightStateListener {
                     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);
index 945601109ed53c22ba41618c0c930e2d365d89d6..0cccb64928e9d723517c410c9ae5aac16ef5082c 100644 (file)
@@ -371,12 +371,14 @@ public enum LifxProduct {
 
     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;
             }
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHevCycleConfigurationRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHevCycleConfigurationRequest.java
new file mode 100644 (file)
index 0000000..69c7846
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * 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 };
+    }
+}
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHevCycleRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHevCycleRequest.java
new file mode 100644 (file)
index 0000000..eb24879
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * 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 };
+    }
+}
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLastHevCycleResultRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLastHevCycleResultRequest.java
new file mode 100644 (file)
index 0000000..476da0f
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * 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 };
+    }
+}
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/HevCycleState.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/HevCycleState.java
new file mode 100644 (file)
index 0000000..75b5e86
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * 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 + "]";
+    }
+}
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/LightLastHevCycleResult.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/LightLastHevCycleResult.java
new file mode 100644 (file)
index 0000000..14cde69
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * 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();
+    }
+}
index ba9ea39f0f4c329d3c6d4e95c65a265bc77969a0..bd68da0fd71566a3db20ad9676eb33661fe17628 100644 (file)
@@ -52,10 +52,13 @@ public class PacketFactory {
         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);
@@ -71,16 +74,21 @@ public class PacketFactory {
         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);
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetHevCycleConfigurationRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetHevCycleConfigurationRequest.java
new file mode 100644 (file)
index 0000000..3dcab88
--- /dev/null
@@ -0,0 +1,81 @@
+/**
+ * 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 };
+    }
+}
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetHevCycleRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetHevCycleRequest.java
new file mode 100644 (file)
index 0000000..5c06235
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * 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 };
+    }
+}
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHevCycleConfigurationResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHevCycleConfigurationResponse.java
new file mode 100644 (file)
index 0000000..1b2fd2c
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * 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[] {};
+    }
+}
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHevCycleResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHevCycleResponse.java
new file mode 100644 (file)
index 0000000..92e2b02
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * 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[] {};
+    }
+}
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLastHevCycleResultResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLastHevCycleResultResponse.java
new file mode 100644 (file)
index 0000000..6f80659
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * 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[] {};
+    }
+}
diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/BoolIntField.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/BoolIntField.java
new file mode 100644 (file)
index 0000000..037e5c3
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * 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));
+    }
+}
index fd0684298003cf92e1449abddcb9a7812a790b14..9be4f24b4a84f33eec55497aec8116a0d8c6d13b 100644 (file)
@@ -25,6 +25,7 @@ import java.util.HashMap;
 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;
@@ -42,11 +43,13 @@ import org.openhab.binding.lifx.internal.LifxLightStateChanger;
 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;
@@ -94,6 +97,7 @@ public class LifxLightHandler extends BaseThingHandler {
     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;
@@ -191,6 +195,14 @@ public class LifxLightHandler extends BaseThingHandler {
             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())) {
@@ -263,6 +275,8 @@ public class LifxLightHandler extends BaseThingHandler {
             if (speed != null) {
                 effectFlameSpeed = speed;
             }
+            hevCycleDuration = getHevCycleDuration();
+
             channelStates.clear();
             currentLightState = new CurrentLightState();
             pendingLightState = new LifxLightState();
@@ -410,6 +424,19 @@ public class LifxLightHandler extends BaseThingHandler {
         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();
 
@@ -486,121 +513,129 @@ public class LifxLightHandler extends BaseThingHandler {
     @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);
@@ -612,6 +647,36 @@ public class LifxLightHandler extends BaseThingHandler {
         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);
@@ -688,6 +753,11 @@ public class LifxLightHandler extends BaseThingHandler {
         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);
     }
index fab89fa7f1984f881271dfc71f8c33a01ccb5c3d..865c33fede9087e2dafa4e48032c23563b08e688 100644 (file)
@@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 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;
@@ -45,6 +46,14 @@ public interface LifxLightStateListener {
      */
     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.
      *
index b2f662f94f3ab8ef3ab33a77984a72131a9de189..13ed432e1ebe93e153d36c939dd3f23f123cdc4b 100644 (file)
                </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>
index 1cc88dac0005a85ca059272348a4415094eb2bc4..c4f511d6ac9378a360cd095ea8bc33ed5690c077 100644 (file)
@@ -4,16 +4,18 @@ binding.lifx.description = This is the binding for LIFX lights.
 
 # 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
@@ -21,30 +23,34 @@ channel-type.lifx.brightness.label = Brightness
 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.
index 1a6afa243d36db286d48cfb646e3ea87c01e88b8..c21aab2872b98d94f09f667fd30dabbe4705f09c 100644 (file)
@@ -4,15 +4,17 @@ binding.lifx.description = Dit is de binding voor LIFX lampen.
 
 # 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).
 
@@ -21,24 +23,34 @@ channel-type.lifx.brightness.label = Helderheid
 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.
index 5536a9ec2eee829b0c18b06292dfbf1b20d11c9a..4e5260d4b4ceab7001cbf7fd17fd743cc2ad0799 100644 (file)
                </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>
diff --git a/bundles/org.openhab.binding.lifx/src/main/resources/OH-INF/thing/colorhevlight.xml b/bundles/org.openhab.binding.lifx/src/main/resources/OH-INF/thing/colorhevlight.xml
new file mode 100644 (file)
index 0000000..9506454
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>