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.opensprinkler.internal.handler;
15 import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.*;
17 import java.math.BigDecimal;
19 import javax.measure.quantity.Time;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApi;
25 import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
26 import org.openhab.binding.opensprinkler.internal.api.exception.GeneralApiException;
27 import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerStationConfig;
28 import org.openhab.core.library.types.OnOffType;
29 import org.openhab.core.library.types.QuantityType;
30 import org.openhab.core.library.unit.Units;
31 import org.openhab.core.thing.Channel;
32 import org.openhab.core.thing.ChannelUID;
33 import org.openhab.core.thing.Thing;
34 import org.openhab.core.thing.ThingStatus;
35 import org.openhab.core.thing.ThingStatusDetail;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.RefreshType;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
42 * @author Florian Schmidt - Refactoring
45 public class OpenSprinklerStationHandler extends OpenSprinklerBaseHandler {
46 private final Logger logger = LoggerFactory.getLogger(OpenSprinklerStationHandler.class);
49 private OpenSprinklerStationConfig config;
51 private BigDecimal nextDurationTime;
53 public OpenSprinklerStationHandler(Thing thing) {
58 public void initialize() {
59 config = getConfig().as(OpenSprinklerStationConfig.class);
63 public void handleCommand(ChannelUID channelUID, Command command) {
64 OpenSprinklerApi api = getApi();
66 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "OpenSprinkler bridge has no initialized API.");
70 if (command != RefreshType.REFRESH) {
71 switch (channelUID.getIdWithoutGroup()) {
73 handleNextDurationCommand(channelUID, command);
76 handleStationStateCommand(api, command);
79 handleQueuedCommand(api, command);
86 @SuppressWarnings("null")
87 private void handleNextDurationCommand(ChannelUID channelUID, Command command) {
88 if (!(command instanceof QuantityType<?>)) {
89 logger.info("Ignoring implausible non-QuantityType command for NEXT_DURATION");
92 QuantityType<?> quantity = (QuantityType<?>) command;
93 this.nextDurationTime = quantity.toUnit(Units.SECOND).toBigDecimal();
94 updateState(channelUID, quantity);
97 private void handleStationStateCommand(OpenSprinklerApi api, Command command) {
98 if (!(command instanceof OnOffType)) {
99 logger.error("Received invalid command type for OpenSprinkler station ({}).", command);
103 if (command == OnOffType.ON) {
104 api.openStation(this.getStationIndex(), nextStationDuration());
106 api.closeStation(this.getStationIndex());
108 } catch (CommunicationApiException | GeneralApiException exp) {
109 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
110 "Could not control the station channel " + (this.getStationIndex() + 1)
111 + " for the OpenSprinkler. Error: " + exp.getMessage());
115 private void handleQueuedCommand(OpenSprinklerApi api, Command command) {
116 if (command == OnOffType.ON) {
119 handleStationStateCommand(api, command);
122 private BigDecimal nextStationDuration() {
123 BigDecimal nextDurationItemValue = nextDurationValue();
124 Channel nextDuration = getThing().getChannel(NEXT_DURATION);
125 if (nextDuration != null && isLinked(nextDuration.getUID()) && nextDurationItemValue != null) {
126 return nextDurationItemValue;
128 return new BigDecimal(64800);
132 * Handles determining a channel's current state from the OpenSprinkler device.
134 * @param stationId Int of the station to control. Starts at 0.
135 * @return State representation for the channel.
138 private OnOffType getStationState(int stationId) {
139 boolean stationOn = false;
140 OpenSprinklerApi api = getApi();
142 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
143 "OpenSprinkler bridge has no initialized API.");
148 stationOn = api.isStationOpen(stationId);
149 } catch (GeneralApiException | CommunicationApiException exp) {
150 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
151 "Could not get the station channel " + stationId
152 + " current state from the OpenSprinkler thing. Error: " + exp.getMessage());
158 return OnOffType.OFF;
163 * Handles determining a channel's current state from the OpenSprinkler device.
165 * @param stationId Int of the station to control. Starts at 0.
166 * @return State representation for the channel.
168 private @Nullable QuantityType<Time> getRemainingWaterTime(int stationId) {
169 long remainingWaterTime = 0;
170 OpenSprinklerApi api = getApi();
172 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
173 "OpenSprinkler bridge has no initialized API.");
178 remainingWaterTime = api.retrieveProgram(stationId).remainingWaterTime;
179 } catch (CommunicationApiException exp) {
180 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
181 "Could not get current state of station channel " + stationId
182 + " for the OpenSprinkler device. Exception received: " + exp);
185 return new QuantityType<>(remainingWaterTime, Units.SECOND);
189 protected void updateChannel(@NonNull ChannelUID channel) {
190 OnOffType currentDeviceState = getStationState(this.getStationIndex());
191 QuantityType<Time> remainingWaterTime = getRemainingWaterTime(config.stationIndex);
192 switch (channel.getIdWithoutGroup()) {
194 if (currentDeviceState != null) {
195 updateState(channel, currentDeviceState);
198 case REMAINING_WATER_TIME:
199 if (remainingWaterTime != null) {
200 updateState(channel, remainingWaterTime);
204 BigDecimal duration = nextDurationValue();
205 if (duration != null) {
206 updateState(channel, new QuantityType<>(duration, Units.SECOND));
210 if (remainingWaterTime != null && currentDeviceState != null && currentDeviceState == OnOffType.OFF
211 && remainingWaterTime.intValue() != 0) {
212 updateState(channel, OnOffType.ON);
214 updateState(channel, OnOffType.OFF);
218 logger.debug("Not updating unknown channel {}", channel);
222 private @Nullable BigDecimal nextDurationValue() {
223 return nextDurationTime;
226 private int getStationIndex() {
227 OpenSprinklerStationConfig config = this.config;
228 if (config == null) {
229 throw new IllegalStateException();
231 return config.stationIndex;