added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.zway</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,25 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons
== Third-party Content
commons-lang
* License: Apache 2.0 License
* Project: https://commons.apache.org/proper/commons-lang/
* Source: https://gitbox.apache.org/repos/asf?p=commons-lang.git
zway-lib
* License: EPL 1.0 License
* Project: https://github.com/pathec/ZWay-library-for-Java
* Project: https://github.com/pathec/ZWay-library-for-Java

View File

@@ -0,0 +1,192 @@
# Z-Way Binding
Z-Way is a software to configure and control a Z-Wave network.
The software comes with a full stack from Z-Wave transceiver with certified firmware to a REST API on high level.
Z-Way comes in three parts [cf. [Z-Wave.Me]([Z-Wave.Me](https://www.z-wave.me/index.php?id=1))]:
- a firmware that runs on the Z-Wave transceiver chip
- the communication stack that runs on different host Operating Systems
- an automation engine and optionally a web server to implement a User Interface
All parts together represents a smart home controller for Z-Wave.
The entire infrastructure is maintained and developed by [Z-Wave.Me](https://www.z-wave.me/index.php?id=1) with the help of a large community.
Please note the **known issues** below.
### Approach
The idea behind is the integration of Z-Wave through a bridge (Z-Way controller).
The existing, certified Z-Way stack can be used to build, configure and control the Z-Wave network.
By using the REST API all devices are loaded from Z-Way and represented as openHAB elements.
The sensor data and actuator states are constantly updated and commands are passed to the Z-Way system.
The Binding uses the Z-Way library for Java ([GitHub](https://github.com/pathec/ZWay-library-for-Java)).
## Supported Things
The Z-Way Binding provides different thing types.
The core component is the bridge which represents the Z-Way server.
For the integration of devices, two thing types are available.
In Z-Way there are devices which represent physical devices and (virtual) devices which are defined in Apps.
The difference is that physical devices usually have several functions.
Z-Way server constructs (virtual) devices for each function, such as motion sensors or door contacts.
In openHAB, these functions are bundled into physical devices and represented as things with channels for each function.
This type of thing is a *Z-Wave Device*.
On the other hand all virtual devices are mapped to *Z-Way Virtual Devices* with exactly one channel.
- *Z-Way Server* (Bridge) represents a bridge with general settings and communication tasks.
- *Z-Way Virtual Device* represents one (virtual) device with the corresponding channel. A bridge is necessary as an intermediary between openHAB thing and Z-Way device.
- *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.
## Discovery
A discovery service for Z-Way servers scans local network and must always be started manually.
Z-Way doesn't support any discovery protocol like UPnP for this purpose.
That is why first all IP addresses in local network are checked on port 8083.
If the server answers, a ZAutomation request (*/ZAutomation/api/v1/status*) is performed to ensure, the found server runs Z-Way.
Another discovery service provides available devices (a configured bridge is necessary).
The device discovery service is performed at a specified interval, but can also be started manually.
Note: In the Z-Way server device can be disabled or made invisible. Only for active and visible Z-Way devices channels will created.
## Binding Configuration
No configuration is necessary.
## Thing Configuration
The textual configuration (via \*.thing files) isn't useful because the resulting elements are read-only.
But the configuration and properties of things are changed at runtime and channels are dynamically added and removed.
### Z-Way Server (Bridge)
Besides the username and password all required Z-Way information are found during discovery.
| Configuration Name | Mandatory | Default | Desciption |
|--------------------------|-----------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| zwayServerIpAddress | | localhost | 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. |
| zwayServerPort | | 8083 | The port of the Z-Way server (1 to 65535) |
| zwayServerProtocol | | http | Protocol to connect to the Z-Way server (http or https) |
| zwayServerUsername | | admin | Username to access the Z-Way server. |
| zwayServerPassword | X | | Password to access the Z-Way server. |
| pollingInterval | | 3600 | Refresh device states and registration from Z-Way server in seconds (at least 60). |
Only the Z-Way server can be configured textual:
```
Bridge zway:zwayServer:192_168_2_42 [ zwayServerIpAddress="localhost", zwayServerPort=8083, zwayServerProtocol="http", zwayServerUsername="admin", zwayServerPassword="admin", pollingInterval=3600 ] {
// associated things have to be created with the Paper UI
}
```
### Z-Way Virtual Device
| Configuration Name | Mandatory | Default | Description |
|--------------------|-----------|---------|-----------------------------|
| deviceId | X | | Device ID of virtual device |
| bridge reference | X | | |
### Z-Wave Device
| Configuration Name | Mandatory | Default | Description |
|--------------------|-----------|---------|------------------------------|
| nodeId | X | | Node ID of the Z-Wave device |
| bridge reference | X | | |
## Channels
### Channels with detailed information for the devices
The following channels are currently supported.
| Channel Type ID | Item Type | Category | Asssigned for Z-Way device type and probe type |
|------------------------|-----------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| sensorTemperature | Number | Temperature | SensorMultilevel - temperature |
| sensorLuminosity | Number | Light | SensorMultilevel - luminosity |
| sensorHumidity | Number | Humidity | SensorMultilevel - humidity |
| sensorBarometer | Number | Pressure | SensorMultilevel - barometer |
| sensorUltraviolet | Number | Light | SensorMultilevel - ultraviolet |
| sensorCO2 | Number | CarbonDioxide | SensorMultilevel - *Special case:* no probe type for carbon dioxide sensors available - probe title *CO2 Level* acts as selection criterion |
| sensorEnergy | Number | Energy | SensorMultilevel - energy |
| sensorMeterKWh | Number | Energy | SensorMultilevel - meterElectric_kilowatt_per_hour |
| sensorMeterW | Number | Energy | SensorMultilevel - meterElectric_watt |
| sensorSmoke | Switch | Smoke | SensorBinary - smoke |
| sensorCo | Switch | Gas | SensorBinary - co |
| sensorFlood | Switch | Water | SensorBinary - flood |
| sensorTamper | Switch | Alarm | SensorBinary - tamper |
| sensorDoorWindow | Contact | Contact | SensorBinary - door-window |
| sensorMotion | Switch | Motion | SensorBinary - general_purpose, motion |
| switchPowerOutlet | Switch | PowerOutlet | SwitchBinary - *Special case:* no probe type for power outlet available - icon *switch* acts as selection criterion |
| switchColorTemperature | Dimmer | ColorLight | SwitchMultilevel - switchColor_soft_white, switchColor_cold_white |
| thermostatMode | Switch | Temperature | SwitchBinary - thermostat_mode |
Currently unsupported Z-Way probe types:
- SensorBinary: cooling, all alarm types (resulting from Z-Wave command class AlarmSensor(deprecated) and Alarm)
- SensorMultilevel: meterElectric_pulse_count, meterElectric_voltage, meterElectric_ampere, meterElectric_power_factor
### Universial channels for the devices
The following channels represent universial channels if no further device information are available, only depending on the Z-Way device types (for available device types see [Z-Way Documentation](https://zwayhomeautomation.docs.apiary.io/#reference/devices)).
| Channel Type ID | Item Type | Category | Assigned for Z-Way device type |
|------------------|-----------|-------------|-------------------------------------------|
| battery | Number | Battery | Battery |
| doorlock | Switch | Door | Doorlock |
| sensorBinary | Switch | Switch | SensorBinary |
| sensorMultilevel | Number | - | SensorMultilevel |
| switchBinary | Switch | Switch | SwitchBinary |
| switchMultilevel | Dimmer | - | SwitchMultilevel |
| switchColor | Color | ColorLight | SwitchRGBW |
| switchControl | Switch | Switch | SwitchControl, ToggleButton, SwitchToggle |
| thermostat | Number | Temperature | Thermostat |
| sensorDiscrete | Number | - | SensorDiscrete |
Unsupported Z-Way device types: Camera, SensorMultiline, Text. The integration of these types isn't planned.
### Channels for the Z-Way Server (Bridge)
| Channel Type ID | Item Type | Category | Description |
|-----------------|-----------|----------|--------------------------------------------------------------------------------------|
| actions | String | - | It is currently possible to update all devices. |
| secureInclusion | Switch | Switch | Change inclusion type for further inclusions. |
| inclusion | Switch | Switch | Start inclusion mode (after a timeout the inclusion will be automatically finished). |
| exclusion | Switch | Switch | Start exclusion mode (after a timeout the exclusion will be automatically finished). |
## Locations
The locations of the Z-Way devices are loaded during the discovery.
Based on the location ID of Z-Way device, the name of the Z-Way room is then allocated to the location property of the thing.
## Example
Because textual configuration isn't useful, follow the instructions in the [Getting Started](doc/GETTING_STARTED.md) document.
## Developer stuff
### Known issues
- The Z-Way Binding only works, when simple mode of item linking is enabled during thing creation.
### Structure of Z-Way Binding
![Z-Way Binding](doc/images/Z-Way-Binding.png)
### Features
- Discovery of the Z-Way server and devices
- Control of the Z-Wave devices in openHAB
- Receive updates of sensor data and actuator states in openHAB
### Restrictions
- Z-Way device types (especially the probe types) supported by openHAB channels with detailed information (scale types and so on) are not complete.
- Configuration of the Z-Wave network by the binding is currently not possible (physical device configuration)
- Only polling is available. Further versions will contain other mechanisms under usage of the WebSocket implementation of Z-Way or MQTT.
![BMWi](doc/BMWi_4C_Gef_en.jpg)

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,72 @@
# Getting Started
# Prepare Z-Way Server
1. [Download](https://razberry.z-wave.me/z-way-server/) Z-Way v2.2.3 or newer (further information about installing Z-Way you can find [here](http://razberry.z-wave.me/index.php?id=24))
# Prepare openHAB
1. [Download](https://www.openhab.org/download/) openHAB 2 (further information about installing openHAB you can find [here](https://www.openhab.org/docs/installation/))
2. Install Z-Way Binding
- via Extension Manager
- via Download
```shell
cd /opt/openhab2/addons
sudo wget https://github.com/openhab/openhab-addons/files/636686/org.openhab.binding.zway-2.0.0-SNAPSHOT.zip
sudo unzip org.openhab.binding.zway-2.0.0-SNAPSHOT.zip
sudo rm org.openhab.binding.zway-2.0.0-SNAPSHOT.zip
```
3. Start openHAB by running `start.bat` or `start.sh`
4. The first start may take up to 5 minutes. You should reach openHAB2 at `http://localhost:8080`
# Configure Z-Way in openHAB
## Create and configure a Z-Way Bridge
Open the *Paper UI*
![openHAB Home](images/getting-started/01-openHAB-Home.png)
Open the *Inbox* and create a Z-Way Server
![openHAB Inbox](images/getting-started/02-Inbox.png)
![openHAB Inbox](images/getting-started/03-Create-bridge.png)
Open the *Configuration* - *Things* and select Z-Way Server
![openHAB Thing details](images/getting-started/05-Bridge-details.png)
Change configuration: In order for the Observer mechanism works, the IP address, port and protocol of both systems are required.
- **Z-Way Server**
- **IP address** (default: localhost)
- **port** (default: 8083)
- **protocol** (default: http)
- **username** (default: admin)
- **password** must be set (no default value)
- **options**
- **polling interval** (default: 3600) in seconds
**The following picture is no longer up to date!**
![openHAB Thing settings](images/getting-started/06-Bridge-settings.png)
## Create and configure Z-Way Devices
Open the *Inbox* and start manually the *Discovery* for the Z-Way Binding. The *Discovery* must be started manually at this point, because the *Discovery* is running only every 4 minutes. The start time of the interval is the creation of Z-Way Server, even if the configuration is not yet complete. After the Z-Way Server is fully configured, the interval must be awaited or started manually.
![openHAB Discovery](images/getting-started/07-Device-discovery.png)
![openHAB Discovery](images/getting-started/08-Device-discovery.png)
Create *Things* from the *Discovery Results*
![openHAB Inbox](images/getting-started/09-Create-device.png)
Example: openHAB *Thing* compared to the equivalent Z-Way *Devices*
![openHAB Thing](images/getting-started/10-Z-Way-device.png)
![Z-Way Devices](images/getting-started/11-Z-Way-device.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.zway</artifactId>
<name>openHAB Add-ons :: Bundles :: ZWay Binding</name>
<properties>
<dep.noembedding>commons-lang3</dep.noembedding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.pathec</groupId>
<artifactId>zway-lib</artifactId>
<version>0.2.9.OH</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -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>

View File

@@ -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";
}

View File

@@ -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<>()));
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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) {
}
}

View File

@@ -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());
}
}
}

View File

@@ -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()));
}
}
}

View File

@@ -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()));
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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).

View File

@@ -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).

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>