]> git.basschouten.com Git - openhab-addons.git/blob
0bf43f57662fe04f43ad03a70d16af21d37c5b04
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.danfossairunit.internal;
14
15 import static org.openhab.binding.danfossairunit.internal.DanfossAirUnitBindingConstants.*;
16
17 import java.io.IOException;
18 import java.net.InetAddress;
19 import java.net.UnknownHostException;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.core.thing.ChannelUID;
26 import org.openhab.core.thing.Thing;
27 import org.openhab.core.thing.ThingStatus;
28 import org.openhab.core.thing.ThingStatusDetail;
29 import org.openhab.core.thing.binding.BaseThingHandler;
30 import org.openhab.core.types.Command;
31 import org.openhab.core.types.RefreshType;
32 import org.openhab.core.types.State;
33 import org.openhab.core.types.UnDefType;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * The {@link DanfossAirUnitHandler} is responsible for handling commands, which are
39  * sent to one of the channels.
40  *
41  * @author Ralf Duckstein - Initial contribution
42  * @author Robert Bach - heavy refactorings
43  * @author Jacob Laursen - Refactoring, bugfixes and enhancements
44  */
45 @NonNullByDefault
46 public class DanfossAirUnitHandler extends BaseThingHandler {
47
48     private static final int TCP_PORT = 30046;
49     private static final int POLLING_INTERVAL_SECONDS = 5;
50     private final Logger logger = LoggerFactory.getLogger(DanfossAirUnitHandler.class);
51     private @NonNullByDefault({}) DanfossAirUnitConfiguration config;
52     private @Nullable ValueCache valueCache;
53     private @Nullable ScheduledFuture<?> pollingJob;
54     private @Nullable DanfossAirUnitCommunicationController communicationController;
55     private @Nullable DanfossAirUnit airUnit;
56
57     public DanfossAirUnitHandler(Thing thing) {
58         super(thing);
59     }
60
61     @Override
62     public void handleCommand(ChannelUID channelUID, Command command) {
63         if (command instanceof RefreshType) {
64             updateAllChannels();
65         } else {
66             try {
67                 DanfossAirUnit localAirUnit = this.airUnit;
68                 if (localAirUnit != null) {
69                     Channel channel = Channel.getByName(channelUID.getIdWithoutGroup());
70                     DanfossAirUnitWriteAccessor writeAccessor = channel.getWriteAccessor();
71                     if (writeAccessor != null) {
72                         updateState(channelUID, writeAccessor.access(localAirUnit, command));
73                     }
74                 } else {
75                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.NONE,
76                             "Air unit connection not initialized.");
77                     return;
78                 }
79             } catch (IllegalArgumentException e) {
80                 logger.debug("Ignoring unknown channel id: {}", channelUID.getIdWithoutGroup(), e);
81             } catch (IOException ioe) {
82                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, ioe.getMessage());
83             }
84         }
85     }
86
87     @Override
88     public void initialize() {
89         updateStatus(ThingStatus.UNKNOWN);
90         config = getConfigAs(DanfossAirUnitConfiguration.class);
91         valueCache = new ValueCache(config.updateUnchangedValuesEveryMillis);
92         try {
93             var localCommunicationController = new DanfossAirUnitCommunicationController(
94                     InetAddress.getByName(config.host), TCP_PORT);
95             this.communicationController = localCommunicationController;
96             var localAirUnit = new DanfossAirUnit(localCommunicationController);
97             this.airUnit = localAirUnit;
98             scheduler.execute(() -> {
99                 try {
100                     thing.setProperty(PROPERTY_UNIT_NAME, localAirUnit.getUnitName());
101                     thing.setProperty(PROPERTY_SERIAL, localAirUnit.getUnitSerialNumber());
102                     startPolling();
103                     updateStatus(ThingStatus.ONLINE);
104                 } catch (IOException e) {
105                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
106                 }
107             });
108         } catch (UnknownHostException e) {
109             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
110                     "Unknown host: " + config.host);
111             return;
112         }
113     }
114
115     private void updateAllChannels() {
116         DanfossAirUnit localAirUnit = this.airUnit;
117         if (localAirUnit == null) {
118             return;
119         }
120
121         logger.debug("Updating DanfossHRV data '{}'", getThing().getUID());
122
123         for (Channel channel : Channel.values()) {
124             if (Thread.interrupted()) {
125                 logger.debug("Polling thread interrupted...");
126                 return;
127             }
128             try {
129                 updateState(channel.getGroup().getGroupName(), channel.getChannelName(),
130                         channel.getReadAccessor().access(localAirUnit));
131                 if (getThing().getStatus() == ThingStatus.OFFLINE) {
132                     updateStatus(ThingStatus.ONLINE);
133                 }
134             } catch (UnexpectedResponseValueException e) {
135                 updateState(channel.getGroup().getGroupName(), channel.getChannelName(), UnDefType.UNDEF);
136                 logger.debug(
137                         "Cannot update channel {}: an unexpected or invalid response has been received from the air unit: {}",
138                         channel.getChannelName(), e.getMessage());
139                 if (getThing().getStatus() == ThingStatus.OFFLINE) {
140                     updateStatus(ThingStatus.ONLINE);
141                 }
142             } catch (IOException e) {
143                 updateState(channel.getGroup().getGroupName(), channel.getChannelName(), UnDefType.UNDEF);
144                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
145                 logger.debug("Cannot update channel {}: an error occurred retrieving the value: {}",
146                         channel.getChannelName(), e.getMessage());
147             }
148         }
149     }
150
151     @Override
152     public void dispose() {
153         logger.debug("Disposing Danfoss HRV handler '{}'", getThing().getUID());
154
155         stopPolling();
156
157         this.airUnit = null;
158
159         DanfossAirUnitCommunicationController localCommunicationController = this.communicationController;
160         if (localCommunicationController != null) {
161             localCommunicationController.disconnect();
162         }
163         this.communicationController = null;
164     }
165
166     private synchronized void startPolling() {
167         this.pollingJob = scheduler.scheduleWithFixedDelay(this::updateAllChannels, POLLING_INTERVAL_SECONDS,
168                 config.refreshInterval, TimeUnit.SECONDS);
169     }
170
171     private synchronized void stopPolling() {
172         ScheduledFuture<?> localPollingJob = this.pollingJob;
173         if (localPollingJob != null) {
174             localPollingJob.cancel(true);
175         }
176         this.pollingJob = null;
177     }
178
179     private void updateState(String groupId, String channelId, State state) {
180         ValueCache cache = valueCache;
181         if (cache == null) {
182             return;
183         }
184
185         if (cache.updateValue(channelId, state)) {
186             updateState(new ChannelUID(thing.getUID(), groupId, channelId), state);
187         }
188     }
189 }