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