]> git.basschouten.com Git - openhab-addons.git/blob
e62347afa2ab3f58f6c486f3a1c8e2a5282ec369
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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 /**
14  * The {@link ShellyCoIoTVersion1} implements the parsing for CoIoT version 1
15  *
16  * @author Markus Michels - Initial contribution
17  */
18 package org.openhab.binding.shelly.internal.coap;
19
20 import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
21 import static org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.*;
22 import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
23
24 import java.util.List;
25 import java.util.Map;
26
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotDescrBlk;
29 import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotDescrSen;
30 import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotSensor;
31 import org.openhab.binding.shelly.internal.handler.ShellyBaseHandler;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.OpenClosedType;
34 import org.openhab.core.library.unit.SIUnits;
35 import org.openhab.core.library.unit.SmartHomeUnits;
36 import org.openhab.core.types.State;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * The {@link ShellyCoIoTVersion1} implements the parsing for CoIoT version 2
42  *
43  * @author Markus Michels - Initial contribution
44  */
45 @NonNullByDefault
46 public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCoIoTInterface {
47     private final Logger logger = LoggerFactory.getLogger(ShellyCoIoTVersion2.class);
48
49     public ShellyCoIoTVersion2(String thingName, ShellyBaseHandler thingHandler, Map<String, CoIotDescrBlk> blkMap,
50             Map<String, CoIotDescrSen> sensorMap) {
51         super(thingName, thingHandler, blkMap, sensorMap);
52     }
53
54     @Override
55     public int getVersion() {
56         return ShellyCoapJSonDTO.COIOT_VERSION_2;
57     }
58
59     /**
60      * Process CoIoT status update message. If a status update is received, but the device description has not been
61      * received yet a GET is send to query device description.
62      *
63      * @param devId device id included in the status packet
64      * @param payload CoAP payload (Json format), example: {"G":[[0,112,0]]}
65      * @param serial Serial for this request. If this the the same as last serial
66      *            the update was already sent and processed so this one gets
67      *            ignored.
68      */
69     @Override
70     public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen sen, CoIotSensor s,
71             Map<String, State> updates) {
72         // first check the base implementation
73         if (super.handleStatusUpdate(sensorUpdates, sen, s, updates)) {
74             // process by the base class
75             return true;
76         }
77
78         // Process status information and convert into channel updates
79         // Integer rIndex = Integer.parseInt(sen.links) + 1;
80         int rIndex = getIdFromBlk(sen);
81         String rGroup = getProfile().numRelays <= 1 ? CHANNEL_GROUP_RELAY_CONTROL
82                 : CHANNEL_GROUP_RELAY_CONTROL + rIndex;
83         String mGroup = profile.numMeters == 1 ? CHANNEL_GROUP_METER
84                 : CHANNEL_GROUP_METER + (profile.isEMeter ? getIdFromBlk(sen) : rIndex);
85
86         boolean processed = true;
87         double value = getDouble(s.value);
88         String reason = "";
89         switch (sen.id) {
90             case "3103": // H, humidity, 0-100 percent, unknown 999
91             case "3106": // L, luminosity, lux, U32, -1
92             case "3109": // S, tilt, 0-180deg, -1
93             case "3110": // S, luminosityLevel, dark/twilight/bright, "unknown"=unknown
94             case "3111": // B, battery, 0-100%, unknown -1
95             case "3112": // S, charger, 0/1
96             case "3115": // S, sensorError, 0/1
97             case "5101": // S, brightness, 1-100%
98                 // processed by base handler
99                 break;
100             case "6109": // P, overpowerValue, W, U32
101             case "9101":
102                 // Relay: S, mode, relay/roller or
103                 // Dimmer: S, mode, color/white
104                 // skip, could check against thing mode...
105                 break;
106
107             case "1101": // S, output, 0/1
108                 updatePower(profile, updates, rIndex, sen, s, sensorUpdates);
109                 break;
110             case "1102": // roler_0: S, roller, open/close/stop -> roller state
111                 updateChannel(updates, CHANNEL_GROUP_ROL_CONTROL, CHANNEL_ROL_CONTROL_STATE, getStringType(s.valueStr));
112                 break;
113             case "1103": // roller_0: S, rollerPos, 0-100, unknown -1
114                 int pos = Math.max(SHELLY_MIN_ROLLER_POS, Math.min((int) value, SHELLY_MAX_ROLLER_POS));
115                 updateChannel(updates, CHANNEL_GROUP_ROL_CONTROL, CHANNEL_ROL_CONTROL_CONTROL,
116                         toQuantityType(new Double(SHELLY_MAX_ROLLER_POS - pos), SmartHomeUnits.PERCENT));
117                 break;
118             case "1105": // S, valvle, closed/opened/not_connected/failure/closing/opening/checking or unbknown
119                 updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VALVE, getStringType(s.valueStr));
120                 break;
121
122             case "2101": // Input_0: S, input, 0/1
123             case "2201": // Input_1: S, input, 0/1
124             case "2301": // Input_2: S, input, 0/1
125             case "2401": // Input_3: S, input, 0/1
126                 handleInput(sen, s, rGroup, updates);
127                 break;
128             case "2102": // Input_0: EV, inputEvent, S/SS/SSS/L
129             case "2202": // Input_1: EV, inputEvent
130             case "2302": // Input_2: EV, inputEvent
131             case "2402": // Input_3: EV, inputEvent
132                 handleInputEvent(sen, getString(s.valueStr), -1, updates);
133                 break;
134             case "2103": // EVC, inputEventCnt, U16
135             case "2203": // EVC, inputEventCnt, U16
136             case "2303": // EVC, inputEventCnt, U16
137             case "2403": // EVC, inputEventCnt, U16
138                 handleInputEvent(sen, "", getInteger((int) s.value), updates);
139                 break;
140             case "3101": // sensor_0: T, extTemp, C, -55/125; unknown 999
141             case "3201": // sensor_1: T, extTemp, C, -55/125; unknown 999
142             case "3301": // sensor_2: T, extTemp, C, -55/125; unknown 999
143                 int idx = getExtTempId(sen.id);
144                 if (idx >= 0) {
145                     // H&T, Fllod, DW only have 1 channel, 1/1PM with Addon have up to to 3 sensors
146                     String channel = profile.isSensor ? CHANNEL_SENSOR_TEMP : CHANNEL_SENSOR_TEMP + idx;
147                     updateChannel(updates, CHANNEL_GROUP_SENSOR, channel,
148                             toQuantityType(value, DIGITS_TEMP, SIUnits.CELSIUS));
149                 } else {
150                     logger.debug("{}: Unable to get extSensorId {} from {}/{}", thingName, sen.id, sen.type, sen.desc);
151                 }
152                 break;
153             case "3104": // T, deviceTemp, Celsius -40/300; 999=unknown
154                 updateChannel(updates, CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ITEMP,
155                         toQuantityType(value, DIGITS_NONE, SIUnits.CELSIUS));
156                 break;
157             case "3102": // sensor_0: T, extTemp, F, -67/257, unknown 999
158             case "3202": // sensor_1: T, extTemp, F, -67/257, unknown 999
159             case "3302": // sensor_2: T, extTemp, F, -67/257, unknown 999
160             case "3105": // T, deviceTemp, Fahrenheit -40/572
161                 // skip, we use only C
162                 break;
163
164             case "3107": // C, Gas concentration, U16
165                 updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_PPM, getDecimal(s.value));
166                 break;
167             case "3108": // DW: S, dwIsOpened, 0/1, -1=unknown
168                 if (value != -1) {
169                     updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_CONTACT,
170                             value != 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
171                 } else {
172                     logger.debug("{}: Sensor error reported, check device, battery and installation", thingName);
173                 }
174                 break;
175             case "3113": // S, sensorOp, warmup/normal/fault
176                 updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_SSTATE, getStringType(s.valueStr));
177                 break;
178             case "3114": // S, selfTest, not_completed/completed/running/pending
179                 updateChannel(updates, CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_SELFTTEST, getStringType(s.valueStr));
180                 break;
181             case "3117": // S, extInput, 0/1
182                 handleInput(sen, s, rGroup, updates);
183                 break;
184
185             case "4101": // relay_0: P, power, W
186             case "4201": // relay_1: P, power, W
187             case "4301": // relay_2: P, power, W
188             case "4401": // relay_3: P, power, W
189             case "4105": // emeter_0: P, power, W
190             case "4205": // emeter_1: P, power, W
191             case "4305": // emeter_2: P, power, W
192             case "4102": // roller_0: P, rollerPower, W, 0-2300, unknown -1
193             case "4202": // roller_1: P, rollerPower, W, 0-2300, unknown -1
194                 updateChannel(updates, mGroup, CHANNEL_METER_CURRENTWATTS,
195                         toQuantityType(s.value, DIGITS_WATT, SmartHomeUnits.WATT));
196                 updateChannel(updates, mGroup, CHANNEL_LAST_UPDATE, getTimestamp());
197                 break;
198
199             case "4103": // relay_0: E, energy, Wmin, U32
200             case "4203": // relay_1: E, energy, Wmin, U32
201             case "4303": // relay_2: E, energy, Wmin, U32
202             case "4403": // relay_3: E, energy, Wmin, U32
203             case "4104": // roller_0: E, rollerEnergy, Wmin, U32, -1
204             case "4204": // roller_0: E, rollerEnergy, Wmin, U32, -1
205             case "4106": // emeter_0: E, energy, Wh, U32
206             case "4206": // emeter_1: E, energy, Wh, U32
207             case "4306": // emeter_2: E, energy, Wh, U32
208                 double total = profile.isEMeter ? s.value / 1000 : s.value / 60 / 1000;
209                 updateChannel(updates, mGroup, CHANNEL_METER_TOTALKWH,
210                         toQuantityType(total, DIGITS_KWH, SmartHomeUnits.KILOWATT_HOUR));
211                 break;
212
213             case "4107": // emeter_0: E, energyReturned, Wh, U32, -1
214             case "4207": // emeter_1: E, energyReturned, Wh, U32, -1
215             case "4307": // emeter_2: E, energyReturned, Wh, U32, -1
216                 updateChannel(updates, mGroup, CHANNEL_EMETER_TOTALRET,
217                         toQuantityType(getDouble(s.value) / 1000, DIGITS_KWH, SmartHomeUnits.KILOWATT_HOUR));
218                 break;
219
220             case "4108": // emeter_0: V, voltage, 0-265V, U32, -1
221             case "4208": // emeter_1: V, voltage, 0-265V, U32, -1
222             case "4308": // emeter_2: V, voltage, 0-265V, U32, -1
223                 updateChannel(updates, mGroup, CHANNEL_EMETER_VOLTAGE,
224                         toQuantityType(getDouble(s.value), DIGITS_VOLT, SmartHomeUnits.VOLT));
225                 break;
226
227             case "4109": // emeter_0: A, current, 0/120A, -1
228             case "4209": // emeter_1: A, current, 0/120A, -1
229             case "4309": // emeter_2: A, current, 0/120A, -1
230                 updateChannel(updates, rGroup, CHANNEL_EMETER_CURRENT,
231                         toQuantityType(getDouble(s.value), DIGITS_VOLT, SmartHomeUnits.AMPERE));
232                 break;
233
234             case "4110": // emeter_0: S, powerFactor, 0/1, -1
235             case "4210": // emeter_1: S, powerFactor, 0/1, -1
236             case "4310": // emeter_2: S, powerFactor, 0/1, -1
237                 updateChannel(updates, rGroup, CHANNEL_EMETER_PFACTOR, getDecimal(s.value));
238                 break;
239
240             case "6101": // A, overtemp, 0/1
241                 if (s.value == 1) {
242                     thingHandler.postEvent(ALARM_TYPE_OVERTEMP, true);
243                 }
244                 break;
245             case "6102": // relay_0: A, overpower, 0/1
246             case "6202": // relay_1: A, overpower, 0/1
247             case "6302": // relay_2: A, overpower, 0/1
248             case "6402": // relay_3: A, overpower, 0/1
249                 if (s.value == 1) {
250                     thingHandler.postEvent(ALARM_TYPE_OVERPOWER, true);
251                 }
252                 break;
253             case "6104": // relay_0: A, loadError, 0/1
254             case "6204": // relay_1: A, loadError, 0/1
255             case "6304": // relay_2: A, loadError, 0/1
256             case "6404": // relay_3: A, loadError, 0/1
257                 if (s.value == 1) {
258                     thingHandler.postEvent(ALARM_TYPE_LOADERR, true);
259                 }
260                 break;
261             case "6103": // roller_0: A, rollerStopReason, normal/safety_switch/obstacle/overpower
262                 reason = getString(s.valueStr);
263                 updateChannel(updates, CHANNEL_GROUP_ROL_CONTROL, CHANNEL_ROL_CONTROL_STOPR, getStringType(reason));
264                 if (!reason.isEmpty() && !reason.equalsIgnoreCase(SHELLY_API_STOPR_NORMAL)) {
265                     thingHandler.postEvent("ROLLER_" + reason.toUpperCase(), true);
266                 }
267             case "6106": // A, flood, 0/1, -1
268                 updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD,
269                         value == 1 ? OnOffType.ON : OnOffType.OFF);
270                 break;
271             case "6108": // A, gas, none/mild/heavy/test or unknown
272                 updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ALARM_STATE, getStringType(s.valueStr));
273                 break;
274             case "6110": // A, vibration, 0/1, -1=unknown
275                 updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VIBRATION,
276                         value == 1 ? OnOffType.ON : OnOffType.OFF);
277                 break;
278             case "9102": // EV, wakeupEvent, battery/button/periodic/poweron/sensor/ext_power, "unknown"=unknown
279                 thingHandler.updateWakeupReason(s.valueArray);
280                 break;
281             case "9103": // EVC, cfgChanged, U16
282                 if ((lastCfgCount != -1) && (lastCfgCount != s.value)) {
283                     thingHandler.requestUpdates(1, true); // refresh config
284                 }
285                 lastCfgCount = (int) s.value;
286                 break;
287
288             default:
289                 processed = false;
290         }
291         return processed;
292     }
293
294     @Override
295     public CoIotDescrSen fixDescription(CoIotDescrSen sen, Map<String, CoIotDescrBlk> blkMap) {
296         return sen;
297     }
298 }