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.DecimalType;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.QuantityType;
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;
41 import tec.uom.se.unit.Units;
44 * @author Florian Schmidt - Refactoring
47 public class OpenSprinklerStationHandler extends OpenSprinklerBaseHandler {
48 private final Logger logger = LoggerFactory.getLogger(OpenSprinklerStationHandler.class);
51 private OpenSprinklerStationConfig config;
53 private BigDecimal nextDurationTime;
55 public OpenSprinklerStationHandler(Thing thing) {
60 public void initialize() {
61 config = getConfig().as(OpenSprinklerStationConfig.class);
65 public void handleCommand(ChannelUID channelUID, Command command) {
66 OpenSprinklerApi api = getApi();
68 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "OpenSprinkler bridge has no initialized API.");
72 if (command != RefreshType.REFRESH) {
73 switch (channelUID.getIdWithoutGroup()) {
75 handleNextDurationCommand(channelUID, command);
78 handleStationStateCommand(api, command);
81 handleQueuedCommand(api, command);
88 private void handleNextDurationCommand(ChannelUID channelUID, Command command) {
89 if (!(command instanceof QuantityType<?>)) {
90 logger.info("Ignoring implausible non-QuantityType command for NEXT_DURATION");
93 QuantityType<?> quantity = (QuantityType<?>) command;
94 this.nextDurationTime = quantity.toBigDecimal();
95 updateState(channelUID, quantity);
98 private void handleStationStateCommand(OpenSprinklerApi api, Command command) {
99 if (!(command instanceof OnOffType)) {
100 logger.error("Received invalid command type for OpenSprinkler station ({}).", command);
104 if (command == OnOffType.ON) {
105 api.openStation(this.getStationIndex(), nextStationDuration());
107 api.closeStation(this.getStationIndex());
109 } catch (CommunicationApiException | GeneralApiException exp) {
110 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
111 "Could not control the station channel " + (this.getStationIndex() + 1)
112 + " for the OpenSprinkler. Error: " + exp.getMessage());
116 private void handleQueuedCommand(OpenSprinklerApi api, Command command) {
117 if (command == OnOffType.ON) {
120 handleStationStateCommand(api, command);
123 private BigDecimal nextStationDuration() {
124 BigDecimal nextDurationItemValue = nextDurationValue();
125 Channel nextDuration = getThing().getChannel(NEXT_DURATION);
126 if (nextDuration != null && isLinked(nextDuration.getUID()) && nextDurationItemValue != null) {
127 return nextDurationItemValue;
129 return new BigDecimal(64800);
133 * Handles determining a channel's current state from the OpenSprinkler device.
135 * @param stationId Int of the station to control. Starts at 0.
136 * @return State representation for the channel.
139 private OnOffType getStationState(int stationId) {
140 boolean stationOn = false;
141 OpenSprinklerApi api = getApi();
143 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
144 "OpenSprinkler bridge has no initialized API.");
149 stationOn = api.isStationOpen(stationId);
150 } catch (GeneralApiException | CommunicationApiException exp) {
151 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
152 "Could not get the station channel " + stationId
153 + " current state from the OpenSprinkler thing. Error: " + exp.getMessage());
159 return OnOffType.OFF;
164 * Handles determining a channel's current state from the OpenSprinkler device.
166 * @param stationId Int of the station to control. Starts at 0.
167 * @return State representation for the channel.
169 private @Nullable QuantityType<Time> getRemainingWaterTime(int stationId) {
170 long remainingWaterTime = 0;
171 OpenSprinklerApi api = getApi();
173 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
174 "OpenSprinkler bridge has no initialized API.");
179 remainingWaterTime = api.retrieveProgram(stationId).remainingWaterTime;
180 } catch (CommunicationApiException exp) {
181 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
182 "Could not get current state of station channel " + stationId
183 + " for the OpenSprinkler device. Exception received: " + exp);
186 return new QuantityType<>(remainingWaterTime, Units.SECOND);
190 protected void updateChannel(@NonNull ChannelUID channel) {
191 OnOffType currentDeviceState = getStationState(this.getStationIndex());
192 QuantityType<Time> remainingWaterTime = getRemainingWaterTime(config.stationIndex);
193 switch (channel.getIdWithoutGroup()) {
195 if (currentDeviceState != null) {
196 updateState(channel, currentDeviceState);
199 case REMAINING_WATER_TIME:
200 if (remainingWaterTime != null) {
201 updateState(channel, remainingWaterTime);
205 BigDecimal duration = nextDurationValue();
206 if (duration != null) {
207 updateState(channel, new DecimalType(duration));
211 if (remainingWaterTime != null && currentDeviceState != null && currentDeviceState == OnOffType.OFF
212 && remainingWaterTime.intValue() != 0) {
213 updateState(channel, OnOffType.ON);
215 updateState(channel, OnOffType.OFF);
219 logger.debug("Not updating unknown channel {}", channel);
223 private @Nullable BigDecimal nextDurationValue() {
224 return nextDurationTime;
227 private int getStationIndex() {
228 OpenSprinklerStationConfig config = this.config;
229 if (config == null) {
230 throw new IllegalStateException();
232 return config.stationIndex;