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