]> git.basschouten.com Git - openhab-addons.git/blob
75622ce78db79841762f7ae13e80b7ef4be66996
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.lib.device;
14
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19
20 import org.openhab.binding.yeelight.internal.lib.device.connection.ConnectionBase;
21 import org.openhab.binding.yeelight.internal.lib.enums.ActiveMode;
22 import org.openhab.binding.yeelight.internal.lib.enums.DeviceMode;
23 import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
24 import org.openhab.binding.yeelight.internal.lib.enums.MethodAction;
25 import org.openhab.binding.yeelight.internal.lib.listeners.DeviceConnectionStateListener;
26 import org.openhab.binding.yeelight.internal.lib.listeners.DeviceStatusChangeListener;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import com.google.gson.JsonArray;
31 import com.google.gson.JsonElement;
32 import com.google.gson.JsonObject;
33 import com.google.gson.JsonParser;
34
35 /**
36  * The {@link DeviceBase} is a generic class for all devices.
37  *
38  * @author Coaster Li - Initial contribution
39  * @author Daniel Walters - Correct handling of brightness
40  * @author Joe Ho - Added duration to some commands
41  */
42 public abstract class DeviceBase {
43     private final Logger logger = LoggerFactory.getLogger(DeviceBase.class);
44
45     private static final String TAG = DeviceBase.class.getSimpleName();
46
47     protected String mDeviceId;
48     protected String mDeviceName;
49     protected DeviceType mDeviceType;
50     protected String mAddress;
51     private String[] mSupportProps;
52     protected int mPort;
53     private int mFwVersion;
54     protected boolean bIsOnline;
55     protected boolean bIsAutoConnect;
56     protected ConnectionBase mConnection;
57     protected ConnectState mConnectState = ConnectState.DISCONNECTED;
58     protected DeviceStatus mDeviceStatus;
59     private Map<String, Object> mBulbInfo = null;
60     protected List<String> mQueryList = new ArrayList<>();
61
62     protected int mMinCt, mMaxCt;
63
64     List<DeviceConnectionStateListener> mConnectionListeners = new ArrayList<>();
65     List<DeviceStatusChangeListener> mStatusChangeListeners = new ArrayList<>();
66
67     public DeviceBase(String id) {
68         mDeviceId = id;
69         mDeviceStatus = new DeviceStatus();
70     }
71
72     public DeviceBase(String id, boolean isAutoConnect) {
73         mDeviceId = id;
74         this.bIsAutoConnect = isAutoConnect;
75     }
76
77     public void onNotify(String response) {
78         boolean needNotify = true;
79         JsonObject message = JsonParser.parseString(response).getAsJsonObject();
80         try {
81             if (message.has("method")) {
82                 String method = message.get("method").toString().replace("\"", "");
83                 if ("props".equals(method)) {// Property notify
84                     String params = message.get("params").toString();
85                     JsonObject propsObject = JsonParser.parseString(params).getAsJsonObject();
86                     for (Entry<String, JsonElement> prop : propsObject.entrySet()) {
87                         final YeelightDeviceProperty property = YeelightDeviceProperty.fromString(prop.getKey());
88                         if (null == property) {
89                             logger.debug("Unhandled property: {}", prop.getKey());
90                             continue;
91                         }
92
93                         switch (property) {
94                             case POWER:
95                                 if ("\"off\"".equals(prop.getValue().toString())) {
96                                     mDeviceStatus.setPowerOff(true);
97                                 } else if ("\"on\"".equals(prop.getValue().toString())) {
98                                     mDeviceStatus.setPowerOff(false);
99                                 }
100                                 break;
101                             case BRIGHT:
102                                 mDeviceStatus.setBrightness(prop.getValue().getAsInt());
103                                 break;
104                             case CT:
105                                 mDeviceStatus.setCt(prop.getValue().getAsInt());
106                                 mDeviceStatus.setMode(DeviceMode.MODE_SUNHINE);
107                                 break;
108                             case RGB: {
109                                 mDeviceStatus.setMode(DeviceMode.MODE_COLOR);
110                                 int color = prop.getValue().getAsInt();
111                                 mDeviceStatus.setColor(color);
112
113                                 mDeviceStatus.setR((color >> 16) & 0xFF);
114                                 mDeviceStatus.setG((color >> 8) & 0xFF);
115                                 mDeviceStatus.setB(color & 0xFF);
116                                 break;
117                             }
118                             case HUE:
119                                 mDeviceStatus.setMode(DeviceMode.MODE_HSV);
120                                 mDeviceStatus.setHue(prop.getValue().getAsInt());
121                                 break;
122                             case SAT:
123                                 mDeviceStatus.setMode(DeviceMode.MODE_HSV);
124                                 mDeviceStatus.setSat(prop.getValue().getAsInt());
125                                 break;
126                             case COLOR_MODE:
127                                 switch (prop.getValue().getAsInt()) {
128                                     case DeviceStatus.MODE_COLOR:
129                                         mDeviceStatus.setMode(DeviceMode.MODE_COLOR);
130                                         break;
131                                     case DeviceStatus.MODE_COLORTEMPERATURE:
132                                         mDeviceStatus.setMode(DeviceMode.MODE_SUNHINE);
133                                         break;
134                                     case DeviceStatus.MODE_HSV:
135                                         mDeviceStatus.setMode(DeviceMode.MODE_HSV);
136                                         break;
137                                     default:
138                                         break;
139                                 }
140                                 break;
141                             case FLOWING:
142                                 mDeviceStatus.setIsFlowing(prop.getValue().getAsInt() == 1);
143                                 break;
144                             case FLOW_PARAMS:
145                                 // {"method":"props","params":{"flow_params":"0,0,1000,1,15935488,31,1000,1,13366016,31,1000,1,62370,31,1000,1,7995635,31"}}
146                                 String[] flowStrs = prop.getValue().toString().replace("\"", "").split(",");
147                                 if (flowStrs.length > 2 && (flowStrs.length - 2) % 4 == 0) {
148                                     mDeviceStatus.setFlowCount(Integer.parseInt(flowStrs[0]));
149                                     mDeviceStatus.setFlowEndAction(Integer.parseInt(flowStrs[1]));
150                                     if (mDeviceStatus.getFlowItems() == null) {
151                                         mDeviceStatus.setFlowItems(new ArrayList<>());
152                                     }
153                                     mDeviceStatus.getFlowItems().clear();
154                                     for (int i = 0; i < ((flowStrs.length - 2) / 4); i++) {
155                                         ColorFlowItem item = new ColorFlowItem();
156                                         item.duration = Integer.valueOf(flowStrs[4 * i + 2]);
157                                         item.mode = Integer.valueOf(flowStrs[4 * i + 3]);
158                                         item.value = Integer.valueOf(flowStrs[4 * i + 4]);
159                                         item.brightness = Integer.valueOf(flowStrs[4 * i + 5]);
160                                         mDeviceStatus.getFlowItems().add(item);
161                                     }
162                                 }
163                                 break;
164                             case DELAYOFF:
165                                 int delayOff = prop.getValue().getAsInt();
166                                 if (delayOff > 0 && delayOff <= 60) {
167                                     mDeviceStatus.setDelayOff(delayOff);
168                                 } else {
169                                     mDeviceStatus.setDelayOff(DeviceStatus.DEFAULT_NO_DELAY);
170                                 }
171                                 break;
172                             case MUSIC_ON:
173                                 mDeviceStatus.setMusicOn(prop.getValue().getAsInt() == 1);
174                                 break;
175                             case NAME:
176                                 mDeviceName = prop.getValue().toString();
177                                 break;
178                             case BG_RGB: {
179                                 int color = prop.getValue().getAsInt();
180                                 mDeviceStatus.setBackgroundR((color >> 16) & 0xFF);
181                                 mDeviceStatus.setBackgroundG((color >> 8) & 0xFF);
182                                 mDeviceStatus.setBackgroundB(color & 0xFF);
183                                 break;
184                             }
185                             case BG_HUE:
186                                 mDeviceStatus.setBackgroundHue(prop.getValue().getAsInt());
187
188                                 break;
189                             case BG_SAT:
190                                 mDeviceStatus.setBackgroundSat(prop.getValue().getAsInt());
191                                 break;
192                             case BG_BRIGHT:
193                                 mDeviceStatus.setBackgroundBrightness(prop.getValue().getAsInt());
194                                 break;
195                             case BG_POWER:
196                                 if ("\"off\"".equals(prop.getValue().toString())) {
197                                     mDeviceStatus.setBackgroundIsPowerOff(true);
198                                 } else if ("\"on\"".equals(prop.getValue().toString())) {
199                                     mDeviceStatus.setBackgroundIsPowerOff(false);
200                                 }
201                                 break;
202                             case NL_BR:
203                                 // when the light is switched from nightlight-mode to sunlight-mode it will send nl_br:0
204                                 // therefore we have to ignore the case.
205                                 final int intValue = prop.getValue().getAsInt();
206                                 if (intValue > 0) {
207                                     mDeviceStatus.setBrightness(intValue);
208                                 }
209                                 break;
210                             case ACTIVE_MODE:
211                                 int activeModeInt = prop.getValue().getAsInt();
212                                 final ActiveMode activeMode = ActiveMode.values()[activeModeInt];
213                                 mDeviceStatus.setActiveMode(activeMode);
214                                 break;
215                             default:
216                                 logger.debug("Maybe unsupported property: {} - {}", property, prop.getKey());
217                                 break;
218                         }
219                     }
220                 }
221
222             } else if (message.has("id") && message.has("result")) {
223                 // no method, but result : ["ok"]
224                 JsonArray result = message.get("result").getAsJsonArray();
225                 if ("\"ok\"".equals(result.get(0).toString())) {
226                     logger.info("######### this is control command response, don't need to notify status change!");
227                     needNotify = false;
228                 }
229             }
230
231             if (needNotify) {
232                 logger.info("status = {}", mDeviceStatus);
233                 for (DeviceStatusChangeListener statusChangeListener : mStatusChangeListeners) {
234                     statusChangeListener.onStatusChanged(mDeviceStatus);
235                 }
236             }
237         } catch (Exception e) {
238             logger.debug("Exception", e);
239         }
240     }
241
242     public void open(int duration) {
243         mConnection.invoke(
244                 new DeviceMethod(MethodAction.SWITCH, new Object[] { "on", DeviceMethod.EFFECT_SMOOTH, duration }));
245     }
246
247     public void close(int duration) {
248         mConnection.invoke(
249                 new DeviceMethod(MethodAction.SWITCH, new Object[] { "off", DeviceMethod.EFFECT_SMOOTH, duration }));
250     }
251
252     public void decreaseBrightness(int duration) {
253         int bright = getDeviceStatus().getBrightness() - 10;
254         if (bright <= 0) {
255             close(duration);
256         } else {
257             setBrightness(bright, duration);
258         }
259     }
260
261     public void increaseBrightness(int duration) {
262         int bright = getDeviceStatus().getBrightness() + 10;
263         if (bright > 100) {
264             bright = 100;
265         }
266         setBrightness(bright, duration);
267     }
268
269     public void setBrightness(int brightness, int duration) {
270         mConnection.invoke(MethodFactory.buildBrightnessMethd(brightness, DeviceMethod.EFFECT_SMOOTH, duration));
271     }
272
273     public void setColor(int color, int duration) {
274         mConnection.invoke(MethodFactory.buildRgbMethod(color, DeviceMethod.EFFECT_SMOOTH, duration));
275     }
276
277     public void increaseCt(int duration) {
278         int ct = getDeviceStatus().getCt() - ((mMaxCt - mMinCt) / 10);
279         if (ct < mMinCt) {
280             ct = mMinCt;
281         }
282         setCT(ct, duration);
283     }
284
285     public void decreaseCt(int duration) {
286         int ct = getDeviceStatus().getCt() + ((mMaxCt - mMinCt) / 10);
287         if (ct > mMaxCt) {
288             ct = mMaxCt;
289         }
290         setCT(ct, duration);
291     }
292
293     public void setCT(int ct, int duration) {
294         mConnection.invoke(MethodFactory.buildCTMethod(ct, DeviceMethod.EFFECT_SMOOTH, duration));
295     }
296
297     public void connect() {
298         setConnectionState(ConnectState.CONNECTTING);
299         mConnection.connect();
300     }
301
302     public void disconnect() {
303         setConnectionState(ConnectState.DISCONNECTED);
304         mConnection.disconnect();
305     }
306
307     public void setConnectionState(ConnectState connectState) {
308         logger.debug("{}: set connection state to: {}", TAG, connectState.name());
309         if (connectState == ConnectState.DISCONNECTED) {
310             setOnline(false);
311         }
312         if (mConnectState != connectState) {
313             mConnectState = connectState;
314             if (mConnectionListeners != null) {
315                 for (DeviceConnectionStateListener listener : mConnectionListeners) {
316                     listener.onConnectionStateChanged(mConnectState);
317                 }
318             }
319         }
320     }
321
322     public void sendCustomCommand(String action, String params) {
323         mConnection.invokeCustom(new DeviceMethod(action, params));
324     }
325
326     // ===================== setter and getter=====================
327
328     public String getDeviceId() {
329         return mDeviceId;
330     }
331
332     public void setDeviceName(String name) {
333         mDeviceName = name;
334     }
335
336     public String getDeviceName() {
337         return mDeviceName;
338     }
339
340     public DeviceType getDeviceType() {
341         return mDeviceType;
342     }
343
344     public String getDeviceModel() {
345         return mDeviceType.name();
346     }
347
348     public String getAddress() {
349         return mAddress;
350     }
351
352     public void setAddress(String address) {
353         this.mAddress = address;
354     }
355
356     public int getPort() {
357         return mPort;
358     }
359
360     public void setPort(int port) {
361         this.mPort = port;
362     }
363
364     public boolean isAutoConnect() {
365         return bIsAutoConnect;
366     }
367
368     public int getFwVersion() {
369         return mFwVersion;
370     }
371
372     public void setFwVersion(int fwVersion) {
373         this.mFwVersion = fwVersion;
374     }
375
376     public Map<String, Object> getBulbInfo() {
377         return mBulbInfo;
378     }
379
380     public void setBulbInfo(Map<String, Object> bulbInfo) {
381         this.mBulbInfo = bulbInfo;
382     }
383
384     public String[] getSupportProps() {
385         return mSupportProps;
386     }
387
388     public void setSupportProps(String[] supportProps) {
389         this.mSupportProps = supportProps;
390     }
391
392     public void setAutoConnect(boolean isAutoConnect) {
393         if (bIsAutoConnect != isAutoConnect) {
394             this.bIsAutoConnect = isAutoConnect;
395             checkAutoConnect();
396         }
397     }
398
399     public DeviceStatus getDeviceStatus() {
400         return mDeviceStatus;
401     }
402
403     public void registerConnectStateListener(DeviceConnectionStateListener listener) {
404         mConnectionListeners.add(listener);
405     }
406
407     public void registerStatusChangedListener(DeviceStatusChangeListener listener) {
408         mStatusChangeListeners.add(listener);
409     }
410
411     public void setOnline(boolean isOnline) {
412         bIsOnline = isOnline;
413         checkAutoConnect();
414     }
415
416     public boolean isOnline() {
417         return bIsOnline;
418     }
419
420     public ConnectState getConnectionState() {
421         return mConnectState;
422     }
423
424     public void queryStatus() {
425         DeviceMethod cmd = MethodFactory.buildQuery(this);
426         mQueryList.add(cmd.getCmdId());
427         mConnection.invoke(cmd);
428     }
429
430     private void checkAutoConnect() {
431         logger.debug(
432                 "{}: CheckAutoConnect: online: {}, autoConnect: {}, connection state: {}, device = {}, device id: {}",
433                 TAG, bIsOnline, bIsAutoConnect, mConnectState.name(), this, this.getDeviceId());
434         if (bIsOnline && bIsAutoConnect && mConnectState == ConnectState.DISCONNECTED) {
435             connect();
436         }
437     }
438 }