Skip to content

Commit

Permalink
[lifx] Support HEV clean cycle (openhab#11262)
Browse files Browse the repository at this point in the history
* Implement HEV packets
* Add colorhevlight thing type with a hevcycle channel
* Update documentation

Signed-off-by: Wouter Born <github@maindrain.net>
  • Loading branch information
wborn authored and thinkingstone committed Nov 7, 2021
1 parent d1d3e92 commit 2d09d57
Show file tree
Hide file tree
Showing 25 changed files with 1,140 additions and 215 deletions.
131 changes: 78 additions & 53 deletions bundles/org.openhab.binding.lifx/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Expand All @@ -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";
Expand All @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@
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;
Expand Down Expand Up @@ -133,6 +136,9 @@ public void stop() {
private void sendLightStateRequests() {
communicationHandler.sendPacket(new GetRequest());

if (features.hasFeature(HEV)) {
communicationHandler.sendPacket(new GetHevCycleRequest());
}
if (features.hasFeature(INFRARED)) {
communicationHandler.sendPacket(new GetLightInfraredRequest());
}
Expand All @@ -157,14 +163,16 @@ public void handleResponsePacket(Packet packet) {
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();
Expand Down Expand Up @@ -192,6 +200,11 @@ private void handleLightPowerStatus(StateLightPowerResponse packet) {
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);
Expand All @@ -209,11 +222,11 @@ private void handleMultiZoneStatus(StateMultiZoneResponse packet) {
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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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;
Expand All @@ -40,6 +41,7 @@
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;
Expand All @@ -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();
Expand All @@ -76,6 +79,10 @@ public HSBK[] getColors() {
return colorsCopy;
}

public @Nullable HevCycleState getHevCycleState() {
return hevCycleState;
}

public @Nullable PercentType getInfrared() {
return infrared;
}
Expand Down Expand Up @@ -158,6 +165,13 @@ public void setTemperature(int kelvin, int zoneIndex) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@
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;
Expand Down Expand Up @@ -308,6 +311,17 @@ public void handlePowerStateChange(@Nullable PowerState oldPowerState, PowerStat
}
}

@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();
Expand All @@ -325,7 +339,7 @@ public void handleSignalStrengthChange(@Nullable SignalStrength oldSignalStrengt

@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);
}
Expand Down Expand Up @@ -360,6 +374,13 @@ public void handleResponsePacket(Packet 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,12 +371,14 @@ public Features getFeatures(String version) {

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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 };
}
}
Original file line number Diff line number Diff line change
@@ -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 };
}
}
Loading

0 comments on commit 2d09d57

Please sign in to comment.