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.zway.internal.handler;
15 import static org.openhab.binding.zway.internal.ZWayBindingConstants.*;
17 import java.text.DateFormat;
18 import java.text.SimpleDateFormat;
19 import java.util.Calendar;
20 import java.util.List;
22 import java.util.concurrent.TimeUnit;
24 import org.openhab.binding.zway.internal.config.ZWayZWaveDeviceConfiguration;
25 import org.openhab.core.thing.Thing;
26 import org.openhab.core.thing.ThingStatus;
27 import org.openhab.core.thing.ThingStatusDetail;
28 import org.openhab.core.thing.ThingStatusInfo;
29 import org.openhab.core.thing.ThingTypeUID;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import de.fh_zwickau.informatik.sensor.model.devices.Device;
34 import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
35 import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
38 * The {@link ZWayZWaveDeviceHandler} is responsible for handling commands, which are
39 * sent to one of the channels.
41 * @author Patrick Hecker - Initial contribution
43 public class ZWayZWaveDeviceHandler extends ZWayDeviceHandler {
44 public static final ThingTypeUID SUPPORTED_THING_TYPE = THING_TYPE_DEVICE;
46 private final Logger logger = LoggerFactory.getLogger(getClass());
48 private ZWayZWaveDeviceConfiguration mConfig;
50 private class Initializer implements Runnable {
54 ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
55 if (zwayBridgeHandler != null && zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
56 ThingStatusInfo statusInfo = zwayBridgeHandler.getThing().getStatusInfo();
57 logger.debug("Change Z-Way Z-Wave device status to bridge status: {}", statusInfo);
59 // Set thing status to bridge status
60 updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
62 // Add all available channels
63 logger.debug("Add all available channels");
64 DeviceList deviceList = getZWayBridgeHandler().getZWayApi().getDevices();
65 if (deviceList != null) {
66 logger.debug("Z-Way devices loaded ({} physical devices)",
67 deviceList.getDevicesGroupByNodeId().size());
69 // https://community.openhab.org/t/oh2-major-bug-with-scheduled-jobs/12350/11
70 // If any execution of the task encounters an exception, subsequent executions are
71 // suppressed. Otherwise, the task will only terminate via cancellation or
72 // termination of the executor.
74 // physical device means all virtual devices grouped by physical device
75 Map<Integer, List<Device>> physicalDevice = deviceList.getDevicesByNodeId(mConfig.getNodeId());
76 if (physicalDevice != null) {
77 logger.debug("Z-Wave device with node id {} found with {} virtual devices",
78 mConfig.getNodeId(), physicalDevice.get(mConfig.getNodeId()).size());
80 for (Map.Entry<Integer, List<Device>> entry : physicalDevice.entrySet()) {
81 logger.debug("Add channels for physical device with node id: {}", mConfig.getNodeId());
83 List<Device> devices = entry.getValue();
85 for (Device device : devices) {
86 if (device.getVisibility() && !device.getPermanentlyHidden()) {
87 logger.debug("Add channel for virtual device: {}",
88 device.getMetrics().getTitle());
89 addDeviceAsChannel(device);
91 logger.debug("Device {} has been skipped, because it was hidden in Z-Way.",
92 device.getMetrics().getTitle());
97 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
98 "Z-Way physical device with node id " + mConfig.getNodeId() + " not found.");
101 // Check command classes (only for ThermostatMode)
102 ZWaveDevice zwaveDevice = getZWayBridgeHandler().getZWayApi()
103 .getZWaveDevice(mConfig.getNodeId());
104 if (!"".equals(zwaveDevice.getInstances().get0().getCommandClasses().get64().getName())) {
105 // Load available thermostat modes
106 Map<Integer, String> modes = zwaveDevice.getInstances().get0().getCommandClasses().get64()
107 .getThermostatModes();
110 "Z-Wave device implements command class ThermostatMode with the following modes: {}",
113 addCommandClassThermostatModeAsChannel(modes, mConfig.getNodeId());
116 // Starts polling job and register all linked items
117 completeInitialization();
118 } catch (Throwable t) {
119 if (t instanceof Exception) {
120 logger.error("{}", t.getMessage());
121 } else if (t instanceof Error) {
122 logger.error("{}", t.getMessage());
124 logger.error("Unexpected error");
126 if (getThing().getStatus() == ThingStatus.ONLINE) {
127 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
128 "Error occurred when adding device as channel.");
132 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
133 "Devices not loaded");
136 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
137 "Z-Way bridge handler not found or not ONLINE.");
142 public ZWayZWaveDeviceHandler(Thing thing) {
147 public void initialize() {
148 logger.debug("Initializing Z-Way device handler ...");
150 // Set thing status to a valid status
151 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
152 "Checking configuration and bridge...");
154 // Configuration - thing status update with an error message
155 mConfig = loadAndCheckConfiguration();
157 if (mConfig != null) {
158 logger.debug("Configuration complete: {}", mConfig);
160 // Start an extra thread to check the connection, because it takes sometimes more
161 // than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
162 scheduler.schedule(new Initializer(), 2, TimeUnit.SECONDS);
164 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Z-Way node id required!");
168 private void completeInitialization() {
169 super.initialize(); // starts polling job and register all linked items
172 private ZWayZWaveDeviceConfiguration loadAndCheckConfiguration() {
173 ZWayZWaveDeviceConfiguration config = getConfigAs(ZWayZWaveDeviceConfiguration.class);
175 if (config.getNodeId() == null) {
176 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
177 "Z-Wave device couldn't create, because the node id is missing.");
185 public void dispose() {
186 logger.debug("Dispose Z-Way Z-Wave handler ...");
188 if (mConfig.getNodeId() != null) {
189 mConfig.setNodeId(null);
196 protected void refreshLastUpdate() {
197 logger.debug("Refresh last update for Z-Wave device");
199 // Check Z-Way bridge handler
200 ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
201 if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
202 logger.debug("Z-Way bridge handler not found or not ONLINE.");
206 // Load and check Z-Wave device from Z-Way server (Z-Wave API)
207 ZWaveDevice zwaveDevice = zwayBridgeHandler.getZWayApi().getZWaveDevice(mConfig.getNodeId());
208 if (zwaveDevice == null) {
209 logger.debug("Z-Wave device not found.");
213 Calendar lastUpdateOfDevice = Calendar.getInstance();
215 .setTimeInMillis(Long.valueOf(zwaveDevice.getData().getLastReceived().getUpdateTime()) * 1000);
217 if (lastUpdate == null || lastUpdateOfDevice.after(lastUpdate)) {
218 lastUpdate = lastUpdateOfDevice;
221 DateFormat formatter = new SimpleDateFormat("dd.MM.yyyy hh:mm:ss");
222 updateProperty(DEVICE_PROP_LAST_UPDATE, formatter.format(lastUpdate.getTime()));