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