]> git.basschouten.com Git - openhab-addons.git/blob
3c266e827d8f299a5f368868eec2ef46c168107e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.energenie.internal.handler;
14
15 import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*;
16
17 import java.io.IOException;
18 import java.net.UnknownHostException;
19 import java.time.Duration;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22 import java.util.regex.Matcher;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.energenie.internal.EnergenieProtocolEnum;
27 import org.openhab.binding.energenie.internal.config.EnergenieConfiguration;
28 import org.openhab.core.cache.ExpiringCache;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.ThingStatusDetail;
34 import org.openhab.core.thing.binding.BaseThingHandler;
35 import org.openhab.core.thing.util.ThingHandlerHelper;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.RefreshType;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * The {@link EnergenieHandler} is responsible for handling commands, which are
43  * sent to one of the channels.
44  *
45  * @author Hans-Jörg Merk - Initial contribution
46  */
47 @NonNullByDefault
48 public class EnergenieHandler extends BaseThingHandler {
49
50     private static final String CHANNEL_SOCKET_PREFIX = "socket";
51     private final Logger logger = LoggerFactory.getLogger(EnergenieHandler.class);
52
53     /**
54      * Use cache for refresh command to not update again when call is made within 4 seconds of previous call.
55      */
56     private final ExpiringCache<Boolean> refreshCache = new ExpiringCache<>(Duration.ofSeconds(5), this::refreshState);
57     private final String statusOn;
58
59     private @Nullable EnergenieSocket energenieSocket;
60     private @Nullable ScheduledFuture<?> refreshJob;
61
62     private int refreshInterval;
63     private @Nullable String host;
64
65     public EnergenieHandler(final Thing thing, final EnergenieProtocolEnum protocol) {
66         super(thing);
67         this.statusOn = protocol.getStatusOn();
68     }
69
70     @Override
71     public void handleCommand(final ChannelUID channelUID, final Command command) {
72         if (command instanceof RefreshType) {
73             refreshCache.getValue();
74         } else if (command instanceof OnOffType) {
75             final byte[] ctrl = { DONT_SWITCH, DONT_SWITCH, DONT_SWITCH, DONT_SWITCH };
76             final Matcher matcher = CHANNEL_SOCKET.matcher(channelUID.getId());
77
78             try {
79                 boolean found = false;
80
81                 if (matcher.find()) {
82                     final int index = Integer.parseInt(matcher.group(1)) - 1;
83
84                     if (index >= 0 && index < SOCKET_COUNT) {
85                         ctrl[index] = OnOffType.ON.equals(command) ? SWITCH_ON : SWITCH_OFF;
86                         stateUpdate(energenieSocket.sendCommand(ctrl));
87                         found = true;
88                     }
89                 }
90                 if (!found) {
91                     logger.debug("Invalid channel id {}, should be value between 1 and {}", channelUID, SOCKET_COUNT);
92                 }
93             } catch (final IOException e) {
94                 updateStatus(ThingStatus.OFFLINE);
95                 logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT, e);
96             }
97         }
98     }
99
100     @Override
101     public void initialize() {
102         final EnergenieConfiguration config = getConfigAs(EnergenieConfiguration.class);
103
104         if (!config.host.isEmpty() && !config.password.isEmpty()) {
105             refreshInterval = EnergenieConfiguration.DEFAULT_REFRESH_INTERVAL;
106             host = config.host;
107             logger.debug("Initializing EnergenieHandler for Host '{}'", config.host);
108             energenieSocket = new EnergenieSocket(config.host, config.password);
109
110             updateStatus(ThingStatus.UNKNOWN);
111             refreshJob = scheduler.scheduleWithFixedDelay(this::refreshState, 6, refreshInterval, TimeUnit.SECONDS);
112         } else {
113             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
114                     "Can not access device , IP-Address or password not set");
115         }
116     }
117
118     @Override
119     public void dispose() {
120         logger.debug("EnergenieHandler disposed.");
121         final ScheduledFuture<?> refreshJob = this.refreshJob;
122
123         if (refreshJob != null) {
124             refreshJob.cancel(true);
125             this.refreshJob = null;
126         }
127     }
128
129     private boolean refreshState() {
130         final EnergenieSocket socket = this.energenieSocket;
131
132         if (socket != null) {
133             try {
134                 stateUpdate(socket.retrieveStatus());
135                 if (thing.getStatus() != ThingStatus.ONLINE && ThingHandlerHelper.isHandlerInitialized(thing)) {
136                     updateStatus(ThingStatus.ONLINE);
137                 }
138                 return true;
139             } catch (final UnknownHostException e) {
140                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
141                         "Can't find host: " + e.getMessage());
142             } catch (final IOException e) {
143                 logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT, e);
144                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
145                         "Couldn't get I/O for the connection");
146             } catch (final RuntimeException e) {
147                 logger.debug("Unexpected error", e);
148                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
149             }
150         }
151         return false;
152     }
153
154     public void stateUpdate(final byte[] status) {
155         for (int i = 0; i < 4; i++) {
156             final String socket = CHANNEL_SOCKET_PREFIX + (i + 1);
157             final String stringStatus = String.format("0x%02x", status[i]);
158             updateState(socket, OnOffType.from(stringStatus.equals(statusOn)));
159         }
160     }
161 }