]> git.basschouten.com Git - openhab-addons.git/blob
a0d8ca45146a2a26370c6225cd117147bfda690d
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
14
15 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.*;
16
17 import java.time.ZonedDateTime;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Objects;
21 import java.util.Optional;
22
23 import javax.measure.Quantity;
24 import javax.measure.Unit;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.core.library.types.DateTimeType;
29 import org.openhab.core.library.types.DecimalType;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.library.types.QuantityType;
32 import org.openhab.core.library.types.StringType;
33 import org.openhab.core.thing.Channel;
34 import org.openhab.core.thing.ChannelGroupUID;
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.binding.BaseThingHandler;
40 import org.openhab.core.thing.binding.builder.ChannelBuilder;
41 import org.openhab.core.thing.binding.builder.ThingBuilder;
42 import org.openhab.core.thing.type.ChannelType;
43 import org.openhab.core.thing.type.ChannelTypeRegistry;
44 import org.openhab.core.types.Command;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * The {@link WundergroundUpdateReceiverHandler} is responsible for handling commands, which are
50  * sent to one of the channels.
51  *
52  * @author Daniel Demus - Initial contribution
53  */
54 @NonNullByDefault
55 public class WundergroundUpdateReceiverHandler extends BaseThingHandler {
56
57     public String getStationId() {
58         return config.stationId;
59     }
60
61     private final Logger logger = LoggerFactory.getLogger(WundergroundUpdateReceiverHandler.class);
62     private final WundergroundUpdateReceiverServlet wundergroundUpdateReceiverServlet;
63     private final WundergroundUpdateReceiverDiscoveryService discoveryService;
64     private final WundergroundUpdateReceiverUnknownChannelTypeProvider channelTypeProvider;
65     private final ChannelTypeRegistry channelTypeRegistry;
66
67     private final ChannelUID dateutcDatetimeChannel;
68     private final ChannelUID lastReceivedChannel;
69     private final ChannelUID queryStateChannel;
70     private final ChannelUID queryTriggerChannel;
71
72     private WundergroundUpdateReceiverConfiguration config = new WundergroundUpdateReceiverConfiguration();
73
74     public WundergroundUpdateReceiverHandler(Thing thing,
75             WundergroundUpdateReceiverServlet wunderGroundUpdateReceiverServlet,
76             WundergroundUpdateReceiverDiscoveryService discoveryService,
77             WundergroundUpdateReceiverUnknownChannelTypeProvider channelTypeProvider,
78             ChannelTypeRegistry channelTypeRegistry) {
79         super(thing);
80         this.discoveryService = discoveryService;
81         this.channelTypeProvider = channelTypeProvider;
82         this.channelTypeRegistry = channelTypeRegistry;
83
84         final ChannelGroupUID metadatGroupUID = new ChannelGroupUID(getThing().getUID(), METADATA_GROUP);
85
86         this.dateutcDatetimeChannel = new ChannelUID(metadatGroupUID, DATEUTC_DATETIME);
87         this.lastReceivedChannel = new ChannelUID(metadatGroupUID, LAST_RECEIVED);
88         this.queryTriggerChannel = new ChannelUID(metadatGroupUID, LAST_QUERY_TRIGGER);
89         this.queryStateChannel = new ChannelUID(metadatGroupUID, LAST_QUERY_STATE);
90
91         this.wundergroundUpdateReceiverServlet = Objects.requireNonNull(wunderGroundUpdateReceiverServlet);
92     }
93
94     @Override
95     public void handleCommand(ChannelUID channelUID, Command command) {
96         logger.trace("Ignoring command {}", command);
97     }
98
99     @Override
100     public void handleConfigurationUpdate(Map<String, Object> configurationParameters) {
101         super.handleConfigurationUpdate(configurationParameters);
102         this.wundergroundUpdateReceiverServlet.handlerConfigUpdated(this);
103     }
104
105     @Override
106     public void initialize() {
107         this.config = getConfigAs(WundergroundUpdateReceiverConfiguration.class);
108         wundergroundUpdateReceiverServlet.addHandler(this);
109         @Nullable
110         Map<String, String[]> requestParameters = discoveryService.getUnhandledStationRequest(config.stationId);
111         if (requestParameters != null && thing.getChannels().isEmpty()) {
112             final String[] noValues = new String[0];
113             ThingBuilder thingBuilder = editThing();
114             List.of(LAST_RECEIVED, LAST_QUERY_TRIGGER, DATEUTC_DATETIME, LAST_QUERY_STATE)
115                     .forEach((String channelId) -> buildChannel(thingBuilder, channelId, noValues));
116             requestParameters
117                     .forEach((String parameter, String[] query) -> buildChannel(thingBuilder, parameter, query));
118             updateThing(thingBuilder.build());
119         }
120         discoveryService.removeUnhandledStationId(config.stationId);
121         if (wundergroundUpdateReceiverServlet.isActive()) {
122             updateStatus(ThingStatus.ONLINE);
123             logger.debug("Wunderground update receiver listening for updates to station id {}", config.stationId);
124             return;
125         }
126         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
127                 wundergroundUpdateReceiverServlet.getErrorDetail());
128     }
129
130     @Override
131     public void dispose() {
132         wundergroundUpdateReceiverServlet.removeHandler(this.getStationId());
133         super.dispose();
134         logger.debug("Wunderground update receiver stopped listening for updates to station id {}", config.stationId);
135     }
136
137     public void updateChannelStates(Map<String, String> requestParameters) {
138         requestParameters.forEach(this::updateChannelState);
139         updateState(lastReceivedChannel, new DateTimeType());
140         updateState(dateutcDatetimeChannel, safeResolvUtcDateTime(requestParameters.getOrDefault(DATEUTC, NOW)));
141         String lastQuery = requestParameters.getOrDefault(LAST_QUERY, "");
142         if (lastQuery.isEmpty()) {
143             return;
144         }
145         updateState(queryStateChannel, StringType.valueOf(lastQuery));
146         triggerChannel(queryTriggerChannel, lastQuery);
147     }
148
149     private void buildChannel(ThingBuilder thingBuilder, String parameter, String... query) {
150         @Nullable
151         WundergroundUpdateReceiverParameterMapping channelTypeMapping = WundergroundUpdateReceiverParameterMapping
152                 .getOrCreateMapping(parameter, String.join("", query), channelTypeProvider);
153         if (channelTypeMapping == null) {
154             return;
155         }
156         ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeMapping.channelTypeId);
157         if (channelType == null) {
158             return;
159         }
160         ChannelBuilder channelBuilder = ChannelBuilder
161                 .create(new ChannelUID(thing.getUID(), channelTypeMapping.channelGroup, parameter))
162                 .withType(channelTypeMapping.channelTypeId).withAcceptedItemType(channelType.getItemType());
163         thingBuilder.withChannel(channelBuilder.build());
164     }
165
166     private DateTimeType safeResolvUtcDateTime(String dateUtc) {
167         if (!dateUtc.isEmpty() && !NOW.equals(dateUtc)) {
168             try {
169                 // Supposedly the format is "yyyy-MM-dd hh:mm:ss" from the weather station
170                 return new DateTimeType(ZonedDateTime.parse(dateUtc.replace(" ", "T") + "Z"));
171             } catch (Exception ex) {
172                 logger.warn("The weather station is submitting unparsable datetime values: {}", dateUtc);
173             }
174         }
175         return new DateTimeType();
176     }
177
178     public void updateChannelState(String channelId, String[] stateParts) {
179         updateChannelState(channelId, String.join("", stateParts));
180     }
181
182     public void updateChannelState(String parameterName, String state) {
183         Optional<Channel> channel = getThing().getChannels().stream()
184                 .filter(ch -> parameterName.equals(ch.getUID().getIdWithoutGroup())).findFirst();
185         if (channel.isPresent()) {
186             ChannelUID channelUID = channel.get().getUID();
187             @Nullable
188             Float numberValue = null;
189             try {
190                 numberValue = Float.valueOf(state);
191             } catch (NumberFormatException ignored) {
192             }
193
194             if (numberValue == null) {
195                 updateState(channelUID, StringType.valueOf(state));
196                 return;
197             }
198             @Nullable
199             Unit<? extends Quantity<?>> unit = WundergroundUpdateReceiverParameterMapping.getUnit(parameterName);
200             if (unit != null) {
201                 updateState(channelUID, new QuantityType<>(numberValue, unit));
202             } else if (LOW_BATTERY.equals(parameterName)) {
203                 updateState(channelUID, OnOffType.from(state));
204             } else {
205                 updateState(channelUID, new DecimalType(numberValue));
206             }
207         } else if (this.discoveryService.isDiscovering()
208                 && !WundergroundUpdateReceiverParameterMapping.isExcluded(parameterName)) {
209             ThingBuilder thingBuilder = editThing();
210             buildChannel(thingBuilder, parameterName, state);
211             updateThing(thingBuilder.build());
212         }
213     }
214 }