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 return this.getBridge() != null ? (SomfyTahomaBridgeHandler) this.getBridge().getHandler() : null;
102 private String getURL() {
103 return getThing().getConfiguration().get("url") != null ? getThing().getConfiguration().get("url").toString()
107 private void setAvailable() {
108 if (ThingStatus.ONLINE != thing.getStatus()) {
109 updateStatus(ThingStatus.ONLINE);
113 private void setUnavailable() {
114 if (ThingStatus.OFFLINE != thing.getStatus() && !isAlwaysOnline()) {
115 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, UNAVAILABLE);
119 protected void sendCommand(String cmd) {
120 sendCommand(cmd, "[]");
123 protected void sendCommand(String cmd, String param) {
124 SomfyTahomaBridgeHandler handler = getBridgeHandler();
125 if (handler != null) {
126 handler.sendCommand(url, cmd, param);
130 protected void refresh(String channel) {
131 SomfyTahomaBridgeHandler handler = getBridgeHandler();
132 if (handler != null && stateNames.containsKey(channel)) {
133 handler.refresh(url, stateNames.get(channel));
137 protected void executeActionGroup() {
138 SomfyTahomaBridgeHandler handler = getBridgeHandler();
139 if (handler != null) {
140 handler.executeActionGroup(url);
144 protected @Nullable String getCurrentExecutions() {
145 SomfyTahomaBridgeHandler handler = getBridgeHandler();
146 if (handler != null) {
147 return handler.getCurrentExecutions(url);
152 protected void cancelExecution(String executionId) {
153 SomfyTahomaBridgeHandler handler = getBridgeHandler();
154 if (handler != null) {
155 handler.cancelExecution(executionId);
159 protected SomfyTahomaStatus getTahomaStatus(String id) {
160 SomfyTahomaBridgeHandler handler = getBridgeHandler();
161 if (handler != null) {
162 return handler.getTahomaStatus(id);
164 return new SomfyTahomaStatus();
167 private void cacheStateType(SomfyTahomaState state) {
168 if (state.getType() > 0 && !typeTable.containsKey(state.getName())) {
169 typeTable.put(state.getName(), state.getType());
173 protected void cacheStateType(String stateName, int type) {
174 if (type > 0 && !typeTable.containsKey(stateName)) {
175 typeTable.put(stateName, type);
179 protected @Nullable State parseTahomaState(@Nullable SomfyTahomaState state) {
180 return parseTahomaState(null, state);
183 protected @Nullable State parseTahomaState(@Nullable String acceptedState, @Nullable SomfyTahomaState state) {
185 return UnDefType.NULL;
188 int type = state.getType();
191 if (typeTable.containsKey(state.getName())) {
192 type = typeTable.get(state.getName());
194 cacheStateType(state);
198 logger.debug("{} Cannot recognize the state type for: {}!", url, state.getValue());
202 logger.trace("Value to parse: {}, type: {}", state.getValue(), type);
205 Double valPct = Double.parseDouble(state.getValue().toString());
206 return new PercentType(normalizePercent(valPct));
208 Double valDec = Double.parseDouble(state.getValue().toString());
209 return new DecimalType(valDec);
212 String value = state.getValue().toString();
213 if ("String".equals(acceptedState)) {
214 return new StringType(value);
216 return parseStringState(value);
221 } catch (IllegalArgumentException ex) {
222 logger.debug("{} Error while parsing Tahoma state! Value: {} type: {}", url, state.getValue(), type, ex);
227 private int normalizePercent(Double valPct) {
228 int value = valPct.intValue();
231 } else if (value > 100) {
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);
245 switch (value.toLowerCase()) {
251 return OnOffType.OFF;
253 case "nopersoninside":
256 return OpenClosedType.CLOSED;
262 return OpenClosedType.OPEN;
264 return UnDefType.UNDEF;
266 logger.debug("{} Unknown thing state returned: {}", url, value);
267 return UnDefType.UNDEF;
271 public void updateThingStatus(List<SomfyTahomaState> states) {
272 SomfyTahomaState state = getStatusState(states);
273 updateThingStatus(state);
276 private @Nullable SomfyTahomaState getStatusState(List<SomfyTahomaState> states) {
277 for (SomfyTahomaState state : states) {
278 if (STATUS_STATE.equals(state.getName()) && state.getType() == TYPE_STRING) {
285 private void updateThingStatus(@Nullable SomfyTahomaState state) {
287 // Most probably we are dealing with RTS device which does not return states
288 // so we have to setup ONLINE status manually
292 if (STATUS_STATE.equals(state.getName()) && state.getType() == TYPE_STRING) {
293 if (UNAVAILABLE.equals(state.getValue())) {
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);
310 updateThingChannels(state);
313 updateProperties(properties);
316 private void updateRSSIChannel(SomfyTahomaState state) {
318 Channel ch = thing.getChannel(RSSI);
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);
328 public void updateThingChannels(SomfyTahomaState state) {
329 stateNames.forEach((k, v) -> {
330 if (v.equals(state.getName())) {
331 Channel ch = thing.getChannel(k);
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);
343 public int toInteger(Command command) {
344 return (command instanceof DecimalType) ? ((DecimalType) command).intValue() : 0;