]> git.basschouten.com Git - openhab-addons.git/blob
582e3ca67d79494a13d2581fcf6837d8b4bd9819
[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.openuv.internal.handler;
14
15 import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.*;
16
17 import java.time.ZoneId;
18 import java.time.ZonedDateTime;
19 import java.time.temporal.ChronoUnit;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22
23 import javax.measure.quantity.Angle;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.openuv.internal.config.ReportConfiguration;
28 import org.openhab.binding.openuv.internal.config.SafeExposureConfiguration;
29 import org.openhab.binding.openuv.internal.json.OpenUVResult;
30 import org.openhab.core.library.types.DateTimeType;
31 import org.openhab.core.library.types.HSBType;
32 import org.openhab.core.library.types.PointType;
33 import org.openhab.core.library.types.QuantityType;
34 import org.openhab.core.library.unit.SmartHomeUnits;
35 import org.openhab.core.thing.Bridge;
36 import org.openhab.core.thing.Channel;
37 import org.openhab.core.thing.ChannelUID;
38 import org.openhab.core.thing.Thing;
39 import org.openhab.core.thing.ThingStatus;
40 import org.openhab.core.thing.ThingStatusDetail;
41 import org.openhab.core.thing.ThingStatusInfo;
42 import org.openhab.core.thing.binding.BaseThingHandler;
43 import org.openhab.core.thing.type.ChannelTypeUID;
44 import org.openhab.core.types.Command;
45 import org.openhab.core.types.RefreshType;
46 import org.openhab.core.types.State;
47 import org.openhab.core.types.UnDefType;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 /**
52  * The {@link OpenUVReportHandler} is responsible for handling commands, which are
53  * sent to one of the channels.
54  *
55  * @author Gaël L'hopital - Initial contribution
56  */
57 @NonNullByDefault
58 public class OpenUVReportHandler extends BaseThingHandler {
59     private final Logger logger = LoggerFactory.getLogger(OpenUVReportHandler.class);
60
61     private @NonNullByDefault({}) OpenUVBridgeHandler bridgeHandler;
62     private @Nullable ScheduledFuture<?> refreshJob;
63     private @Nullable ScheduledFuture<?> uvMaxJob;
64     private boolean suspendUpdates = false;
65
66     public OpenUVReportHandler(Thing thing) {
67         super(thing);
68     }
69
70     @Override
71     public void initialize() {
72         logger.debug("Initializing OpenUV handler.");
73
74         ReportConfiguration config = getConfigAs(ReportConfiguration.class);
75
76         if (config.refresh < 3) {
77             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
78                     "Parameter 'refresh' must be higher than 3 minutes to stay in free API plan");
79         } else {
80             Bridge bridge = getBridge();
81             if (bridge == null) {
82                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid bridge");
83             } else {
84                 bridgeHandler = (OpenUVBridgeHandler) bridge.getHandler();
85                 updateStatus(ThingStatus.UNKNOWN);
86                 startAutomaticRefresh();
87             }
88         }
89     }
90
91     /**
92      * Start the job screening UV Max reached
93      *
94      * @param openUVData
95      */
96     private void scheduleUVMaxEvent(OpenUVResult openUVData) {
97         ScheduledFuture<?> job = this.uvMaxJob;
98         if ((job == null || job.isCancelled())) {
99             State uvMaxTime = openUVData.getUVMaxTime();
100             if (uvMaxTime != UnDefType.NULL) {
101                 ZonedDateTime uvMaxZdt = ((DateTimeType) uvMaxTime).getZonedDateTime();
102                 long timeDiff = ChronoUnit.MINUTES.between(ZonedDateTime.now(ZoneId.systemDefault()), uvMaxZdt);
103                 if (timeDiff > 0) {
104                     logger.debug("Scheduling {} in {} minutes", UV_MAX_EVENT, timeDiff);
105                     uvMaxJob = scheduler.schedule(() -> {
106                         triggerChannel(UV_MAX_EVENT);
107                         uvMaxJob = null;
108                     }, timeDiff, TimeUnit.MINUTES);
109                 }
110             }
111         }
112     }
113
114     /**
115      * Start the job refreshing the data
116      */
117     private void startAutomaticRefresh() {
118         ScheduledFuture<?> job = this.refreshJob;
119         if (job == null || job.isCancelled()) {
120             ReportConfiguration config = getConfigAs(ReportConfiguration.class);
121             refreshJob = scheduler.scheduleWithFixedDelay(() -> {
122                 if (!suspendUpdates) {
123                     updateChannels(config);
124                 }
125             }, 0, config.refresh, TimeUnit.MINUTES);
126         }
127     }
128
129     private void updateChannels(ReportConfiguration config) {
130         ThingStatusInfo bridgeStatusInfo = bridgeHandler.getThing().getStatusInfo();
131         if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
132             PointType location = new PointType(config.location);
133             OpenUVResult openUVData = bridgeHandler.getUVData(location.getLatitude().toString(),
134                     location.getLongitude().toString(), location.getAltitude().toString());
135             if (openUVData != null) {
136                 scheduleUVMaxEvent(openUVData);
137                 getThing().getChannels().forEach(channel -> {
138                     updateChannel(channel.getUID(), openUVData);
139                 });
140                 updateStatus(ThingStatus.ONLINE);
141             } else {
142                 updateStatus(ThingStatus.OFFLINE, bridgeStatusInfo.getStatusDetail(),
143                         bridgeStatusInfo.getDescription());
144             }
145         }
146     }
147
148     @Override
149     public void dispose() {
150         logger.debug("Disposing the OpenUV handler.");
151         ScheduledFuture<?> refresh = this.refreshJob;
152         if (refresh != null && !refresh.isCancelled()) {
153             refresh.cancel(true);
154         }
155         refreshJob = null;
156
157         ScheduledFuture<?> uxMax = this.uvMaxJob;
158         if (uxMax != null && !uxMax.isCancelled()) {
159             uxMax.cancel(true);
160         }
161         uvMaxJob = null;
162     }
163
164     @SuppressWarnings("unchecked")
165     @Override
166     public void handleCommand(ChannelUID channelUID, Command command) {
167         if (command instanceof RefreshType) {
168             scheduler.execute(() -> {
169                 ReportConfiguration config = getConfigAs(ReportConfiguration.class);
170                 updateChannels(config);
171             });
172         } else if (ELEVATION.equals(channelUID.getId()) && command instanceof QuantityType) {
173             QuantityType<?> qtty = (QuantityType<?>) command;
174             if ("°".equals(qtty.getUnit().toString())) {
175                 suspendUpdates = ((QuantityType<Angle>) qtty).doubleValue() < 0;
176             } else {
177                 logger.info("The OpenUV Report handles Sun Elevation of Number:Angle type, {} does not fit.", command);
178             }
179         } else {
180             logger.info("The OpenUV Report Thing handles Refresh or Sun Elevation command and not '{}'", command);
181         }
182     }
183
184     /**
185      * Update the channel from the last OpenUV data retrieved
186      *
187      * @param channelUID the id identifying the channel to be updated
188      * @param openUVData
189      *
190      */
191     private void updateChannel(ChannelUID channelUID, OpenUVResult openUVData) {
192         Channel channel = getThing().getChannel(channelUID.getId());
193         if (channel != null && isLinked(channelUID)) {
194             ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
195             if (channelTypeUID != null) {
196                 switch (channelTypeUID.getId()) {
197                     case UV_INDEX:
198                         updateState(channelUID, openUVData.getUv());
199                         break;
200                     case UV_COLOR:
201                         updateState(channelUID, getAsHSB(openUVData.getUv().intValue()));
202                         break;
203                     case UV_MAX:
204                         updateState(channelUID, openUVData.getUvMax());
205                         break;
206                     case OZONE:
207                         updateState(channelUID, new QuantityType<>(openUVData.getOzone(), SmartHomeUnits.DOBSON_UNIT));
208                         break;
209                     case OZONE_TIME:
210                         updateState(channelUID, openUVData.getOzoneTime());
211                         break;
212                     case UV_MAX_TIME:
213                         updateState(channelUID, openUVData.getUVMaxTime());
214                         break;
215                     case UV_TIME:
216                         updateState(channelUID, openUVData.getUVTime());
217                         break;
218                     case SAFE_EXPOSURE:
219                         SafeExposureConfiguration configuration = channel.getConfiguration()
220                                 .as(SafeExposureConfiguration.class);
221                         if (configuration.index != -1) {
222                             updateState(channelUID,
223                                     openUVData.getSafeExposureTime().getSafeExposure(configuration.index));
224                         }
225                         break;
226                 }
227             }
228         }
229     }
230
231     private State getAsHSB(int uv) {
232         if (uv >= 11) {
233             return HSBType.fromRGB(106, 27, 154);
234         } else if (uv >= 8) {
235             return HSBType.fromRGB(183, 28, 28);
236         } else if (uv >= 6) {
237             return HSBType.fromRGB(239, 108, 0);
238         } else if (uv >= 3) {
239             return HSBType.fromRGB(249, 168, 37);
240         } else {
241             return HSBType.fromRGB(85, 139, 47);
242         }
243     }
244 }