2 * Copyright (c) 2010-2023 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.*;
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.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;
42 * The {@link NikoHomeControlEnergyMeterHandler} is responsible for handling commands, which are
43 * sent to one of the channels.
45 * @author Mark Herwege - Initial Contribution
48 public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implements NhcEnergyMeterEvent {
50 private final Logger logger = LoggerFactory.getLogger(NikoHomeControlEnergyMeterHandler.class);
52 private volatile @Nullable NhcEnergyMeter nhcEnergyMeter;
54 private volatile boolean initialized = false;
56 private String energyMeterId = "";
58 public NikoHomeControlEnergyMeterHandler(Thing thing) {
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);
70 if (REFRESH.equals(command)) {
71 energyMeterEvent(nhcEnergyMeter.getPower());
76 public void initialize() {
79 NikoHomeControlEnergyMeterConfig config = getConfig().as(NikoHomeControlEnergyMeterConfig.class);
81 energyMeterId = config.energyMeterId;
83 NikoHomeControlBridgeHandler bridgeHandler = getBridgeHandler();
84 if (bridgeHandler == null) {
85 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
86 "@text/offline.configuration-error.invalid-bridge-handler");
90 updateStatus(ThingStatus.UNKNOWN);
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);
100 private synchronized void startCommunication() {
101 NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
103 if (nhcComm == null) {
107 if (!nhcComm.communicationActive()) {
108 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
109 "@text/offline.communication-error");
113 NhcEnergyMeter nhcEnergyMeter = nhcComm.getEnergyMeters().get(energyMeterId);
114 if (nhcEnergyMeter == null) {
115 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
116 "@text/offline.configuration-error.energyMeterId");
120 nhcEnergyMeter.setEventHandler(this);
122 updateProperties(nhcEnergyMeter);
124 String location = nhcEnergyMeter.getLocation();
125 if (thing.getLocation() == null) {
126 thing.setLocation(location);
129 this.nhcEnergyMeter = nhcEnergyMeter;
131 logger.debug("energy meter intialized {}", energyMeterId);
133 Bridge bridge = getBridge();
134 if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
135 updateStatus(ThingStatus.ONLINE);
137 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
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);
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();
159 nhcEnergyMeter = null;
163 private void updateProperties(NhcEnergyMeter nhcEnergyMeter) {
164 Map<String, String> properties = new HashMap<>();
166 if (nhcEnergyMeter instanceof NhcEnergyMeter2) {
167 NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) nhcEnergyMeter;
168 properties.put(PROPERTY_DEVICE_TYPE, energyMeter.getDeviceType());
169 properties.put(PROPERTY_DEVICE_TECHNOLOGY, energyMeter.getDeviceTechnology());
170 properties.put(PROPERTY_DEVICE_MODEL, energyMeter.getDeviceModel());
173 thing.setProperties(properties);
177 public void energyMeterEvent(@Nullable Integer power) {
179 updateState(CHANNEL_POWER, UnDefType.UNDEF);
181 updateState(CHANNEL_POWER, new QuantityType<>(power, Units.WATT));
183 updateStatus(ThingStatus.ONLINE);
187 public void energyMeterInitialized() {
188 Bridge bridge = getBridge();
189 if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
190 updateStatus(ThingStatus.ONLINE);
195 public void energyMeterRemoved() {
196 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
197 "@text/offline.configuration-error.energyMeterRemoved");
201 // Subscribing to power readings starts an intensive data flow, therefore only do it when there is an item linked to
203 public void channelLinked(ChannelUID channelUID) {
204 NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
205 if (nhcComm != null) {
206 // This can be expensive, therefore do it in a job.
207 scheduler.submit(() -> {
208 if (!nhcComm.communicationActive()) {
209 restartCommunication(nhcComm);
212 if (nhcComm.communicationActive()) {
213 nhcComm.startEnergyMeter(energyMeterId);
214 updateStatus(ThingStatus.ONLINE);
221 public void channelUnlinked(ChannelUID channelUID) {
222 NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
223 if (nhcComm != null) {
224 // This can be expensive, therefore do it in a job.
225 scheduler.submit(() -> {
226 if (!nhcComm.communicationActive()) {
227 restartCommunication(nhcComm);
230 if (nhcComm.communicationActive()) {
231 nhcComm.stopEnergyMeter(energyMeterId);
232 // as this is momentary power production/consumption, we set it UNDEF as we do not get readings
234 updateState(CHANNEL_POWER, UnDefType.UNDEF);
235 updateStatus(ThingStatus.ONLINE);
241 private void restartCommunication(NikoHomeControlCommunication nhcComm) {
242 // We lost connection but the connection object is there, so was correctly started.
243 // Try to restart communication.
244 nhcComm.scheduleRestartCommunication();
245 // If still not active, take thing offline and return.
246 if (!nhcComm.communicationActive()) {
247 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
248 "@text/offline.communication-error");
251 // Also put the bridge back online
252 NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
253 if (nhcBridgeHandler != null) {
254 nhcBridgeHandler.bridgeOnline();
256 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
257 "@text/offline.configuration-error.invalid-bridge-handler");
261 private @Nullable NikoHomeControlCommunication getCommunication(
262 @Nullable NikoHomeControlBridgeHandler nhcBridgeHandler) {
263 return nhcBridgeHandler != null ? nhcBridgeHandler.getCommunication() : null;
266 private @Nullable NikoHomeControlBridgeHandler getBridgeHandler() {
267 Bridge nhcBridge = getBridge();
268 return nhcBridge != null ? (NikoHomeControlBridgeHandler) nhcBridge.getHandler() : null;
272 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
273 ThingStatus bridgeStatus = bridgeStatusInfo.getStatus();
274 if (ThingStatus.ONLINE.equals(bridgeStatus)) {
276 scheduler.submit(this::startCommunication);
278 updateStatus(ThingStatus.ONLINE);
281 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);