2 * Copyright (c) 2010-2021 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.yeelight.internal.handler;
15 import static org.openhab.binding.yeelight.internal.YeelightBindingConstants.*;
17 import java.util.concurrent.TimeUnit;
19 import org.openhab.binding.yeelight.internal.lib.device.ConnectState;
20 import org.openhab.binding.yeelight.internal.lib.device.DeviceBase;
21 import org.openhab.binding.yeelight.internal.lib.device.DeviceFactory;
22 import org.openhab.binding.yeelight.internal.lib.device.DeviceStatus;
23 import org.openhab.binding.yeelight.internal.lib.enums.DeviceAction;
24 import org.openhab.binding.yeelight.internal.lib.enums.DeviceMode;
25 import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
26 import org.openhab.binding.yeelight.internal.lib.listeners.DeviceConnectionStateListener;
27 import org.openhab.binding.yeelight.internal.lib.listeners.DeviceStatusChangeListener;
28 import org.openhab.binding.yeelight.internal.lib.services.DeviceManager;
29 import org.openhab.core.library.types.*;
30 import org.openhab.core.thing.*;
31 import org.openhab.core.thing.binding.BaseThingHandler;
32 import org.openhab.core.types.Command;
33 import org.openhab.core.types.RefreshType;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * The {@link YeelightHandlerBase} is responsible for handling commands, which are
39 * sent to one of the channels.
41 * @author Coaster Li - Initial contribution
42 * @author Joe Ho - Added Duration Thing parameter
43 * @author Nikita Pogudalov - Added DeviceType for ceiling 1
45 public abstract class YeelightHandlerBase extends BaseThingHandler
46 implements DeviceConnectionStateListener, DeviceStatusChangeListener {
48 private final Logger logger = LoggerFactory.getLogger(YeelightHandlerBase.class);
49 protected DeviceBase mDevice;
51 // Reading the deviceId from the properties map.
52 private String deviceId = getThing().getConfiguration().get(PARAMETER_DEVICE_ID).toString();
54 public YeelightHandlerBase(Thing thing) {
58 protected void updateUI(DeviceStatus status) {
62 public void initialize() {
63 logger.debug("Initializing, Device ID: {}", deviceId);
64 mDevice = DeviceFactory.build(getDeviceModel(getThing().getThingTypeUID()).name(), deviceId);
65 mDevice.setDeviceName(getThing().getLabel());
66 mDevice.setAutoConnect(true);
67 DeviceManager.getInstance().addDevice(mDevice);
68 mDevice.registerConnectStateListener(this);
69 mDevice.registerStatusChangedListener(this);
70 updateStatusHelper(mDevice.getConnectionState());
71 DeviceManager.getInstance().startDiscovery(15 * 1000);
74 private DeviceType getDeviceModel(ThingTypeUID typeUID) {
75 if (typeUID.equals(THING_TYPE_CEILING)) {
76 return DeviceType.ceiling;
77 } else if (typeUID.equals(THING_TYPE_CEILING1)) {
78 return DeviceType.ceiling1;
79 } else if (typeUID.equals(THING_TYPE_CEILING3)) {
80 return DeviceType.ceiling3;
81 } else if (typeUID.equals(THING_TYPE_CEILING4)) {
82 return DeviceType.ceiling4;
83 } else if (typeUID.equals(THING_TYPE_WONDER)) {
84 return DeviceType.color;
85 } else if (typeUID.equals(THING_TYPE_DOLPHIN)) {
86 return DeviceType.mono;
87 } else if (typeUID.equals(THING_TYPE_CTBULB)) {
88 return DeviceType.ct_bulb;
89 } else if (typeUID.equals(THING_TYPE_STRIPE)) {
90 return DeviceType.stripe;
91 } else if (typeUID.equals(THING_TYPE_DESKLAMP)) {
92 return DeviceType.desklamp;
99 public void onConnectionStateChanged(ConnectState connectState) {
100 logger.debug("onConnectionStateChanged -> {}", connectState.name());
101 updateStatusHelper(connectState);
104 public void updateStatusHelper(ConnectState connectState) {
105 switch (connectState) {
107 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Device is offline!");
108 if (mDevice.isAutoConnect()) {
109 DeviceManager.sInstance.startDiscovery(5 * 1000);
110 logger.debug("Thing OFFLINE. Initiated discovery");
114 updateStatus(ThingStatus.ONLINE);
115 mDevice.queryStatus();
118 updateStatus(ThingStatus.UNKNOWN);
124 public void channelLinked(ChannelUID channelUID) {
125 logger.debug("ChannelLinked -> {}", channelUID.getId());
126 super.channelLinked(channelUID);
128 Runnable task = () -> {
129 mDevice.queryStatus();
131 scheduler.schedule(task, 500, TimeUnit.MILLISECONDS);
134 public void handleCommandHelper(ChannelUID channelUID, Command command, String logInfo) {
135 logger.debug("{}: {}", logInfo, command);
137 // If device is disconnected, start discovery to reconnect.
138 if (mDevice.isAutoConnect() && mDevice.getConnectionState() != ConnectState.CONNECTED) {
139 DeviceManager.getInstance().startDiscovery(5 * 1000);
141 if (command instanceof RefreshType) {
142 logger.debug("Refresh channel: {} Command: {}", channelUID, command);
144 DeviceManager.getInstance().startDiscovery(5 * 1000);
145 DeviceStatus s = mDevice.getDeviceStatus();
147 switch (channelUID.getId()) {
148 case CHANNEL_BRIGHTNESS:
149 updateState(channelUID, new PercentType(s.getBrightness()));
152 updateState(channelUID, HSBType.fromRGB(s.getR(), s.getG(), s.getB()));
154 case CHANNEL_COLOR_TEMPERATURE:
155 updateState(channelUID, new PercentType(s.getCt()));
157 case CHANNEL_BACKGROUND_COLOR:
158 final HSBType hsbType = new HSBType(new DecimalType(s.getHue()), new PercentType(s.getSat()),
159 new PercentType(s.getBackgroundBrightness()));
160 updateState(channelUID, hsbType);
167 switch (channelUID.getId()) {
168 case CHANNEL_BRIGHTNESS:
169 if (command instanceof PercentType) {
170 handlePercentMessage((PercentType) command);
171 } else if (command instanceof OnOffType) {
172 handleOnOffCommand((OnOffType) command);
173 } else if (command instanceof IncreaseDecreaseType) {
174 handleIncreaseDecreaseBrightnessCommand((IncreaseDecreaseType) command);
178 if (command instanceof HSBType) {
179 HSBType hsbCommand = (HSBType) command;
180 if (hsbCommand.getBrightness().intValue() == 0) {
181 handleOnOffCommand(OnOffType.OFF);
183 handleHSBCommand(hsbCommand);
185 } else if (command instanceof PercentType) {
186 handlePercentMessage((PercentType) command);
187 } else if (command instanceof OnOffType) {
188 handleOnOffCommand((OnOffType) command);
189 } else if (command instanceof IncreaseDecreaseType) {
190 handleIncreaseDecreaseBrightnessCommand((IncreaseDecreaseType) command);
193 case CHANNEL_COLOR_TEMPERATURE:
194 if (command instanceof PercentType) {
195 handleColorTemperatureCommand((PercentType) command);
196 } else if (command instanceof IncreaseDecreaseType) {
197 handleIncreaseDecreaseBrightnessCommand((IncreaseDecreaseType) command);
201 case CHANNEL_BACKGROUND_COLOR:
202 if (command instanceof HSBType) {
203 HSBType hsbCommand = (HSBType) command;
204 handleBackgroundHSBCommand(hsbCommand);
205 } else if (command instanceof PercentType) {
206 handleBackgroundBrightnessPercentMessage((PercentType) command);
207 } else if (command instanceof OnOffType) {
208 handleBackgroundOnOffCommand((OnOffType) command);
211 case CHANNEL_NIGHTLIGHT:
212 if (command instanceof OnOffType) {
213 DeviceAction pAction = command == OnOffType.ON ? DeviceAction.nightlight_on
214 : DeviceAction.nightlight_off;
215 pAction.putDuration(getDuration());
216 DeviceManager.getInstance().doAction(deviceId, pAction);
219 case CHANNEL_COMMAND:
220 if (!command.toString().isEmpty()) {
221 String[] tokens = command.toString().split(";");
222 String methodAction = tokens[0];
223 String methodParams = "";
224 if (tokens.length > 1) {
225 methodParams = tokens[1];
227 logger.debug("{}: {} {}", logInfo, methodAction, methodParams);
228 handleCustomCommand(methodAction, methodParams);
229 updateState(channelUID, new StringType(""));
237 void handlePercentMessage(PercentType brightness) {
238 DeviceAction pAction;
239 if (brightness.intValue() == 0) {
240 pAction = DeviceAction.close;
241 pAction.putDuration(getDuration());
242 DeviceManager.getInstance().doAction(deviceId, pAction);
244 if (mDevice.getDeviceStatus().isPowerOff()) {
245 pAction = DeviceAction.open;
246 // hard coded to fast open, the duration should apply to brightness increase only
247 pAction.putDuration(0);
248 DeviceManager.getInstance().doAction(deviceId, pAction);
250 pAction = DeviceAction.brightness;
251 pAction.putValue(brightness.intValue());
252 pAction.putDuration(getDuration());
253 DeviceManager.getInstance().doAction(deviceId, pAction);
257 void handleIncreaseDecreaseBrightnessCommand(IncreaseDecreaseType increaseDecrease) {
258 DeviceAction idbAcation = increaseDecrease == IncreaseDecreaseType.INCREASE ? DeviceAction.increase_bright
259 : DeviceAction.decrease_bright;
260 idbAcation.putDuration(getDuration());
261 DeviceManager.getInstance().doAction(deviceId, idbAcation);
264 void handleIncreaseDecreaseColorTemperatureCommand(IncreaseDecreaseType increaseDecrease) {
265 DeviceAction idctAcation = increaseDecrease == IncreaseDecreaseType.INCREASE ? DeviceAction.increase_ct
266 : DeviceAction.decrease_ct;
267 idctAcation.putDuration(getDuration());
268 DeviceManager.getInstance().doAction(deviceId, idctAcation);
271 void handleOnOffCommand(OnOffType onoff) {
272 DeviceAction ofAction = onoff == OnOffType.ON ? DeviceAction.open : DeviceAction.close;
273 ofAction.putDuration(getDuration());
274 DeviceManager.getInstance().doAction(deviceId, ofAction);
277 void handleHSBCommand(HSBType color) {
278 DeviceAction cAction = DeviceAction.color;
279 cAction.putValue(color.getRGB() & 0xFFFFFF);
280 cAction.putDuration(getDuration());
281 DeviceManager.getInstance().doAction(deviceId, cAction);
284 void handleBackgroundHSBCommand(HSBType color) {
285 DeviceAction cAction = DeviceAction.background_color;
287 // TODO: actions seem to be an insufficiant abstraction.
288 cAction.putValue(color.getHue() + "," + color.getSaturation());
289 cAction.putDuration(getDuration());
290 DeviceManager.getInstance().doAction(deviceId, cAction);
293 void handleBackgroundBrightnessPercentMessage(PercentType brightness) {
294 DeviceAction pAction;
296 pAction = DeviceAction.background_brightness;
297 pAction.putValue(brightness.intValue());
298 pAction.putDuration(getDuration());
299 DeviceManager.getInstance().doAction(deviceId, pAction);
302 private void handleBackgroundOnOffCommand(OnOffType command) {
303 DeviceAction pAction = command == OnOffType.ON ? DeviceAction.background_on : DeviceAction.background_off;
304 pAction.putDuration(getDuration());
305 DeviceManager.getInstance().doAction(deviceId, pAction);
308 void handleColorTemperatureCommand(PercentType ct) {
309 DeviceAction ctAction = DeviceAction.colortemperature;
310 ctAction.putValue(COLOR_TEMPERATURE_STEP * ct.intValue() + COLOR_TEMPERATURE_MINIMUM);
311 ctAction.putDuration(getDuration());
312 DeviceManager.getInstance().doAction(deviceId, ctAction);
315 void handleCustomCommand(String action, String params) {
316 DeviceManager.getInstance().doCustomAction(deviceId, action, params);
320 public void onStatusChanged(DeviceStatus status) {
321 logger.debug("UpdateState->{}", status);
325 void updateBrightnessAndColorUI(DeviceStatus status) {
326 PercentType brightness = status.isPowerOff() ? PercentType.ZERO : new PercentType(status.getBrightness());
328 HSBType tempHsbType = HSBType.fromRGB(status.getR(), status.getG(), status.getB());
329 HSBType hsbType = status.getMode() == DeviceMode.MODE_HSV
330 ? new HSBType(new DecimalType(status.getHue()), new PercentType(status.getSat()), brightness)
331 : new HSBType(tempHsbType.getHue(), tempHsbType.getSaturation(), brightness);
333 logger.debug("Update Color->{}", hsbType);
334 updateState(CHANNEL_COLOR, hsbType);
336 logger.debug("Update CT->{}", status.getCt());
337 updateState(CHANNEL_COLOR_TEMPERATURE,
338 new PercentType((status.getCt() - COLOR_TEMPERATURE_MINIMUM) / COLOR_TEMPERATURE_STEP));
342 // Duration should not be null, but just in case do a null check.
343 return getThing().getConfiguration().get(PARAMETER_DURATION) == null ? 500
344 : ((Number) getThing().getConfiguration().get(PARAMETER_DURATION)).intValue();