]> git.basschouten.com Git - openhab-addons.git/blob
4842f337df36a145bee0bc9769c0b6a21bd751c8
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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     protected 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, EXEC_URL + "apply");
128         }
129     }
130
131     protected void refresh(String channel) {
132         SomfyTahomaBridgeHandler handler = getBridgeHandler();
133         String stateName = stateNames.get(channel);
134         if (handler != null && stateName != null) {
135             handler.refresh(url, stateName);
136         }
137     }
138
139     protected void executeActionGroup() {
140         SomfyTahomaBridgeHandler handler = getBridgeHandler();
141         if (handler != null) {
142             handler.executeActionGroup(url);
143         }
144     }
145
146     protected @Nullable String getCurrentExecutions() {
147         SomfyTahomaBridgeHandler handler = getBridgeHandler();
148         if (handler != null) {
149             return handler.getCurrentExecutions(url);
150         }
151         return null;
152     }
153
154     protected void cancelExecution(String executionId) {
155         SomfyTahomaBridgeHandler handler = getBridgeHandler();
156         if (handler != null) {
157             handler.cancelExecution(executionId);
158         }
159     }
160
161     protected SomfyTahomaStatus getTahomaStatus(String id) {
162         SomfyTahomaBridgeHandler handler = getBridgeHandler();
163         if (handler != null) {
164             return handler.getTahomaStatus(id);
165         }
166         return new SomfyTahomaStatus();
167     }
168
169     private void cacheStateType(SomfyTahomaState state) {
170         if (state.getType() > 0 && !typeTable.containsKey(state.getName())) {
171             typeTable.put(state.getName(), state.getType());
172         }
173     }
174
175     protected void cacheStateType(String stateName, int type) {
176         if (type > 0 && !typeTable.containsKey(stateName)) {
177             typeTable.put(stateName, type);
178         }
179     }
180
181     protected @Nullable State parseTahomaState(@Nullable SomfyTahomaState state) {
182         return parseTahomaState(null, state);
183     }
184
185     protected @Nullable State parseTahomaState(@Nullable String acceptedState, @Nullable SomfyTahomaState state) {
186         if (state == null) {
187             return UnDefType.NULL;
188         }
189
190         int type = state.getType();
191
192         try {
193             if (typeTable.containsKey(state.getName())) {
194                 type = typeTable.get(state.getName());
195             } else {
196                 cacheStateType(state);
197             }
198
199             if (type == 0) {
200                 logger.debug("{} Cannot recognize the state type for: {}!", url, state.getValue());
201                 return null;
202             }
203
204             logger.trace("Value to parse: {}, type: {}", state.getValue(), type);
205             switch (type) {
206                 case TYPE_PERCENT:
207                     Double valPct = Double.parseDouble(state.getValue().toString());
208                     return new PercentType(normalizePercent(valPct));
209                 case TYPE_DECIMAL:
210                     Double valDec = Double.parseDouble(state.getValue().toString());
211                     return new DecimalType(valDec);
212                 case TYPE_STRING:
213                 case TYPE_BOOLEAN:
214                     String value = state.getValue().toString();
215                     if ("String".equals(acceptedState)) {
216                         return new StringType(value);
217                     } else {
218                         return parseStringState(value);
219                     }
220                 default:
221                     return null;
222             }
223         } catch (IllegalArgumentException ex) {
224             logger.debug("{} Error while parsing Tahoma state! Value: {} type: {}", url, state.getValue(), type, ex);
225         }
226         return null;
227     }
228
229     private int normalizePercent(Double valPct) {
230         int value = valPct.intValue();
231         if (value < 0) {
232             value = 0;
233         } else if (value > 100) {
234             value = 100;
235         }
236         return value;
237     }
238
239     private State parseStringState(String value) {
240         if (value.endsWith("%")) {
241             // convert "100%" to 100 decimal
242             String val = value.replace("%", "");
243             logger.trace("converting: {} to value: {}", value, val);
244             Double valDec = Double.parseDouble(val);
245             return new DecimalType(valDec);
246         }
247         switch (value.toLowerCase()) {
248             case "on":
249             case "true":
250                 return OnOffType.ON;
251             case "off":
252             case "false":
253                 return OnOffType.OFF;
254             case "notdetected":
255             case "nopersoninside":
256             case "closed":
257             case "locked":
258                 return OpenClosedType.CLOSED;
259             case "detected":
260             case "personinside":
261             case "open":
262             case "opened":
263             case "unlocked":
264                 return OpenClosedType.OPEN;
265             case "unknown":
266                 return UnDefType.UNDEF;
267             default:
268                 logger.debug("{} Unknown thing state returned: {}", url, value);
269                 return UnDefType.UNDEF;
270         }
271     }
272
273     public void updateThingStatus(List<SomfyTahomaState> states) {
274         SomfyTahomaState state = getStatusState(states);
275         updateThingStatus(state);
276     }
277
278     private @Nullable SomfyTahomaState getStatusState(List<SomfyTahomaState> states) {
279         for (SomfyTahomaState state : states) {
280             if (STATUS_STATE.equals(state.getName()) && state.getType() == TYPE_STRING) {
281                 return state;
282             }
283         }
284         return null;
285     }
286
287     private void updateThingStatus(@Nullable SomfyTahomaState state) {
288         if (state == null) {
289             // Most probably we are dealing with RTS device which does not return states
290             // so we have to setup ONLINE status manually
291             setAvailable();
292             return;
293         }
294         if (STATUS_STATE.equals(state.getName()) && state.getType() == TYPE_STRING) {
295             if (UNAVAILABLE.equals(state.getValue())) {
296                 setUnavailable();
297             } else {
298                 setAvailable();
299             }
300         }
301     }
302
303     public void updateThingChannels(List<SomfyTahomaState> states) {
304         Map<String, String> properties = new HashMap<>();
305         for (SomfyTahomaState state : states) {
306             logger.trace("{} processing state: {} with value: {}", url, state.getName(), state.getValue());
307             properties.put(state.getName(), state.getValue().toString());
308             if (RSSI_LEVEL_STATE.equals(state.getName())) {
309                 // RSSI channel is a dynamic one
310                 updateRSSIChannel(state);
311             } else {
312                 updateThingChannels(state);
313             }
314         }
315         updateProperties(properties);
316     }
317
318     private void updateRSSIChannel(SomfyTahomaState state) {
319         createRSSIChannel();
320         Channel ch = thing.getChannel(RSSI);
321         if (ch != null) {
322             logger.debug("{} updating RSSI channel with value: {}", url, state.getValue());
323             State newState = parseTahomaState(ch.getAcceptedItemType(), state);
324             if (newState != null) {
325                 updateState(ch.getUID(), newState);
326             }
327         }
328     }
329
330     public void updateThingChannels(SomfyTahomaState state) {
331         stateNames.forEach((k, v) -> {
332             if (v.equals(state.getName())) {
333                 Channel ch = thing.getChannel(k);
334                 if (ch != null) {
335                     logger.debug("{} updating channel: {} with value: {}", url, k, state.getValue());
336                     State newState = parseTahomaState(ch.getAcceptedItemType(), state);
337                     if (newState != null) {
338                         updateState(ch.getUID(), newState);
339                     }
340                 }
341             }
342         });
343     }
344
345     public int toInteger(Command command) {
346         return (command instanceof DecimalType) ? ((DecimalType) command).intValue() : 0;
347     }
348 }