]> git.basschouten.com Git - openhab-addons.git/blob
7eb12ecbc317e2dfbb165130f11843e5c3e5a39d
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.yeelight.internal.handler;
14
15 import static org.openhab.binding.yeelight.internal.YeelightBindingConstants.*;
16
17 import java.util.concurrent.TimeUnit;
18
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;
36
37 /**
38  * The {@link YeelightHandlerBase} is responsible for handling commands, which are
39  * sent to one of the channels.
40  *
41  * @author Coaster Li - Initial contribution
42  * @author Joe Ho - Added Duration Thing parameter
43  * @author Nikita Pogudalov - Added DeviceType for ceiling 1
44  */
45 public abstract class YeelightHandlerBase extends BaseThingHandler
46         implements DeviceConnectionStateListener, DeviceStatusChangeListener {
47
48     private final Logger logger = LoggerFactory.getLogger(YeelightHandlerBase.class);
49     protected DeviceBase mDevice;
50
51     // Reading the deviceId from the properties map.
52     private String deviceId = getThing().getConfiguration().get(PARAMETER_DEVICE_ID).toString();
53
54     public YeelightHandlerBase(Thing thing) {
55         super(thing);
56     }
57
58     protected void updateUI(DeviceStatus status) {
59     }
60
61     @Override
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);
72     }
73
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;
93         } else {
94             return null;
95         }
96     }
97
98     @Override
99     public void onConnectionStateChanged(ConnectState connectState) {
100         logger.debug("onConnectionStateChanged -> {}", connectState.name());
101         updateStatusHelper(connectState);
102     }
103
104     public void updateStatusHelper(ConnectState connectState) {
105         switch (connectState) {
106             case DISCONNECTED:
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");
111                 }
112                 break;
113             case CONNECTED:
114                 updateStatus(ThingStatus.ONLINE);
115                 mDevice.queryStatus();
116                 break;
117             default:
118                 updateStatus(ThingStatus.UNKNOWN);
119                 break;
120         }
121     }
122
123     @Override
124     public void channelLinked(ChannelUID channelUID) {
125         logger.debug("ChannelLinked -> {}", channelUID.getId());
126         super.channelLinked(channelUID);
127
128         Runnable task = () -> {
129             mDevice.queryStatus();
130         };
131         scheduler.schedule(task, 500, TimeUnit.MILLISECONDS);
132     }
133
134     public void handleCommandHelper(ChannelUID channelUID, Command command, String logInfo) {
135         logger.debug("{}: {}", logInfo, command);
136
137         // If device is disconnected, start discovery to reconnect.
138         if (mDevice.isAutoConnect() && mDevice.getConnectionState() != ConnectState.CONNECTED) {
139             DeviceManager.getInstance().startDiscovery(5 * 1000);
140         }
141         if (command instanceof RefreshType) {
142             logger.debug("Refresh channel: {} Command: {}", channelUID, command);
143
144             DeviceManager.getInstance().startDiscovery(5 * 1000);
145             DeviceStatus s = mDevice.getDeviceStatus();
146
147             switch (channelUID.getId()) {
148                 case CHANNEL_BRIGHTNESS:
149                     updateState(channelUID, new PercentType(s.getBrightness()));
150                     break;
151                 case CHANNEL_COLOR:
152                     updateState(channelUID, HSBType.fromRGB(s.getR(), s.getG(), s.getB()));
153                     break;
154                 case CHANNEL_COLOR_TEMPERATURE:
155                     updateState(channelUID, new PercentType(s.getCt()));
156                     break;
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);
161                     break;
162                 default:
163                     break;
164             }
165             return;
166         }
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);
175                 }
176                 break;
177             case CHANNEL_COLOR:
178                 if (command instanceof HSBType) {
179                     HSBType hsbCommand = (HSBType) command;
180                     if (hsbCommand.getBrightness().intValue() == 0) {
181                         handleOnOffCommand(OnOffType.OFF);
182                     } else {
183                         handleHSBCommand(hsbCommand);
184                     }
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);
191                 }
192                 break;
193             case CHANNEL_COLOR_TEMPERATURE:
194                 if (command instanceof PercentType) {
195                     handleColorTemperatureCommand((PercentType) command);
196                 } else if (command instanceof IncreaseDecreaseType) {
197                     handleIncreaseDecreaseBrightnessCommand((IncreaseDecreaseType) command);
198                 }
199                 break;
200
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);
209                 }
210                 break;
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);
217                 }
218                 break;
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];
226                     }
227                     logger.debug("{}: {} {}", logInfo, methodAction, methodParams);
228                     handleCustomCommand(methodAction, methodParams);
229                     updateState(channelUID, new StringType(""));
230                 }
231                 break;
232             default:
233                 break;
234         }
235     }
236
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);
243         } else {
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);
249             }
250             pAction = DeviceAction.brightness;
251             pAction.putValue(brightness.intValue());
252             pAction.putDuration(getDuration());
253             DeviceManager.getInstance().doAction(deviceId, pAction);
254         }
255     }
256
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);
262     }
263
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);
269     }
270
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);
275     }
276
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);
282     }
283
284     void handleBackgroundHSBCommand(HSBType color) {
285         DeviceAction cAction = DeviceAction.background_color;
286
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);
291     }
292
293     void handleBackgroundBrightnessPercentMessage(PercentType brightness) {
294         DeviceAction pAction;
295
296         pAction = DeviceAction.background_brightness;
297         pAction.putValue(brightness.intValue());
298         pAction.putDuration(getDuration());
299         DeviceManager.getInstance().doAction(deviceId, pAction);
300     }
301
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);
306     }
307
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);
313     }
314
315     void handleCustomCommand(String action, String params) {
316         DeviceManager.getInstance().doCustomAction(deviceId, action, params);
317     }
318
319     @Override
320     public void onStatusChanged(DeviceStatus status) {
321         logger.debug("UpdateState->{}", status);
322         updateUI(status);
323     }
324
325     void updateBrightnessAndColorUI(DeviceStatus status) {
326         PercentType brightness = status.isPowerOff() ? PercentType.ZERO : new PercentType(status.getBrightness());
327
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);
332
333         logger.debug("Update Color->{}", hsbType);
334         updateState(CHANNEL_COLOR, hsbType);
335
336         logger.debug("Update CT->{}", status.getCt());
337         updateState(CHANNEL_COLOR_TEMPERATURE,
338                 new PercentType((status.getCt() - COLOR_TEMPERATURE_MINIMUM) / COLOR_TEMPERATURE_STEP));
339     }
340
341     int getDuration() {
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();
345     }
346 }