2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.nikohomecontrol.internal.handler;
15 import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.CHANNEL_POWER;
16 import static org.openhab.core.types.RefreshType.REFRESH;
18 import java.util.HashMap;
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;
41 * The {@link NikoHomeControlEnergyMeterHandler} is responsible for handling commands, which are
42 * sent to one of the channels.
44 * @author Mark Herwege - Initial Contribution
47 public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implements NhcEnergyMeterEvent {
49 private final Logger logger = LoggerFactory.getLogger(NikoHomeControlEnergyMeterHandler.class);
51 private volatile @Nullable NhcEnergyMeter nhcEnergyMeter;
53 private String energyMeterId = "";
55 public NikoHomeControlEnergyMeterHandler(Thing thing) {
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);
67 if (command == REFRESH) {
68 energyMeterEvent(nhcEnergyMeter.getPower());
73 public void initialize() {
74 NikoHomeControlEnergyMeterConfig config = getConfig().as(NikoHomeControlEnergyMeterConfig.class);
76 energyMeterId = config.energyMeterId;
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);
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);
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");
101 nhcEnergyMeter.setEventHandler(this);
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);
111 this.nhcEnergyMeter = nhcEnergyMeter;
113 logger.debug("energy meter intialized {}", energyMeterId);
115 Bridge bridge = getBridge();
116 if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
117 updateStatus(ThingStatus.ONLINE);
119 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
125 public void dispose() {
126 NikoHomeControlCommunication nhcComm = getCommunication();
128 if (nhcComm != null) {
129 nhcComm.stopEnergyMeter(energyMeterId);
133 private void updateProperties() {
134 Map<String, String> properties = new HashMap<>();
136 if (nhcEnergyMeter instanceof NhcEnergyMeter2) {
137 NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) nhcEnergyMeter;
138 properties.put("model", energyMeter.getModel());
139 properties.put("technology", energyMeter.getTechnology());
142 thing.setProperties(properties);
146 public void energyMeterEvent(@Nullable Integer power) {
148 updateState(CHANNEL_POWER, UnDefType.UNDEF);
150 updateState(CHANNEL_POWER, new QuantityType<>(power, Units.WATT));
152 updateStatus(ThingStatus.ONLINE);
156 public void energyMeterInitialized() {
157 Bridge bridge = getBridge();
158 if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
159 updateStatus(ThingStatus.ONLINE);
164 public void energyMeterRemoved() {
165 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
166 "Energy meter " + energyMeterId + " has been removed from the controller");
170 // Subscribing to power readings starts an intensive data flow, therefore only do it when there is an item linked to
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);
180 // This can be expensive, therefore do it in a job.
181 scheduler.submit(() -> {
182 if (!nhcComm.communicationActive()) {
183 restartCommunication(nhcComm);
186 if (nhcComm.communicationActive()) {
187 nhcComm.startEnergyMeter(energyMeterId);
188 updateStatus(ThingStatus.ONLINE);
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);
202 // This can be expensive, therefore do it in a job.
203 scheduler.submit(() -> {
204 if (!nhcComm.communicationActive()) {
205 restartCommunication(nhcComm);
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);
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");
226 // Also put the bridge back online
227 NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
228 if (nhcBridgeHandler != null) {
229 nhcBridgeHandler.bridgeOnline();
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);
240 NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication();
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);
251 NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler();
252 return nhcBridgeHandler;