2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.mqtt.homeassistant.internal.component;
15 import java.math.BigDecimal;
16 import java.util.List;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
21 import org.openhab.binding.mqtt.generic.mapping.ColorMode;
22 import org.openhab.binding.mqtt.generic.values.ColorValue;
23 import org.openhab.binding.mqtt.generic.values.NumberValue;
24 import org.openhab.binding.mqtt.generic.values.OnOffValue;
25 import org.openhab.binding.mqtt.generic.values.PercentageValue;
26 import org.openhab.binding.mqtt.generic.values.TextValue;
27 import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel;
28 import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
29 import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComponentException;
30 import org.openhab.core.library.unit.Units;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.types.Command;
34 import com.google.gson.annotations.SerializedName;
37 * A MQTT light, following the
38 * https://www.home-assistant.io/components/light.mqtt/ specification.
40 * Individual concrete classes implement the differing semantics of the
41 * three different schemas.
43 * As of now, only on/off, brightness, and RGB are fully implemented and tested.
44 * HS and XY are implemented, but not tested. Color temp is only
45 * implemented (but not tested) for the default schema.
47 * @author David Graeff - Initial contribution
48 * @author Cody Cutrer - Re-write for (nearly) full support
51 public abstract class Light extends AbstractComponent<Light.ChannelConfiguration>
52 implements ChannelStateUpdateListener {
53 protected static final String DEFAULT_SCHEMA = "default";
54 protected static final String JSON_SCHEMA = "json";
55 protected static final String TEMPLATE_SCHEMA = "template";
57 protected static final String STATE_CHANNEL_ID = "state";
58 protected static final String ON_OFF_CHANNEL_ID = "on_off";
59 protected static final String BRIGHTNESS_CHANNEL_ID = "brightness";
60 protected static final String COLOR_MODE_CHANNEL_ID = "color_mode";
61 protected static final String COLOR_TEMP_CHANNEL_ID = "color_temp";
62 protected static final String EFFECT_CHANNEL_ID = "effect";
63 // This channel is a synthetic channel that may send to other channels
65 protected static final String COLOR_CHANNEL_ID = "color";
67 protected static final String DUMMY_TOPIC = "dummy";
69 protected static final String ON_COMMAND_TYPE_FIRST = "first";
70 protected static final String ON_COMMAND_TYPE_BRIGHTNESS = "brightness";
71 protected static final String ON_COMMAND_TYPE_LAST = "last";
74 * Configuration class for MQTT component
76 static class ChannelConfiguration extends AbstractChannelConfiguration {
77 ChannelConfiguration() {
81 /* Attributes that control the basic structure of the light */
83 protected String schema = DEFAULT_SCHEMA;
84 protected @Nullable Boolean optimistic; // All schemas
85 protected boolean brightness = false; // JSON schema only
86 @SerializedName("supported_color_modes")
87 protected @Nullable List<LightColorMode> supportedColorModes; // JSON schema only
88 // Defines when on the payload_on is sent. Using last (the default) will send
89 // any style (brightness, color, etc)
90 // topics first and then a payload_on to the command_topic. Using first will
91 // send the payload_on and then any
92 // style topics. Using brightness will only send brightness commands instead of
93 // the payload_on to turn the light
95 @SerializedName("on_command_type")
96 protected String onCommandType = ON_COMMAND_TYPE_LAST; // Default schema only
98 /* Basic control attributes */
100 @SerializedName("state_topic")
101 protected @Nullable String stateTopic; // All Schemas
102 @SerializedName("state_value_template")
103 protected @Nullable String stateValueTemplate; // Default schema only
104 @SerializedName("state_template")
105 protected @Nullable String stateTemplate; // Template schema only
106 @SerializedName("payload_on")
107 protected String payloadOn = "ON"; // Default schema only
108 @SerializedName("payload_off")
109 protected String payloadOff = "OFF"; // Default schema only
110 @SerializedName("command_topic")
111 protected @Nullable String commandTopic; // All schemas
112 @SerializedName("command_on_template")
113 protected @Nullable String commandOnTemplate; // Template schema only; required
114 @SerializedName("command_off_template")
115 protected @Nullable String commandOffTemplate; // Template schema only; required
117 /* Brightness attributes */
119 @SerializedName("brightness_scale")
120 protected int brightnessScale = 255; // Default, JSON schemas only
121 @SerializedName("brightness_state_topic")
122 protected @Nullable String brightnessStateTopic; // Default schema only
123 @SerializedName("brightness_value_template")
124 protected @Nullable String brightnessValueTemplate; // Default schema only
125 @SerializedName("brightness_template")
126 protected @Nullable String brightnessTemplate; // Template schema only
127 @SerializedName("brightness_command_topic")
128 protected @Nullable String brightnessCommandTopic; // Default schema only
129 @SerializedName("brightness_command_template")
130 protected @Nullable String brightnessCommandTemplate; // Default schema only
132 /* White value attributes */
134 @SerializedName("white_scale")
135 protected int whiteScale = 255; // Default, JSON schemas only
136 @SerializedName("white_command_topic")
137 protected @Nullable String whiteCommandTopic; // Default schema only
139 /* Color mode attributes */
141 @SerializedName("color_mode_state_topic")
142 protected @Nullable String colorModeStateTopic; // Default schema only
143 @SerializedName("color_mode_value_template")
144 protected @Nullable String colorModeValueTemplate; // Default schema only
146 /* Color temp attributes */
148 @SerializedName("min_mireds")
149 protected @Nullable Integer minMireds; // All schemas
150 @SerializedName("max_mireds")
151 protected @Nullable Integer maxMireds; // All schemas
152 @SerializedName("color_temp_state_topic")
153 protected @Nullable String colorTempStateTopic; // Default schema only
154 @SerializedName("color_temp_value_template")
155 protected @Nullable String colorTempValueTemplate; // Default schema only
156 @SerializedName("color_temp_template")
157 protected @Nullable String colorTempTemplate; // Template schema only
158 @SerializedName("color_temp_command_topic")
159 protected @Nullable String colorTempCommandTopic; // Default schema only
160 @SerializedName("color_temp_command_template")
161 protected @Nullable String colorTempCommandTemplate; // Default schema only
163 /* Effect attributes */
164 @SerializedName("effect_list")
165 protected @Nullable List<String> effectList; // All schemas
166 @SerializedName("effect_state_topic")
167 protected @Nullable String effectStateTopic; // Default schema only
168 @SerializedName("effect_value_template")
169 protected @Nullable String effectValueTemplate; // Default schema only
170 @SerializedName("effect_template")
171 protected @Nullable String effectTemplate; // Template schema only
172 @SerializedName("effect_command_topic")
173 protected @Nullable String effectCommandTopic; // Default schema only
174 @SerializedName("effect_command_template")
175 protected @Nullable String effectCommandTemplate; // Default schema only
178 @SerializedName("hs_state_topic")
179 protected @Nullable String hsStateTopic; // Default schema only
180 @SerializedName("hs_value_template")
181 protected @Nullable String hsValueTemplate; // Default schema only
182 @SerializedName("hs_command_topic")
183 protected @Nullable String hsCommandTopic; // Default schema only
186 @SerializedName("rgb_state_topic")
187 protected @Nullable String rgbStateTopic; // Default schema only
188 @SerializedName("rgb_value_template")
189 protected @Nullable String rgbValueTemplate; // Default schema only
190 @SerializedName("red_template")
191 protected @Nullable String redTemplate; // Template schema only
192 @SerializedName("green_template")
193 protected @Nullable String greenTemplate; // Template schema only
194 @SerializedName("blue_template")
195 protected @Nullable String blueTemplate; // Template schema only
196 @SerializedName("rgb_command_topic")
197 protected @Nullable String rgbCommandTopic; // Default schema only
198 @SerializedName("rgb_command_template")
199 protected @Nullable String rgbCommandTemplate; // Default schema only
201 /* RGBW attributes */
202 @SerializedName("rgbw_state_topic")
203 protected @Nullable String rgbwStateTopic; // Default schema only
204 @SerializedName("rgbw_value_template")
205 protected @Nullable String rgbwValueTemplate; // Default schema only
206 @SerializedName("rgbw_command_topic")
207 protected @Nullable String rgbwCommandTopic; // Default schema only
208 @SerializedName("rgbw_command_template")
209 protected @Nullable String rgbwCommandTemplate; // Default schema only
211 /* RGBWW attributes */
212 @SerializedName("rgbww_state_topic")
213 protected @Nullable String rgbwwStateTopic; // Default schema only
214 @SerializedName("rgbww_value_template")
215 protected @Nullable String rgbwwValueTemplate; // Default schema only
216 @SerializedName("rgbww_command_topic")
217 protected @Nullable String rgbwwCommandTopic; // Default schema only
218 @SerializedName("rgbww_command_template")
219 protected @Nullable String rgbwwCommandTemplate; // Default schema only
222 @SerializedName("xy_command_topic")
223 protected @Nullable String xyCommandTopic; // Default schema only
224 @SerializedName("xy_state_topic")
225 protected @Nullable String xyStateTopic; // Default schema only
226 @SerializedName("xy_value_template")
227 protected @Nullable String xyValueTemplate; // Default schema only
230 protected final boolean optimistic;
231 protected boolean hasColorChannel = false;
233 protected @Nullable ComponentChannel onOffChannel;
234 protected @Nullable ComponentChannel brightnessChannel;
236 // State has to be stored here, in order to mux multiple
237 // MQTT sources into single OpenHAB channels
238 protected OnOffValue onOffValue;
239 protected PercentageValue brightnessValue;
240 protected final NumberValue colorTempValue;
241 protected final @Nullable TextValue effectValue;
242 protected final ColorValue colorValue = new ColorValue(ColorMode.HSB, null, null, 100);
244 protected final ChannelStateUpdateListener channelStateUpdateListener;
246 public static Light create(ComponentFactory.ComponentConfiguration builder, boolean newStyleChannels)
247 throws UnsupportedComponentException {
248 String schema = builder.getConfig(ChannelConfiguration.class).schema;
251 return new DefaultSchemaLight(builder, newStyleChannels);
253 return new JSONSchemaLight(builder, newStyleChannels);
254 case TEMPLATE_SCHEMA:
255 return new TemplateSchemaLight(builder, newStyleChannels);
257 throw new UnsupportedComponentException(
258 "Component '" + builder.getHaID() + "' of schema '" + schema + "' is not supported!");
262 protected Light(ComponentFactory.ComponentConfiguration builder, boolean newStyleChannels) {
263 super(builder, ChannelConfiguration.class, newStyleChannels);
264 this.channelStateUpdateListener = builder.getUpdateListener();
267 Boolean optimistic = channelConfiguration.optimistic;
268 if (optimistic != null) {
269 this.optimistic = optimistic;
271 this.optimistic = (channelConfiguration.stateTopic == null);
274 onOffValue = new OnOffValue(channelConfiguration.payloadOn, channelConfiguration.payloadOff);
275 brightnessValue = new PercentageValue(null, new BigDecimal(channelConfiguration.brightnessScale), null, null,
278 List<String> effectList = channelConfiguration.effectList;
279 if (effectList != null) {
280 effectValue = new TextValue(effectList.toArray(new String[0]));
285 BigDecimal min = null, max = null;
286 if (channelConfiguration.minMireds != null) {
287 min = new BigDecimal(channelConfiguration.minMireds);
289 if (channelConfiguration.maxMireds != null) {
290 max = new BigDecimal(channelConfiguration.maxMireds);
292 colorTempValue = new NumberValue(min, max, BigDecimal.ONE, Units.MIRED);
297 protected abstract void buildChannels();
300 public void postChannelCommand(ChannelUID channelUID, Command value) {
301 throw new UnsupportedOperationException();
305 public void triggerChannel(ChannelUID channelUID, String eventPayload) {
306 throw new UnsupportedOperationException();