]> git.basschouten.com Git - openhab-addons.git/blob
571715634ce15bb2459b4d53818e996c5d55c48f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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         if (nhcComm != null) {
153             nhcComm.stopEnergyMeter(energyMeterId);
154             NhcEnergyMeter energyMeter = nhcComm.getEnergyMeters().get(energyMeterId);
155             if (energyMeter != null) {
156                 energyMeter.unsetEventHandler();
157             }
158         }
159         nhcEnergyMeter = null;
160         super.dispose();
161     }
162
163     private void updateProperties(NhcEnergyMeter nhcEnergyMeter) {
164         Map<String, String> properties = new HashMap<>();
165
166         if (nhcEnergyMeter instanceof NhcEnergyMeter2) {
167             NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) nhcEnergyMeter;
168             properties.put(PROPERTY_DEVICE_TYPE, energyMeter.getDeviceType());
169             properties.put(PROPERTY_DEVICE_TECHNOLOGY, energyMeter.getDeviceTechnology());
170             properties.put(PROPERTY_DEVICE_MODEL, energyMeter.getDeviceModel());
171         }
172
173         thing.setProperties(properties);
174     }
175
176     @Override
177     public void energyMeterEvent(@Nullable Integer power) {
178         if (power == null) {
179             updateState(CHANNEL_POWER, UnDefType.UNDEF);
180         } else {
181             updateState(CHANNEL_POWER, new QuantityType<>(power, Units.WATT));
182         }
183         updateStatus(ThingStatus.ONLINE);
184     }
185
186     @Override
187     public void energyMeterInitialized() {
188         Bridge bridge = getBridge();
189         if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
190             updateStatus(ThingStatus.ONLINE);
191         }
192     }
193
194     @Override
195     public void energyMeterRemoved() {
196         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
197                 "@text/offline.configuration-error.energyMeterRemoved");
198     }
199
200     @Override
201     // Subscribing to power readings starts an intensive data flow, therefore only do it when there is an item linked to
202     // the channel
203     public void channelLinked(ChannelUID channelUID) {
204         NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
205         if (nhcComm != null) {
206             // This can be expensive, therefore do it in a job.
207             scheduler.submit(() -> {
208                 if (!nhcComm.communicationActive()) {
209                     restartCommunication(nhcComm);
210                 }
211
212                 if (nhcComm.communicationActive()) {
213                     nhcComm.startEnergyMeter(energyMeterId);
214                     updateStatus(ThingStatus.ONLINE);
215                 }
216             });
217         }
218     }
219
220     @Override
221     public void channelUnlinked(ChannelUID channelUID) {
222         NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
223         if (nhcComm != null) {
224             // This can be expensive, therefore do it in a job.
225             scheduler.submit(() -> {
226                 if (!nhcComm.communicationActive()) {
227                     restartCommunication(nhcComm);
228                 }
229
230                 if (nhcComm.communicationActive()) {
231                     nhcComm.stopEnergyMeter(energyMeterId);
232                     // as this is momentary power production/consumption, we set it UNDEF as we do not get readings
233                     // anymore
234                     updateState(CHANNEL_POWER, UnDefType.UNDEF);
235                     updateStatus(ThingStatus.ONLINE);
236                 }
237             });
238         }
239     }
240
241     private void restartCommunication(NikoHomeControlCommunication nhcComm) {
242         // We lost connection but the connection object is there, so was correctly started.
243         // Try to restart communication.
244         nhcComm.scheduleRestartCommunication();
245         // If still not active, take thing offline and return.
246         if (!nhcComm.communicationActive()) {
247             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
248                     "@text/offline.communication-error");
249             return;
250         }
251         // Also put the bridge back online
252         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
253         if (nhcBridgeHandler != null) {
254             nhcBridgeHandler.bridgeOnline();
255         } else {
256             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
257                     "@text/offline.configuration-error.invalid-bridge-handler");
258         }
259     }
260
261     private @Nullable NikoHomeControlCommunication getCommunication(
262             @Nullable NikoHomeControlBridgeHandler nhcBridgeHandler) {
263         return nhcBridgeHandler != null ? nhcBridgeHandler.getCommunication() : null;
264     }
265
266     private @Nullable NikoHomeControlBridgeHandler getBridgeHandler() {
267         Bridge nhcBridge = getBridge();
268         return nhcBridge != null ? (NikoHomeControlBridgeHandler) nhcBridge.getHandler() : null;
269     }
270
271     @Override
272     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
273         ThingStatus bridgeStatus = bridgeStatusInfo.getStatus();
274         if (ThingStatus.ONLINE.equals(bridgeStatus)) {
275             if (!initialized) {
276                 scheduler.submit(this::startCommunication);
277             } else {
278                 updateStatus(ThingStatus.ONLINE);
279             }
280         } else {
281             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
282         }
283     }
284 }