2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.somfytahoma.internal.handler;
15 import static org.openhab.binding.somfytahoma.internal.SomfyTahomaBindingConstants.*;
17 import java.util.HashMap;
18 import java.util.List;
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;
38 * The {@link SomfyTahomaBaseThingHandler} is base thing handler for all things.
40 * @author Ondrej Pecta - Initial contribution
43 public abstract class SomfyTahomaBaseThingHandler extends BaseThingHandler {
45 private final Logger logger = LoggerFactory.getLogger(getClass());
46 private HashMap<String, Integer> typeTable = new HashMap<>();
47 protected HashMap<String, String> stateNames = new HashMap<>();
49 public SomfyTahomaBaseThingHandler(Thing thing) {
53 public HashMap<String, String> getStateNames() {
57 private String url = "";
60 public void initialize() {
62 if (getThing().getProperties().containsKey(RSSI_LEVEL_STATE)) {
65 updateStatus(ThingStatus.ONLINE);
68 private void createRSSIChannel() {
69 if (thing.getChannel(RSSI) == null) {
70 logger.debug("{} Creating a rssi channel", url);
71 createChannel(RSSI, "Number", "RSSI Level");
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());
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());
90 public Logger getLogger() {
94 protected boolean isAlwaysOnline() {
98 protected @Nullable SomfyTahomaBridgeHandler getBridgeHandler() {
99 Bridge localBridge = this.getBridge();
100 return localBridge != null ? (SomfyTahomaBridgeHandler) localBridge.getHandler() : null;
103 private String getURL() {
104 return getThing().getConfiguration().get("url") != null ? getThing().getConfiguration().get("url").toString()
108 private void setAvailable() {
109 if (ThingStatus.ONLINE != thing.getStatus()) {
110 updateStatus(ThingStatus.ONLINE);
114 private void setUnavailable() {
115 if (ThingStatus.OFFLINE != thing.getStatus() && !isAlwaysOnline()) {
116 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, UNAVAILABLE);
120 protected void sendCommand(String cmd) {
121 sendCommand(cmd, "[]");
124 protected void sendCommand(String cmd, String param) {
125 SomfyTahomaBridgeHandler handler = getBridgeHandler();
126 if (handler != null) {
127 handler.sendCommand(url, cmd, param);
131 protected void refresh(String channel) {
132 SomfyTahomaBridgeHandler handler = getBridgeHandler();
133 if (handler != null && stateNames.containsKey(channel)) {
134 handler.refresh(url, stateNames.get(channel));
138 protected void executeActionGroup() {
139 SomfyTahomaBridgeHandler handler = getBridgeHandler();
140 if (handler != null) {
141 handler.executeActionGroup(url);
145 protected @Nullable String getCurrentExecutions() {
146 SomfyTahomaBridgeHandler handler = getBridgeHandler();
147 if (handler != null) {
148 return handler.getCurrentExecutions(url);
153 protected void cancelExecution(String executionId) {
154 SomfyTahomaBridgeHandler handler = getBridgeHandler();
155 if (handler != null) {
156 handler.cancelExecution(executionId);
160 protected SomfyTahomaStatus getTahomaStatus(String id) {
161 SomfyTahomaBridgeHandler handler = getBridgeHandler();
162 if (handler != null) {
163 return handler.getTahomaStatus(id);
165 return new SomfyTahomaStatus();
168 private void cacheStateType(SomfyTahomaState state) {
169 if (state.getType() > 0 && !typeTable.containsKey(state.getName())) {
170 typeTable.put(state.getName(), state.getType());
174 protected void cacheStateType(String stateName, int type) {
175 if (type > 0 && !typeTable.containsKey(stateName)) {
176 typeTable.put(stateName, type);
180 protected @Nullable State parseTahomaState(@Nullable SomfyTahomaState state) {
181 return parseTahomaState(null, state);
184 protected @Nullable State parseTahomaState(@Nullable String acceptedState, @Nullable SomfyTahomaState state) {
186 return UnDefType.NULL;
189 int type = state.getType();
192 if (typeTable.containsKey(state.getName())) {
193 type = typeTable.get(state.getName());
195 cacheStateType(state);
199 logger.debug("{} Cannot recognize the state type for: {}!", url, state.getValue());
203 logger.trace("Value to parse: {}, type: {}", state.getValue(), type);
206 Double valPct = Double.parseDouble(state.getValue().toString());
207 return new PercentType(normalizePercent(valPct));
209 Double valDec = Double.parseDouble(state.getValue().toString());
210 return new DecimalType(valDec);
213 String value = state.getValue().toString();
214 if ("String".equals(acceptedState)) {
215 return new StringType(value);
217 return parseStringState(value);
222 } catch (IllegalArgumentException ex) {
223 logger.debug("{} Error while parsing Tahoma state! Value: {} type: {}", url, state.getValue(), type, ex);
228 private int normalizePercent(Double valPct) {
229 int value = valPct.intValue();
232 } else if (value > 100) {
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);
246 switch (value.toLowerCase()) {
252 return OnOffType.OFF;
254 case "nopersoninside":
257 return OpenClosedType.CLOSED;
263 return OpenClosedType.OPEN;
265 return UnDefType.UNDEF;
267 logger.debug("{} Unknown thing state returned: {}", url, value);
268 return UnDefType.UNDEF;
272 public void updateThingStatus(List<SomfyTahomaState> states) {
273 SomfyTahomaState state = getStatusState(states);
274 updateThingStatus(state);
277 private @Nullable SomfyTahomaState getStatusState(List<SomfyTahomaState> states) {
278 for (SomfyTahomaState state : states) {
279 if (STATUS_STATE.equals(state.getName()) && state.getType() == TYPE_STRING) {
286 private void updateThingStatus(@Nullable SomfyTahomaState state) {
288 // Most probably we are dealing with RTS device which does not return states
289 // so we have to setup ONLINE status manually
293 if (STATUS_STATE.equals(state.getName()) && state.getType() == TYPE_STRING) {
294 if (UNAVAILABLE.equals(state.getValue())) {
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);
311 updateThingChannels(state);
314 updateProperties(properties);
317 private void updateRSSIChannel(SomfyTahomaState state) {
319 Channel ch = thing.getChannel(RSSI);
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);
329 public void updateThingChannels(SomfyTahomaState state) {
330 stateNames.forEach((k, v) -> {
331 if (v.equals(state.getName())) {
332 Channel ch = thing.getChannel(k);
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);
344 public int toInteger(Command command) {
345 return (command instanceof DecimalType) ? ((DecimalType) command).intValue() : 0;