]> git.basschouten.com Git - openhab-addons.git/blob
ca1538e1e8fb46d109cbc85ceecdd388b80f3c6e
[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.deconz.internal.handler;
14
15 import static org.openhab.binding.deconz.internal.BindingConstants.*;
16 import static org.openhab.core.library.unit.MetricPrefix.*;
17 import static org.openhab.core.library.unit.SIUnits.*;
18 import static org.openhab.core.library.unit.Units.*;
19
20 import java.util.List;
21 import java.util.Set;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.deconz.internal.dto.SensorConfig;
26 import org.openhab.binding.deconz.internal.dto.SensorState;
27 import org.openhab.binding.deconz.internal.dto.SensorUpdateConfig;
28 import org.openhab.core.library.types.HSBType;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.OpenClosedType;
31 import org.openhab.core.library.types.QuantityType;
32 import org.openhab.core.library.types.StringType;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.ThingTypeUID;
36 import org.openhab.core.thing.type.ChannelKind;
37 import org.openhab.core.types.Command;
38 import org.openhab.core.types.RefreshType;
39
40 import com.google.gson.Gson;
41
42 /**
43  * This sensor Thing doesn't establish any connections, that is done by the bridge Thing.
44  *
45  * It waits for the bridge to come online, grab the websocket connection and bridge configuration
46  * and registers to the websocket connection as a listener.
47  *
48  * A REST API call is made to get the initial sensor state.
49  *
50  * Every sensor and switch is supported by this Thing, because a unified state is kept
51  * in {@link #sensorState}. Every field that got received by the REST API for this specific
52  * sensor is published to the framework.
53  *
54  * @author David Graeff - Initial contribution
55  */
56 @NonNullByDefault
57 public class SensorThingHandler extends SensorBaseThingHandler {
58     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_PRESENCE_SENSOR,
59             THING_TYPE_DAYLIGHT_SENSOR, THING_TYPE_POWER_SENSOR, THING_TYPE_CONSUMPTION_SENSOR, THING_TYPE_LIGHT_SENSOR,
60             THING_TYPE_TEMPERATURE_SENSOR, THING_TYPE_HUMIDITY_SENSOR, THING_TYPE_PRESSURE_SENSOR, THING_TYPE_SWITCH,
61             THING_TYPE_OPENCLOSE_SENSOR, THING_TYPE_WATERLEAKAGE_SENSOR, THING_TYPE_FIRE_SENSOR,
62             THING_TYPE_ALARM_SENSOR, THING_TYPE_VIBRATION_SENSOR, THING_TYPE_BATTERY_SENSOR,
63             THING_TYPE_CARBONMONOXIDE_SENSOR, THING_TYPE_AIRQUALITY_SENSOR, THING_TYPE_COLOR_CONTROL);
64
65     private static final List<String> CONFIG_CHANNELS = List.of(CHANNEL_BATTERY_LEVEL, CHANNEL_BATTERY_LOW,
66             CHANNEL_ENABLED, CHANNEL_TEMPERATURE);
67
68     public SensorThingHandler(Thing thing, Gson gson) {
69         super(thing, gson);
70     }
71
72     @Override
73     public void handleCommand(ChannelUID channelUID, Command command) {
74         if (command instanceof RefreshType) {
75             sensorState.buttonevent = null;
76             valueUpdated(channelUID, sensorState, false);
77             return;
78         }
79         switch (channelUID.getId()) {
80             case CHANNEL_ENABLED:
81                 if (command instanceof OnOffType) {
82                     SensorUpdateConfig newConfig = new SensorUpdateConfig();
83                     newConfig.on = OnOffType.ON.equals(command);
84                     sendCommand(newConfig, command, channelUID, null);
85                 }
86                 break;
87         }
88     }
89
90     @Override
91     protected void valueUpdated(ChannelUID channelUID, SensorConfig newConfig) {
92         super.valueUpdated(channelUID, newConfig);
93         switch (channelUID.getId()) {
94             case CHANNEL_ENABLED:
95                 updateState(channelUID, OnOffType.from(newConfig.on));
96                 break;
97             case CHANNEL_TEMPERATURE:
98                 Float temperature = newConfig.temperature;
99                 if (temperature != null) {
100                     updateState(channelUID, new QuantityType<>(temperature / 100, CELSIUS));
101                 }
102                 break;
103         }
104     }
105
106     @Override
107     protected void valueUpdated(ChannelUID channelUID, SensorState newState, boolean initializing) {
108         super.valueUpdated(channelUID, newState, initializing);
109         switch (channelUID.getId()) {
110             case CHANNEL_BATTERY_LEVEL:
111                 updateDecimalTypeChannel(channelUID, newState.battery);
112                 break;
113             case CHANNEL_LIGHT:
114                 Boolean dark = newState.dark;
115                 if (dark != null) {
116                     Boolean daylight = newState.daylight;
117                     if (dark) { // if it's dark, it's dark ;)
118                         updateState(channelUID, new StringType("Dark"));
119                     } else if (daylight != null) { // if its not dark, it might be between darkness and daylight
120                         if (daylight) {
121                             updateState(channelUID, new StringType("Daylight"));
122                         } else {
123                             updateState(channelUID, new StringType("Sunset"));
124                         }
125                     } else { // if no daylight value is known, we assume !dark means daylight
126                         updateState(channelUID, new StringType("Daylight"));
127                     }
128                 }
129                 break;
130             case CHANNEL_POWER:
131                 updateQuantityTypeChannel(channelUID, newState.power, WATT);
132                 break;
133             case CHANNEL_CONSUMPTION:
134                 updateQuantityTypeChannel(channelUID, newState.consumption, WATT_HOUR);
135                 break;
136             case CHANNEL_VOLTAGE:
137                 updateQuantityTypeChannel(channelUID, newState.voltage, VOLT);
138                 break;
139             case CHANNEL_CURRENT:
140                 updateQuantityTypeChannel(channelUID, newState.current, MILLI(AMPERE));
141                 break;
142             case CHANNEL_LIGHT_LUX:
143                 updateQuantityTypeChannel(channelUID, newState.lux, LUX);
144                 break;
145             case CHANNEL_COLOR:
146                 final double @Nullable [] xy = newState.xy;
147                 if (xy != null && xy.length == 2) {
148                     updateState(channelUID, HSBType.fromXY((float) xy[0], (float) xy[1]));
149                 }
150                 break;
151             case CHANNEL_LIGHT_LEVEL:
152                 updateDecimalTypeChannel(channelUID, newState.lightlevel);
153                 break;
154             case CHANNEL_DARK:
155                 updateSwitchChannel(channelUID, newState.dark);
156                 break;
157             case CHANNEL_DAYLIGHT:
158                 updateSwitchChannel(channelUID, newState.daylight);
159                 break;
160             case CHANNEL_TEMPERATURE:
161                 updateQuantityTypeChannel(channelUID, newState.temperature, CELSIUS, 1.0 / 100);
162                 break;
163             case CHANNEL_HUMIDITY:
164                 updateQuantityTypeChannel(channelUID, newState.humidity, PERCENT, 1.0 / 100);
165                 break;
166             case CHANNEL_PRESSURE:
167                 updateQuantityTypeChannel(channelUID, newState.pressure, HECTO(PASCAL));
168                 break;
169             case CHANNEL_PRESENCE:
170                 updateSwitchChannel(channelUID, newState.presence);
171                 break;
172             case CHANNEL_VALUE:
173                 updateDecimalTypeChannel(channelUID, newState.status);
174                 break;
175             case CHANNEL_OPENCLOSE:
176                 Boolean open = newState.open;
177                 if (open != null) {
178                     updateState(channelUID, open ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
179                 }
180                 break;
181             case CHANNEL_WATERLEAKAGE:
182                 updateSwitchChannel(channelUID, newState.water);
183                 break;
184             case CHANNEL_FIRE:
185                 updateSwitchChannel(channelUID, newState.fire);
186                 break;
187             case CHANNEL_ALARM:
188                 updateSwitchChannel(channelUID, newState.alarm);
189                 break;
190             case CHANNEL_TAMPERED:
191                 updateSwitchChannel(channelUID, newState.tampered);
192                 break;
193             case CHANNEL_VIBRATION:
194                 updateSwitchChannel(channelUID, newState.vibration);
195                 break;
196             case CHANNEL_CARBONMONOXIDE:
197                 updateSwitchChannel(channelUID, newState.carbonmonoxide);
198                 break;
199             case CHANNEL_AIRQUALITY:
200                 updateStringChannel(channelUID, newState.airquality);
201                 break;
202             case CHANNEL_AIRQUALITYPPB:
203                 updateDecimalTypeChannel(channelUID, newState.airqualityppb);
204                 break;
205             case CHANNEL_BUTTON:
206                 updateDecimalTypeChannel(channelUID, newState.buttonevent);
207                 break;
208             case CHANNEL_BUTTONEVENT:
209                 Integer buttonevent = newState.buttonevent;
210                 if (buttonevent != null && !initializing) {
211                     triggerChannel(channelUID, String.valueOf(buttonevent));
212                 }
213                 break;
214             case CHANNEL_GESTURE:
215                 updateDecimalTypeChannel(channelUID, newState.gesture);
216                 break;
217             case CHANNEL_GESTUREEVENT:
218                 Integer gesture = newState.gesture;
219                 if (gesture != null && !initializing) {
220                     triggerChannel(channelUID, String.valueOf(gesture));
221                 }
222                 break;
223         }
224     }
225
226     @Override
227     protected void createTypeSpecificChannels(SensorConfig sensorConfig, SensorState sensorState) {
228         // some Xiaomi sensors
229         if (sensorConfig.temperature != null) {
230             createChannel(CHANNEL_TEMPERATURE, ChannelKind.STATE);
231         }
232
233         // ZHAPresence - e.g. IKEA TRÅDFRI motion sensor
234         if (sensorState.dark != null) {
235             createChannel(CHANNEL_DARK, ChannelKind.STATE);
236         }
237
238         // ZHAConsumption - e.g Bitron 902010/25 or Heiman SmartPlug
239         if (sensorState.power != null) {
240             createChannel(CHANNEL_POWER, ChannelKind.STATE);
241         }
242
243         // ZHAPower - e.g. Heiman SmartPlug
244         if (sensorState.voltage != null) {
245             createChannel(CHANNEL_VOLTAGE, ChannelKind.STATE);
246         }
247         if (sensorState.current != null) {
248             createChannel(CHANNEL_CURRENT, ChannelKind.STATE);
249         }
250
251         // IAS Zone sensor - e.g. Heiman HS1MS motion sensor
252         if (sensorState.tampered != null) {
253             createChannel(CHANNEL_TAMPERED, ChannelKind.STATE);
254         }
255
256         // e.g. Aqara Cube
257         if (sensorState.gesture != null) {
258             createChannel(CHANNEL_GESTURE, ChannelKind.STATE);
259             createChannel(CHANNEL_GESTUREEVENT, ChannelKind.TRIGGER);
260         }
261     }
262
263     @Override
264     protected List<String> getConfigChannels() {
265         return CONFIG_CHANNELS;
266     }
267 }