2 * Copyright (c) 2010-2020 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.SmartHomeUnits;
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 @NonNullByDefault({}) NhcEnergyMeter nhcEnergyMeter;
53 private String energyMeterId = "";
55 public NikoHomeControlEnergyMeterHandler(Thing thing) {
60 public void handleCommand(ChannelUID channelUID, Command command) {
61 if (command == REFRESH) {
62 energyMeterEvent(nhcEnergyMeter.getPower());
67 public void initialize() {
68 NikoHomeControlEnergyMeterConfig config = getConfig().as(NikoHomeControlEnergyMeterConfig.class);
70 energyMeterId = config.energyMeterId;
72 NikoHomeControlCommunication nhcComm = getCommunication();
73 if (nhcComm == null) {
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 "
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 "
95 nhcEnergyMeter.setEventHandler(this);
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);
105 logger.debug("Niko Home Control: energy meter intialized {}", energyMeterId);
107 Bridge bridge = getBridge();
108 if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
109 updateStatus(ThingStatus.ONLINE);
111 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
117 public void dispose() {
118 NikoHomeControlCommunication nhcComm = getCommunication();
120 if (nhcComm != null) {
121 nhcComm.stopEnergyMeter(energyMeterId);
125 private void updateProperties() {
126 Map<String, String> properties = new HashMap<>();
128 if (nhcEnergyMeter instanceof NhcEnergyMeter2) {
129 NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) nhcEnergyMeter;
130 properties.put("model", energyMeter.getModel());
131 properties.put("technology", energyMeter.getTechnology());
134 thing.setProperties(properties);
138 public void energyMeterEvent(@Nullable Integer power) {
140 updateState(CHANNEL_POWER, UnDefType.UNDEF);
142 updateState(CHANNEL_POWER, new QuantityType<>(power, SmartHomeUnits.WATT));
144 updateStatus(ThingStatus.ONLINE);
148 public void energyMeterRemoved() {
149 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
150 "Niko Home Control: energy meter has been removed from the controller " + energyMeterId);
154 // Subscribing to power readings starts an intensive data flow, therefore only do it when there is an item linked to
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 "
165 // This can be expensive, therefore do it in a job.
166 scheduler.submit(() -> {
167 if (!nhcComm.communicationActive()) {
168 restartCommunication(nhcComm);
171 if (nhcComm.communicationActive()) {
172 nhcComm.startEnergyMeter(energyMeterId);
173 updateStatus(ThingStatus.ONLINE);
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 "
188 // This can be expensive, therefore do it in a job.
189 scheduler.submit(() -> {
190 if (!nhcComm.communicationActive()) {
191 restartCommunication(nhcComm);
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);
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");
213 // Also put the bridge back online
214 NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
215 if (nhcBridgeHandler != null) {
216 nhcBridgeHandler.bridgeOnline();
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);
227 NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication();
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);
238 NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler();
239 return nhcBridgeHandler;