]> git.basschouten.com Git - openhab-addons.git/blob
4dd9b2570523270b221f77c920cc5c92bab7eaca
[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.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.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApi;
24 import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
25 import org.openhab.binding.opensprinkler.internal.api.exception.GeneralApiException;
26 import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerStationConfig;
27 import org.openhab.core.library.types.OnOffType;
28 import org.openhab.core.library.types.QuantityType;
29 import org.openhab.core.library.unit.Units;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.ThingStatusDetail;
34 import org.openhab.core.types.Command;
35 import org.openhab.core.types.RefreshType;
36
37 /**
38  * @author Chris Graham - Initial contribution
39  * @author Florian Schmidt - Refactoring
40  */
41 @NonNullByDefault
42 public class OpenSprinklerStationHandler extends OpenSprinklerBaseHandler {
43     private OpenSprinklerStationConfig config = new OpenSprinklerStationConfig();
44
45     public OpenSprinklerStationHandler(Thing thing) {
46         super(thing);
47     }
48
49     @Override
50     public void initialize() {
51         super.initialize();
52         config = getConfig().as(OpenSprinklerStationConfig.class);
53         OpenSprinklerApi api = getApi();
54         if (api != null && config.stationIndex >= api.getNumberOfStations()) {
55             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
56                     "Station Index is higher than the number of stations that the OpenSprinkler is reporting. Make sure your Station Index is correct.");
57         }
58     }
59
60     @Override
61     public void handleCommand(ChannelUID channelUID, Command command) {
62         OpenSprinklerApi api = getApi();
63         if (api == null) {
64             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "OpenSprinkler bridge has no initialized API.");
65             return;
66         }
67         try {
68             if (command != RefreshType.REFRESH) {
69                 switch (channelUID.getIdWithoutGroup()) {
70                     case NEXT_DURATION:
71                         handleNextDurationCommand(channelUID, command);
72                         break;
73                     case STATION_STATE:
74                         if (!(command instanceof OnOffType)) {
75                             logger.warn("Received invalid command type for OpenSprinkler station ({}).", command);
76                             return;
77                         }
78                         if (command == OnOffType.ON) {
79                             api.openStation(config.stationIndex, nextDurationValue());
80                         } else {
81                             api.closeStation(config.stationIndex);
82                         }
83                         break;
84                     case STATION_QUEUED:
85                         if (command == OnOffType.OFF) {
86                             api.closeStation(config.stationIndex);
87                         }
88                         break;
89                     case CHANNEL_IGNORE_RAIN:
90                         api.ignoreRain(config.stationIndex, command == OnOffType.ON);
91                         break;
92                 }
93                 OpenSprinklerHttpBridgeHandler localBridge = bridgeHandler;
94                 if (localBridge == null) {
95                     return;
96                 }
97                 // update all controls after a command is sent in case a long poll time is set.
98                 localBridge.delayedRefresh();
99             }
100         } catch (GeneralApiException | CommunicationApiException e) {
101             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
102                     "Could not control the station channel " + (config.stationIndex + 1)
103                             + " for the OpenSprinkler. Error: " + e.getMessage());
104         }
105     }
106
107     /**
108      * Handles determining a channel's current state from the OpenSprinkler device.
109      *
110      * @param stationId Int of the station to control. Starts at 0.
111      * @return State representation for the channel.
112      */
113     @Nullable
114     private OnOffType getStationState(int stationId) {
115         boolean stationOn = false;
116         OpenSprinklerApi api = getApi();
117         if (api == null) {
118             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
119                     "OpenSprinkler bridge has no initialized API.");
120             return null;
121         }
122
123         try {
124             stationOn = api.isStationOpen(stationId);
125         } catch (GeneralApiException | CommunicationApiException exp) {
126             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
127                     "Could not get the station channel " + stationId
128                             + " current state from the OpenSprinkler thing. Error: " + exp.getMessage());
129         }
130
131         if (stationOn) {
132             return OnOffType.ON;
133         } else {
134             return OnOffType.OFF;
135         }
136     }
137
138     /**
139      * Handles determining a channel's current state from the OpenSprinkler device.
140      *
141      * @param stationId Int of the station to control. Starts at 0.
142      * @return State representation for the channel.
143      */
144     private @Nullable QuantityType<Time> getRemainingWaterTime(int stationId) {
145         long remainingWaterTime = 0;
146         OpenSprinklerApi api = getApi();
147         if (api == null) {
148             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
149                     "OpenSprinkler bridge has no initialized API.");
150             return null;
151         }
152
153         try {
154             remainingWaterTime = api.retrieveProgram(stationId).remainingWaterTime;
155         } catch (CommunicationApiException exp) {
156             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
157                     "Could not get current state of station channel " + stationId
158                             + " for the OpenSprinkler device. Exception received: " + exp);
159         }
160
161         return new QuantityType<>(remainingWaterTime, Units.SECOND);
162     }
163
164     @Override
165     protected void updateChannel(ChannelUID channel) {
166         OnOffType currentDeviceState = getStationState(config.stationIndex);
167         QuantityType<Time> remainingWaterTime = getRemainingWaterTime(config.stationIndex);
168         OpenSprinklerApi api = getApi();
169         switch (channel.getIdWithoutGroup()) {
170             case STATION_STATE:
171                 if (currentDeviceState != null) {
172                     updateState(channel, currentDeviceState);
173                 }
174                 break;
175             case REMAINING_WATER_TIME:
176                 if (remainingWaterTime != null) {
177                     updateState(channel, remainingWaterTime);
178                 }
179                 break;
180             case NEXT_DURATION:
181                 BigDecimal duration = nextDurationValue();
182                 updateState(channel, new QuantityType<>(duration, Units.SECOND));
183                 break;
184             case STATION_QUEUED:
185                 if (remainingWaterTime != null && currentDeviceState != null && currentDeviceState == OnOffType.OFF
186                         && remainingWaterTime.intValue() != 0) {
187                     updateState(channel, OnOffType.ON);
188                 } else {
189                     updateState(channel, OnOffType.OFF);
190                 }
191                 break;
192             case CHANNEL_IGNORE_RAIN:
193                 if (api != null && api.isIgnoringRain(config.stationIndex)) {
194                     updateState(channel, OnOffType.ON);
195                 } else {
196                     updateState(channel, OnOffType.OFF);
197                 }
198                 break;
199             default:
200                 logger.debug("Not updating unknown channel {}", channel);
201         }
202     }
203 }