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