added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.zway-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-zway" description="Z-Way Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle dependency="true">mvn:org.apache.commons/commons-lang3/3.4</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.zway/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link ZWayBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ZWayBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "zway";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "zwayServer");
|
||||
public static final ThingTypeUID THING_TYPE_DEVICE = new ThingTypeUID(BINDING_ID, "zwayDevice");
|
||||
public static final ThingTypeUID THING_TYPE_VIRTUAL_DEVICE = new ThingTypeUID(BINDING_ID, "zwayVirtualDevice");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_DEVICE_THING_TYPES_UIDS = Collections
|
||||
.unmodifiableSet(Stream.of(THING_TYPE_DEVICE, THING_TYPE_VIRTUAL_DEVICE).collect(Collectors.toSet()));
|
||||
|
||||
// List of ignored devices for Discovery
|
||||
public static final Set<String> DISCOVERY_IGNORED_DEVICES = Collections
|
||||
.unmodifiableSet(Collections.singleton(("BatteryPolling")));
|
||||
|
||||
// List of all Channel IDs
|
||||
public static final String BATTERY_CHANNEL = "battery";
|
||||
public static final String DOORLOCK_CHANNEL = "doorlock";
|
||||
public static final String SENSOR_BINARY_CHANNEL = "sensorBinary";
|
||||
public static final String SENSOR_MULTILEVEL_CHANNEL = "sensorMultilevel";
|
||||
public static final String SENSOR_DISCRETE_CHANNEL = "sensorDiscrete";
|
||||
public static final String SWITCH_BINARY_CHANNEL = "switchBinary";
|
||||
public static final String SWITCH_CONTROL_CHANNEL = "switchControl";
|
||||
public static final String SWITCH_MULTILEVEL_CHANNEL = "switchMultilevel";
|
||||
// switch multilevel (color)
|
||||
public static final String SWITCH_COLOR_CHANNEL = "switchColor";
|
||||
public static final String SWITCH_COLOR_TEMPERATURE_CHANNEL = "switchColorTemperature";
|
||||
// thermostat
|
||||
public static final String THERMOSTAT_MODE_CHANNEL = "thermostatMode";
|
||||
public static final String THERMOSTAT_SET_POINT_CHANNEL = "thermostatSetPoint";
|
||||
|
||||
public static final String THERMOSTAT_MODE_CC_CHANNEL = "thermostatModeCC";
|
||||
|
||||
// sensor multilevel
|
||||
public static final String SENSOR_TEMPERATURE_CHANNEL = "sensorTemperature";
|
||||
public static final String SENSOR_LUMINOSITY_CHANNEL = "sensorLuminosity";
|
||||
public static final String SENSOR_HUMIDITY_CHANNEL = "sensorHumidity";
|
||||
public static final String SENSOR_BAROMETER_CHANNEL = "sensorBarometer";
|
||||
public static final String SENSOR_ULTRAVIOLET_CHANNEL = "sensorUltraviolet";
|
||||
public static final String SENSOR_CO2_CHANNEL = "sensorCO2";
|
||||
public static final String SENSOR_ENERGY_CHANNEL = "sensorEnergy";
|
||||
// sensor multilevel (meter)
|
||||
public static final String SENSOR_METER_KWH_CHANNEL = "sensorMeterKWh";
|
||||
public static final String SENSOR_METER_W_CHANNEL = "sensorMeterW";
|
||||
// sensor binary
|
||||
public static final String SENSOR_SMOKE_CHANNEL = "sensorSmoke";
|
||||
public static final String SENSOR_CO_CHANNEL = "sensorCo";
|
||||
public static final String SENSOR_FLOOD_CHANNEL = "sensorFlood";
|
||||
public static final String SENSOR_TAMPER_CHANNEL = "sensorTamper";
|
||||
public static final String SENSOR_DOOR_WINDOW_CHANNEL = "sensorDoorWindow";
|
||||
public static final String SENSOR_MOTION_CHANNEL = "sensorMotion";
|
||||
// switch binary
|
||||
public static final String SWITCH_POWER_OUTLET_CHANNEL = "switchPowerOutlet";
|
||||
// switch multilevel
|
||||
public static final String SWITCH_ROLLERSHUTTER_CHANNEL = "switchBlinds";
|
||||
// special channels
|
||||
public static final String ACTIONS_CHANNEL = "actions";
|
||||
public static final String SECURE_INCLUSION_CHANNEL = "secureInclusion";
|
||||
public static final String INCLUSION_CHANNEL = "inclusion";
|
||||
public static final String EXCLUSION_CHANNEL = "exclusion";
|
||||
|
||||
public static final String ACTIONS_CHANNEL_OPTION_REFRESH = "REFRESH";
|
||||
|
||||
/* Bridge config properties */
|
||||
public static final String BRIDGE_CONFIG_ZWAY_SERVER_IP_ADDRESS = "zwayServerIpAddress";
|
||||
public static final String BRIDGE_CONFIG_ZWAY_SERVER_PORT = "zwayServerPort";
|
||||
public static final String BRIDGE_CONFIG_ZWAY_SERVER_PROTOCOL = "zwayServerProtocol";
|
||||
public static final String BRIDGE_CONFIG_ZWAY_SERVER_USERNAME = "zwayServerUsername";
|
||||
public static final String BRIDGE_CONFIG_ZWAY_SERVER_PASSWORD = "zwayServerPassword";
|
||||
public static final String BRIDGE_CONFIG_POLLING_INTERVAL = "pollingInterval";
|
||||
|
||||
public static final String DEVICE_CONFIG_NODE_ID = "nodeId";
|
||||
public static final String DEVICE_CONFIG_VIRTUAL_DEVICE_ID = "deviceId";
|
||||
|
||||
public static final String DEVICE_PROP_LOCATION = "location";
|
||||
public static final String DEVICE_PROP_MANUFACTURER_ID = "manufacturerId";
|
||||
public static final String DEVICE_PROP_DEVICE_TYPE = "deviceType";
|
||||
public static final String DEVICE_PROP_ZDDXMLFILE = "zddxmlfile";
|
||||
public static final String DEVICE_PROP_SDK = "SDK";
|
||||
public static final String DEVICE_PROP_LAST_UPDATE = "lastUpdate";
|
||||
|
||||
/* Bridge properties */
|
||||
public static final String BRIDGE_PROP_SOFTWARE_REVISION_VERSION = "softwareRevisionVersion";
|
||||
public static final String BRIDGE_PROP_SOFTWARE_REVISION_DATE = "softwareRevisionDate";
|
||||
public static final String BRIDGE_PROP_SDK = "SDK";
|
||||
public static final String BRIDGE_PROP_MANUFACTURER_ID = "manufacturerId";
|
||||
public static final String BRIDGE_PROP_SECURE_INCLUSION = "secureInclusion";
|
||||
public static final String BRIDGE_PROP_FREQUENCY = "frequency";
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.openhab.binding.zway.internal.discovery.ZWayDeviceDiscoveryService;
|
||||
import org.openhab.binding.zway.internal.handler.ZWayBridgeHandler;
|
||||
import org.openhab.binding.zway.internal.handler.ZWayZAutomationDeviceHandler;
|
||||
import org.openhab.binding.zway.internal.handler.ZWayZWaveDeviceHandler;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* The {@link ZWayHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.zway")
|
||||
public class ZWayHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(
|
||||
Stream.of(ZWayBridgeHandler.SUPPORTED_THING_TYPE, ZWayZAutomationDeviceHandler.SUPPORTED_THING_TYPE,
|
||||
ZWayZWaveDeviceHandler.SUPPORTED_THING_TYPE).collect(Collectors.toSet()));
|
||||
|
||||
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
if (ZWayBridgeHandler.SUPPORTED_THING_TYPE.equals(thing.getThingTypeUID())) {
|
||||
ZWayBridgeHandler handler = new ZWayBridgeHandler((Bridge) thing);
|
||||
registerDeviceDiscoveryService(handler);
|
||||
|
||||
return handler;
|
||||
} else if (ZWayZAutomationDeviceHandler.SUPPORTED_THING_TYPE.equals(thing.getThingTypeUID())) {
|
||||
return new ZWayZAutomationDeviceHandler(thing);
|
||||
} else if (ZWayZWaveDeviceHandler.SUPPORTED_THING_TYPE.equals(thing.getThingTypeUID())) {
|
||||
return new ZWayZWaveDeviceHandler(thing);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void removeHandler(ThingHandler thingHandler) {
|
||||
if (thingHandler instanceof ZWayBridgeHandler) {
|
||||
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.remove(thingHandler.getThing().getUID());
|
||||
if (serviceReg != null) {
|
||||
serviceReg.unregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void registerDeviceDiscoveryService(ZWayBridgeHandler handler) {
|
||||
ZWayDeviceDiscoveryService discoveryService = new ZWayDeviceDiscoveryService(handler);
|
||||
this.discoveryServiceRegs.put(handler.getThing().getUID(),
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal.config;
|
||||
|
||||
import static org.openhab.binding.zway.internal.ZWayBindingConstants.*;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
/**
|
||||
* The {@link ZWayBridgeConfiguration} class defines the model for a bridge configuration.
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution, remove openHAB configuration
|
||||
*/
|
||||
public class ZWayBridgeConfiguration {
|
||||
private String zwayServerIpAddress;
|
||||
private Integer zwayServerPort;
|
||||
private String zwayServerProtocol;
|
||||
|
||||
private String zwayServerUsername;
|
||||
private String zwayServerPassword;
|
||||
|
||||
private Integer pollingInterval;
|
||||
|
||||
public String getZWayIpAddress() {
|
||||
return zwayServerIpAddress;
|
||||
}
|
||||
|
||||
public void setZWayIpAddress(String ipAddress) {
|
||||
this.zwayServerIpAddress = ipAddress;
|
||||
}
|
||||
|
||||
public Integer getZWayPort() {
|
||||
return zwayServerPort;
|
||||
}
|
||||
|
||||
public void setZWayPort(Integer port) {
|
||||
this.zwayServerPort = port;
|
||||
}
|
||||
|
||||
public String getZWayProtocol() {
|
||||
return zwayServerProtocol;
|
||||
}
|
||||
|
||||
public void setZWayProtocol(String protocol) {
|
||||
this.zwayServerProtocol = protocol;
|
||||
}
|
||||
|
||||
public String getZWayUsername() {
|
||||
return zwayServerUsername;
|
||||
}
|
||||
|
||||
public void setZWayUsername(String username) {
|
||||
this.zwayServerUsername = username;
|
||||
}
|
||||
|
||||
public String getZWayPassword() {
|
||||
return zwayServerPassword;
|
||||
}
|
||||
|
||||
public void setZWayPassword(String password) {
|
||||
this.zwayServerPassword = password;
|
||||
}
|
||||
|
||||
public Integer getPollingInterval() {
|
||||
return pollingInterval;
|
||||
}
|
||||
|
||||
public void setPollingInterval(Integer pollingInterval) {
|
||||
this.pollingInterval = pollingInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this).append(BRIDGE_CONFIG_ZWAY_SERVER_IP_ADDRESS, this.getZWayIpAddress())
|
||||
.append(BRIDGE_CONFIG_ZWAY_SERVER_PORT, this.getZWayPort())
|
||||
.append(BRIDGE_CONFIG_ZWAY_SERVER_PROTOCOL, this.getZWayProtocol())
|
||||
.append(BRIDGE_CONFIG_ZWAY_SERVER_USERNAME, this.getZWayUsername())
|
||||
.append(BRIDGE_CONFIG_ZWAY_SERVER_PASSWORD, this.getZWayPassword())
|
||||
.append(BRIDGE_CONFIG_POLLING_INTERVAL, this.getPollingInterval()).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal.config;
|
||||
|
||||
import static org.openhab.binding.zway.internal.ZWayBindingConstants.DEVICE_CONFIG_VIRTUAL_DEVICE_ID;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
/**
|
||||
* The {@link ZWayZAutomationDeviceConfiguration} class defines the model for a Z-Way device configuration.
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution
|
||||
*/
|
||||
public class ZWayZAutomationDeviceConfiguration {
|
||||
private String deviceId;
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this).append(DEVICE_CONFIG_VIRTUAL_DEVICE_ID, this.getDeviceId()).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal.config;
|
||||
|
||||
import static org.openhab.binding.zway.internal.ZWayBindingConstants.DEVICE_CONFIG_NODE_ID;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
/**
|
||||
* The {@link ZWayZWaveDeviceConfiguration} class defines the model for a Z-Wave device configuration.
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution
|
||||
*/
|
||||
public class ZWayZWaveDeviceConfiguration {
|
||||
private Integer nodeId;
|
||||
|
||||
public Integer getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public void setNodeId(Integer nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this).append(DEVICE_CONFIG_NODE_ID, this.getNodeId()).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal.converter;
|
||||
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.Color;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.Device;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.Battery;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.Doorlock;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SensorBinary;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SensorDiscrete;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SensorMultilevel;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchBinary;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchControl;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchMultilevel;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchRGBW;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchToggle;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.Thermostat;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.ToggleButton;
|
||||
|
||||
/**
|
||||
* The {@link ZWayDeviceStateConverter} is responsible for converting Z-Way device level to openHAB states
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution
|
||||
*/
|
||||
public class ZWayDeviceStateConverter {
|
||||
public static State toState(Device device, Channel channel) {
|
||||
// Store level locally
|
||||
String level = device.getMetrics().getLevel();
|
||||
|
||||
// Set item state to level depending on device type
|
||||
if (device instanceof Battery) {
|
||||
return getMultilevelState(level);
|
||||
} else if (device instanceof Doorlock) {
|
||||
return getBinaryState(level.toLowerCase());
|
||||
} else if (device instanceof SensorBinary) {
|
||||
if (channel.getAcceptedItemType().equals("Contact")) {
|
||||
return getDoorlockState(level.toLowerCase());
|
||||
} else {
|
||||
return getBinaryState(level.toLowerCase());
|
||||
}
|
||||
} else if (device instanceof SensorMultilevel) {
|
||||
return getMultilevelState(level);
|
||||
} else if (device instanceof SwitchBinary) {
|
||||
return getBinaryState(level.toLowerCase());
|
||||
} else if (device instanceof SwitchMultilevel) {
|
||||
if (channel.getAcceptedItemType().equals("Rollershutter")
|
||||
|| channel.getAcceptedItemType().equals("Dimmer")) {
|
||||
return getPercentState(level);
|
||||
} else {
|
||||
return getMultilevelState(level);
|
||||
}
|
||||
} else if (device instanceof SwitchRGBW) {
|
||||
return getColorState(device.getMetrics().getColor());
|
||||
} else if (device instanceof Thermostat) {
|
||||
return getMultilevelState(level);
|
||||
} else if (device instanceof SwitchControl) {
|
||||
return getBinaryState(level.toLowerCase());
|
||||
} else if (device instanceof ToggleButton || device instanceof SwitchToggle) {
|
||||
return getBinaryState(level.toLowerCase());
|
||||
} else if (device instanceof SensorDiscrete) {
|
||||
return getMultilevelState(level);
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an value in an openHAB type.
|
||||
*
|
||||
* @param multilevel sensor value
|
||||
* @return transformed openHAB state
|
||||
*/
|
||||
private static State getMultilevelState(String multilevelValue) {
|
||||
if (multilevelValue != null) {
|
||||
return new DecimalType(multilevelValue);
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
private static State getPercentState(String multilevelValue) {
|
||||
if (multilevelValue != null) {
|
||||
return new PercentType(multilevelValue);
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an value in an openHAB type.
|
||||
*
|
||||
* @param binary switch value
|
||||
* @return transformed openHAB state
|
||||
*/
|
||||
private static State getBinaryState(String binarySwitchState) {
|
||||
if (binarySwitchState != null) {
|
||||
if (binarySwitchState.equals("on")) {
|
||||
return OnOffType.ON;
|
||||
} else if (binarySwitchState.equals("off")) {
|
||||
return OnOffType.OFF;
|
||||
}
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an value in an openHAB type.
|
||||
* - ON to OPEN
|
||||
* - OFF to CLOSED
|
||||
*
|
||||
* @param binary sensor state
|
||||
* @return
|
||||
*/
|
||||
private static State getDoorlockState(String binarySensorState) {
|
||||
if (binarySensorState != null) {
|
||||
if (binarySensorState.equals("on")) {
|
||||
return OpenClosedType.OPEN;
|
||||
} else if (binarySensorState.equals("off")) {
|
||||
return OpenClosedType.CLOSED;
|
||||
}
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an value in an openHAB type.
|
||||
*
|
||||
* @param Z-Way color value
|
||||
* @return transformed openHAB state
|
||||
*/
|
||||
private static State getColorState(Color colorSwitchState) {
|
||||
if (colorSwitchState != null && colorSwitchState.getRed() != null && colorSwitchState.getGreen() != null
|
||||
&& colorSwitchState.getBlue() != null) {
|
||||
HSBType hsbType = HSBType.fromRGB(colorSwitchState.getRed(), colorSwitchState.getGreen(),
|
||||
colorSwitchState.getBlue());
|
||||
|
||||
return hsbType;
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal.discovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.net.util.SubnetUtils;
|
||||
import org.openhab.binding.zway.internal.ZWayBindingConstants;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ZWayBridgeDiscoveryService} is responsible for device discovery.
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution
|
||||
*/
|
||||
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.zway")
|
||||
public class ZWayBridgeDiscoveryService extends AbstractDiscoveryService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private static final int SEARCH_TIME = 240;
|
||||
|
||||
public ZWayBridgeDiscoveryService() {
|
||||
super(ZWayBindingConstants.SUPPORTED_DEVICE_THING_TYPES_UIDS, SEARCH_TIME);
|
||||
logger.debug("Initializing ZWayBridgeDiscoveryService");
|
||||
}
|
||||
|
||||
private void scan() {
|
||||
logger.debug("Starting scan for Z-Way Server");
|
||||
|
||||
ValidateIPV4 validator = new ValidateIPV4();
|
||||
|
||||
try {
|
||||
Enumeration<NetworkInterface> enumNetworkInterface = NetworkInterface.getNetworkInterfaces();
|
||||
while (enumNetworkInterface.hasMoreElements()) {
|
||||
NetworkInterface networkInterface = enumNetworkInterface.nextElement();
|
||||
if (networkInterface.isUp() && !networkInterface.isVirtual() && !networkInterface.isLoopback()) {
|
||||
for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
|
||||
if (validator.isValidIPV4(address.getAddress().getHostAddress())) {
|
||||
String ipAddress = address.getAddress().getHostAddress();
|
||||
Short prefix = address.getNetworkPrefixLength();
|
||||
|
||||
logger.debug("Scan IP address for Z-Way Server: {}", ipAddress);
|
||||
|
||||
// Search on localhost first
|
||||
scheduler.execute(new ZWayServerScan(ipAddress));
|
||||
|
||||
String subnet = ipAddress + "/" + prefix;
|
||||
SubnetUtils utils = new SubnetUtils(subnet);
|
||||
String[] addresses = utils.getInfo().getAllAddresses();
|
||||
|
||||
for (String addressInSubnet : addresses) {
|
||||
scheduler.execute(new ZWayServerScan(addressInSubnet));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
logger.warn("Error occurred while searching Z-Way servers ({})", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean pingHost(String host, int port, int timeout) {
|
||||
try (Socket socket = new Socket()) {
|
||||
socket.connect(new InetSocketAddress(host, port), timeout);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false; // Either timeout or unreachable or failed DNS lookup.
|
||||
}
|
||||
}
|
||||
|
||||
public class ZWayServerScan implements Runnable {
|
||||
private String ipAddress;
|
||||
|
||||
public ZWayServerScan(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!pingHost(ipAddress, 8083, 500)) {
|
||||
return; // Error occurred while searching Z-Way servers (Unreachable)
|
||||
}
|
||||
|
||||
try {
|
||||
URL url = new URL("http://" + ipAddress + ":8083/ZAutomation/api/v1/status");
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
if (connection.getResponseCode() == 401) {
|
||||
ThingUID thingUID = new ThingUID(ZWayBindingConstants.THING_TYPE_BRIDGE,
|
||||
ipAddress.replaceAll("\\.", "_"));
|
||||
|
||||
// Attention: if is already present as thing in the ThingRegistry
|
||||
// the configuration for thing will be updated!
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
|
||||
.withProperty(ZWayBindingConstants.BRIDGE_CONFIG_ZWAY_SERVER_IP_ADDRESS, ipAddress)
|
||||
.withLabel("Z-Way Server " + ipAddress).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Discovery resulted in an unexpected exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
scan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
super.stopScan();
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
}
|
||||
|
||||
class ValidateIPV4 {
|
||||
private final String ipV4Regex = "^((0|1\\d?\\d?|2[0-4]?\\d?|25[0-5]?|[3-9]\\d?)\\.){3}(0|1\\d?\\d?|2[0-4]?\\d?|25[0-5]?|[3-9]\\d?)$";
|
||||
private Pattern ipV4Pattern = Pattern.compile(ipV4Regex);
|
||||
|
||||
public boolean isValidIPV4(final String s) {
|
||||
return ipV4Pattern.matcher(s).matches();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal.discovery;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.openhab.binding.zway.internal.ZWayBindingConstants;
|
||||
import org.openhab.binding.zway.internal.handler.ZWayBridgeHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.Device;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.Camera;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SensorMultiline;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.Text;
|
||||
import de.fh_zwickau.informatik.sensor.model.locations.LocationList;
|
||||
import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
|
||||
|
||||
/**
|
||||
* The {@link ZWayDeviceDiscoveryService} is responsible for device discovery.
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution
|
||||
*/
|
||||
public class ZWayDeviceDiscoveryService extends AbstractDiscoveryService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private static final int SEARCH_TIME = 60;
|
||||
private static final int INITIAL_DELAY = 15;
|
||||
private static final int SCAN_INTERVAL = 240;
|
||||
|
||||
private ZWayBridgeHandler mBridgeHandler;
|
||||
private ZWayDeviceScan mZWayDeviceScanningRunnable;
|
||||
private ScheduledFuture<?> mZWayDeviceScanningJob;
|
||||
|
||||
public ZWayDeviceDiscoveryService(ZWayBridgeHandler bridgeHandler) {
|
||||
super(ZWayBindingConstants.SUPPORTED_DEVICE_THING_TYPES_UIDS, SEARCH_TIME);
|
||||
logger.debug("Initializing ZWayBridgeDiscoveryService");
|
||||
mBridgeHandler = bridgeHandler;
|
||||
mZWayDeviceScanningRunnable = new ZWayDeviceScan();
|
||||
activate(null);
|
||||
}
|
||||
|
||||
private void scan() {
|
||||
logger.debug("Starting scan on Z-Way Server {}", mBridgeHandler.getThing().getUID());
|
||||
|
||||
// Z-Way bridge have to be ONLINE because configuration is needed
|
||||
if (mBridgeHandler == null || !mBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Z-Way bridge handler not found or not ONLINE.");
|
||||
return;
|
||||
}
|
||||
|
||||
LocationList locationList = mBridgeHandler.getZWayApi().getLocations();
|
||||
|
||||
DeviceList deviceList = mBridgeHandler.getZWayApi().getDevices();
|
||||
if (deviceList != null) {
|
||||
Map<Integer, List<Device>> physicalDevices = deviceList.getDevicesGroupByNodeId();
|
||||
for (Map.Entry<Integer, List<Device>> entry : physicalDevices.entrySet()) {
|
||||
final Integer nodeId = entry.getKey();
|
||||
List<Device> devices = entry.getValue();
|
||||
|
||||
final ThingUID bridgeUID = mBridgeHandler.getThing().getUID();
|
||||
|
||||
String location = "";
|
||||
|
||||
String deviceTypes = "";
|
||||
Integer index = 0;
|
||||
for (Device device : devices) {
|
||||
if (index != 0 && index != devices.size()) {
|
||||
deviceTypes += ", ";
|
||||
}
|
||||
deviceTypes += device.getDeviceType();
|
||||
index++;
|
||||
|
||||
// Add location, assuming that each (virtual) device is assigned to the same room
|
||||
if (locationList != null) {
|
||||
// Add only the location if this differs from globalRoom (with id 0)
|
||||
if (device.getLocation() != -1 && device.getLocation() != 0) {
|
||||
try {
|
||||
location = locationList.getLocationById(device.getLocation()).getTitle();
|
||||
} catch (NullPointerException npe) {
|
||||
location = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug("Z-Way device found with {} virtual devices - device types: {}", devices.size(),
|
||||
deviceTypes);
|
||||
|
||||
ZWaveDevice zwaveDevice = mBridgeHandler.getZWayApi().getZWaveDevice(nodeId);
|
||||
if (zwaveDevice != null) {
|
||||
String givenName = "Device " + nodeId;
|
||||
if (!zwaveDevice.getData().getGivenName().getValue().equals("")) {
|
||||
givenName = zwaveDevice.getData().getGivenName().getValue();
|
||||
} else if (!zwaveDevice.getData().getDeviceTypeString().getValue().equals("")) {
|
||||
givenName += " - " + zwaveDevice.getData().getDeviceTypeString().getValue();
|
||||
}
|
||||
// Add additional information as properties
|
||||
String vendorString = zwaveDevice.getData().getVendorString().getValue();
|
||||
if (!zwaveDevice.getData().getVendorString().getValue().equals("")) {
|
||||
givenName += " (" + vendorString + ")";
|
||||
}
|
||||
String manufacturerId = zwaveDevice.getData().getManufacturerId().getValue();
|
||||
String deviceType = zwaveDevice.getData().getDeviceTypeString().getValue();
|
||||
String zddxmlfile = zwaveDevice.getData().getZDDXMLFile().getValue();
|
||||
String sdk = zwaveDevice.getData().getSDK().getValue();
|
||||
|
||||
ThingUID thingUID = new ThingUID(ZWayBindingConstants.THING_TYPE_DEVICE,
|
||||
mBridgeHandler.getThing().getUID(), nodeId.toString());
|
||||
|
||||
/*
|
||||
* Properties
|
||||
* - Configuration: DEVICE_CONFIG_NODE_ID
|
||||
* - ESH default properties:
|
||||
* --- PROPERTY_VENDOR
|
||||
* --- other default properties not available
|
||||
* - Custom properties:
|
||||
* --- DEVICE_LOCATION
|
||||
* --- DEVICE_MANUFACTURER_ID
|
||||
* --- DEVICE_DEVICE_TYPE
|
||||
* --- DEVICE_ZDDXMLFILE
|
||||
* --- DEVICE_SDK
|
||||
*/
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(givenName)
|
||||
.withBridge(bridgeUID).withProperty(ZWayBindingConstants.DEVICE_CONFIG_NODE_ID, nodeId)
|
||||
.withProperty(Thing.PROPERTY_VENDOR, vendorString)
|
||||
.withProperty(ZWayBindingConstants.DEVICE_PROP_LOCATION, location)
|
||||
.withProperty(ZWayBindingConstants.DEVICE_PROP_MANUFACTURER_ID, manufacturerId)
|
||||
.withProperty(ZWayBindingConstants.DEVICE_PROP_DEVICE_TYPE, deviceType)
|
||||
.withProperty(ZWayBindingConstants.DEVICE_PROP_ZDDXMLFILE, zddxmlfile)
|
||||
.withProperty(ZWayBindingConstants.DEVICE_PROP_SDK, sdk).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
} else {
|
||||
logger.warn("Z-Wave device not loaded");
|
||||
}
|
||||
}
|
||||
|
||||
for (Device device : deviceList.getDevices()) {
|
||||
if (device.getVisibility() && !device.getPermanentlyHidden()) {
|
||||
if (ZWayBindingConstants.DISCOVERY_IGNORED_DEVICES.contains(device.getDeviceId().split("_")[0])) {
|
||||
logger.debug("Skip device: {}", device.getMetrics().getTitle());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (device instanceof SensorMultiline || device instanceof Camera || device instanceof Text) {
|
||||
logger.debug("Skip device because the device type is not supported: {}",
|
||||
device.getMetrics().getTitle());
|
||||
continue;
|
||||
}
|
||||
|
||||
ThingUID bridgeUID = mBridgeHandler.getThing().getUID();
|
||||
|
||||
String location = "";
|
||||
// Add location, assuming that each (virtual) device is assigned to the same room
|
||||
if (locationList != null) {
|
||||
// Add only the location if this differs from globalRoom (with id 0)
|
||||
if (device.getLocation() != -1 && device.getLocation() != 0) {
|
||||
try {
|
||||
location = locationList.getLocationById(device.getLocation()).getTitle();
|
||||
} catch (NullPointerException npe) {
|
||||
location = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Z-Way virtual device found with device type: {} - {} - {}", device.getDeviceType(),
|
||||
device.getMetrics().getProbeTitle(), device.getNodeId());
|
||||
|
||||
ThingUID thingUID = new ThingUID(ZWayBindingConstants.THING_TYPE_VIRTUAL_DEVICE,
|
||||
mBridgeHandler.getThing().getUID(), device.getDeviceId());
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
|
||||
.withLabel(device.getMetrics().getTitle()).withBridge(bridgeUID)
|
||||
.withProperty(ZWayBindingConstants.DEVICE_CONFIG_VIRTUAL_DEVICE_ID, device.getDeviceId())
|
||||
.withProperty(ZWayBindingConstants.DEVICE_PROP_LOCATION, location).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn("Devices not loaded");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
scan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
super.stopScan();
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
if (mZWayDeviceScanningJob == null || mZWayDeviceScanningJob.isCancelled()) {
|
||||
logger.debug("Starting background scanning job");
|
||||
mZWayDeviceScanningJob = scheduler.scheduleWithFixedDelay(mZWayDeviceScanningRunnable, INITIAL_DELAY,
|
||||
SCAN_INTERVAL, TimeUnit.SECONDS);
|
||||
} else {
|
||||
logger.debug("Scanning job is allready active");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
if (mZWayDeviceScanningJob != null && !mZWayDeviceScanningJob.isCancelled()) {
|
||||
mZWayDeviceScanningJob.cancel(false);
|
||||
mZWayDeviceScanningJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class ZWayDeviceScan implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
scan();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal.handler;
|
||||
|
||||
import static org.openhab.binding.zway.internal.ZWayBindingConstants.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openhab.binding.zway.internal.config.ZWayBridgeConfiguration;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.fh_zwickau.informatik.sensor.IZWayApi;
|
||||
import de.fh_zwickau.informatik.sensor.IZWayApiCallbacks;
|
||||
import de.fh_zwickau.informatik.sensor.ZWayApiHttp;
|
||||
import de.fh_zwickau.informatik.sensor.model.devicehistory.DeviceHistory;
|
||||
import de.fh_zwickau.informatik.sensor.model.devicehistory.DeviceHistoryList;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.Device;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.instances.Instance;
|
||||
import de.fh_zwickau.informatik.sensor.model.instances.InstanceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.locations.Location;
|
||||
import de.fh_zwickau.informatik.sensor.model.locations.LocationList;
|
||||
import de.fh_zwickau.informatik.sensor.model.modules.ModuleList;
|
||||
import de.fh_zwickau.informatik.sensor.model.namespaces.NamespaceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.notifications.Notification;
|
||||
import de.fh_zwickau.informatik.sensor.model.notifications.NotificationList;
|
||||
import de.fh_zwickau.informatik.sensor.model.profiles.Profile;
|
||||
import de.fh_zwickau.informatik.sensor.model.profiles.ProfileList;
|
||||
import de.fh_zwickau.informatik.sensor.model.zwaveapi.controller.ZWaveController;
|
||||
import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
|
||||
|
||||
/**
|
||||
* The {@link ZWayBridgeHandler} manages the connection between Z-Way API and binding.
|
||||
*
|
||||
* During the initialization the following tasks are performed:
|
||||
* - load and check configuration
|
||||
* - instantiate a Z-Way API that used in the whole binding
|
||||
* - authenticate to the Z-Way server
|
||||
* - initialize all containing device things
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution, remove observer mechanism
|
||||
* @author Johannes Einig - Bridge now stores DeviceList
|
||||
*/
|
||||
public class ZWayBridgeHandler extends BaseBridgeHandler implements IZWayApiCallbacks {
|
||||
|
||||
public static final ThingTypeUID SUPPORTED_THING_TYPE = THING_TYPE_BRIDGE;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private BridgePolling bridgePolling;
|
||||
private ScheduledFuture<?> pollingJob;
|
||||
|
||||
private ResetInclusionExclusion resetInclusionExclusion;
|
||||
private ScheduledFuture<?> resetInclusionExclusionJob;
|
||||
|
||||
private ZWayBridgeConfiguration mConfig;
|
||||
private IZWayApi mZWayApi;
|
||||
|
||||
private DeviceList deviceList;
|
||||
|
||||
/**
|
||||
* Initializer authenticate the Z-Way API instance with bridge configuration.
|
||||
*
|
||||
* If Z-Way API successfully authenticated:
|
||||
* - check existence of openHAB Connector in Z-Way server and configure openHAB server
|
||||
* - initialize all containing device things
|
||||
*/
|
||||
private class Initializer implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
logger.debug("Authenticate to the Z-Way server ...");
|
||||
|
||||
// https://community.openhab.org/t/oh2-major-bug-with-scheduled-jobs/12350/11
|
||||
// If any execution of the task encounters an exception, subsequent executions are
|
||||
// suppressed. Otherwise, the task will only terminate via cancellation or
|
||||
// termination of the executor.
|
||||
try {
|
||||
// Authenticate - thing status update with a error message
|
||||
if (mZWayApi.getLogin() != null) {
|
||||
// Thing status set to online in login callback
|
||||
logger.info("Z-Way bridge successfully authenticated");
|
||||
// Gets the latest deviceList from zWay during bridge initialization
|
||||
deviceList = mZWayApi.getDevices();
|
||||
|
||||
// Initialize bridge polling
|
||||
if (pollingJob == null || pollingJob.isCancelled()) {
|
||||
logger.debug("Starting polling job at intervall {}", mConfig.getPollingInterval());
|
||||
pollingJob = scheduler.scheduleWithFixedDelay(bridgePolling, 10, mConfig.getPollingInterval(),
|
||||
TimeUnit.SECONDS);
|
||||
} else {
|
||||
// Called when thing or bridge updated ...
|
||||
logger.debug("Polling is allready active");
|
||||
}
|
||||
|
||||
// Initializing all containing device things
|
||||
logger.debug("Initializing all configured devices ...");
|
||||
for (Thing thing : getThing().getThings()) {
|
||||
ThingHandler handler = thing.getHandler();
|
||||
if (handler != null) {
|
||||
logger.debug("Initializing device: {}", thing.getLabel());
|
||||
handler.initialize();
|
||||
} else {
|
||||
logger.warn("Initializing device failed (DeviceHandler is null): {}", thing.getLabel());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn("Z-Way bridge couldn't authenticated");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (t instanceof Exception) {
|
||||
logger.error("{}", t.getMessage());
|
||||
} else if (t instanceof Error) {
|
||||
logger.error("{}", t.getMessage());
|
||||
} else {
|
||||
logger.error("Unexpected error");
|
||||
}
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Error occurred when initialize bridge.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposer clean up openHAB Connector configuration
|
||||
*/
|
||||
private class Remover implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Removing all containing device things
|
||||
logger.debug("Removing all configured devices ...");
|
||||
for (Thing thing : getThing().getThings()) {
|
||||
ThingHandler handler = thing.getHandler();
|
||||
if (handler != null) {
|
||||
logger.debug("Removing device: {}", thing.getLabel());
|
||||
handler.handleRemoval();
|
||||
} else {
|
||||
logger.warn("Removing device failed (DeviceHandler is null): {}", thing.getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
// status update will finally remove the thing
|
||||
updateStatus(ThingStatus.REMOVED);
|
||||
}
|
||||
}
|
||||
|
||||
public ZWayBridgeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
|
||||
bridgePolling = new BridgePolling();
|
||||
resetInclusionExclusion = new ResetInclusionExclusion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// possible commands: check Z-Way server, check openHAB Connector, reconnect, ...
|
||||
logger.debug("Handle command for channel: {} with command: {}", channelUID.getId(), command.toString());
|
||||
|
||||
if (channelUID.getId().equals(ACTIONS_CHANNEL)) {
|
||||
if (command.toString().equals(ACTIONS_CHANNEL_OPTION_REFRESH)) {
|
||||
logger.debug("Handle bridge refresh command for all configured devices ...");
|
||||
for (Thing thing : getThing().getThings()) {
|
||||
ZWayDeviceHandler handler = (ZWayDeviceHandler) thing.getHandler();
|
||||
if (handler != null) {
|
||||
logger.debug("Refreshing device: {}", thing.getLabel());
|
||||
handler.refreshAllChannels();
|
||||
} else {
|
||||
logger.warn("Refreshing device failed (DeviceHandler is null): {}", thing.getLabel());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (channelUID.getId().equals(SECURE_INCLUSION_CHANNEL)) {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
logger.debug("Enable bridge secure inclusion ...");
|
||||
mZWayApi.updateControllerData("secureInclusion", "true");
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
logger.debug("Disable bridge secure inclusion ...");
|
||||
mZWayApi.updateControllerData("secureInclusion", "false");
|
||||
}
|
||||
} else if (channelUID.getId().equals(INCLUSION_CHANNEL)) {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
logger.debug("Handle bridge start inclusion command ...");
|
||||
mZWayApi.getZWaveInclusion(1);
|
||||
|
||||
// Start reset job
|
||||
if (resetInclusionExclusionJob == null || resetInclusionExclusionJob.isCancelled()) {
|
||||
logger.debug("Starting reset inclusion and exclusion job in 30 seconds");
|
||||
resetInclusionExclusionJob = scheduler.schedule(resetInclusionExclusion, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
logger.debug("Handle bridge stop inclusion command ...");
|
||||
mZWayApi.getZWaveInclusion(0);
|
||||
}
|
||||
} else if (channelUID.getId().equals(EXCLUSION_CHANNEL)) {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
logger.debug("Handle bridge start exclusion command ...");
|
||||
mZWayApi.getZWaveExclusion(1);
|
||||
|
||||
// Start reset job
|
||||
if (resetInclusionExclusionJob == null || resetInclusionExclusionJob.isCancelled()) {
|
||||
logger.debug("Starting reset inclusion and exclusion job in 30 seconds");
|
||||
resetInclusionExclusionJob = scheduler.schedule(resetInclusionExclusion, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
logger.debug("Handle bridge stop exclusion command ...");
|
||||
mZWayApi.getZWaveExclusion(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.info("Initializing Z-Way bridge ...");
|
||||
|
||||
// Set thing status to a valid status
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Checking configuration...");
|
||||
|
||||
// Configuration - thing status update with a error message
|
||||
mConfig = loadAndCheckConfiguration();
|
||||
|
||||
if (mConfig != null) {
|
||||
logger.debug("Configuration complete: {}", mConfig);
|
||||
|
||||
mZWayApi = new ZWayApiHttp(mConfig.getZWayIpAddress(), mConfig.getZWayPort(), mConfig.getZWayProtocol(),
|
||||
mConfig.getZWayUsername(), mConfig.getZWayPassword(), -1, false, this);
|
||||
|
||||
// Start an extra thread, because it takes sometimes more
|
||||
// than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
|
||||
scheduler.execute(new Initializer());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing Z-Way bridge ...");
|
||||
|
||||
if (pollingJob != null && !pollingJob.isCancelled()) {
|
||||
pollingJob.cancel(true);
|
||||
pollingJob = null;
|
||||
}
|
||||
|
||||
if (resetInclusionExclusionJob != null && !resetInclusionExclusionJob.isCancelled()) {
|
||||
resetInclusionExclusionJob.cancel(true);
|
||||
resetInclusionExclusionJob = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private class BridgePolling implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.debug("Starting polling for bridge: {}", getThing().getLabel());
|
||||
if (getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
updateControllerData();
|
||||
} else {
|
||||
logger.debug("Polling not possible, bridge isn't ONLINE");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateControllerData() {
|
||||
// Add additional information as properties or update channels
|
||||
|
||||
ZWaveController zwaveController = mZWayApi.getZWaveController();
|
||||
if (zwaveController != null) {
|
||||
Map<String, String> properties = editProperties();
|
||||
// ESH default properties
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, zwaveController.getData().getAPIVersion().getValue());
|
||||
properties.put(Thing.PROPERTY_HARDWARE_VERSION, zwaveController.getData().getZWaveChip().getValue());
|
||||
// Thing.PROPERTY_MODEL_ID not available, only manufacturerProductId
|
||||
properties.put(Thing.PROPERTY_SERIAL_NUMBER, zwaveController.getData().getUuid().getValue());
|
||||
properties.put(Thing.PROPERTY_VENDOR, zwaveController.getData().getVendor().getValue());
|
||||
|
||||
// Custom properties
|
||||
properties.put(BRIDGE_PROP_SOFTWARE_REVISION_VERSION,
|
||||
zwaveController.getData().getSoftwareRevisionVersion().getValue());
|
||||
properties.put(BRIDGE_PROP_SOFTWARE_REVISION_DATE,
|
||||
zwaveController.getData().getSoftwareRevisionDate().getValue());
|
||||
properties.put(BRIDGE_PROP_SDK, zwaveController.getData().getSDK().getValue());
|
||||
properties.put(BRIDGE_PROP_MANUFACTURER_ID, zwaveController.getData().getManufacturerId().getValue());
|
||||
properties.put(BRIDGE_PROP_SECURE_INCLUSION, zwaveController.getData().getSecureInclusion().getValue());
|
||||
properties.put(BRIDGE_PROP_FREQUENCY, zwaveController.getData().getFrequency().getValue());
|
||||
updateProperties(properties);
|
||||
|
||||
// Update channels
|
||||
if (zwaveController.getData().getSecureInclusion().getValue().equals("true")) {
|
||||
updateState(SECURE_INCLUSION_CHANNEL, OnOffType.ON);
|
||||
} else {
|
||||
updateState(SECURE_INCLUSION_CHANNEL, OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inclusion/Exclusion must be reset manually, also channel states.
|
||||
*/
|
||||
private class ResetInclusionExclusion implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.debug("Reset inclusion and exclusion for bridge: {}", getThing().getLabel());
|
||||
if (getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
mZWayApi.getZWaveInclusion(0);
|
||||
mZWayApi.getZWaveExclusion(0);
|
||||
|
||||
updateState(INCLUSION_CHANNEL, OnOffType.OFF);
|
||||
updateState(EXCLUSION_CHANNEL, OnOffType.OFF);
|
||||
} else {
|
||||
logger.debug("Reset inclusion and exclusion not possible, bridge isn't ONLINE");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRemoval() {
|
||||
logger.debug("Handle removal Z-Way bridge ...");
|
||||
|
||||
// Start an extra thread, because it takes sometimes more
|
||||
// than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
|
||||
scheduler.execute(new Remover());
|
||||
|
||||
// super.handleRemoval() called in every case in scheduled task ...
|
||||
}
|
||||
|
||||
protected ZWayBridgeConfiguration getZWayBridgeConfiguration() {
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
******* DeviceList handling*****
|
||||
********************************
|
||||
* Updates the deviceList every time a
|
||||
* ChildHandler is initialized or disposed
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) {
|
||||
updateDeviceList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) {
|
||||
updateDeviceList();
|
||||
}
|
||||
|
||||
private void updateDeviceList() {
|
||||
if (mZWayApi != null) {
|
||||
logger.debug("ChildHandler changed. Updating device List");
|
||||
deviceList = mZWayApi.getDevices();
|
||||
} else {
|
||||
logger.debug("Bridge Handler not online. No update of device list performed.");
|
||||
}
|
||||
}
|
||||
|
||||
private ZWayBridgeConfiguration loadAndCheckConfiguration() {
|
||||
ZWayBridgeConfiguration config = getConfigAs(ZWayBridgeConfiguration.class);
|
||||
|
||||
/****************************************
|
||||
****** Z-Way server configuration ******
|
||||
****************************************/
|
||||
|
||||
// Z-Way IP address
|
||||
if (StringUtils.trimToNull(config.getZWayIpAddress()) == null) {
|
||||
config.setZWayIpAddress("localhost"); // default value
|
||||
}
|
||||
|
||||
// Z-Way Port
|
||||
if (config.getZWayPort() == null) {
|
||||
config.setZWayPort(8083);
|
||||
}
|
||||
|
||||
// Z-Way Protocol
|
||||
if (StringUtils.trimToNull(config.getZWayProtocol()) == null) {
|
||||
config.setZWayProtocol("http");
|
||||
}
|
||||
|
||||
// Z-Way Password
|
||||
if (StringUtils.trimToNull(config.getZWayPassword()) == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"The connection to the Z-Way Server can't established, because the Z-Way password is missing. Please set a Z-Way password.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Z-Way Username
|
||||
if (StringUtils.trimToNull(config.getZWayUsername()) == null) {
|
||||
config.setZWayUsername("admin"); // default value
|
||||
}
|
||||
|
||||
/***********************************
|
||||
****** General configuration ******
|
||||
**********************************/
|
||||
|
||||
// Polling interval
|
||||
if (config.getPollingInterval() == null) {
|
||||
config.setPollingInterval(3600);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Z-Way API instance
|
||||
*/
|
||||
public IZWayApi getZWayApi() {
|
||||
return mZWayApi;
|
||||
}
|
||||
|
||||
public DeviceList getDeviceList() {
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
/********************************
|
||||
****** Z-Way API callback ******
|
||||
*******************************/
|
||||
|
||||
@Override
|
||||
public void getStatusResponse(String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRestartResponse(Boolean status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLoginResponse(String sessionId) {
|
||||
logger.debug("New session id: {}", sessionId);
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getNamespacesResponse(NamespaceList namespaces) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getModulesResponse(ModuleList moduleList) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getInstancesResponse(InstanceList instanceList) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInstanceResponse(Instance instance) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getInstanceResponse(Instance instance) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putInstanceResponse(Instance instance) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteInstanceResponse(boolean status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDevicesResponse(DeviceList deviceList) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putDeviceResponse(Device device) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceResponse(Device device) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceCommandResponse(String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLocationsResponse(LocationList locationList) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postLocationResponse(Location location) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLocationResponse(Location location) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putLocationResponse(Location location) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteLocationResponse(boolean status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getProfilesResponse(ProfileList profileList) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProfileResponse(Profile profile) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getProfileResponse(Profile profile) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putProfileResponse(Profile profile) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProfileResponse(boolean status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getNotificationsResponse(NotificationList notificationList) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getNotificationResponse(Notification notification) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putNotificationResponse(Notification notification) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceHistoriesResponse(DeviceHistoryList deviceHistoryList) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceHistoryResponse(DeviceHistory deviceHistory) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apiError(String message, boolean invalidateState) {
|
||||
if (invalidateState) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void httpStatusError(int httpStatus, String message, boolean invalidateState) {
|
||||
if (invalidateState) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
message + "(HTTP status code: " + httpStatus + ").");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticationError() {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Authentication error. Please check username and password.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void responseFormatError(String message, boolean invalidateApiState) {
|
||||
if (invalidateApiState) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void message(int code, String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getZWaveDeviceResponse(ZWaveDevice zwaveDevice) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getZWaveControllerResponse(ZWaveController zwaveController) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,785 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal.handler;
|
||||
|
||||
import static de.fh_zwickau.informatik.sensor.ZWayConstants.*;
|
||||
import static org.openhab.binding.zway.internal.ZWayBindingConstants.*;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.openhab.binding.zway.internal.ZWayBindingConstants;
|
||||
import org.openhab.binding.zway.internal.converter.ZWayDeviceStateConverter;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.StopMoveType;
|
||||
import org.openhab.core.library.types.UpDownType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.Device;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.Battery;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.Doorlock;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SensorBinary;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SensorDiscrete;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SensorMultilevel;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchBinary;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchControl;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchMultilevel;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchRGBW;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchToggle;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.Thermostat;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.types.ToggleButton;
|
||||
import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
|
||||
|
||||
/**
|
||||
* The {@link ZWayDeviceHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution, remove observer mechanism
|
||||
* @author Johannes Einig - Now uses the bridge handler cached device list
|
||||
*/
|
||||
public abstract class ZWayDeviceHandler extends BaseThingHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private DevicePolling devicePolling;
|
||||
private ScheduledFuture<?> pollingJob;
|
||||
protected Calendar lastUpdate;
|
||||
|
||||
protected abstract void refreshLastUpdate();
|
||||
|
||||
/**
|
||||
* Initialize polling job
|
||||
*/
|
||||
private class Initializer implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// https://community.openhab.org/t/oh2-major-bug-with-scheduled-jobs/12350/11
|
||||
// If any execution of the task encounters an exception, subsequent executions are
|
||||
// suppressed. Otherwise, the task will only terminate via cancellation or
|
||||
// termination of the executor.
|
||||
try {
|
||||
// Z-Way bridge have to be ONLINE because configuration is needed
|
||||
ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
|
||||
if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Z-Way bridge handler not found or not ONLINE.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize device polling
|
||||
if (pollingJob == null || pollingJob.isCancelled()) {
|
||||
logger.debug("Starting polling job at intervall {}",
|
||||
zwayBridgeHandler.getZWayBridgeConfiguration().getPollingInterval());
|
||||
pollingJob = scheduler.scheduleWithFixedDelay(devicePolling, 10,
|
||||
zwayBridgeHandler.getZWayBridgeConfiguration().getPollingInterval(), TimeUnit.SECONDS);
|
||||
} else {
|
||||
// Called when thing or bridge updated ...
|
||||
logger.debug("Polling is allready active");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (t instanceof Exception) {
|
||||
logger.error("{}", t.getMessage());
|
||||
} else if (t instanceof Error) {
|
||||
logger.error("{}", t.getMessage());
|
||||
} else {
|
||||
logger.error("Unexpected error");
|
||||
}
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Error occurred when starting polling.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Disposer implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Z-Way bridge have to be ONLINE because configuration is needed
|
||||
ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
|
||||
if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Z-Way bridge handler not found or not ONLINE.");
|
||||
|
||||
// status update will remove finally
|
||||
updateStatus(ThingStatus.REMOVED);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// status update will remove finally
|
||||
updateStatus(ThingStatus.REMOVED);
|
||||
}
|
||||
}
|
||||
|
||||
public ZWayDeviceHandler(Thing thing) {
|
||||
super(thing);
|
||||
|
||||
devicePolling = new DevicePolling();
|
||||
}
|
||||
|
||||
protected synchronized ZWayBridgeHandler getZWayBridgeHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
return null;
|
||||
}
|
||||
ThingHandler handler = bridge.getHandler();
|
||||
if (handler instanceof ZWayBridgeHandler) {
|
||||
return (ZWayBridgeHandler) handler;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
setLocation();
|
||||
|
||||
// Start an extra thread to check the connection, because it takes sometimes more
|
||||
// than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
|
||||
scheduler.execute(new Initializer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (pollingJob != null && !pollingJob.isCancelled()) {
|
||||
pollingJob.cancel(true);
|
||||
pollingJob = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRemoval() {
|
||||
logger.debug("Handle removal Z-Way device ...");
|
||||
|
||||
// Start an extra thread, because it takes sometimes more
|
||||
// than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
|
||||
scheduler.execute(new Disposer());
|
||||
|
||||
// super.handleRemoval() called in every case in scheduled task ...
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
// Only called if status ONLINE or OFFLINE
|
||||
logger.debug("Z-Way bridge status changed: {}", bridgeStatusInfo);
|
||||
|
||||
if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Bridge status is offline.");
|
||||
} else if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
|
||||
// Initialize thing, if all OK the status of device thing will be ONLINE
|
||||
|
||||
// Start an extra thread to check the connection, because it takes sometimes more
|
||||
// than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
|
||||
scheduler.execute(new Initializer());
|
||||
}
|
||||
}
|
||||
|
||||
private class DevicePolling implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.debug("Starting polling for device: {}", getThing().getLabel());
|
||||
if (getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
// Refresh device states
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
logger.debug("Checking link state of channel: {}", channel.getLabel());
|
||||
if (isLinked(channel.getUID().getId())) {
|
||||
logger.debug("Refresh items that linked with channel: {}", channel.getLabel());
|
||||
|
||||
// https://community.openhab.org/t/oh2-major-bug-with-scheduled-jobs/12350/11
|
||||
// If any execution of the task encounters an exception, subsequent executions are
|
||||
// suppressed. Otherwise, the task will only terminate via cancellation or
|
||||
// termination of the executor.
|
||||
try {
|
||||
refreshChannel(channel);
|
||||
} catch (Throwable t) {
|
||||
if (t instanceof Exception) {
|
||||
logger.error("Error occurred when performing polling:{}", t.getMessage());
|
||||
} else if (t instanceof Error) {
|
||||
logger.error("Error occurred when performing polling:{}", t.getMessage());
|
||||
} else {
|
||||
logger.error("Error occurred when performing polling: Unexpected error");
|
||||
}
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
|
||||
"Error occurred when performing polling.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Polling for device: {} not possible (channel {} not linked", thing.getLabel(),
|
||||
channel.getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh last update
|
||||
refreshLastUpdate();
|
||||
} else {
|
||||
logger.debug("Polling not possible, Z-Way device isn't ONLINE");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void setLocation() {
|
||||
Map<String, String> properties = getThing().getProperties();
|
||||
// Load location from properties
|
||||
String location = properties.get(ZWayBindingConstants.DEVICE_PROP_LOCATION);
|
||||
if (location != null && !location.equals("") && getThing().getLocation() == null) {
|
||||
logger.debug("Set location to {}", location);
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
thingBuilder.withLocation(location);
|
||||
thingBuilder.withLabel(thing.getLabel());
|
||||
updateThing(thingBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
protected void refreshAllChannels() {
|
||||
scheduler.execute(new DevicePolling());
|
||||
}
|
||||
|
||||
private void refreshChannel(Channel channel) {
|
||||
// Check Z-Way bridge handler
|
||||
ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
|
||||
if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Z-Way bridge handler not found or not ONLINE.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check device id associated with channel
|
||||
String deviceId = channel.getProperties().get("deviceId");
|
||||
if (deviceId != null) {
|
||||
// Load and check device from Z-Way server
|
||||
DeviceList deviceList = zwayBridgeHandler.getZWayApi().getDevices();
|
||||
if (deviceList != null) {
|
||||
// 1.) Load only the current value from Z-Way server
|
||||
Device device = deviceList.getDeviceById(deviceId);
|
||||
if (device == null) {
|
||||
logger.debug("ZAutomation device not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
updateState(channel.getUID(), ZWayDeviceStateConverter.toState(device, channel));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
logger.debug(
|
||||
"IllegalArgumentException ({}) during refresh channel for device: {} (level: {}) with channel: {}",
|
||||
iae.getMessage(), device.getMetrics().getTitle(), device.getMetrics().getLevel(),
|
||||
channel.getChannelTypeUID());
|
||||
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
|
||||
"Channel refresh for device: " + device.getMetrics().getTitle() + " (level: "
|
||||
+ device.getMetrics().getLevel() + ") with channel: " + channel.getChannelTypeUID()
|
||||
+ " failed!");
|
||||
}
|
||||
// 2.) Trigger update function, soon as the value has been updated, openHAB will be notified
|
||||
try {
|
||||
device.update();
|
||||
} catch (Exception e) {
|
||||
logger.debug("{} doesn't support update (triggered during refresh channel)",
|
||||
device.getMetrics().getTitle());
|
||||
}
|
||||
} else {
|
||||
logger.warn("Devices not loaded");
|
||||
}
|
||||
} else {
|
||||
// Check channel for command classes
|
||||
// Channel thermostat mode
|
||||
if (channel.getUID().equals(new ChannelUID(getThing().getUID(), THERMOSTAT_MODE_CC_CHANNEL))) {
|
||||
// Load physical device
|
||||
Integer nodeId = Integer.parseInt(channel.getProperties().get("nodeId"));
|
||||
ZWaveDevice physicalDevice = zwayBridgeHandler.getZWayApi().getZWaveDevice(nodeId);
|
||||
|
||||
if (physicalDevice != null) {
|
||||
updateState(channel.getUID(), new DecimalType(physicalDevice.getInstances().get0()
|
||||
.getCommandClasses().get64().getData().getMode().getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelLinked(ChannelUID channelUID) {
|
||||
logger.debug("Z-Way device channel linked: {}", channelUID);
|
||||
|
||||
ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
|
||||
if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Z-Way bridge handler not found or not ONLINE.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Method called when channel linked and not when server started!!!
|
||||
|
||||
super.channelLinked(channelUID); // performs a refresh command
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelUnlinked(ChannelUID channelUID) {
|
||||
logger.debug("Z-Way device channel unlinked: {}", channelUID);
|
||||
|
||||
ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
|
||||
if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Z-Way bridge handler not found or not ONLINE.");
|
||||
return;
|
||||
}
|
||||
|
||||
super.channelUnlinked(channelUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, final Command command) {
|
||||
logger.debug("Handle command for channel: {} with command: {}", channelUID.getId(), command.toString());
|
||||
|
||||
// Check Z-Way bridge handler
|
||||
ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
|
||||
if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Z-Way bridge handler not found or not ONLINE.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Load device id from channel's properties for the compatibility of ZAutomation and ZWave devices
|
||||
final Channel channel = getThing().getChannel(channelUID.getId());
|
||||
final String deviceId = channel.getProperties().get("deviceId");
|
||||
|
||||
if (deviceId != null) {
|
||||
DeviceList deviceList = zwayBridgeHandler.getDeviceList();
|
||||
if (deviceList != null) {
|
||||
Device device = deviceList.getDeviceById(deviceId);
|
||||
if (device == null) {
|
||||
logger.debug("ZAutomation device not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (command instanceof RefreshType) {
|
||||
logger.debug("Handle command: RefreshType");
|
||||
|
||||
refreshChannel(channel);
|
||||
} else {
|
||||
if (device instanceof Battery) {
|
||||
// possible commands: update()
|
||||
} else if (device instanceof Doorlock) {
|
||||
// possible commands: open(), close()
|
||||
if (command instanceof OnOffType) {
|
||||
logger.debug("Handle command: OnOffType");
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
device.open();
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
device.close();
|
||||
}
|
||||
}
|
||||
} else if (device instanceof SensorBinary) {
|
||||
// possible commands: update()
|
||||
} else if (device instanceof SensorMultilevel) {
|
||||
// possible commands: update()
|
||||
} else if (device instanceof SwitchBinary) {
|
||||
// possible commands: update(), on(), off()
|
||||
if (command instanceof OnOffType) {
|
||||
logger.debug("Handle command: OnOffType");
|
||||
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
device.on();
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
device.off();
|
||||
}
|
||||
}
|
||||
} else if (device instanceof SwitchMultilevel) {
|
||||
// possible commands: update(), on(), up(), off(), down(), min(), max(), upMax(),
|
||||
// increase(), decrease(), exact(level), exactSmooth(level, duration), stop(), startUp(),
|
||||
// startDown()
|
||||
if (command instanceof DecimalType || command instanceof PercentType) {
|
||||
logger.debug("Handle command: DecimalType");
|
||||
|
||||
device.exact(command.toString());
|
||||
} else if (command instanceof UpDownType) {
|
||||
if (command.equals(UpDownType.UP)) {
|
||||
logger.debug("Handle command: UpDownType.Up");
|
||||
|
||||
device.startUp();
|
||||
} else if (command.equals(UpDownType.DOWN)) {
|
||||
logger.debug("Handle command: UpDownType.Down");
|
||||
|
||||
device.startDown();
|
||||
}
|
||||
} else if (command instanceof StopMoveType) {
|
||||
logger.debug("Handle command: StopMoveType");
|
||||
|
||||
device.stop();
|
||||
} else if (command instanceof OnOffType) {
|
||||
logger.debug("Handle command: OnOffType");
|
||||
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
device.on();
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
device.off();
|
||||
}
|
||||
}
|
||||
} else if (device instanceof SwitchRGBW) {
|
||||
// possible commands: on(), off(), exact(red, green, blue)
|
||||
if (command instanceof HSBType) {
|
||||
logger.debug("Handle command: HSBType");
|
||||
|
||||
HSBType hsb = (HSBType) command;
|
||||
|
||||
// first set on/off
|
||||
if (hsb.getBrightness().intValue() > 0) {
|
||||
if (device.getMetrics().getLevel().toLowerCase().equals("off")) {
|
||||
device.on();
|
||||
}
|
||||
|
||||
// then set color
|
||||
int red = (int) Math.round(255 * (hsb.getRed().doubleValue() / 100));
|
||||
int green = (int) Math.round(255 * (hsb.getGreen().doubleValue() / 100));
|
||||
int blue = (int) Math.round(255 * (hsb.getBlue().doubleValue() / 100));
|
||||
|
||||
device.exact(red, green, blue);
|
||||
} else {
|
||||
device.off();
|
||||
}
|
||||
}
|
||||
} else if (device instanceof Thermostat) {
|
||||
if (command instanceof DecimalType) {
|
||||
logger.debug("Handle command: DecimalType");
|
||||
|
||||
device.exact(command.toString());
|
||||
}
|
||||
} else if (device instanceof SwitchControl) {
|
||||
// possible commands: on(), off(), exact(level), upstart(), upstop(), downstart(),
|
||||
// downstop()
|
||||
if (command instanceof OnOffType) {
|
||||
logger.debug("Handle command: OnOffType");
|
||||
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
device.on();
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
device.off();
|
||||
}
|
||||
}
|
||||
} else if (device instanceof ToggleButton || device instanceof SwitchToggle) {
|
||||
// possible commands: on(), off(), exact(level), upstart(), upstop(), downstart(),
|
||||
// downstop()
|
||||
if (command instanceof OnOffType) {
|
||||
logger.debug("Handle command: OnOffType");
|
||||
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
device.on();
|
||||
} // no else - only ON command is sent to Z-Way
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (UnsupportedOperationException e) {
|
||||
logger.warn("Unknown command: {}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.warn("Devices not loaded");
|
||||
}
|
||||
} else if (channel.getUID().equals(new ChannelUID(getThing().getUID(), THERMOSTAT_MODE_CC_CHANNEL))) {
|
||||
// Load physical device
|
||||
Integer nodeId = Integer.parseInt(channel.getProperties().get("nodeId"));
|
||||
if (command instanceof DecimalType) {
|
||||
logger.debug("Handle command: DecimalType");
|
||||
|
||||
zwayBridgeHandler.getZWayApi().getZWaveDeviceThermostatModeSet(nodeId,
|
||||
Integer.parseInt(command.toString()));
|
||||
} else if (command instanceof RefreshType) {
|
||||
logger.debug("Handle command: RefreshType");
|
||||
|
||||
refreshChannel(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void addDeviceAsChannel(Device device) {
|
||||
// Device.probeType
|
||||
// |
|
||||
// Device.metrics.probeType
|
||||
// |
|
||||
// Device.metrics.icon
|
||||
// |
|
||||
// Command class
|
||||
// |
|
||||
// Default, depends on device type
|
||||
|
||||
if (device != null) {
|
||||
logger.debug("Add virtual device as channel: {}", device.getMetrics().getTitle());
|
||||
|
||||
HashMap<String, String> properties = new HashMap<>();
|
||||
properties.put("deviceId", device.getDeviceId());
|
||||
|
||||
String id = "";
|
||||
String acceptedItemType = "";
|
||||
|
||||
// 1. Set basically channel types without further information
|
||||
if (device instanceof Battery) {
|
||||
id = BATTERY_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
} else if (device instanceof Doorlock) {
|
||||
id = DOORLOCK_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
} else if (device instanceof SensorBinary) {
|
||||
id = SENSOR_BINARY_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
} else if (device instanceof SensorMultilevel) {
|
||||
id = SENSOR_MULTILEVEL_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
} else if (device instanceof SwitchBinary) {
|
||||
id = SWITCH_BINARY_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
} else if (device instanceof SwitchMultilevel) {
|
||||
id = SWITCH_MULTILEVEL_CHANNEL;
|
||||
acceptedItemType = "Dimmer";
|
||||
} else if (device instanceof SwitchRGBW) {
|
||||
id = SWITCH_COLOR_CHANNEL;
|
||||
acceptedItemType = "Color";
|
||||
} else if (device instanceof Thermostat) {
|
||||
id = THERMOSTAT_SET_POINT_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
} else if (device instanceof SwitchControl) {
|
||||
id = SWITCH_CONTROL_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
} else if (device instanceof ToggleButton || device instanceof SwitchToggle) {
|
||||
id = SWITCH_CONTROL_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
} else if (device instanceof SensorDiscrete) {
|
||||
id = SENSOR_DISCRETE_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
}
|
||||
|
||||
// 2. Check if device information includes further information about sensor type
|
||||
if (!device.getProbeType().equals("")) {
|
||||
if (device instanceof SensorMultilevel) {
|
||||
switch (device.getProbeType()) {
|
||||
case PROBE_TYPE_TEMPERATURE:
|
||||
id = SENSOR_TEMPERATURE_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
break;
|
||||
case PROBE_TYPE_LUMINOSITY:
|
||||
id = SENSOR_LUMINOSITY_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
break;
|
||||
case PROBE_TYPE_HUMIDITY:
|
||||
id = SENSOR_HUMIDITY_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
break;
|
||||
case PROBE_TYPE_BAROMETER:
|
||||
id = SENSOR_BAROMETER_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
break;
|
||||
case PROBE_TYPE_ULTRAVIOLET:
|
||||
id = SENSOR_ULTRAVIOLET_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
break;
|
||||
case PROBE_TYPE_ENERGY:
|
||||
id = SENSOR_ENERGY_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
break;
|
||||
case PROBE_TYPE_METER_ELECTRIC_KILOWATT_PER_HOUR:
|
||||
id = SENSOR_METER_KWH_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
break;
|
||||
case PROBE_TYPE_METER_ELECTRIC_WATT:
|
||||
id = SENSOR_METER_W_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (device instanceof SensorBinary) {
|
||||
switch (device.getProbeType()) {
|
||||
case PROBE_TYPE_GENERAL_PURPOSE:
|
||||
if (device.getMetrics().getIcon().equals(ICON_MOTION)) {
|
||||
id = SENSOR_MOTION_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
}
|
||||
break;
|
||||
case PROBE_TYPE_SMOKE:
|
||||
id = SENSOR_SMOKE_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
break;
|
||||
case PROBE_TYPE_CO:
|
||||
id = SENSOR_CO_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
break;
|
||||
case PROBE_TYPE_FLOOD:
|
||||
id = SENSOR_FLOOD_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
break;
|
||||
case PROBE_TYPE_COOLING:
|
||||
// TODO
|
||||
break;
|
||||
case PROBE_TYPE_TAMPER:
|
||||
id = SENSOR_TAMPER_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
break;
|
||||
case PROBE_TYPE_DOOR_WINDOW:
|
||||
id = SENSOR_DOOR_WINDOW_CHANNEL;
|
||||
acceptedItemType = "Contact";
|
||||
break;
|
||||
case PROBE_TYPE_MOTION:
|
||||
id = SENSOR_MOTION_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (device instanceof SwitchMultilevel) {
|
||||
switch (device.getProbeType()) {
|
||||
case PROBE_TYPE_SWITCH_COLOR_COLD_WHITE:
|
||||
id = SWITCH_COLOR_TEMPERATURE_CHANNEL;
|
||||
acceptedItemType = "Dimmer";
|
||||
break;
|
||||
case PROBE_TYPE_SWITCH_COLOR_SOFT_WHITE:
|
||||
id = SWITCH_COLOR_TEMPERATURE_CHANNEL;
|
||||
acceptedItemType = "Dimmer";
|
||||
break;
|
||||
case PROBE_TYPE_MOTOR:
|
||||
id = SWITCH_ROLLERSHUTTER_CHANNEL;
|
||||
acceptedItemType = "Rollershutter";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (device instanceof SwitchBinary) {
|
||||
switch (device.getProbeType()) {
|
||||
case PROBE_TYPE_THERMOSTAT_MODE:
|
||||
id = THERMOSTAT_MODE_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (!device.getMetrics().getProbeTitle().equals("")) {
|
||||
if (device instanceof SensorMultilevel) {
|
||||
switch (device.getMetrics().getProbeTitle()) {
|
||||
case PROBE_TITLE_CO2_LEVEL:
|
||||
id = SENSOR_CO2_CHANNEL;
|
||||
acceptedItemType = "Number";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (!device.getMetrics().getIcon().equals("")) {
|
||||
if (device instanceof SwitchBinary) {
|
||||
switch (device.getMetrics().getIcon()) {
|
||||
case ICON_SWITCH:
|
||||
id = SWITCH_POWER_OUTLET_CHANNEL;
|
||||
acceptedItemType = "Switch";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Eventually take account of the command classes
|
||||
}
|
||||
|
||||
// If at least one rule could mapped to a channel
|
||||
if (!id.equals("")) {
|
||||
addChannel(id, acceptedItemType, device.getMetrics().getTitle(), properties);
|
||||
|
||||
logger.debug(
|
||||
"Channel for virtual device added with channel id: {}, accepted item type: {} and title: {}",
|
||||
id, acceptedItemType, device.getMetrics().getTitle());
|
||||
} else {
|
||||
// Thing status will not be updated because thing could have more than one channel
|
||||
logger.warn("No channel for virtual device added: {}", device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void addChannel(String id, String acceptedItemType, String label,
|
||||
HashMap<String, String> properties) {
|
||||
boolean channelExists = false;
|
||||
|
||||
// Check if a channel for this virtual device exist. Attention: same channel type could multiple assigned to a
|
||||
// thing. That's why not check the existence of channel type.
|
||||
List<Channel> channels = getThing().getChannels();
|
||||
for (Channel channel : channels) {
|
||||
if (channel.getProperties().get("deviceId") != null
|
||||
&& channel.getProperties().get("deviceId").equals(properties.get("deviceId"))) {
|
||||
channelExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!channelExists) {
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, id);
|
||||
Channel channel = ChannelBuilder
|
||||
.create(new ChannelUID(getThing().getUID(), id + "-" + properties.get("deviceId")),
|
||||
acceptedItemType)
|
||||
.withType(channelTypeUID).withLabel(label).withProperties(properties).build();
|
||||
thingBuilder.withChannel(channel);
|
||||
thingBuilder.withLabel(thing.getLabel());
|
||||
updateThing(thingBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void addCommandClassThermostatModeAsChannel(Map<Integer, String> modes, Integer nodeId) {
|
||||
logger.debug("Add command class thermostat mode as channel");
|
||||
|
||||
ChannelUID channelUID = new ChannelUID(getThing().getUID(), THERMOSTAT_MODE_CC_CHANNEL);
|
||||
|
||||
boolean channelExists = false;
|
||||
|
||||
// Check if a channel for this virtual device exist. Attention: same channel type could multiple assigned to a
|
||||
// thing. That's why not check the existence of channel type.
|
||||
List<Channel> channels = getThing().getChannels();
|
||||
for (Channel channel : channels) {
|
||||
if (channel.getUID().equals(channelUID)) {
|
||||
channelExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!channelExists) {
|
||||
// Prepare properties (convert modes map)
|
||||
HashMap<String, String> properties = new HashMap<>();
|
||||
|
||||
// Add node id (for refresh and command handling)
|
||||
properties.put("nodeId", nodeId.toString());
|
||||
|
||||
// Add channel
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
|
||||
Channel channel = ChannelBuilder.create(channelUID, "Number")
|
||||
.withType(new ChannelTypeUID(BINDING_ID, THERMOSTAT_MODE_CC_CHANNEL))
|
||||
.withLabel("Thermostat mode (Command Class)").withDescription("Possible modes: " + modes.toString())
|
||||
.withProperties(properties).build();
|
||||
thingBuilder.withChannel(channel);
|
||||
thingBuilder.withLabel(thing.getLabel());
|
||||
updateThing(thingBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal.handler;
|
||||
|
||||
import static org.openhab.binding.zway.internal.ZWayBindingConstants.*;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openhab.binding.zway.internal.config.ZWayZAutomationDeviceConfiguration;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.Device;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
|
||||
|
||||
/**
|
||||
* The {@link ZWayZAutomationDeviceHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution
|
||||
*/
|
||||
public class ZWayZAutomationDeviceHandler extends ZWayDeviceHandler {
|
||||
public static final ThingTypeUID SUPPORTED_THING_TYPE = THING_TYPE_VIRTUAL_DEVICE;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private ZWayZAutomationDeviceConfiguration mConfig;
|
||||
|
||||
private class Initializer implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
|
||||
if (zwayBridgeHandler != null && zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
ThingStatusInfo statusInfo = zwayBridgeHandler.getThing().getStatusInfo();
|
||||
|
||||
logger.debug("Change Z-Way device status to bridge status: {}", statusInfo.getStatus());
|
||||
|
||||
// Set thing status to bridge status
|
||||
updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
|
||||
|
||||
// Add all available channels
|
||||
DeviceList deviceList = getZWayBridgeHandler().getZWayApi().getDevices();
|
||||
if (deviceList != null) {
|
||||
logger.debug("Z-Way devices loaded ({} virtual devices)", deviceList.getDevices().size());
|
||||
|
||||
// https://community.openhab.org/t/oh2-major-bug-with-scheduled-jobs/12350/11
|
||||
// If any execution of the task encounters an exception, subsequent executions are
|
||||
// suppressed. Otherwise, the task will only terminate via cancellation or
|
||||
// termination of the executor.
|
||||
try {
|
||||
Device device = deviceList.getDeviceById(mConfig.getDeviceId());
|
||||
|
||||
if (device != null) {
|
||||
logger.debug("Add channel for virtual device: {}", device.getMetrics().getTitle());
|
||||
|
||||
addDeviceAsChannel(device);
|
||||
|
||||
// Starts polling job and register all linked items
|
||||
completeInitialization();
|
||||
} else {
|
||||
logger.warn("Initializing Z-Way device handler failed (virtual device not found): {}",
|
||||
getThing().getLabel());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Z-Way virtual device with id " + mConfig.getDeviceId() + " not found.");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (t instanceof Exception) {
|
||||
logger.error("{}", t.getMessage());
|
||||
} else if (t instanceof Error) {
|
||||
logger.error("{}", t.getMessage());
|
||||
} else {
|
||||
logger.error("Unexpected error");
|
||||
}
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Error occurred when adding device as channel.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Devices not loaded");
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Z-Way bridge handler not found or not ONLINE.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ZWayZAutomationDeviceHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing Z-Way ZAutomation device handler ...");
|
||||
|
||||
// Set thing status to a valid status
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
|
||||
"Checking configuration and bridge...");
|
||||
|
||||
// Configuration - thing status update with a error message
|
||||
mConfig = loadAndCheckConfiguration();
|
||||
|
||||
if (mConfig != null) {
|
||||
logger.debug("Configuration complete: {}", mConfig);
|
||||
|
||||
// Start an extra thread to check the connection, because it takes sometimes more
|
||||
// than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
|
||||
scheduler.schedule(new Initializer(), 2, TimeUnit.SECONDS);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Z-Way device id required!");
|
||||
}
|
||||
}
|
||||
|
||||
private void completeInitialization() {
|
||||
super.initialize(); // starts polling job and register all linked items
|
||||
}
|
||||
|
||||
private ZWayZAutomationDeviceConfiguration loadAndCheckConfiguration() {
|
||||
ZWayZAutomationDeviceConfiguration config = getConfigAs(ZWayZAutomationDeviceConfiguration.class);
|
||||
|
||||
if (StringUtils.trimToNull(config.getDeviceId()) == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Z-Wave device couldn't create, because the device id is missing.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Dispose Z-Way ZAutomation handler ...");
|
||||
|
||||
if (mConfig.getDeviceId() != null) {
|
||||
mConfig.setDeviceId(null);
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshLastUpdate() {
|
||||
logger.debug("Refresh last update for virtual device");
|
||||
|
||||
// Check Z-Way bridge handler
|
||||
ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
|
||||
if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Z-Way bridge handler not found or not ONLINE.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Load and check device from Z-Way server
|
||||
DeviceList deviceList = zwayBridgeHandler.getZWayApi().getDevices();
|
||||
if (deviceList != null) {
|
||||
Device device = deviceList.getDeviceById(mConfig.getDeviceId());
|
||||
if (device == null) {
|
||||
logger.debug("ZAutomation device not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Calendar lastUpdateOfDevice = Calendar.getInstance();
|
||||
lastUpdateOfDevice.setTimeInMillis(new Long(device.getUpdateTime()) * 1000);
|
||||
|
||||
if (lastUpdate == null || lastUpdateOfDevice.after(lastUpdate)) {
|
||||
lastUpdate = lastUpdateOfDevice;
|
||||
}
|
||||
|
||||
DateFormat formatter = new SimpleDateFormat("dd.MM.yyyy hh:mm:ss");
|
||||
updateProperty(DEVICE_PROP_LAST_UPDATE, formatter.format(lastUpdate.getTime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.zway.internal.handler;
|
||||
|
||||
import static org.openhab.binding.zway.internal.ZWayBindingConstants.*;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.openhab.binding.zway.internal.config.ZWayZWaveDeviceConfiguration;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.Device;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
|
||||
|
||||
/**
|
||||
* The {@link ZWayZWaveDeviceHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Patrick Hecker - Initial contribution
|
||||
*/
|
||||
public class ZWayZWaveDeviceHandler extends ZWayDeviceHandler {
|
||||
public static final ThingTypeUID SUPPORTED_THING_TYPE = THING_TYPE_DEVICE;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private ZWayZWaveDeviceConfiguration mConfig;
|
||||
|
||||
private class Initializer implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
|
||||
if (zwayBridgeHandler != null && zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
ThingStatusInfo statusInfo = zwayBridgeHandler.getThing().getStatusInfo();
|
||||
logger.debug("Change Z-Way Z-Wave device status to bridge status: {}", statusInfo);
|
||||
|
||||
// Set thing status to bridge status
|
||||
updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
|
||||
|
||||
// Add all available channels
|
||||
logger.debug("Add all available channels");
|
||||
DeviceList deviceList = getZWayBridgeHandler().getZWayApi().getDevices();
|
||||
if (deviceList != null) {
|
||||
logger.debug("Z-Way devices loaded ({} physical devices)",
|
||||
deviceList.getDevicesGroupByNodeId().size());
|
||||
|
||||
// https://community.openhab.org/t/oh2-major-bug-with-scheduled-jobs/12350/11
|
||||
// If any execution of the task encounters an exception, subsequent executions are
|
||||
// suppressed. Otherwise, the task will only terminate via cancellation or
|
||||
// termination of the executor.
|
||||
try {
|
||||
// physical device means all virtual devices grouped by physical device
|
||||
Map<Integer, List<Device>> physicalDevice = deviceList.getDevicesByNodeId(mConfig.getNodeId());
|
||||
if (physicalDevice != null) {
|
||||
logger.debug("Z-Wave device with node id {} found with {} virtual devices",
|
||||
mConfig.getNodeId(), physicalDevice.get(mConfig.getNodeId()).size());
|
||||
|
||||
for (Map.Entry<Integer, List<Device>> entry : physicalDevice.entrySet()) {
|
||||
logger.debug("Add channels for physical device with node id: {}", mConfig.getNodeId());
|
||||
|
||||
List<Device> devices = entry.getValue();
|
||||
|
||||
for (Device device : devices) {
|
||||
if (device.getVisibility() && !device.getPermanentlyHidden()) {
|
||||
logger.debug("Add channel for virtual device: {}",
|
||||
device.getMetrics().getTitle());
|
||||
addDeviceAsChannel(device);
|
||||
} else {
|
||||
logger.debug("Device {} has been skipped, because it was hidden in Z-Way.",
|
||||
device.getMetrics().getTitle());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Z-Way physical device with node id " + mConfig.getNodeId() + " not found.");
|
||||
}
|
||||
|
||||
// Check command classes (only for ThermostatMode)
|
||||
ZWaveDevice zwaveDevice = getZWayBridgeHandler().getZWayApi()
|
||||
.getZWaveDevice(mConfig.getNodeId());
|
||||
if (!zwaveDevice.getInstances().get0().getCommandClasses().get64().getName().equals("")) {
|
||||
// Load available thermostat modes
|
||||
Map<Integer, String> modes = zwaveDevice.getInstances().get0().getCommandClasses().get64()
|
||||
.getThermostatModes();
|
||||
|
||||
logger.debug(
|
||||
"Z-Wave device implements command class ThermostatMode with the following modes: {}",
|
||||
modes.toString());
|
||||
|
||||
addCommandClassThermostatModeAsChannel(modes, mConfig.getNodeId());
|
||||
}
|
||||
|
||||
// Starts polling job and register all linked items
|
||||
completeInitialization();
|
||||
} catch (Throwable t) {
|
||||
if (t instanceof Exception) {
|
||||
logger.error("{}", t.getMessage());
|
||||
} else if (t instanceof Error) {
|
||||
logger.error("{}", t.getMessage());
|
||||
} else {
|
||||
logger.error("Unexpected error");
|
||||
}
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Error occurred when adding device as channel.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Devices not loaded");
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Z-Way bridge handler not found or not ONLINE.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ZWayZWaveDeviceHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing Z-Way device handler ...");
|
||||
|
||||
// Set thing status to a valid status
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
|
||||
"Checking configuration and bridge...");
|
||||
|
||||
// Configuration - thing status update with a error message
|
||||
mConfig = loadAndCheckConfiguration();
|
||||
|
||||
if (mConfig != null) {
|
||||
logger.debug("Configuration complete: {}", mConfig);
|
||||
|
||||
// Start an extra thread to check the connection, because it takes sometimes more
|
||||
// than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
|
||||
scheduler.schedule(new Initializer(), 2, TimeUnit.SECONDS);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Z-Way node id required!");
|
||||
}
|
||||
}
|
||||
|
||||
private void completeInitialization() {
|
||||
super.initialize(); // starts polling job and register all linked items
|
||||
}
|
||||
|
||||
private ZWayZWaveDeviceConfiguration loadAndCheckConfiguration() {
|
||||
ZWayZWaveDeviceConfiguration config = getConfigAs(ZWayZWaveDeviceConfiguration.class);
|
||||
|
||||
if (config.getNodeId() == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Z-Wave device couldn't create, because the node id is missing.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Dispose Z-Way Z-Wave handler ...");
|
||||
|
||||
if (mConfig.getNodeId() != null) {
|
||||
mConfig.setNodeId(null);
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshLastUpdate() {
|
||||
logger.debug("Refresh last update for Z-Wave device");
|
||||
|
||||
// Check Z-Way bridge handler
|
||||
ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
|
||||
if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Z-Way bridge handler not found or not ONLINE.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Load and check Z-Wave device from Z-Way server (Z-Wave API)
|
||||
ZWaveDevice zwaveDevice = zwayBridgeHandler.getZWayApi().getZWaveDevice(mConfig.getNodeId());
|
||||
if (zwaveDevice == null) {
|
||||
logger.debug("Z-Wave device not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Calendar lastUpdateOfDevice = Calendar.getInstance();
|
||||
lastUpdateOfDevice.setTimeInMillis(new Long(zwaveDevice.getData().getLastReceived().getUpdateTime()) * 1000);
|
||||
|
||||
if (lastUpdate == null || lastUpdateOfDevice.after(lastUpdate)) {
|
||||
lastUpdate = lastUpdateOfDevice;
|
||||
}
|
||||
|
||||
DateFormat formatter = new SimpleDateFormat("dd.MM.yyyy hh:mm:ss");
|
||||
updateProperty(DEVICE_PROP_LAST_UPDATE, formatter.format(lastUpdate.getTime()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="zway" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>Z-Way Binding</name>
|
||||
<description>
|
||||
<![CDATA[
|
||||
Z-Way is a home automation software to configure and control a Z-Wave network. The ZAutomation interface provides
|
||||
all Z-Wave devices and handles incoming commands. The Z-Way Binding uses this HTTP interface to load all device
|
||||
and make them available during the discovery process.<br>
|
||||
Currently only a continuous polling is available!
|
||||
]]>
|
||||
</description>
|
||||
<author>Patrick Hecker</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
<config-description uri="binding:zway:zwayServer">
|
||||
<parameter-group name="zwayServer">
|
||||
<label>Z-Way Server</label>
|
||||
<description>The configuration of the Z-Way server. Except for the username and password all the information detected
|
||||
during the discovery.</description>
|
||||
</parameter-group>
|
||||
|
||||
<parameter-group name="binding">
|
||||
<label>Options</label>
|
||||
<description>These settings affect functions of the binding.</description>
|
||||
</parameter-group>
|
||||
|
||||
<parameter name="zwayServerIpAddress" groupName="zwayServer" type="text">
|
||||
<context>network-address</context>
|
||||
<label>IP Address</label>
|
||||
<description>The IP address or hostname of the Z-Way server. If Z-Way and openHAB are running on the same machine,
|
||||
the default value can be used.</description>
|
||||
<default>localhost</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="zwayServerPort" groupName="zwayServer" type="integer" required="false" min="1" max="65535">
|
||||
<label>Port</label>
|
||||
<description>The port of the Z-Way server</description>
|
||||
<default>8083</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="zwayServerProtocol" groupName="zwayServer" type="text" required="false">
|
||||
<label>Protocol</label>
|
||||
<description>Protocol to connect to the Z-Way server (http or https)</description>
|
||||
<default>http</default>
|
||||
<options>
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
</options>
|
||||
</parameter>
|
||||
|
||||
<parameter name="zwayServerUsername" groupName="zwayServer" type="text">
|
||||
<label>Username</label>
|
||||
<description>Username to access the Z-Way server.</description>
|
||||
<default>admin</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="zwayServerPassword" groupName="zwayServer" type="text" required="true">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>Password to access the Z-Way server.</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="pollingInterval" groupName="binding" type="integer" required="false" min="60" max="3600"
|
||||
unit="s">
|
||||
<label>Polling Interval</label>
|
||||
<description>Refresh device states and registration from Z-Way server.</description>
|
||||
<unitLabel>Seconds</unitLabel>
|
||||
<default>3600</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,92 @@
|
||||
# binding
|
||||
binding.zway.name = Z-Way Binding
|
||||
binding.zway.description = Z-Way is a home automation software to configure and control a Z-Wave network. The ZAutomation interface provides all Z-Wave devices and handles incoming commands. The Z-Way Binding uses this HTTP interface to load all device and make them available during the discovery process.<br> Currently only a continuous polling is available!
|
||||
|
||||
# thing types
|
||||
thing-type.zway.zwayServer.label = Z-Way Server
|
||||
thing-type.zway.zwayServer.description = The Z-Way server represents a bridge with general settings and communication tasks.
|
||||
|
||||
thing-type.config.zway.zwayServer.zwayServer.label = Z-Way server
|
||||
thing-type.config.zway.zwayServer.zwayServer.description = The configuration of the Z-Way server. Except for the username and password all the information detected during the discovery.
|
||||
thing-type.config.zway.zwayServer.binding.label = Options
|
||||
thing-type.config.zway.zwayServer.binding.description = These settings affect functions of the binding.
|
||||
|
||||
thing-type.config.zway.zwayServer.zwayServerIpAddress.label = IP address
|
||||
thing-type.config.zway.zwayServer.zwayServerIpAddress.description = The IP address or hostname of the Z-Way server. If Z-Way and openHAB are running on the same machine, the default value can be used.
|
||||
thing-type.config.zway.zwayServer.zwayServerPort.label = Port
|
||||
thing-type.config.zway.zwayServer.zwayServerPort.description = The port of the Z-Way server
|
||||
thing-type.config.zway.zwayServer.zwayServerProtocol.label = Protocol
|
||||
thing-type.config.zway.zwayServer.zwayServerProtocol.description = Protocol to connect to the Z-Way server (http or https)
|
||||
thing-type.config.zway.zwayServer.zwayServerUsername.label = Username
|
||||
thing-type.config.zway.zwayServer.zwayServerUsername.description = Username to access the Z-Way server.
|
||||
thing-type.config.zway.zwayServer.zwayServerPassword.label = Password
|
||||
thing-type.config.zway.zwayServer.zwayServerPassword.description = Password to access the Z-Way server.
|
||||
|
||||
thing-type.config.zway.zwayServer.pollingInterval.label = Polling Interval
|
||||
thing-type.config.zway.zwayServer.pollingInterval.description = Refresh device states and registration from Z-Way server.
|
||||
|
||||
thing-type.zway.zwayDevice.label = Z-Wave Device
|
||||
thing-type.zway.zwayDevice.description = A Z-Wave device represents a device of real world. Each device function will be mapped to a separate channel. The bridge is necessary as an intermediary between openHAB thing and Z-Way device.
|
||||
|
||||
thing-type.config.zway.zwayDevice.nodeId.label = Node Id
|
||||
thing-type.config.zway.zwayDevice.nodeId.description = Node Id of the Z-Wave device
|
||||
|
||||
thing-type.zway.zwayVirtualDevice.label = Z-Way Virtual Device
|
||||
thing-type.zway.zwayVirtualDevice.description = A Z-Way virtual device represents one sensor, actor or Z-Way App with the corresponding channel. The bridge is necessary as an intermediary between openHAB thing and Z-Way device.
|
||||
|
||||
thing-type.config.zway.zwayVirtualDevice.deviceId.label = Device Id
|
||||
thing-type.config.zway.zwayVirtualDevice.deviceId.description = Device Id of virtual device
|
||||
|
||||
# channel types
|
||||
channel-type.zway.sensorTemperature.label = Temperature
|
||||
channel-type.zway.sensorLuminosity.label = Luminiscence
|
||||
channel-type.zway.sensorHumidity.label = Humidity
|
||||
channel-type.zway.sensorBarometer.label = Barometer
|
||||
channel-type.zway.sensorUltraviolet.label = Ultraviolet
|
||||
channel-type.zway.sensorCO2.label = CarbonDioxide
|
||||
channel-type.zway.sensorEnergy.label = Energy
|
||||
channel-type.zway.sensorMeterKWh.label = Energy
|
||||
channel-type.zway.sensorMeterW.label = Energy
|
||||
channel-type.zway.sensorSmoke.label = Smoke
|
||||
channel-type.zway.sensorCo.label = Gas
|
||||
channel-type.zway.sensorFlood.label = Flood
|
||||
channel-type.zway.sensorTamper.label = Tamper
|
||||
channel-type.zway.sensorDoorWindow.label = DoorWindow
|
||||
channel-type.zway.sensorMotion.label = Motion
|
||||
channel-type.zway.switchPowerOutlet.label = PowerOutlet
|
||||
channel-type.zway.switchColorTemperature.label = Color Temperature
|
||||
|
||||
# channel type without further information
|
||||
channel-type.zway.battery.label = Battery
|
||||
channel-type.zway.doorlock.label = Doorlock
|
||||
channel-type.zway.sensorBinary.label = Sensor binary
|
||||
channel-type.zway.sensorBinary.description = This channel represents a universal channel if no further device information is available.
|
||||
channel-type.zway.sensorMultilevel.label = Sensor multilevel
|
||||
channel-type.zway.sensorMultilevel.description = This channel represents a universal channel if no further device information is available.
|
||||
channel-type.zway.switchBinary.label = Switch binary
|
||||
channel-type.zway.switchBinary.description = This channel represents a universal channel if no further device information is available.
|
||||
channel-type.zway.switchMultilevel.label = Switch multilevel
|
||||
channel-type.zway.switchMultilevel.description = This channel represents a universal channel if no further device information is available.
|
||||
channel-type.zway.switchColor.label = Switch color
|
||||
channel-type.zway.switchColor.description = This channel represents the RGBW switch device type from Z-Way.
|
||||
channel-type.zway.switchControl.label = Switch control
|
||||
channel-type.zway.switchControl.description = This channel represents a universal channel if no further device information is available.
|
||||
channel-type.zway.sensorDiscrete.label = Sensor discrete
|
||||
channel-type.zway.sensorDiscrete.description = This channel represents a two-digit value. The first digit is the button/scene number and the second digit points to action/keyAttribute (have a look at http://z-wave.sigmadesigns.com/wp-content/uploads/2016/08/SDS12657-12-Z-Wave-Command-Class-Specification-A-M.pdf, p. 153).
|
||||
channel-type.zway.thermostatMode.label = Thermostat mode
|
||||
channel-type.zway.thermostatMode.description = The channel allows the control or display of a thermostat (mode). A thermostat can have up to three states (modes): off, heating and cooling. The state of heating and cooling is alternately set at the state on.
|
||||
channel-type.zway.thermostatSetPoint.label = Thermostat set point
|
||||
|
||||
channel-type.zway.thermostatModeV2.label = Thermostat mode (Command Class)
|
||||
channel-type.zway.thermostatModeV2.description = The channel allows the control or display of a thermostat (mode) from command class. The modes differ from device to device.
|
||||
|
||||
channel-type.zway.actions.label = Actions
|
||||
channel-type.zway.actions.description = Available actions of the Z-Wave Controller
|
||||
channel-type.zway.actions.option.REFRESH = Refresh all things
|
||||
|
||||
channel-type.zway.secureInclusion.label = Secure inclusion
|
||||
channel-type.zway.secureInclusion.description = Change inclusion type for further inclusions.
|
||||
channel-type.zway.inclusion.label = Inclusion
|
||||
channel-type.zway.inclusion.description = Start inclusion mode (after a timeout the inclusion will be automatically finished).
|
||||
channel-type.zway.exclusion.label = Exclusion
|
||||
channel-type.zway.exclusion.description = Start exclusion mode (after a timeout the exclusion will be automatically finished).
|
||||
@@ -0,0 +1,92 @@
|
||||
# binding
|
||||
binding.zway.name = Z-Way Binding
|
||||
binding.zway.description = Das Z-Way-System ist ein Softwarepaket für den Z-Wave Funkstandard. Das System besteht im wesentlichen aus einer Firmware für Z-Wave Transceiver, einer Kommunikations- und einer Automatisierungskomponente zur Steuerung und Konfiguration des Netzwerks. Das Z-Way Binding nutzt die HTTP-Schnittstelle um alle Geräte zu laden und im Discovery-Prozess zur Verfügung zu stellen.<br>Aktuell wird nur Polling zum Aktualisieren der Gerätezustände verwendet!
|
||||
|
||||
# thing types
|
||||
thing-type.zway.zwayServer.label = Z-Way Server
|
||||
thing-type.zway.zwayServer.description = Der Z-Way Server repräsentiert das Z-Way System als Bridge mit der grundlegendene Konfiguration zum Verbindungsaufbau. Die gesamte Kommunikation mit Z-Way organisiert diese Komponente.
|
||||
|
||||
thing-type.config.zway.zwayServer.zwayServer.label = Z-Way Server
|
||||
thing-type.config.zway.zwayServer.zwayServer.description = Die Konfiguration des Z-Way Server wird bis auf den Benutzername und das Passwort während des Discovery-Prozesses ermittlt.
|
||||
thing-type.config.zway.zwayServer.binding.label = Optionen
|
||||
thing-type.config.zway.zwayServer.binding.description = Diese Einstellungen betreffen Funktionen des Bindings.
|
||||
|
||||
thing-type.config.zway.zwayServer.zwayServerIpAddress.label = IP-Adresse
|
||||
thing-type.config.zway.zwayServer.zwayServerIpAddress.description = Die Adresse unter der Z-Way erreichbar ist. Sollte sich der Z-Way Server und openHAB auf dem selben Rechner befinden, kann der Standardwert beibehalten werden.
|
||||
thing-type.config.zway.zwayServer.zwayServerPort.label = Port
|
||||
thing-type.config.zway.zwayServer.zwayServerPort.description = Der Port unter dem das openHAB-System erreichbar ist.
|
||||
thing-type.config.zway.zwayServer.zwayServerProtocol.label = Protokoll
|
||||
thing-type.config.zway.zwayServer.zwayServerProtocol.description = HTTP/HTTPS
|
||||
thing-type.config.zway.zwayServer.zwayServerUsername.label = Benutzername
|
||||
thing-type.config.zway.zwayServer.zwayServerUsername.description = Benutzername für den Zugriff auf das Z-Way System.
|
||||
thing-type.config.zway.zwayServer.zwayServerPassword.label = Passwort
|
||||
thing-type.config.zway.zwayServer.zwayServerPassword.description = Passwort für den Zugriff auf das Z-Way System.
|
||||
|
||||
thing-type.config.zway.zwayServer.pollingInterval.label = Polling Interval
|
||||
thing-type.config.zway.zwayServer.pollingInterval.description = Aktualisiert den Gerätezustand und die Registrierung beim <i>OpenHAB Konnektor</i>
|
||||
|
||||
thing-type.zway.zwayDevice.label = Z-Wave Gerät
|
||||
thing-type.zway.zwayDevice.description = Ein Z-Wave Gerät repräsentiert ein physisch existierendes Gerät. Dabei wird jede Gerätefunktion (Temperatursensor, Luftfeuchtigkeitsmesser usw.) einem Channel zugeordnet. Eine Bridge (Z-Way Server) wird als Vermittler zwischen openHAB und Z-Way benötigt.
|
||||
|
||||
thing-type.config.zway.zwayDevice.nodeId.label = Node Id
|
||||
thing-type.config.zway.zwayDevice.nodeId.description = Node Id des Geräts im Z-Wave Netzwerk
|
||||
|
||||
thing-type.zway.zwayVirtualDevice.label = Z-Way virtuelles Gerät
|
||||
thing-type.zway.zwayVirtualDevice.description = Ein virtuelles Gerät repräsentiert genau eine Sensor, Aktor oder ein Z-Way App mit einem Channel. Eine Bridge (Z-Way Server) wird als Vermittler zwischen openHAB und Z-Way benötigt.
|
||||
thing-type.config.zway.zwayVirtualDevice.deviceId.label = Device Id
|
||||
thing-type.config.zway.zwayVirtualDevice.deviceId.description = Device Id des virtuellen Geräts
|
||||
|
||||
# channel types
|
||||
channel-type.zway.sensorTemperature.label = Temperatur
|
||||
channel-type.zway.sensorLuminosity.label = Helligkeit
|
||||
channel-type.zway.sensorHumidity.label = Luftfeuchtigkeit
|
||||
channel-type.zway.sensorBarometer.label = Luftdruck
|
||||
channel-type.zway.sensorUltraviolet.label = Lichtintensität
|
||||
channel-type.zway.sensorCO2.label = Kohlendioxid
|
||||
channel-type.zway.sensorEnergy.label = Energie
|
||||
channel-type.zway.sensorMeterKWh.label = Energie (kWh)
|
||||
channel-type.zway.sensorMeterW.label = Energie (W)
|
||||
channel-type.zway.sensorSmoke.label = Rauch
|
||||
channel-type.zway.sensorCo.label = Gas
|
||||
channel-type.zway.sensorFlood.label = Überflutung
|
||||
channel-type.zway.sensorTamper.label = Manipulation
|
||||
channel-type.zway.sensorDoorWindow.label = Tür-/Fensterkontakt
|
||||
channel-type.zway.sensorMotion.label = Bewegung
|
||||
channel-type.zway.switchPowerOutlet.label = Steckdose
|
||||
channel-type.zway.switchColorTemperature.label = Farbtemperatur
|
||||
|
||||
# channel type without further information
|
||||
|
||||
channel-type.zway.battery.label = Batterie
|
||||
channel-type.zway.doorlock.label = Türschloss
|
||||
channel-type.zway.sensorBinary.label = Binärsensor
|
||||
channel-type.zway.sensorBinary.description = Dieser Channel ist allgemeingültig für verschiedene Geräte des gleichen Gerätetyps, wenn keine weiteren Informationen zur Verfügung stehen.
|
||||
channel-type.zway.sensorMultilevel.label = Multilevel-Sensor
|
||||
channel-type.zway.sensorMultilevel.description = Dieser Channel ist allgemeingültig für verschiedene Geräte des gleichen Gerätetyps, wenn keine weiteren Informationen zur Verfügung stehen.
|
||||
channel-type.zway.switchBinary.label = Binärschalter
|
||||
channel-type.zway.switchBinary.description = Dieser Channel ist allgemeingültig für verschiedene Geräte des gleichen Gerätetyps, wenn keine weiteren Informationen zur Verfügung stehen.
|
||||
channel-type.zway.switchMultilevel.label = Multilevel-Schalter
|
||||
channel-type.zway.switchMultilevel.description = Dieser Channel ist allgemeingültig für verschiedene Geräte des gleichen Gerätetyps, wenn keine weiteren Informationen zur Verfügung stehen.
|
||||
channel-type.zway.switchColor.label = RGB-Schalter
|
||||
channel-type.zway.switchColor.description = Dieser Channel repräsentiert einen RGB-Schalter von Z-Way.
|
||||
channel-type.zway.switchControl.label = Binärschalter
|
||||
channel-type.zway.switchControl.description = Dieser Channel ist allgemeingültig für verschiedene Geräte des gleichen Gerätetyps, wenn keine weiteren Informationen zur Verfügung stehen.
|
||||
channel-type.zway.sensorDiscrete.label = Diskreter Sensor
|
||||
channel-type.zway.sensorDiscrete.description = Dieser Channel repräsentiert einen zweistelligen Wert. Die erste Zahl ist der Button/Szene und der zweite Wert beschreibt die Aktion/KeyAttribute (siehe auch http://z-wave.sigmadesigns.com/wp-content/uploads/2016/08/SDS12657-12-Z-Wave-Command-Class-Specification-A-M.pdf, S. 153).
|
||||
channel-type.zway.thermostatMode.label = Thermostat Modus
|
||||
channel-type.zway.thermostatMode.description = Dieser Channel erlaubt die Steuerung und Anzeige eines Thermostats (Modus). Ein Thermostat kann einer der drei Zustände einnehmen (Modi): "Aus", "Heizen" oder "Kühlen". Der Zustand "Heizen" und "Kühlen" wird wechselweise im Zustand "An" gesetzt.
|
||||
channel-type.zway.thermostatSetPoint.label = Thermostat Sollwert
|
||||
|
||||
channel-type.zway.thermostatModeV2.label = Thermostat Modus (Kommandoklasse)
|
||||
channel-type.zway.thermostatModeV2.description = Dieser Channel erlaubt die Steuerung und Anzeige eines Thermostats (Modus) auf Basis der Kommandoklasse. Die verfügbaren Modi variieren von Gerät zu Gerät.
|
||||
|
||||
channel-type.zway.actions.label = Aktionen
|
||||
channel-type.zway.actions.description = Aktionen des Z-Wave Controller
|
||||
channel-type.zway.actions.option.REFRESH = Alle Geräte aktualisieren
|
||||
|
||||
channel-type.zway.secureInclusion.label = Sichere Inklusion
|
||||
channel-type.zway.secureInclusion.description = ändert den Inklusionsmodus für folgende Inklusionen.
|
||||
channel-type.zway.inclusion.label = Inklusionsmodus
|
||||
channel-type.zway.inclusion.description = Starten des Inklusionsmodus (nach einem Timeout wird der Modus automatisch verlassen).
|
||||
channel-type.zway.exclusion.label = Exklusionsmodus
|
||||
channel-type.zway.exclusion.description = Starten des Exklusionsmodus (nach einem Timeout wird der Modus automatisch verlassen).
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="zway"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<bridge-type id="zwayServer">
|
||||
|
||||
<label>Z-Way Server</label>
|
||||
<description>
|
||||
<![CDATA[
|
||||
The Z-Way server represents a bridge with general settings and communication tasks.
|
||||
]]>
|
||||
</description>
|
||||
|
||||
<channels>
|
||||
<channel id="actions" typeId="actions"/>
|
||||
<channel id="secureInclusion" typeId="secureInclusion"/>
|
||||
<channel id="inclusion" typeId="inclusion"/>
|
||||
<channel id="exclusion" typeId="exclusion"/>
|
||||
</channels>
|
||||
|
||||
<config-description-ref uri="binding:zway:zwayServer"/>
|
||||
|
||||
</bridge-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,266 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="zway"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<channel-type id="sensorTemperature">
|
||||
<item-type>Number</item-type>
|
||||
<label>Temperature</label>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f °C"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorLuminosity">
|
||||
<item-type>Number</item-type>
|
||||
<label>Luminiscence</label>
|
||||
<category>Light</category>
|
||||
<state readOnly="true" pattern="%.1f Lux"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorHumidity">
|
||||
<item-type>Number</item-type>
|
||||
<label>Humidity</label>
|
||||
<category>Humidity</category>
|
||||
<state readOnly="true" pattern="%.1f %%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorBarometer">
|
||||
<item-type>Number</item-type>
|
||||
<label>Barometer</label>
|
||||
<category>Pressure</category>
|
||||
<state readOnly="true" pattern="%.1f"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorUltraviolet">
|
||||
<item-type>Number</item-type>
|
||||
<label>Ultraviolet</label>
|
||||
<category>Light</category>
|
||||
<state readOnly="true" pattern="%.1f UV index"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorCO2">
|
||||
<item-type>Number</item-type>
|
||||
<label>CO2</label>
|
||||
<category>CarbonDioxide</category>
|
||||
<state readOnly="true" pattern="%.1f ppm"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorEnergy">
|
||||
<item-type>Number</item-type>
|
||||
<label>Energy</label>
|
||||
<category>Energy</category>
|
||||
<state readOnly="true" pattern="%.1f W"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorMeterKWh">
|
||||
<item-type>Number</item-type>
|
||||
<label>Energy</label>
|
||||
<category>Energy</category>
|
||||
<state readOnly="true" pattern="%.1f kWh"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorMeterW">
|
||||
<item-type>Number</item-type>
|
||||
<label>Energy</label>
|
||||
<category>Energy</category>
|
||||
<state readOnly="true" pattern="%.1f W"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorSmoke">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Smoke</label>
|
||||
<category>Smoke</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorCo">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Gas</label>
|
||||
<category>Gas</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorFlood">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Flood</label>
|
||||
<category>Water</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorTamper">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Tamper</label>
|
||||
<category>Alarm</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorDoorWindow">
|
||||
<item-type>Contact</item-type>
|
||||
<label>DoorWindow</label>
|
||||
<category>Contact</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorMotion">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Motion</label>
|
||||
<category>Motion</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switchPowerOutlet">
|
||||
<item-type>Switch</item-type>
|
||||
<label>PowerOutlet</label>
|
||||
<category>PowerOutlet</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switchColorTemperature">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Color Temperature</label>
|
||||
<category>ColorLight</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switchBlinds">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Rollshutter</label>
|
||||
<category>Blinds</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- Channel types without further information -->
|
||||
|
||||
<channel-type id="battery">
|
||||
<item-type>Number</item-type>
|
||||
<label>Battery</label>
|
||||
<category>Battery</category>
|
||||
<state readOnly="true" pattern="%.1f %%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="doorlock">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Doorlock</label>
|
||||
<category>Door</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorBinary">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Sensor Binary</label>
|
||||
<description>This channel represents a universal channel if no further device information is available.</description>
|
||||
<category>Switch</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorMultilevel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Sensor Multilevel</label>
|
||||
<description>This channel represents a universal channel if no further device information is available.</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switchBinary">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Switch Binary</label>
|
||||
<description>This channel represents a universal channel if no further device information is available.</description>
|
||||
<category>Switch</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switchMultilevel">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Switch Multilevel</label>
|
||||
<description>This channel represents a universal channel if no further device information is available.</description>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switchColor">
|
||||
<item-type>Color</item-type>
|
||||
<label>Switch Color</label>
|
||||
<description>This channel represents the rgbw switch device type from Z-Way.</description>
|
||||
<category>ColorLight</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="thermostatMode">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Thermostat Mode</label>
|
||||
<description>The channel allows the control or display of a thermostat (mode). A thermostat can have up to three
|
||||
states (modes): off, heating and cooling. The state of heating and cooling is alternately set at the state on.</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="thermostatSetPoint">
|
||||
<item-type>Number</item-type>
|
||||
<label>Thermostat Set Point</label>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switchControl">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Switch Control</label>
|
||||
<description>This channel represents a universal channel if no further device information is available.</description>
|
||||
<category>Switch</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sensorDiscrete">
|
||||
<item-type>Number</item-type>
|
||||
<label>Sensor Discrete</label>
|
||||
<description>This channel represents a two-digit value. The first digit is the button/scene number and the second
|
||||
digit points to action/keyAttribute (have a look at
|
||||
http://z-wave.sigmadesigns.com/wp-content/uploads/2016/08/SDS12657-12-Z-Wave-Command-Class-Specification-A-M.pdf, p.
|
||||
153).</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="thermostatModeCC">
|
||||
<item-type>Number</item-type>
|
||||
<label>Thermostat Mode (Command Class)</label>
|
||||
<description>The channel allows the control or display of a thermostat (mode) from command class. The modes differ
|
||||
from device to device.</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- Special channel types -->
|
||||
|
||||
<channel-type id="actions">
|
||||
<item-type>String</item-type>
|
||||
<label>Actions</label>
|
||||
<description>Available actions of the Z-Wave Controller</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="REFRESH">Refresh all things</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="secureInclusion">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Secure Inclusion</label>
|
||||
<description>Change inclusion type for further inclusions.</description>
|
||||
<category>Switch</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="inclusion">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Inclusion</label>
|
||||
<description>Start inclusion mode (after a timeout the inclusion will be automatically finished).</description>
|
||||
<category>Switch</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="exclusion">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Exclusion</label>
|
||||
<description>Start exclusion mode (after a timeout the exclusion will be automatically finished).</description>
|
||||
<category>Switch</category>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="zway"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="zwayDevice">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="zwayServer"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Z-Wave Device</label>
|
||||
<description>
|
||||
<![CDATA[
|
||||
A Z-Wave device represents a device of real world. Each device function will be mapped to
|
||||
a separate channel. The bridge is necessary as an intermediary between openHAB thing
|
||||
and Z-Way device.
|
||||
]]>
|
||||
</description>
|
||||
<config-description>
|
||||
<parameter name="nodeId" type="integer" required="true">
|
||||
<label>Node Id</label>
|
||||
<description>Node Id of the Z-Wave device</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="zway"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="zwayVirtualDevice">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="zwayServer"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Z-Way Virtual Device</label>
|
||||
<description>
|
||||
<![CDATA[
|
||||
A Z-Way virtual device represents one sensor, actor or Z-Way App with the corresponding channel.
|
||||
The bridge is necessary as an intermediary between openHAB thing and Z-Way device.
|
||||
]]>
|
||||
</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="text" required="true">
|
||||
<label>Device Id</label>
|
||||
<description>Device Id of virtual device</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user