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