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