]> git.basschouten.com Git - openhab-addons.git/blob
1f12dc80951d5ce5086fde3e3dd2b293053603b6
[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.somfytahoma.internal.handler;
14
15 import static org.openhab.binding.somfytahoma.internal.SomfyTahomaBindingConstants.*;
16
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaState;
24 import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaStatus;
25 import org.openhab.core.library.types.*;
26 import org.openhab.core.thing.*;
27 import org.openhab.core.thing.binding.BaseThingHandler;
28 import org.openhab.core.thing.binding.builder.ChannelBuilder;
29 import org.openhab.core.thing.binding.builder.ThingBuilder;
30 import org.openhab.core.types.Command;
31 import org.openhab.core.types.RefreshType;
32 import org.openhab.core.types.State;
33 import org.openhab.core.types.UnDefType;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * The {@link SomfyTahomaBaseThingHandler} is base thing handler for all things.
39  *
40  * @author Ondrej Pecta - Initial contribution
41  */
42 @NonNullByDefault
43 public abstract class SomfyTahomaBaseThingHandler extends BaseThingHandler {
44
45     private final Logger logger = LoggerFactory.getLogger(getClass());
46     private HashMap<String, Integer> typeTable = new HashMap<>();
47     protected HashMap<String, String> stateNames = new HashMap<>();
48
49     public SomfyTahomaBaseThingHandler(Thing thing) {
50         super(thing);
51     }
52
53     public HashMap<String, String> getStateNames() {
54         return stateNames;
55     }
56
57     private String url = "";
58
59     @Override
60     public void initialize() {
61         url = getURL();
62         if (getThing().getProperties().containsKey(RSSI_LEVEL_STATE)) {
63             createRSSIChannel();
64         }
65         updateStatus(ThingStatus.ONLINE);
66     }
67
68     private void createRSSIChannel() {
69         if (thing.getChannel(RSSI) == null) {
70             logger.debug("{} Creating a rssi channel", url);
71             createChannel(RSSI, "Number", "RSSI Level");
72         }
73     }
74
75     private void createChannel(String name, String type, String label) {
76         ThingBuilder thingBuilder = editThing();
77         Channel channel = ChannelBuilder.create(new ChannelUID(thing.getUID(), name), type).withLabel(label).build();
78         thingBuilder.withChannel(channel);
79         updateThing(thingBuilder.build());
80     }
81
82     @Override
83     public void handleCommand(ChannelUID channelUID, Command command) {
84         logger.debug("{} Received command {} for channel {}", url, command, channelUID);
85         if (command instanceof RefreshType) {
86             refresh(channelUID.getId());
87         }
88     }
89
90     public Logger getLogger() {
91         return logger;
92     }
93
94     protected boolean isAlwaysOnline() {
95         return false;
96     }
97
98     protected @Nullable SomfyTahomaBridgeHandler getBridgeHandler() {
99         Bridge localBridge = this.getBridge();
100         return localBridge != null ? (SomfyTahomaBridgeHandler) localBridge.getHandler() : null;
101     }
102
103     private String getURL() {
104         return getThing().getConfiguration().get("url") != null ? getThing().getConfiguration().get("url").toString()
105                 : "";
106     }
107
108     private void setAvailable() {
109         if (ThingStatus.ONLINE != thing.getStatus()) {
110             updateStatus(ThingStatus.ONLINE);
111         }
112     }
113
114     private void setUnavailable() {
115         if (ThingStatus.OFFLINE != thing.getStatus() && !isAlwaysOnline()) {
116             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, UNAVAILABLE);
117         }
118     }
119
120     protected void sendCommand(String cmd) {
121         sendCommand(cmd, "[]");
122     }
123
124     protected void sendCommand(String cmd, String param) {
125         SomfyTahomaBridgeHandler handler = getBridgeHandler();
126         if (handler != null) {
127             handler.sendCommand(url, cmd, param);
128         }
129     }
130
131     protected void refresh(String channel) {
132         SomfyTahomaBridgeHandler handler = getBridgeHandler();
133         if (handler != null && stateNames.containsKey(channel)) {
134             handler.refresh(url, stateNames.get(channel));
135         }
136     }
137
138     protected void executeActionGroup() {
139         SomfyTahomaBridgeHandler handler = getBridgeHandler();
140         if (handler != null) {
141             handler.executeActionGroup(url);
142         }
143     }
144
145     protected @Nullable String getCurrentExecutions() {
146         SomfyTahomaBridgeHandler handler = getBridgeHandler();
147         if (handler != null) {
148             return handler.getCurrentExecutions(url);
149         }
150         return null;
151     }
152
153     protected void cancelExecution(String executionId) {
154         SomfyTahomaBridgeHandler handler = getBridgeHandler();
155         if (handler != null) {
156             handler.cancelExecution(executionId);
157         }
158     }
159
160     protected SomfyTahomaStatus getTahomaStatus(String id) {
161         SomfyTahomaBridgeHandler handler = getBridgeHandler();
162         if (handler != null) {
163             return handler.getTahomaStatus(id);
164         }
165         return new SomfyTahomaStatus();
166     }
167
168     private void cacheStateType(SomfyTahomaState state) {
169         if (state.getType() > 0 && !typeTable.containsKey(state.getName())) {
170             typeTable.put(state.getName(), state.getType());
171         }
172     }
173
174     protected void cacheStateType(String stateName, int type) {
175         if (type > 0 && !typeTable.containsKey(stateName)) {
176             typeTable.put(stateName, type);
177         }
178     }
179
180     protected @Nullable State parseTahomaState(@Nullable SomfyTahomaState state) {
181         return parseTahomaState(null, state);
182     }
183
184     protected @Nullable State parseTahomaState(@Nullable String acceptedState, @Nullable SomfyTahomaState state) {
185         if (state == null) {
186             return UnDefType.NULL;
187         }
188
189         int type = state.getType();
190
191         try {
192             if (typeTable.containsKey(state.getName())) {
193                 type = typeTable.get(state.getName());
194             } else {
195                 cacheStateType(state);
196             }
197
198             if (type == 0) {
199                 logger.debug("{} Cannot recognize the state type for: {}!", url, state.getValue());
200                 return null;
201             }
202
203             logger.trace("Value to parse: {}, type: {}", state.getValue(), type);
204             switch (type) {
205                 case TYPE_PERCENT:
206                     Double valPct = Double.parseDouble(state.getValue().toString());
207                     return new PercentType(normalizePercent(valPct));
208                 case TYPE_DECIMAL:
209                     Double valDec = Double.parseDouble(state.getValue().toString());
210                     return new DecimalType(valDec);
211                 case TYPE_STRING:
212                 case TYPE_BOOLEAN:
213                     String value = state.getValue().toString();
214                     if ("String".equals(acceptedState)) {
215                         return new StringType(value);
216                     } else {
217                         return parseStringState(value);
218                     }
219                 default:
220                     return null;
221             }
222         } catch (IllegalArgumentException ex) {
223             logger.debug("{} Error while parsing Tahoma state! Value: {} type: {}", url, state.getValue(), type, ex);
224         }
225         return null;
226     }
227
228     private int normalizePercent(Double valPct) {
229         int value = valPct.intValue();
230         if (value < 0) {
231             value = 0;
232         } else if (value > 100) {
233             value = 100;
234         }
235         return value;
236     }
237
238     private State parseStringState(String value) {
239         if (value.endsWith("%")) {
240             // convert "100%" to 100 decimal
241             String val = value.replace("%", "");
242             logger.trace("converting: {} to value: {}", value, val);
243             Double valDec = Double.parseDouble(val);
244             return new DecimalType(valDec);
245         }
246         switch (value.toLowerCase()) {
247             case "on":
248             case "true":
249                 return OnOffType.ON;
250             case "off":
251             case "false":
252                 return OnOffType.OFF;
253             case "notdetected":
254             case "nopersoninside":
255             case "closed":
256             case "locked":
257                 return OpenClosedType.CLOSED;
258             case "detected":
259             case "personinside":
260             case "open":
261             case "opened":
262             case "unlocked":
263                 return OpenClosedType.OPEN;
264             case "unknown":
265                 return UnDefType.UNDEF;
266             default:
267                 logger.debug("{} Unknown thing state returned: {}", url, value);
268                 return UnDefType.UNDEF;
269         }
270     }
271
272     public void updateThingStatus(List<SomfyTahomaState> states) {
273         SomfyTahomaState state = getStatusState(states);
274         updateThingStatus(state);
275     }
276
277     private @Nullable SomfyTahomaState getStatusState(List<SomfyTahomaState> states) {
278         for (SomfyTahomaState state : states) {
279             if (STATUS_STATE.equals(state.getName()) && state.getType() == TYPE_STRING) {
280                 return state;
281             }
282         }
283         return null;
284     }
285
286     private void updateThingStatus(@Nullable SomfyTahomaState state) {
287         if (state == null) {
288             // Most probably we are dealing with RTS device which does not return states
289             // so we have to setup ONLINE status manually
290             setAvailable();
291             return;
292         }
293         if (STATUS_STATE.equals(state.getName()) && state.getType() == TYPE_STRING) {
294             if (UNAVAILABLE.equals(state.getValue())) {
295                 setUnavailable();
296             } else {
297                 setAvailable();
298             }
299         }
300     }
301
302     public void updateThingChannels(List<SomfyTahomaState> states) {
303         Map<String, String> properties = new HashMap<>();
304         for (SomfyTahomaState state : states) {
305             logger.trace("{} processing state: {} with value: {}", url, state.getName(), state.getValue());
306             properties.put(state.getName(), state.getValue().toString());
307             if (RSSI_LEVEL_STATE.equals(state.getName())) {
308                 // RSSI channel is a dynamic one
309                 updateRSSIChannel(state);
310             } else {
311                 updateThingChannels(state);
312             }
313         }
314         updateProperties(properties);
315     }
316
317     private void updateRSSIChannel(SomfyTahomaState state) {
318         createRSSIChannel();
319         Channel ch = thing.getChannel(RSSI);
320         if (ch != null) {
321             logger.debug("{} updating RSSI channel with value: {}", url, state.getValue());
322             State newState = parseTahomaState(ch.getAcceptedItemType(), state);
323             if (newState != null) {
324                 updateState(ch.getUID(), newState);
325             }
326         }
327     }
328
329     public void updateThingChannels(SomfyTahomaState state) {
330         stateNames.forEach((k, v) -> {
331             if (v.equals(state.getName())) {
332                 Channel ch = thing.getChannel(k);
333                 if (ch != null) {
334                     logger.debug("{} updating channel: {} with value: {}", url, k, state.getValue());
335                     State newState = parseTahomaState(ch.getAcceptedItemType(), state);
336                     if (newState != null) {
337                         updateState(ch.getUID(), newState);
338                     }
339                 }
340             }
341         });
342     }
343
344     public int toInteger(Command command) {
345         return (command instanceof DecimalType) ? ((DecimalType) command).intValue() : 0;
346     }
347 }