]> git.basschouten.com Git - openhab-addons.git/blob
05350d27bedd729e6b3c1fa79cdff9bd1a22997d
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.opengarage.internal;
14
15 import java.io.IOException;
16 import java.util.concurrent.Future;
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.TimeUnit;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.opengarage.internal.api.ControllerVariables;
23 import org.openhab.binding.opengarage.internal.api.Enums.OpenGarageCommand;
24 import org.openhab.core.library.types.DecimalType;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.library.types.OpenClosedType;
27 import org.openhab.core.library.types.QuantityType;
28 import org.openhab.core.library.types.StopMoveType;
29 import org.openhab.core.library.types.StringType;
30 import org.openhab.core.library.types.UpDownType;
31 import org.openhab.core.library.unit.MetricPrefix;
32 import org.openhab.core.library.unit.SIUnits;
33 import org.openhab.core.thing.Channel;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.Thing;
36 import org.openhab.core.thing.ThingStatus;
37 import org.openhab.core.thing.ThingStatusDetail;
38 import org.openhab.core.thing.binding.BaseThingHandler;
39 import org.openhab.core.types.Command;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * The {@link OpenGarageHandler} is responsible for handling commands, which are
45  * sent to one of the channels.
46  *
47  * @author Paul Smedley - Initial contribution
48  * @author Dan Cunningham - Minor improvements to vehicle state and invert option
49  */
50 @NonNullByDefault
51 public class OpenGarageHandler extends BaseThingHandler {
52
53     private final Logger logger = LoggerFactory.getLogger(OpenGarageHandler.class);
54
55     private long refreshInterval;
56
57     private @NonNullByDefault({}) OpenGarageWebTargets webTargets;
58     private @Nullable ScheduledFuture<?> pollFuture;
59
60     public OpenGarageHandler(Thing thing) {
61         super(thing);
62     }
63
64     @Override
65     public void handleCommand(ChannelUID channelUID, Command command) {
66         try {
67             logger.debug("Received command {} for thing '{}' on channel {}", command, thing.getUID().getAsString(),
68                     channelUID.getId());
69             boolean invert = isChannelInverted(channelUID.getId());
70             switch (channelUID.getId()) {
71                 case OpenGarageBindingConstants.CHANNEL_OG_STATUS:
72                 case OpenGarageBindingConstants.CHANNEL_OG_STATUS_SWITCH:
73                 case OpenGarageBindingConstants.CHANNEL_OG_STATUS_ROLLERSHUTTER:
74                     if (command.equals(OnOffType.ON) || command.equals(UpDownType.UP)) {
75                         changeStatus(invert ? OpenGarageCommand.CLOSE : OpenGarageCommand.OPEN);
76                         return;
77                     } else if (command.equals(OnOffType.OFF) || command.equals(UpDownType.DOWN)) {
78                         changeStatus(invert ? OpenGarageCommand.OPEN : OpenGarageCommand.CLOSE);
79                         return;
80                     } else if (command.equals(StopMoveType.STOP) || command.equals(StopMoveType.MOVE)) {
81                         changeStatus(OpenGarageCommand.CLICK);
82                         return;
83                     }
84                     break;
85                 default:
86             }
87         } catch (OpenGarageCommunicationException ex) {
88             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ex.getMessage());
89         }
90     }
91
92     @Override
93     public void initialize() {
94         OpenGarageConfiguration config = getConfigAs(OpenGarageConfiguration.class);
95         logger.debug("config.hostname = {}, refresh = {}, port = {}", config.hostname, config.refresh, config.port);
96         if (config.hostname == null) {
97             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Hostname/IP address must be set");
98         } else {
99             webTargets = new OpenGarageWebTargets(config.hostname, config.port, config.password);
100             refreshInterval = config.refresh;
101
102             schedulePoll();
103         }
104     }
105
106     @Override
107     public void dispose() {
108         super.dispose();
109         stopPoll();
110     }
111
112     private void schedulePoll() {
113         if (pollFuture != null) {
114             pollFuture.cancel(false);
115         }
116         logger.debug("Scheduling poll for 1 second out, then every {} s", refreshInterval);
117         pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 1, refreshInterval, TimeUnit.SECONDS);
118     }
119
120     private void poll() {
121         try {
122             logger.debug("Polling for state");
123             pollStatus();
124         } catch (IOException e) {
125             logger.debug("Could not connect to OpenGarage controller", e);
126             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
127         } catch (RuntimeException e) {
128             logger.warn("Unexpected error connecting to OpenGarage controller", e);
129             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
130         }
131     }
132
133     private void stopPoll() {
134         final Future<?> future = pollFuture;
135         if (future != null && !future.isCancelled()) {
136             future.cancel(true);
137             pollFuture = null;
138         }
139     }
140
141     private void pollStatus() throws IOException {
142         ControllerVariables controllerVariables = webTargets.getControllerVariables();
143         updateStatus(ThingStatus.ONLINE);
144         if (controllerVariables != null) {
145             updateState(OpenGarageBindingConstants.CHANNEL_OG_DISTANCE,
146                     new QuantityType<>(controllerVariables.dist, MetricPrefix.CENTI(SIUnits.METRE)));
147             boolean invert = isChannelInverted(OpenGarageBindingConstants.CHANNEL_OG_STATUS_SWITCH);
148             switch (controllerVariables.door) {
149                 case 0:
150                     updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS, invert ? OnOffType.ON : OnOffType.OFF);
151                     updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_SWITCH,
152                             invert ? OnOffType.ON : OnOffType.OFF);
153                     updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_ROLLERSHUTTER, UpDownType.DOWN);
154                     updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_CONTACT, OpenClosedType.CLOSED);
155                     break;
156                 case 1:
157                     updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS, invert ? OnOffType.OFF : OnOffType.ON);
158                     updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_SWITCH,
159                             invert ? OnOffType.OFF : OnOffType.ON);
160                     updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_ROLLERSHUTTER, UpDownType.UP);
161                     updateState(OpenGarageBindingConstants.CHANNEL_OG_STATUS_CONTACT, OpenClosedType.OPEN);
162                     break;
163                 default:
164                     logger.warn("Received unknown door value: {}", controllerVariables.door);
165             }
166             switch (controllerVariables.vehicle) {
167                 case 0:
168                     updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, new StringType("No vehicle detected"));
169                     break;
170                 case 1:
171                     updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, new StringType("Vehicle detected"));
172                     break;
173                 case 2:
174                     updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE,
175                             new StringType("Vehicle status unknown"));
176                     break;
177                 default:
178                     logger.warn("Received unknown vehicle value: {}", controllerVariables.vehicle);
179             }
180             updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE_STATUS,
181                     new DecimalType(controllerVariables.vehicle));
182         }
183     }
184
185     private void changeStatus(OpenGarageCommand status) throws OpenGarageCommunicationException {
186         webTargets.setControllerVariables(status);
187     }
188
189     private boolean isChannelInverted(String channelUID) {
190         Channel channel = getThing().getChannel(channelUID);
191         return channel != null && channel.getConfiguration().as(OpenGarageChannelConfiguration.class).invert;
192     }
193 }