Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mqtt.homeassistant] Support color temp on JSON schema lights #14839

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
import org.openhab.binding.mqtt.generic.values.TextValue;
import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComponentException;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
Expand All @@ -47,6 +50,7 @@
@NonNullByDefault
public class JSONSchemaLight extends AbstractRawSchemaLight {
private static final BigDecimal SCALE_FACTOR = new BigDecimal("2.55"); // string to not lose precision
private static final BigDecimal BIG_DECIMAL_HUNDRED = new BigDecimal(100);

private final Logger logger = LoggerFactory.getLogger(JSONSchemaLight.class);

Expand All @@ -67,14 +71,23 @@ protected static class Color {
protected @Nullable Integer transition;
}

TextValue colorModeValue;

public JSONSchemaLight(ComponentFactory.ComponentConfiguration builder) {
super(builder);
colorModeValue = new TextValue();
}

@Override
protected void buildChannels() {
List<LightColorMode> supportedColorModes = channelConfiguration.supportedColorModes;
if (supportedColorModes != null && supportedColorModes.contains(LightColorMode.COLOR_MODE_COLOR_TEMP)) {
colorModeValue = new TextValue(
supportedColorModes.stream().map(LightColorMode::serializedName).toArray(String[]::new));
buildChannel(COLOR_MODE_CHANNEL_ID, colorModeValue, "Color Mode", this).isAdvanced(true).build();
ccutrer marked this conversation as resolved.
Show resolved Hide resolved
}

if (channelConfiguration.colorMode) {
List<LightColorMode> supportedColorModes = channelConfiguration.supportedColorModes;
if (supportedColorModes == null || channelConfiguration.supportedColorModes.isEmpty()) {
throw new UnsupportedComponentException("JSON schema light with color modes '" + getHaID()
+ "' does not define supported_color_modes!");
Expand All @@ -83,6 +96,12 @@ protected void buildChannels() {
if (LightColorMode.hasColorChannel(supportedColorModes)) {
hasColorChannel = true;
}

if (supportedColorModes.contains(LightColorMode.COLOR_MODE_COLOR_TEMP)) {
buildChannel(COLOR_TEMP_CHANNEL_ID, colorTempValue, "Color Temperature", this)
.commandTopic(DUMMY_TOPIC, true, 1).commandFilter(command -> handleColorTempCommand(command))
.build();
}
}

if (hasColorChannel) {
Expand Down Expand Up @@ -118,16 +137,16 @@ protected void publishState(HSBType state) {
json.color = new JSONState.Color();
if (channelConfiguration.supportedColorModes.contains(LightColorMode.COLOR_MODE_HS)) {
json.color.h = state.getHue().toBigDecimal();
json.color.s = state.getSaturation().toBigDecimal();
json.color.s = state.getSaturation().toBigDecimal().divide(BIG_DECIMAL_HUNDRED);
} else if (LightColorMode.hasRGB(Objects.requireNonNull(channelConfiguration.supportedColorModes))) {
var rgb = state.toRGB();
json.color.r = rgb[0].toBigDecimal().multiply(SCALE_FACTOR).intValue();
json.color.g = rgb[1].toBigDecimal().multiply(SCALE_FACTOR).intValue();
json.color.b = rgb[2].toBigDecimal().multiply(SCALE_FACTOR).intValue();
} else { // if (channelConfiguration.supportedColorModes.contains(COLOR_MODE_XY))
var xy = state.toXY();
json.color.x = xy[0].toBigDecimal();
json.color.y = xy[1].toBigDecimal();
json.color.x = xy[0].toBigDecimal().divide(BIG_DECIMAL_HUNDRED);
json.color.y = xy[1].toBigDecimal().divide(BIG_DECIMAL_HUNDRED);
}
}
}
Expand Down Expand Up @@ -163,6 +182,30 @@ protected boolean handleCommand(Command command) {
return false;
}

private boolean handleColorTempCommand(Command command) {
JSONState json = new JSONState();

if (command instanceof DecimalType) {
command = new QuantityType<>(((DecimalType) command).toBigDecimal(), Units.MIRED);
}
if (command instanceof QuantityType) {
QuantityType<?> mireds = ((QuantityType<?>) command).toInvertibleUnit(Units.MIRED);
if (mireds == null) {
logger.warn("Unable to convert {} to mireds", command);
return false;
}
json.state = "ON";
json.colorTemp = mireds.toBigDecimal().intValue();
} else {
return false;
}

String jsonCommand = getGson().toJson(json);
logger.debug("Publishing new state '{}' of light {} to MQTT.", jsonCommand, getName());
rawChannel.getState().publishValue(new StringType(jsonCommand));
return false;
}

@Override
public void updateChannelState(ChannelUID channel, State state) {
ChannelStateUpdateListener listener = this.channelStateUpdateListener;
Expand Down Expand Up @@ -204,6 +247,14 @@ public void updateChannelState(ChannelUID channel, State state) {
}
}

if (jsonState.colorTemp != null) {
colorTempValue.update(new QuantityType(Objects.requireNonNull(jsonState.colorTemp), Units.MIRED));
listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_TEMP_CHANNEL_ID),
colorTempValue.getChannelState());

colorModeValue.update(new StringType(LightColorMode.COLOR_MODE_COLOR_TEMP.serializedName()));
}

if (jsonState.color != null) {
PercentType brightness = brightnessValue.getChannelState() instanceof PercentType
? (PercentType) brightnessValue.getChannelState()
Expand All @@ -216,14 +267,24 @@ public void updateChannelState(ChannelUID channel, State state) {
if (jsonState.color.h != null && jsonState.color.s != null) {
colorValue.update(new HSBType(new DecimalType(Objects.requireNonNull(jsonState.color.h)),
new PercentType(Objects.requireNonNull(jsonState.color.s)), brightness));
colorModeValue.update(new StringType(LightColorMode.COLOR_MODE_HS.serializedName()));
} else if (jsonState.color.x != null && jsonState.color.y != null) {
HSBType newColor = HSBType.fromXY(jsonState.color.x.floatValue(), jsonState.color.y.floatValue());
colorValue.update(new HSBType(newColor.getHue(), newColor.getSaturation(), brightness));
colorModeValue.update(new StringType(LightColorMode.COLOR_MODE_XY.serializedName()));
} else if (jsonState.color.r != null && jsonState.color.g != null && jsonState.color.b != null) {
colorValue.update(HSBType.fromRGB(jsonState.color.r, jsonState.color.g, jsonState.color.b));
colorModeValue.update(new StringType(LightColorMode.COLOR_MODE_RGB.serializedName()));
}
}

if (jsonState.colorMode != null) {
colorModeValue.update(new StringType(jsonState.colorMode.serializedName()));
}

listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_MODE_CHANNEL_ID),
colorModeValue.getChannelState());

if (hasColorChannel) {
listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID), colorValue.getChannelState());
} else if (brightnessChannel != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,13 @@ public static boolean hasColorChannel(List<LightColorMode> supportedColorModes)
public static boolean hasRGB(List<LightColorMode> supportedColorModes) {
return WITH_RGB.stream().anyMatch(cm -> supportedColorModes.contains(cm));
}

public String serializedName() {
try {
return LightColorMode.class.getDeclaredField(toString()).getAnnotation(SerializedName.class).value();
} catch (NoSuchFieldException e) {
// can't happen
throw new IllegalStateException(e);
}
}
}