]> git.basschouten.com Git - openhab-addons.git/blob
a3acc900c7b518a79a83162188cfa8dde692a898
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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 @NonNullByDefault({}) 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         if (command == REFRESH) {
62             energyMeterEvent(nhcEnergyMeter.getPower());
63         }
64     }
65
66     @Override
67     public void initialize() {
68         NikoHomeControlEnergyMeterConfig config = getConfig().as(NikoHomeControlEnergyMeterConfig.class);
69
70         energyMeterId = config.energyMeterId;
71
72         NikoHomeControlCommunication nhcComm = getCommunication();
73         if (nhcComm == null) {
74             return;
75         }
76
77         // We need to do this in a separate thread because we may have to wait for the
78         // communication to become active
79         scheduler.submit(() -> {
80             if (!nhcComm.communicationActive()) {
81                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
82                         "Niko Home Control: no connection with Niko Home Control, could not initialize energy meter "
83                                 + energyMeterId);
84                 return;
85             }
86
87             nhcEnergyMeter = nhcComm.getEnergyMeters().get(energyMeterId);
88             if (nhcEnergyMeter == null) {
89                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
90                         "Niko Home Control: energyMeterId does not match a energy meter in the controller "
91                                 + energyMeterId);
92                 return;
93             }
94
95             nhcEnergyMeter.setEventHandler(this);
96
97             updateProperties();
98
99             // Subscribing to power readings starts an intensive data flow, therefore only do it when there is an item
100             // linked to the channel
101             if (isLinked(CHANNEL_POWER)) {
102                 nhcComm.startEnergyMeter(energyMeterId);
103             }
104
105             logger.debug("Niko Home Control: energy meter intialized {}", energyMeterId);
106
107             Bridge bridge = getBridge();
108             if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
109                 updateStatus(ThingStatus.ONLINE);
110             } else {
111                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
112             }
113         });
114     }
115
116     @Override
117     public void dispose() {
118         NikoHomeControlCommunication nhcComm = getCommunication();
119
120         if (nhcComm != null) {
121             nhcComm.stopEnergyMeter(energyMeterId);
122         }
123     }
124
125     private void updateProperties() {
126         Map<String, String> properties = new HashMap<>();
127
128         if (nhcEnergyMeter instanceof NhcEnergyMeter2) {
129             NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) nhcEnergyMeter;
130             properties.put("model", energyMeter.getModel());
131             properties.put("technology", energyMeter.getTechnology());
132         }
133
134         thing.setProperties(properties);
135     }
136
137     @Override
138     public void energyMeterEvent(@Nullable Integer power) {
139         if (power == null) {
140             updateState(CHANNEL_POWER, UnDefType.UNDEF);
141         } else {
142             updateState(CHANNEL_POWER, new QuantityType<>(power, Units.WATT));
143         }
144         updateStatus(ThingStatus.ONLINE);
145     }
146
147     @Override
148     public void energyMeterRemoved() {
149         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
150                 "Niko Home Control: energy meter has been removed from the controller " + energyMeterId);
151     }
152
153     @Override
154     // Subscribing to power readings starts an intensive data flow, therefore only do it when there is an item linked to
155     // the channel
156     public void channelLinked(ChannelUID channelUID) {
157         NikoHomeControlCommunication nhcComm = getCommunication();
158         if (nhcComm == null) {
159             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
160                     "Niko Home Control: bridge communication not initialized when trying to start energy meter "
161                             + energyMeterId);
162             return;
163         }
164
165         // This can be expensive, therefore do it in a job.
166         scheduler.submit(() -> {
167             if (!nhcComm.communicationActive()) {
168                 restartCommunication(nhcComm);
169             }
170
171             if (nhcComm.communicationActive()) {
172                 nhcComm.startEnergyMeter(energyMeterId);
173                 updateStatus(ThingStatus.ONLINE);
174             }
175         });
176     }
177
178     @Override
179     public void channelUnlinked(ChannelUID channelUID) {
180         NikoHomeControlCommunication nhcComm = getCommunication();
181         if (nhcComm == null) {
182             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
183                     "Niko Home Control: bridge communication not initialized when trying to stop energy meter "
184                             + energyMeterId);
185             return;
186         }
187
188         // This can be expensive, therefore do it in a job.
189         scheduler.submit(() -> {
190             if (!nhcComm.communicationActive()) {
191                 restartCommunication(nhcComm);
192             }
193
194             if (nhcComm.communicationActive()) {
195                 nhcComm.stopEnergyMeter(energyMeterId);
196                 // as this is momentary power production/consumption, we set it UNDEF as we do not get readings anymore
197                 updateState(CHANNEL_POWER, UnDefType.UNDEF);
198                 updateStatus(ThingStatus.ONLINE);
199             }
200         });
201     }
202
203     private void restartCommunication(NikoHomeControlCommunication nhcComm) {
204         // We lost connection but the connection object is there, so was correctly started.
205         // Try to restart communication.
206         nhcComm.restartCommunication();
207         // If still not active, take thing offline and return.
208         if (!nhcComm.communicationActive()) {
209             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
210                     "Niko Home Control: communication socket error");
211             return;
212         }
213         // Also put the bridge back online
214         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
215         if (nhcBridgeHandler != null) {
216             nhcBridgeHandler.bridgeOnline();
217         }
218     }
219
220     private @Nullable NikoHomeControlCommunication getCommunication() {
221         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
222         if (nhcBridgeHandler == null) {
223             updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
224                     "Niko Home Control: no bridge initialized for energy meter " + energyMeterId);
225             return null;
226         }
227         NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication();
228         return nhcComm;
229     }
230
231     private @Nullable NikoHomeControlBridgeHandler getBridgeHandler() {
232         Bridge nhcBridge = getBridge();
233         if (nhcBridge == null) {
234             updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
235                     "Niko Home Control: no bridge initialized for energy meter " + energyMeterId);
236             return null;
237         }
238         NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler();
239         return nhcBridgeHandler;
240     }
241 }