]> git.basschouten.com Git - openhab-addons.git/blob
1f5d66280209d252de272e8565c47c4615b9704c
[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.nikohomecontrol.internal.handler;
14
15 import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*;
16 import static org.openhab.core.types.RefreshType.REFRESH;
17
18 import java.util.HashMap;
19 import java.util.Map;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcEnergyMeter;
24 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcEnergyMeterEvent;
25 import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication;
26 import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcEnergyMeter2;
27 import org.openhab.core.library.types.QuantityType;
28 import org.openhab.core.library.unit.Units;
29 import org.openhab.core.thing.Bridge;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.ThingStatusDetail;
34 import org.openhab.core.thing.ThingStatusInfo;
35 import org.openhab.core.thing.binding.BaseThingHandler;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.UnDefType;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * The {@link NikoHomeControlEnergyMeterHandler} is responsible for handling commands, which are
43  * sent to one of the channels.
44  *
45  * @author Mark Herwege - Initial Contribution
46  */
47 @NonNullByDefault
48 public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implements NhcEnergyMeterEvent {
49
50     private final Logger logger = LoggerFactory.getLogger(NikoHomeControlEnergyMeterHandler.class);
51
52     private volatile @Nullable NhcEnergyMeter nhcEnergyMeter;
53
54     private volatile boolean initialized = false;
55
56     private String energyMeterId = "";
57
58     public NikoHomeControlEnergyMeterHandler(Thing thing) {
59         super(thing);
60     }
61
62     @Override
63     public void handleCommand(ChannelUID channelUID, Command command) {
64         NhcEnergyMeter nhcEnergyMeter = this.nhcEnergyMeter;
65         if (nhcEnergyMeter == null) {
66             logger.debug("energy meter with ID {} not initialized", energyMeterId);
67             return;
68         }
69
70         if (REFRESH.equals(command)) {
71             energyMeterEvent(nhcEnergyMeter.getPower());
72         }
73     }
74
75     @Override
76     public void initialize() {
77         initialized = false;
78
79         NikoHomeControlEnergyMeterConfig config = getConfig().as(NikoHomeControlEnergyMeterConfig.class);
80
81         energyMeterId = config.energyMeterId;
82
83         NikoHomeControlBridgeHandler bridgeHandler = getBridgeHandler();
84         if (bridgeHandler == null) {
85             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
86                     "@text/offline.configuration-error.invalid-bridge-handler");
87             return;
88         }
89
90         updateStatus(ThingStatus.UNKNOWN);
91
92         Bridge bridge = getBridge();
93         if ((bridge != null) && ThingStatus.ONLINE.equals(bridge.getStatus())) {
94             // We need to do this in a separate thread because we may have to wait for the
95             // communication to become active
96             scheduler.submit(this::startCommunication);
97         }
98     }
99
100     private synchronized void startCommunication() {
101         NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
102
103         if (nhcComm == null) {
104             return;
105         }
106
107         if (!nhcComm.communicationActive()) {
108             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
109                     "@text/offline.communication-error");
110             return;
111         }
112
113         NhcEnergyMeter nhcEnergyMeter = nhcComm.getEnergyMeters().get(energyMeterId);
114         if (nhcEnergyMeter == null) {
115             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
116                     "@text/offline.configuration-error.energyMeterId");
117             return;
118         }
119
120         nhcEnergyMeter.setEventHandler(this);
121
122         updateProperties(nhcEnergyMeter);
123
124         String location = nhcEnergyMeter.getLocation();
125         if (thing.getLocation() == null) {
126             thing.setLocation(location);
127         }
128
129         this.nhcEnergyMeter = nhcEnergyMeter;
130
131         logger.debug("energy meter intialized {}", energyMeterId);
132
133         Bridge bridge = getBridge();
134         if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
135             updateStatus(ThingStatus.ONLINE);
136         } else {
137             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
138         }
139
140         // Subscribing to power readings starts an intensive data flow, therefore only do it when there is an item
141         // linked to the channel
142         if (isLinked(CHANNEL_POWER)) {
143             nhcComm.startEnergyMeter(energyMeterId);
144         }
145
146         initialized = true;
147     }
148
149     @Override
150     public void dispose() {
151         NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
152
153         if (nhcComm != null) {
154             nhcComm.stopEnergyMeter(energyMeterId);
155         }
156     }
157
158     private void updateProperties(NhcEnergyMeter nhcEnergyMeter) {
159         Map<String, String> properties = new HashMap<>();
160
161         if (nhcEnergyMeter instanceof NhcEnergyMeter2) {
162             NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) nhcEnergyMeter;
163             properties.put(PROPERTY_DEVICE_TYPE, energyMeter.getDeviceType());
164             properties.put(PROPERTY_DEVICE_TECHNOLOGY, energyMeter.getDeviceTechnology());
165             properties.put(PROPERTY_DEVICE_MODEL, energyMeter.getDeviceModel());
166         }
167
168         thing.setProperties(properties);
169     }
170
171     @Override
172     public void energyMeterEvent(@Nullable Integer power) {
173         if (power == null) {
174             updateState(CHANNEL_POWER, UnDefType.UNDEF);
175         } else {
176             updateState(CHANNEL_POWER, new QuantityType<>(power, Units.WATT));
177         }
178         updateStatus(ThingStatus.ONLINE);
179     }
180
181     @Override
182     public void energyMeterInitialized() {
183         Bridge bridge = getBridge();
184         if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
185             updateStatus(ThingStatus.ONLINE);
186         }
187     }
188
189     @Override
190     public void energyMeterRemoved() {
191         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
192                 "@text/offline.configuration-error.energyMeterRemoved");
193     }
194
195     @Override
196     // Subscribing to power readings starts an intensive data flow, therefore only do it when there is an item linked to
197     // the channel
198     public void channelLinked(ChannelUID channelUID) {
199         NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
200         if (nhcComm != null) {
201             // This can be expensive, therefore do it in a job.
202             scheduler.submit(() -> {
203                 if (!nhcComm.communicationActive()) {
204                     restartCommunication(nhcComm);
205                 }
206
207                 if (nhcComm.communicationActive()) {
208                     nhcComm.startEnergyMeter(energyMeterId);
209                     updateStatus(ThingStatus.ONLINE);
210                 }
211             });
212         }
213     }
214
215     @Override
216     public void channelUnlinked(ChannelUID channelUID) {
217         NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
218         if (nhcComm != null) {
219             // This can be expensive, therefore do it in a job.
220             scheduler.submit(() -> {
221                 if (!nhcComm.communicationActive()) {
222                     restartCommunication(nhcComm);
223                 }
224
225                 if (nhcComm.communicationActive()) {
226                     nhcComm.stopEnergyMeter(energyMeterId);
227                     // as this is momentary power production/consumption, we set it UNDEF as we do not get readings
228                     // anymore
229                     updateState(CHANNEL_POWER, UnDefType.UNDEF);
230                     updateStatus(ThingStatus.ONLINE);
231                 }
232             });
233         }
234     }
235
236     private void restartCommunication(NikoHomeControlCommunication nhcComm) {
237         // We lost connection but the connection object is there, so was correctly started.
238         // Try to restart communication.
239         nhcComm.restartCommunication();
240         // If still not active, take thing offline and return.
241         if (!nhcComm.communicationActive()) {
242             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
243                     "@text/offline.communication-error");
244             return;
245         }
246         // Also put the bridge back online
247         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
248         if (nhcBridgeHandler != null) {
249             nhcBridgeHandler.bridgeOnline();
250         } else {
251             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
252                     "@text/offline.configuration-error.invalid-bridge-handler");
253         }
254     }
255
256     private @Nullable NikoHomeControlCommunication getCommunication(
257             @Nullable NikoHomeControlBridgeHandler nhcBridgeHandler) {
258         return nhcBridgeHandler != null ? nhcBridgeHandler.getCommunication() : null;
259     }
260
261     private @Nullable NikoHomeControlBridgeHandler getBridgeHandler() {
262         Bridge nhcBridge = getBridge();
263         return nhcBridge != null ? (NikoHomeControlBridgeHandler) nhcBridge.getHandler() : null;
264     }
265
266     @Override
267     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
268         ThingStatus bridgeStatus = bridgeStatusInfo.getStatus();
269         if (ThingStatus.ONLINE.equals(bridgeStatus)) {
270             if (!initialized) {
271                 scheduler.submit(this::startCommunication);
272             } else {
273                 updateStatus(ThingStatus.ONLINE);
274             }
275         } else {
276             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
277         }
278     }
279 }