]> git.basschouten.com Git - openhab-addons.git/blob
ab1dcbc92c142b4e8fc1c0425320c75dc3c19371
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.opensprinkler.internal.handler;
14
15 import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.*;
16
17 import java.math.BigDecimal;
18
19 import javax.measure.quantity.Time;
20
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;
40
41 /**
42  * @author Florian Schmidt - Refactoring
43  */
44 @NonNullByDefault
45 public class OpenSprinklerStationHandler extends OpenSprinklerBaseHandler {
46     private final Logger logger = LoggerFactory.getLogger(OpenSprinklerStationHandler.class);
47
48     @Nullable
49     private OpenSprinklerStationConfig config;
50     @Nullable
51     private BigDecimal nextDurationTime;
52
53     public OpenSprinklerStationHandler(Thing thing) {
54         super(thing);
55     }
56
57     @Override
58     public void initialize() {
59         config = getConfig().as(OpenSprinklerStationConfig.class);
60     }
61
62     @Override
63     public void handleCommand(ChannelUID channelUID, Command command) {
64         OpenSprinklerApi api = getApi();
65         if (api == null) {
66             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "OpenSprinkler bridge has no initialized API.");
67             return;
68         }
69
70         if (command != RefreshType.REFRESH) {
71             switch (channelUID.getIdWithoutGroup()) {
72                 case NEXT_DURATION:
73                     handleNextDurationCommand(channelUID, command);
74                     break;
75                 case STATION_STATE:
76                     handleStationStateCommand(api, command);
77                     break;
78                 case STATION_QUEUED:
79                     handleQueuedCommand(api, command);
80                     break;
81             }
82         }
83         updateChannels();
84     }
85
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");
90             return;
91         }
92         QuantityType<?> quantity = (QuantityType<?>) command;
93         this.nextDurationTime = quantity.toUnit(Units.SECOND).toBigDecimal();
94         updateState(channelUID, quantity);
95     }
96
97     private void handleStationStateCommand(OpenSprinklerApi api, Command command) {
98         if (!(command instanceof OnOffType)) {
99             logger.error("Received invalid command type for OpenSprinkler station ({}).", command);
100             return;
101         }
102         try {
103             if (command == OnOffType.ON) {
104                 api.openStation(this.getStationIndex(), nextStationDuration());
105             } else {
106                 api.closeStation(this.getStationIndex());
107             }
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());
112         }
113     }
114
115     private void handleQueuedCommand(OpenSprinklerApi api, Command command) {
116         if (command == OnOffType.ON) {
117             return;
118         }
119         handleStationStateCommand(api, command);
120     }
121
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;
127         }
128         return new BigDecimal(64800);
129     }
130
131     /**
132      * Handles determining a channel's current state from the OpenSprinkler device.
133      *
134      * @param stationId Int of the station to control. Starts at 0.
135      * @return State representation for the channel.
136      */
137     @Nullable
138     private OnOffType getStationState(int stationId) {
139         boolean stationOn = false;
140         OpenSprinklerApi api = getApi();
141         if (api == null) {
142             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
143                     "OpenSprinkler bridge has no initialized API.");
144             return null;
145         }
146
147         try {
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());
153         }
154
155         if (stationOn) {
156             return OnOffType.ON;
157         } else {
158             return OnOffType.OFF;
159         }
160     }
161
162     /**
163      * Handles determining a channel's current state from the OpenSprinkler device.
164      *
165      * @param stationId Int of the station to control. Starts at 0.
166      * @return State representation for the channel.
167      */
168     private @Nullable QuantityType<Time> getRemainingWaterTime(int stationId) {
169         long remainingWaterTime = 0;
170         OpenSprinklerApi api = getApi();
171         if (api == null) {
172             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
173                     "OpenSprinkler bridge has no initialized API.");
174             return null;
175         }
176
177         try {
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);
183         }
184
185         return new QuantityType<>(remainingWaterTime, Units.SECOND);
186     }
187
188     @Override
189     protected void updateChannel(@NonNull ChannelUID channel) {
190         OnOffType currentDeviceState = getStationState(this.getStationIndex());
191         QuantityType<Time> remainingWaterTime = getRemainingWaterTime(config.stationIndex);
192         switch (channel.getIdWithoutGroup()) {
193             case STATION_STATE:
194                 if (currentDeviceState != null) {
195                     updateState(channel, currentDeviceState);
196                 }
197                 break;
198             case REMAINING_WATER_TIME:
199                 if (remainingWaterTime != null) {
200                     updateState(channel, remainingWaterTime);
201                 }
202                 break;
203             case NEXT_DURATION:
204                 BigDecimal duration = nextDurationValue();
205                 if (duration != null) {
206                     updateState(channel, new QuantityType<>(duration, Units.SECOND));
207                 }
208                 break;
209             case STATION_QUEUED:
210                 if (remainingWaterTime != null && currentDeviceState != null && currentDeviceState == OnOffType.OFF
211                         && remainingWaterTime.intValue() != 0) {
212                     updateState(channel, OnOffType.ON);
213                 } else {
214                     updateState(channel, OnOffType.OFF);
215                 }
216                 break;
217             default:
218                 logger.debug("Not updating unknown channel {}", channel);
219         }
220     }
221
222     private @Nullable BigDecimal nextDurationValue() {
223         return nextDurationTime;
224     }
225
226     private int getStationIndex() {
227         OpenSprinklerStationConfig config = this.config;
228         if (config == null) {
229             throw new IllegalStateException();
230         }
231         return config.stationIndex;
232     }
233 }