2 * Copyright (c) 2010-2023 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.opengarage.internal;
15 import java.io.IOException;
16 import java.util.concurrent.Future;
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.TimeUnit;
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;
44 * The {@link OpenGarageHandler} is responsible for handling commands, which are
45 * sent to one of the channels.
47 * @author Paul Smedley - Initial contribution
48 * @author Dan Cunningham - Minor improvements to vehicle state and invert option
51 public class OpenGarageHandler extends BaseThingHandler {
53 private final Logger logger = LoggerFactory.getLogger(OpenGarageHandler.class);
55 private long refreshInterval;
57 private @NonNullByDefault({}) OpenGarageWebTargets webTargets;
58 private @Nullable ScheduledFuture<?> pollFuture;
60 public OpenGarageHandler(Thing thing) {
65 public void handleCommand(ChannelUID channelUID, Command command) {
67 logger.debug("Received command {} for thing '{}' on channel {}", command, thing.getUID().getAsString(),
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);
77 } else if (command.equals(OnOffType.OFF) || command.equals(UpDownType.DOWN)) {
78 changeStatus(invert ? OpenGarageCommand.OPEN : OpenGarageCommand.CLOSE);
80 } else if (command.equals(StopMoveType.STOP) || command.equals(StopMoveType.MOVE)) {
81 changeStatus(OpenGarageCommand.CLICK);
87 } catch (OpenGarageCommunicationException ex) {
88 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ex.getMessage());
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");
99 webTargets = new OpenGarageWebTargets(config.hostname, config.port, config.password);
100 refreshInterval = config.refresh;
107 public void dispose() {
112 private void schedulePoll() {
113 if (pollFuture != null) {
114 pollFuture.cancel(false);
116 logger.debug("Scheduling poll for 1 second out, then every {} s", refreshInterval);
117 pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 1, refreshInterval, TimeUnit.SECONDS);
120 private void poll() {
122 logger.debug("Polling for state");
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());
133 private void stopPoll() {
134 final Future<?> future = pollFuture;
135 if (future != null && !future.isCancelled()) {
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) {
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);
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);
164 logger.warn("Received unknown door value: {}", controllerVariables.door);
166 switch (controllerVariables.vehicle) {
168 updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, new StringType("No vehicle detected"));
171 updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE, new StringType("Vehicle detected"));
174 updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE,
175 new StringType("Vehicle status unknown"));
178 updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE,
179 new StringType("Vehicle status not available"));
182 logger.warn("Received unknown vehicle value: {}", controllerVariables.vehicle);
184 updateState(OpenGarageBindingConstants.CHANNEL_OG_VEHICLE_STATUS,
185 new DecimalType(controllerVariables.vehicle));
189 private void changeStatus(OpenGarageCommand status) throws OpenGarageCommunicationException {
190 webTargets.setControllerVariables(status);
193 private boolean isChannelInverted(String channelUID) {
194 Channel channel = getThing().getChannel(channelUID);
195 return channel != null && channel.getConfiguration().as(OpenGarageChannelConfiguration.class).invert;