[mqtt.homeassistant] implement effect channel for light (#15914)

Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
Cody Cutrer 2023-11-19 09:52:51 -07:00 committed by GitHub
parent dcf4255b98
commit 44e32d3fbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 4 deletions

View File

@ -98,8 +98,9 @@ public class DefaultSchemaLight extends Light {
.build();
}
if (channelConfiguration.effectStateTopic != null || channelConfiguration.effectCommandTopic != null) {
buildChannel(EFFECT_CHANNEL_ID, effectValue, "Lighting effect", this)
if (effectValue != null
&& (channelConfiguration.effectStateTopic != null || channelConfiguration.effectCommandTopic != null)) {
buildChannel(EFFECT_CHANNEL_ID, Objects.requireNonNull(effectValue), "Lighting Effect", this)
.stateTopic(channelConfiguration.effectStateTopic, channelConfiguration.effectValueTemplate)
.commandTopic(channelConfiguration.effectCommandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())

View File

@ -114,6 +114,22 @@ public class JSONSchemaLight extends AbstractRawSchemaLight {
onOffChannel = buildChannel(ON_OFF_CHANNEL_ID, onOffValue, "On/Off State", this)
.commandTopic(DUMMY_TOPIC, true, 1).commandFilter(this::handleCommand).build();
}
if (effectValue != null) {
buildChannel(EFFECT_CHANNEL_ID, Objects.requireNonNull(effectValue), "Lighting Effect", this)
.commandTopic(DUMMY_TOPIC, true, 1).commandFilter(command -> handleEffectCommand(command)).build();
}
}
private boolean handleEffectCommand(Command command) {
if (command instanceof StringType) {
JSONState json = new JSONState();
json.state = "ON";
json.effect = command.toString();
publishState(json);
}
return false;
}
@Override
@ -151,6 +167,10 @@ public class JSONSchemaLight extends AbstractRawSchemaLight {
}
}
publishState(json);
}
private void publishState(JSONState json) {
String command = getGson().toJson(json);
logger.debug("Publishing new state '{}' of light {} to MQTT.", command, getName());
rawChannel.getState().publishValue(new StringType(command));
@ -224,6 +244,15 @@ public class JSONSchemaLight extends AbstractRawSchemaLight {
return;
}
if (effectValue != null) {
if (jsonState.effect != null) {
effectValue.update(new StringType(jsonState.effect));
listener.updateChannelState(buildChannelUID(EFFECT_CHANNEL_ID), effectValue.getChannelState());
} else {
listener.updateChannelState(buildChannelUID(EFFECT_CHANNEL_ID), UnDefType.NULL);
}
}
if (jsonState.state != null) {
onOffValue.update(onOffValue.parseCommand(new StringType(jsonState.state)));
if (brightnessValue.getChannelState() instanceof UnDefType) {

View File

@ -47,7 +47,7 @@ import com.google.gson.annotations.SerializedName;
* three different schemas.
*
* As of now, only on/off, brightness, and RGB are fully implemented and tested.
* HS and XY are implemented, but not tested. Color temp and effect are only
* HS and XY are implemented, but not tested. Color temp is only
* implemented (but not tested) for the default schema.
*
* @author David Graeff - Initial contribution
@ -246,7 +246,7 @@ public abstract class Light extends AbstractComponent<Light.ChannelConfiguration
protected OnOffValue onOffValue;
protected PercentageValue brightnessValue;
protected final NumberValue colorTempValue;
protected final TextValue effectValue = new TextValue();
protected final @Nullable TextValue effectValue;
protected final ColorValue colorValue = new ColorValue(ColorMode.HSB, null, null, 100);
protected final List<ComponentChannel> hiddenChannels = new ArrayList<>();
@ -281,6 +281,13 @@ public abstract class Light extends AbstractComponent<Light.ChannelConfiguration
brightnessValue = new PercentageValue(null, new BigDecimal(channelConfiguration.brightnessScale), null, null,
null);
@Nullable
List<String> effectList = channelConfiguration.effectList;
if (effectList != null) {
effectValue = new TextValue(effectList.toArray(new String[0]));
} else {
effectValue = null;
}
@Nullable
BigDecimal min = null, max = null;
if (channelConfiguration.minMireds != null) {
min = new BigDecimal(channelConfiguration.minMireds);

View File

@ -27,11 +27,13 @@ import org.junit.jupiter.api.Test;
import org.openhab.binding.mqtt.generic.values.ColorValue;
import org.openhab.binding.mqtt.generic.values.OnOffValue;
import org.openhab.binding.mqtt.generic.values.PercentageValue;
import org.openhab.binding.mqtt.generic.values.TextValue;
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel;
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.StringType;
/**
* Tests for {@link Light} confirming to the default schema
@ -285,6 +287,40 @@ public class DefaultSchemaLightTests extends AbstractComponentTests {
assertPublished("zigbee2mqtt/light/set/state", "OFF_");
}
@Test
public void testOnOffWithEffect() throws InterruptedException {
// @formatter:off
var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
"{ " +
" \"name\": \"light\", " +
" \"state_topic\": \"zigbee2mqtt/light/state\", " +
" \"command_topic\": \"zigbee2mqtt/light/set/state\", " +
" \"effect_command_topic\": \"zigbee2mqtt/light/set/effect\", " +
" \"state_value_template\": \"{{ value_json.power }}\", " +
" \"effect_list\": [\"party\", \"rainbow\"]," +
" \"effect_state_topic\": \"zigbee2mqtt/light/effect\"" +
"}");
// @formatter:on
assertThat(component.channels.size(), is(2));
assertThat(component.getName(), is("light"));
assertChannel(component, Light.ON_OFF_CHANNEL_ID, "zigbee2mqtt/light/state", "zigbee2mqtt/light/set/state",
"On/Off State", OnOffValue.class);
assertChannel(component, Light.EFFECT_CHANNEL_ID, "zigbee2mqtt/light/effect", "zigbee2mqtt/light/set/effect",
"Lighting Effect", TextValue.class);
publishMessage("zigbee2mqtt/light/state", "{\"power\": \"ON\"}");
assertState(component, Light.ON_OFF_CHANNEL_ID, OnOffType.ON);
publishMessage("zigbee2mqtt/light/effect", "party");
assertState(component, Light.EFFECT_CHANNEL_ID, new StringType("party"));
publishMessage("zigbee2mqtt/light/state", "{\"power\": \"OFF\"}");
assertState(component, Light.ON_OFF_CHANNEL_ID, OnOffType.OFF);
sendCommand(component, Light.EFFECT_CHANNEL_ID, new StringType("rainbow"));
assertPublished("zigbee2mqtt/light/set/effect", "rainbow");
}
@Override
protected Set<String> getConfigTopics() {
return Set.of(CONFIG_TOPIC);