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 protected 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, EXEC_URL + "apply");
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);
139 protected void executeActionGroup() {
140 SomfyTahomaBridgeHandler handler = getBridgeHandler();
141 if (handler != null) {
142 handler.executeActionGroup(url);
146 protected @Nullable String getCurrentExecutions() {
147 SomfyTahomaBridgeHandler handler = getBridgeHandler();
148 if (handler != null) {
149 return handler.getCurrentExecutions(url);
154 protected void cancelExecution(String executionId) {
155 SomfyTahomaBridgeHandler handler = getBridgeHandler();
156 if (handler != null) {
157 handler.cancelExecution(executionId);
161 protected SomfyTahomaStatus getTahomaStatus(String id) {
162 SomfyTahomaBridgeHandler handler = getBridgeHandler();
163 if (handler != null) {
164 return handler.getTahomaStatus(id);
166 return new SomfyTahomaStatus();
169 private void cacheStateType(SomfyTahomaState state) {
170 if (state.getType() > 0 && !typeTable.containsKey(state.getName())) {
171 typeTable.put(state.getName(), state.getType());
175 protected void cacheStateType(String stateName, int type) {
176 if (type > 0 && !typeTable.containsKey(stateName)) {
177 typeTable.put(stateName, type);
181 protected @Nullable State parseTahomaState(@Nullable SomfyTahomaState state) {
182 return parseTahomaState(null, state);
185 protected @Nullable State parseTahomaState(@Nullable String acceptedState, @Nullable SomfyTahomaState state) {
187 return UnDefType.NULL;
190 int type = state.getType();
193 if (typeTable.containsKey(state.getName())) {
194 type = typeTable.get(state.getName());
196 cacheStateType(state);
200 logger.debug("{} Cannot recognize the state type for: {}!", url, state.getValue());
204 logger.trace("Value to parse: {}, type: {}", state.getValue(), type);
207 Double valPct = Double.parseDouble(state.getValue().toString());
208 return new PercentType(normalizePercent(valPct));
210 Double valDec = Double.parseDouble(state.getValue().toString());
211 return new DecimalType(valDec);
214 String value = state.getValue().toString();
215 if ("String".equals(acceptedState)) {
216 return new StringType(value);
218 return parseStringState(value);
223 } catch (IllegalArgumentException ex) {
224 logger.debug("{} Error while parsing Tahoma state! Value: {} type: {}", url, state.getValue(), type, ex);
229 private int normalizePercent(Double valPct) {
230 int value = valPct.intValue();
233 } else if (value > 100) {
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);
247 switch (value.toLowerCase()) {
253 return OnOffType.OFF;
255 case "nopersoninside":
258 return OpenClosedType.CLOSED;
264 return OpenClosedType.OPEN;
266 return UnDefType.UNDEF;
268 logger.debug("{} Unknown thing state returned: {}", url, value);
269 return UnDefType.UNDEF;
273 public void updateThingStatus(List<SomfyTahomaState> states) {
274 SomfyTahomaState state = getStatusState(states);
275 updateThingStatus(state);
278 private @Nullable SomfyTahomaState getStatusState(List<SomfyTahomaState> states) {
279 for (SomfyTahomaState state : states) {
280 if (STATUS_STATE.equals(state.getName()) && state.getType() == TYPE_STRING) {
287 private void updateThingStatus(@Nullable SomfyTahomaState state) {
289 // Most probably we are dealing with RTS device which does not return states
290 // so we have to setup ONLINE status manually
294 if (STATUS_STATE.equals(state.getName()) && state.getType() == TYPE_STRING) {
295 if (UNAVAILABLE.equals(state.getValue())) {
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);
312 updateThingChannels(state);
315 updateProperties(properties);
318 private void updateRSSIChannel(SomfyTahomaState state) {
320 Channel ch = thing.getChannel(RSSI);
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);
330 public void updateThingChannels(SomfyTahomaState state) {
331 stateNames.forEach((k, v) -> {
332 if (v.equals(state.getName())) {
333 Channel ch = thing.getChannel(k);
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);
345 public int toInteger(Command command) {
346 return (command instanceof DecimalType) ? ((DecimalType) command).intValue() : 0;