diff --git a/addons/binding/org.openhab.binding.vera/.classpath b/addons/binding/org.openhab.binding.vera/.classpath
new file mode 100644
index 0000000000000..a9d178f069e87
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.vera/.project b/addons/binding/org.openhab.binding.vera/.project
new file mode 100644
index 0000000000000..a3d85fe234195
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/.project
@@ -0,0 +1,33 @@
+
+
+ org.openhab.binding.vera
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+ org.eclipse.pde.ds.core.builder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/addons/binding/org.openhab.binding.vera/ESH-INF/binding/binding.xml b/addons/binding/org.openhab.binding.vera/ESH-INF/binding/binding.xml
new file mode 100644
index 0000000000000..b6e38d71ace5e
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/ESH-INF/binding/binding.xml
@@ -0,0 +1,9 @@
+
+
+
+ Vera Binding
+ Vera (lite/edge/plus/etc version) is a popular Z-Wave controller. Vera binding automatically finds the addresses of all such controllers on the local network, and through HTTP api loads all their devices and scenes.
+ Dmitriy Ponomarev
+
+
diff --git a/addons/binding/org.openhab.binding.vera/ESH-INF/config/bridge-config.xml b/addons/binding/org.openhab.binding.vera/ESH-INF/config/bridge-config.xml
new file mode 100644
index 0000000000000..0cc0800ad1b99
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/ESH-INF/config/bridge-config.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+ The configuration of the Vera. All the information can be detected during the discovery.
+
+
+
+ network-address
+
+ The IP address or hostname of the Vera controller.
+ 192.168.1.10
+
+
+
+
+ The port of the Vera controller
+ 3480
+
+
+
+
+ Refresh all values (name, room, state) for all devices and scenes.
+ Seconds
+ 60
+
+
+
+
+ Remove digits, slashes and double spaces from all names. Good for some voice recognition services.
+ false
+
+
+
+
+ Default name for room, if no room specified.
+ No Room
+
+
+
+
+ If enabled, homekit tags are created for all supported devices. Please read Homekit add-on instructions.
+ true
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.vera/ESH-INF/thing/bridge.xml b/addons/binding/org.openhab.binding.vera/ESH-INF/thing/bridge.xml
new file mode 100644
index 0000000000000..756048493bd29
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/ESH-INF/thing/bridge.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ A popular Z-Wave controller Vera (lite/edge/plus/etc).
+
+
+
+
+
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.vera/ESH-INF/thing/channels.xml b/addons/binding/org.openhab.binding.vera/ESH-INF/thing/channels.xml
new file mode 100644
index 0000000000000..664408392ae8b
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/ESH-INF/thing/channels.xml
@@ -0,0 +1,188 @@
+
+
+
+
+ Number
+
+ Temperature
+
+
+
+
+ Number
+
+ Light
+
+
+
+
+ Number
+
+ Humidity
+
+
+
+
+ Number
+
+ Light
+
+
+
+
+ Number
+
+ Energy
+
+
+
+
+ Number
+
+ Energy
+
+
+
+
+ Number
+
+ Energy
+
+
+
+
+ Switch
+
+ Smoke
+
+
+
+
+ Switch
+
+ Gas
+
+
+
+
+ Switch
+
+ Water
+
+
+
+
+ Contact
+
+ Contact
+
+
+
+
+ Switch
+
+ Motion
+
+
+
+
+ Rollershutter
+
+ Blinds
+
+
+
+
+ Number
+
+ Battery
+
+
+
+
+ Switch
+
+ Door
+
+
+
+
+
+
+ Switch
+
+ This channel represents a universal channel if no further device information is available.
+ Switch
+
+
+
+
+ Switch
+
+ This channel represents a universal channel if no further device information is available.
+ Switch
+
+
+
+
+ Dimmer
+
+ This channel represents a universal channel if no further device information is available.
+ Switch
+
+
+
+
+ Color
+
+ This channel represents the rgbw switch device type from Vera.
+ ColorLight
+
+
+
+ Switch
+
+ 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.
+ Temperature
+
+
+
+
+ Number
+
+ Temperature
+
+
+
+
+ Number
+
+ The channel allows the control or display of a thermostat (mode) from command class. The modes differ from device to device.
+ Temperature
+
+
+
+
+
+ Switch
+
+ This channel represents a scene button.
+ Switch
+
+
+
+
+
+
+ String
+
+ Available actions of the Vera Controller
+
+
+
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.vera/ESH-INF/thing/device.xml b/addons/binding/org.openhab.binding.vera/ESH-INF/thing/device.xml
new file mode 100644
index 0000000000000..4d91ba3089c2b
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/ESH-INF/thing/device.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+ A Vera device represents one sensor or actor with the one or more channels.
+
+
+
+
+ Id of the Vera device
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.vera/ESH-INF/thing/scene.xml b/addons/binding/org.openhab.binding.vera/ESH-INF/thing/scene.xml
new file mode 100644
index 0000000000000..2f12c2b14c177
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/ESH-INF/thing/scene.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+ A Vera scene represents one scene with the one channel.
+
+
+
+
+ Id of the Vera scene
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.vera/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.vera/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000000..c6eeb62f6329f
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/META-INF/MANIFEST.MF
@@ -0,0 +1,30 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Vera Binding
+Bundle-SymbolicName: org.openhab.binding.vera;singleton:=true
+Bundle-Vendor: openHAB
+Bundle-Version: 2.1.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ClassPath: .
+Import-Package: com.google.common.collect,
+ com.google.gson,
+ com.google.gson.annotations,
+ org.apache.commons.lang,
+ org.apache.commons.lang.builder,
+ org.apache.commons.net.util,
+ org.eclipse.smarthome.config.core,
+ org.eclipse.smarthome.config.discovery,
+ org.eclipse.smarthome.core.library.types,
+ org.eclipse.smarthome.core.thing,
+ org.eclipse.smarthome.core.thing.binding,
+ org.eclipse.smarthome.core.thing.binding.builder,
+ org.eclipse.smarthome.core.thing.type,
+ org.eclipse.smarthome.core.types,
+ org.openhab.binding.vera,
+ org.openhab.binding.vera.handler,
+ org.osgi.framework,
+ org.slf4j
+Service-Component: OSGI-INF/*.xml
+Export-Package: org.openhab.binding.vera,
+ org.openhab.binding.vera.handler
+Bundle-ActivationPolicy: lazy
diff --git a/addons/binding/org.openhab.binding.vera/OSGI-INF/VeraBridgeDiscovery.xml b/addons/binding/org.openhab.binding.vera/OSGI-INF/VeraBridgeDiscovery.xml
new file mode 100644
index 0000000000000..445764099cc02
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/OSGI-INF/VeraBridgeDiscovery.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.vera/OSGI-INF/VeraHandlerFactory.xml b/addons/binding/org.openhab.binding.vera/OSGI-INF/VeraHandlerFactory.xml
new file mode 100644
index 0000000000000..aebe0f52e7c73
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/OSGI-INF/VeraHandlerFactory.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.vera/README.md b/addons/binding/org.openhab.binding.vera/README.md
new file mode 100644
index 0000000000000..ef06c86753448
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/README.md
@@ -0,0 +1,118 @@
+# Vera Binding
+
+Vera (lite/edge/plus/etc version) is a popular Z-Wave controller. Vera binding automatically finds the addresses of all such controllers on the local network, and through HTTP api loads all their devices and scenes.
+
+## Supported Things
+
+Vera binding provides three thing types: controller, devices and scenes. There may be more than one controller.
+Each device usually has one channel. But some devices has additional channels, such as battery level, power consumption and others.
+Each scene has only one channel, to start scene.
+
+## Discovery
+
+A discovery service for Vera controller scans local network and must always be started manually.
+
+Another discovery service provides available devices. The device discovery service is performed at a specified interval, but can also be started manually.
+
+Note: devices with type "controller" and "interface" not loaded.
+
+## 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.
+
+### Vera Controller (Bridge)
+
+| Name | Type | Description |
+|---------------------|---------------|----------------------------------------------------------------------------------------------------------|
+| veraIpAddress | string | The IP address or hostname of the Vera controller. |
+| veraPort | int | The port of the Vera controller |
+| pollingInterval | int | Refresh all values (name, room, state) for all devices and scenes. |
+| clearNames | boolean | Remove digits, slashes and double spaces from all names. Good for some voice recognition services. |
+| defaulRoomName | string | Default name for room, if no room specified. |
+| homekitIntegration | boolean | If enabled, homekit tags are created for all supported devices. Please read Homekit add-on instructions. |
+
+### Vera Device
+
+| Name | Type | Description |
+|-----------------|---------------|--------------------------|
+| deviceId | string | Id of the Vera device |
+
+### Vera Scene
+
+| Name | Type | Description |
+|-----------------|---------------|--------------------------|
+| sceneId | string | Id of the Vera scene |
+
+## Channels
+
+### Channels with detailed information for the devices
+
+The following channels are currently supported.
+
+| Channel Type ID | Item Type | Category | Vera category-subcategory |
+| ------------------------- | ------------- | ------------- | ---------------------------- |
+| switchMultilevel | Dimmer | Switch | 2-(1-3): Dimmable Light |
+| switchColor | Color | ColorLight | 2-4: Dimmable Light - TODO |
+| switchBinary | Switch | Switch | 3: Switch |
+| sensorDoorWindow | Contact | Contact | 4-1: Security Sensor |
+| sensorFlood | Switch | Water | 4-2: Security Sensor |
+| sensorMotion | Switch | Motion | 4-3: Security Sensor |
+| sensorSmoke | Switch | Smoke | 4-4: Security Sensor |
+| sensorCo | Switch | Gas | 4-5: Security Sensor |
+| sensorBinary | Switch | Switch | 4-6: Security Sensor |
+| doorlock | Switch | Door | 7: Door Lock |
+| switchBlinds | Rollershutter | Blinds | 8: Window Covering |
+| sensorBinary | Switch | Switch | 12: Generic Sensor |
+| switchBinary | Switch | Switch | 14: Scene Controller |
+| sensorHumidity | Number | Humidity | 16: Humidity Sensor |
+| sensorTemperature | Number | Temperature | 17: Temperature Sensor |
+| sensorLuminosity | Number | Light | 18: Light Sensor |
+| sensorEnergy | Number | Energy | 21: Power Meter |
+| sensorUltraviolet | Number | Light | 28: UV Sensor |
+| sensorMeterKWh | Number | Energy | no category |
+| sensorMeterW | Number | Energy | no category |
+| thermostatMode | Switch | Temperature | TODO |
+| thermostatSetPoint | Number | Temperature | TODO |
+| thermostatModeCC | Number | Temperature | TODO |
+| sceneButton | Switch | Switch | |
+
+### Unsupported Vera device categories
+
+- 6: Camera
+- 9: Remote Control
+- 10: IR Transmitter
+- 11: Generic IO
+- 13: Serial Port
+- 15: A/V
+- 19: Z-Wave Interface
+- 20: Insteon Interface
+- 22: Alarm Panel
+- 23: Alarm Partition
+- 24: Siren
+- 25: Weather
+- 26: Philips Controller
+- 27: Appliance
+
+The integration for most of these types isn't planned.
+
+Thermostats and colored lamps are also not supported, but will be implemented later.
+
+## Locations
+
+The locations are loaded during the discovery and based on the Vera devices rooms.
+
+## Developer stuff
+
+### Known issues
+
+No known issues at that moment.
+
+### Features
+
+- Discovery of the Vera controller and devices
+- Control of the Vera devices in openHAB
+- Receive updates of sensor data and actuator states in openHAB
diff --git a/addons/binding/org.openhab.binding.vera/about.html b/addons/binding/org.openhab.binding.vera/about.html
new file mode 100644
index 0000000000000..089907bd268e0
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/about.html
@@ -0,0 +1,32 @@
+
+
+
+
+ About
+
+
+
About This Content
+
+
March 30, 2017
+
License
+
+
+ The openHAB community makes available all content in this plug-in ("Content"). Unless otherwise
+ indicated below, the Content is provided to you under the terms and conditions of the
+ Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+ at http://www.eclipse.org/legal/epl-v10.html.
+ For purposes of the EPL, "Program" will mean the Content.
+
+
+
+ If you did not receive this Content directly from the openHAB community, the Content is
+ being redistributed by another party ("Redistributor") and different terms and conditions may
+ apply to your use of any object code in the Content. Check the Redistributor's license that was
+ provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+ indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+ and such source code may be obtained at openhab.org.
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.vera/build.properties b/addons/binding/org.openhab.binding.vera/build.properties
new file mode 100644
index 0000000000000..0a87c9aa00cea
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/build.properties
@@ -0,0 +1,7 @@
+source..=src/main/java/
+output..=target/classes
+bin.includes = META-INF/,\
+ .,\
+ OSGI-INF/,\
+ ESH-INF/,\
+ about.html
diff --git a/addons/binding/org.openhab.binding.vera/pom.xml b/addons/binding/org.openhab.binding.vera/pom.xml
new file mode 100644
index 0000000000000..d18438012d3ab
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/pom.xml
@@ -0,0 +1,19 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.binding
+ pom
+ 2.1.0-SNAPSHOT
+
+
+ org.openhab.binding.vera
+ 2.1.0-SNAPSHOT
+
+ Vera Binding
+ eclipse-plugin
+
+
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/VeraBindingConstants.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/VeraBindingConstants.java
new file mode 100644
index 0000000000000..dfb50c6877420
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/VeraBindingConstants.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera;
+
+import java.util.Set;
+
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * The {@link VeraBinding} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraBindingConstants {
+
+ public static final String BINDING_ID = "vera";
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "veraController");
+ public static final ThingTypeUID THING_TYPE_DEVICE = new ThingTypeUID(BINDING_ID, "veraDevice");
+ public static final ThingTypeUID THING_TYPE_SCENE = new ThingTypeUID(BINDING_ID, "veraScene");
+
+ public static final Set SUPPORTED_DEVICE_THING_TYPES_UIDS = ImmutableSet.of(THING_TYPE_BRIDGE,
+ THING_TYPE_DEVICE, THING_TYPE_SCENE);
+
+ // 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 SWITCH_BINARY_CHANNEL = "switchBinary";
+ public static final String SWITCH_MULTILEVEL_CHANNEL = "switchMultilevel";
+ public static final String SWITCH_COLOR_CHANNEL = "switchColor";
+
+ // 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_ULTRAVIOLET_CHANNEL = "sensorUltraviolet";
+ 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_DOOR_WINDOW_CHANNEL = "sensorDoorWindow";
+ public static final String SENSOR_MOTION_CHANNEL = "sensorMotion";
+
+ // switch multilevel
+ public static final String SWITCH_ROLLERSHUTTER_CHANNEL = "switchBlinds";
+
+ // special channels
+ public static final String ACTIONS_CHANNEL = "actions";
+ public static final String ACTIONS_CHANNEL_OPTION_REFRESH = "REFRESH";
+
+ /* Bridge config properties */
+ public static final String BRIDGE_CONFIG_VERA_SERVER_IP_ADDRESS = "veraIpAddress";
+ public static final String BRIDGE_CONFIG_VERA_SERVER_PORT = "veraControllerPort";
+ public static final String BRIDGE_CONFIG_POLLING_INTERVAL = "pollingInterval";
+ public static final String BRIDGE_CONFIG_CLEAR_NAMES = "clearNames";
+ public static final String BRIDGE_CONFIG_DEFAULT_ROOM_NAME = "defaultRoomName";
+ public static final String BRIDGE_CONFIG_HOMEKIT_INTEGRATION = "homekitIntegration";
+
+ public static final String DEVICE_CONFIG_ID = "deviceId";
+ public static final String DEVICE_PROP_CATEGORY = "category";
+ public static final String DEVICE_PROP_SUBCATEGORY = "subcategory";
+
+ public static final String SCENE_CONFIG_ID = "sceneId";
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/config/VeraBridgeConfiguration.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/config/VeraBridgeConfiguration.java
new file mode 100644
index 0000000000000..fd652443d4dd8
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/config/VeraBridgeConfiguration.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.config;
+
+import static org.openhab.binding.vera.VeraBindingConstants.*;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+/**
+ * The {@link VeraBridgeConfiguration} class defines the model for a bridge configuration.
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraBridgeConfiguration {
+ private String veraIpAddress;
+ private Integer veraPort;
+ private Integer pollingInterval;
+ private Boolean clearNames;
+ private String defaulRoomName;
+ private Boolean homekitIntegration;
+
+ public String getVeraIpAddress() {
+ return veraIpAddress;
+ }
+
+ public void setVeraIpAddress(String ipAddress) {
+ this.veraIpAddress = ipAddress;
+ }
+
+ public Integer getVeraPort() {
+ return veraPort;
+ }
+
+ public void setVeraPort(Integer port) {
+ this.veraPort = port;
+ }
+
+ public Integer getPollingInterval() {
+ return pollingInterval;
+ }
+
+ public void setPollingInterval(Integer pollingInterval) {
+ this.pollingInterval = pollingInterval;
+ }
+
+ public Boolean getClearNames() {
+ return clearNames;
+ }
+
+ public void setClearNames(Boolean clearNames) {
+ this.clearNames = clearNames;
+ }
+
+ public String getDefaulRoomName() {
+ return defaulRoomName;
+ }
+
+ public void setDefaulRoomName(String defaulRoomName) {
+ this.defaulRoomName = defaulRoomName;
+ }
+
+ public Boolean getHomekitIntegration() {
+ return homekitIntegration;
+ }
+
+ public void setHomekitIntegration(Boolean homekitIntegration) {
+ this.homekitIntegration = homekitIntegration;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).append(BRIDGE_CONFIG_VERA_SERVER_IP_ADDRESS, this.getVeraIpAddress())
+ .append(BRIDGE_CONFIG_VERA_SERVER_PORT, this.getVeraPort())
+ .append(BRIDGE_CONFIG_POLLING_INTERVAL, this.getPollingInterval())
+ .append(BRIDGE_CONFIG_CLEAR_NAMES, this.getClearNames())
+ .append(BRIDGE_CONFIG_DEFAULT_ROOM_NAME, this.getDefaulRoomName())
+ .append(BRIDGE_CONFIG_HOMEKIT_INTEGRATION, this.getHomekitIntegration()).toString();
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/config/VeraDeviceConfiguration.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/config/VeraDeviceConfiguration.java
new file mode 100644
index 0000000000000..f6524081d8706
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/config/VeraDeviceConfiguration.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.config;
+
+import static org.openhab.binding.vera.VeraBindingConstants.DEVICE_CONFIG_ID;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+/**
+ * The {@link VeraDeviceConfiguration} class defines the model for a device configuration.
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraDeviceConfiguration {
+ 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_ID, this.getDeviceId()).toString();
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/config/VeraSceneConfiguration.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/config/VeraSceneConfiguration.java
new file mode 100644
index 0000000000000..d382bf904d4e2
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/config/VeraSceneConfiguration.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.config;
+
+import static org.openhab.binding.vera.VeraBindingConstants.SCENE_CONFIG_ID;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+/**
+ * The {@link VeraSceneConfiguration} class defines the model for a scene configuration.
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraSceneConfiguration {
+ private String sceneId;
+
+ public String getSceneId() {
+ return sceneId;
+ }
+
+ public void setSceneId(String sceneId) {
+ this.sceneId = sceneId;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).append(SCENE_CONFIG_ID, this.getSceneId()).toString();
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/CategoryType.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/CategoryType.java
new file mode 100644
index 0000000000000..fd3bdbd7d99f7
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/CategoryType.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.controller;
+
+public enum CategoryType {
+ Controller(0, "Controller"),
+ Interface(1, "Interface"),
+ DimmableLight(2, "Dimmable Light"),
+ Switch(3, "Switch"),
+ SecuritySensor(4, "Security Sensor"),
+ HVAC(5, "HVAC"),
+ Camera(6, "Camera"),
+ DoorLock(7, "Door Lock"),
+ WindowCovering(8, "Window Covering"),
+ RemoteControl(9, "Remote Control"),
+ IRTransmitter(10, "IR Transmitter"),
+ GenericIO(11, "Generic I/O"),
+ GenericSensor(12, "Generic Sensor"),
+ SerialPort(13, "Serial Port"),
+ SceneController(14, "Scene Controller"),
+ AV(15, "A/V"),
+ HumiditySensor(16, "Humidity Sensor"),
+ TemperatureSensor(17, "Temperature Sensor"),
+ LightSensor(18, "Light Sensor"),
+ ZWaveInterface(19, "Z-Wave Interface"),
+ InsteonInterface(20, "Insteon Interface"),
+ PowerMeter(21, "Power Meter"),
+ AlarmPanel(22, "Alarm Panel"),
+ AlarmPartition(23, "Alarm Partition"),
+ Siren(24, "Siren"),
+ Weather(25, "Weather"),
+ PhilipsController(26, "Philips Controller"),
+ Appliance(27, "Appliance"),
+ UVSensor(28, "UV Sensor"),
+ Unknown(-1, "Unknown");
+
+ private int id;
+ private String name;
+
+ private CategoryType(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return "{" + id + ", " + name + "}";
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/Controller.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/Controller.java
new file mode 100644
index 0000000000000..c50835617ab3a
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/Controller.java
@@ -0,0 +1,194 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.controller;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openhab.binding.vera.config.VeraBridgeConfiguration;
+import org.openhab.binding.vera.controller.json.Device;
+import org.openhab.binding.vera.controller.json.Room;
+import org.openhab.binding.vera.controller.json.Scene;
+import org.openhab.binding.vera.controller.json.Sdata;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+/**
+ * The {@link Controller} class used to connect to the Vera
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class Controller {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final VeraBridgeConfiguration config;
+ private final String veraHost;
+ private final String veraPort;
+ private final Gson gson;
+
+ private Sdata sdata;
+
+ public Controller(VeraBridgeConfiguration config) {
+ this.config = config;
+ this.veraHost = config.getVeraIpAddress();
+ this.veraPort = "" + config.getVeraPort();
+ this.gson = new Gson();
+
+ updateSdata();
+ }
+
+ public Sdata getSdata() {
+ if (sdata == null) {
+ updateSdata();
+ }
+ return sdata;
+ }
+
+ private void denormalizeSdata(Sdata data) {
+ Map roomMap = new HashMap<>();
+ for (Room r : data.getRooms()) {
+ roomMap.put(r.getId(), r);
+ }
+ for (Device d : data.getDevices()) {
+ if (config.getClearNames()) {
+ d.setName(replaceTrash(d.getName()));
+ }
+ if (d.getRoom() != null && roomMap.get(d.getRoom()) != null) {
+ d.setRoomName(roomMap.get(d.getRoom()).getName());
+ } else {
+ d.setRoomName(config.getDefaulRoomName());
+ }
+ try {
+ d.setCategoryType(CategoryType.values()[Integer.parseInt(d.getCategory())]);
+ } catch (IndexOutOfBoundsException e) {
+ logger.warn("Unknown category type: {}", d.getCategory());
+ }
+ }
+ for (Scene s : data.getScenes()) {
+ if (config.getClearNames()) {
+ s.setName(replaceTrash(s.getName()));
+ }
+ if (s.getRoom() != null && roomMap.get(s.getRoom()) != null) {
+ s.setRoomName(roomMap.get(s.getRoom()).getName());
+ } else {
+ s.setRoomName(config.getDefaulRoomName());
+ }
+ }
+ }
+
+ private String request(String request) throws IOException {
+ String result;
+ URL url = new URL(request);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
+ connection.connect();
+ BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ StringBuilder buffer = new StringBuilder();
+ String line;
+ while ((line = br.readLine()) != null) {
+ buffer.append(line).append("\n");
+ }
+ br.close();
+ result = buffer.toString();
+ return result;
+ }
+
+ private boolean sendCommand(String request) {
+ try {
+ URL url = new URL(request);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ connection.getResponseMessage();
+ } catch (IOException e) {
+ logger.warn("Error while get getJson: {} ", request, e);
+ return false;
+ }
+ return true;
+ }
+
+ private String replaceTrash(String name) {
+ String sanitizedName = name.replaceAll("[0-9:/-]", "");
+ sanitizedName = name.replaceAll("\\s+", " ");
+ return sanitizedName.trim();
+ }
+
+ private String getUrl() {
+ return "http://" + veraHost + ":" + veraPort + "/data_request";
+ }
+
+ private void setStatus(Device d, String status) {
+ d.setStatus(status);
+ String service = "urn:upnp-org:serviceId:SwitchPower1";
+ if (CategoryType.DoorLock.equals(d.getCategoryType())) {
+ service = "urn:micasaverde-com:serviceId:DoorLock1";
+ }
+ sendCommand(getUrl() + "?id=action&DeviceNum=" + d.getId() + "&serviceId=" + service
+ + "&action=SetTarget&newTargetValue=" + status);
+ }
+
+ public void turnDeviceOn(Device d) {
+ setStatus(d, "1");
+ }
+
+ public void turnDeviceOff(Device d) {
+ setStatus(d, "0");
+ }
+
+ public void setDimLevel(Device d, String level) {
+ d.setLevel(level);
+ sendCommand(getUrl() + "?id=action&DeviceNum=" + d.getId()
+ + "&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=" + level);
+ }
+
+ public void runScene(String id) {
+ sendCommand(getUrl() + "?id=action&SceneNum=" + id
+ + "&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene");
+ }
+
+ public void updateSdata() {
+ try {
+ String result = request(getUrl() + "?id=sdata&output_format=json");
+ Sdata data = gson.fromJson(result, Sdata.class);
+ denormalizeSdata(data);
+ sdata = data;
+ } catch (IOException e) {
+ logger.warn("Failed to update sdata: {} ", getUrl(), e);
+ }
+ }
+
+ public boolean isConnected() {
+ return sdata != null;
+ }
+
+ public Device getDevice(String deviceId) {
+ for (Device d : sdata.getDevices()) {
+ if (deviceId.equals(d.getId())) {
+ return d;
+ }
+ }
+ return null;
+ }
+
+ public Scene getScene(String sceneId) {
+ for (Scene s : sdata.getScenes()) {
+ if (sceneId.equals(s.getId())) {
+ return s;
+ }
+ }
+ return null;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Category.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Category.java
new file mode 100644
index 0000000000000..2cd4cea3b4a82
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Category.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.controller.json;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author Dmitriy Ponomarev
+ */
+public class Category {
+ @SerializedName("name")
+ private String name;
+
+ @SerializedName("id")
+ private String id;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Device.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Device.java
new file mode 100644
index 0000000000000..2e0bfab66490b
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Device.java
@@ -0,0 +1,171 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.controller.json;
+
+import org.openhab.binding.vera.controller.CategoryType;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author Dmitriy Ponomarev
+ */
+public class Device {
+ private CategoryType categoryType;
+ private String roomName;
+
+ @SerializedName("name")
+ private String name;
+
+ @SerializedName("altid")
+ private String altid;
+
+ @SerializedName("id")
+ private String id;
+
+ @SerializedName("category")
+ private String category;
+
+ @SerializedName("subcategory")
+ private String subcategory;
+
+ @SerializedName("room")
+ private String room;
+
+ @SerializedName("parent")
+ private String parent;
+
+ @SerializedName("kwh")
+ private String kwh;
+
+ @SerializedName("watts")
+ private String watts;
+
+ @SerializedName("batterylevel")
+ private String batterylevel;
+
+ @SerializedName("locked")
+ private String locked;
+
+ @SerializedName("status")
+ private String status;
+
+ @SerializedName("level")
+ private String level;
+
+ @SerializedName("state")
+ private String state;
+
+ @SerializedName("comment")
+ private String comment;
+
+ @SerializedName("humidity")
+ private String humidity;
+
+ @SerializedName("light")
+ private String light;
+
+ @SerializedName("temperature")
+ private String temperature;
+
+ @SerializedName("tripped")
+ private String tripped;
+
+ public CategoryType getCategoryType() {
+ return categoryType;
+ }
+
+ public void setCategoryType(CategoryType categoryType) {
+ this.categoryType = categoryType;
+ }
+
+ public String getRoomName() {
+ return roomName;
+ }
+
+ public void setRoomName(String roomName) {
+ this.roomName = roomName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public String getSubcategory() {
+ return subcategory;
+ }
+
+ public String getRoom() {
+ return room;
+ }
+
+ public String getKwh() {
+ return kwh;
+ }
+
+ public String getWatts() {
+ return watts;
+ }
+
+ public String getBatterylevel() {
+ return batterylevel;
+ }
+
+ public String getLocked() {
+ return locked;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getLevel() {
+ return level;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ public String getHumidity() {
+ return humidity;
+ }
+
+ public String getLight() {
+ return light;
+ }
+
+ public String getTemperature() {
+ return temperature;
+ }
+
+ public String getTripped() {
+ return tripped;
+ }
+
+ @Override
+ public String toString() {
+ return "{" + id + ", " + name + "}";
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Room.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Room.java
new file mode 100644
index 0000000000000..b197577bb53b2
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Room.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.controller.json;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author Dmitriy Ponomarev
+ */
+public class Room {
+ @SerializedName("name")
+ private String name;
+
+ @SerializedName("id")
+ private String id;
+
+ @SerializedName("section")
+ private String section;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Scene.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Scene.java
new file mode 100644
index 0000000000000..0bb00106c9024
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Scene.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.controller.json;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author Dmitriy Ponomarev
+ */
+public class Scene {
+ private String roomName;
+
+ @SerializedName("active")
+ private String active;
+
+ @SerializedName("name")
+ private String name;
+
+ @SerializedName("id")
+ private String id;
+
+ @SerializedName("room")
+ private String room;
+
+ public String getRoomName() {
+ return roomName;
+ }
+
+ public void setRoomName(String roomName) {
+ this.roomName = roomName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getRoom() {
+ return room;
+ }
+
+ public void setRoom(String room) {
+ this.room = room;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Sdata.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Sdata.java
new file mode 100644
index 0000000000000..6380fc44815cd
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Sdata.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.controller.json;
+
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author Dmitriy Ponomarev
+ */
+public class Sdata {
+ @SerializedName("full")
+ private String full;
+
+ @SerializedName("version")
+ private String version;
+
+ @SerializedName("model")
+ private String model;
+
+ @SerializedName("zwave_heal")
+ private String zwave_heal;
+
+ @SerializedName("temperature")
+ private String temperature;
+
+ @SerializedName("serial_number")
+ private String serial_number;
+
+ @SerializedName("fwd1")
+ private String fwd1;
+
+ @SerializedName("fwd2")
+ private String fwd2;
+
+ @SerializedName("ir")
+ private String ir;
+
+ @SerializedName("irtx")
+ private String irtx;
+
+ @SerializedName("loadtime")
+ private String loadtime;
+
+ @SerializedName("dataversion")
+ private String dataversion;
+
+ @SerializedName("state")
+ private String state;
+
+ @SerializedName("comment")
+ private String comment;
+
+ @SerializedName("sections")
+ private List sections;
+
+ @SerializedName("rooms")
+ private List rooms;
+
+ @SerializedName("scenes")
+ private List scenes;
+
+ @SerializedName("devices")
+ private List devices;
+
+ @SerializedName("categories")
+ private List categories;
+
+ public List getRooms() {
+ return rooms;
+ }
+
+ public List getScenes() {
+ return scenes;
+ }
+
+ public List getDevices() {
+ return devices;
+ }
+
+ public List getCategories() {
+ return categories;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Section.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Section.java
new file mode 100644
index 0000000000000..09febd6a82871
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/controller/json/Section.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.controller.json;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author Dmitriy Ponomarev
+ */
+public class Section {
+ @SerializedName("name")
+ private String name;
+
+ @SerializedName("id")
+ private String id;
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/handler/VeraBridgeHandler.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/handler/VeraBridgeHandler.java
new file mode 100644
index 0000000000000..000d904889e0b
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/handler/VeraBridgeHandler.java
@@ -0,0 +1,166 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.handler;
+
+import static org.openhab.binding.vera.VeraBindingConstants.*;
+
+import java.util.Map;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.types.Command;
+import org.openhab.binding.vera.config.VeraBridgeConfiguration;
+import org.openhab.binding.vera.controller.Controller;
+import org.openhab.binding.vera.controller.json.Sdata;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link VeraBridgeHandler} manages the connection between Vera and binding.
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraBridgeHandler extends BaseBridgeHandler {
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private BridgePolling bridgePolling;
+ private ScheduledFuture> pollingJob;
+
+ private VeraBridgeConfiguration mConfig;
+ private Controller controller;
+
+ private class Initializer implements Runnable {
+ @Override
+ public void run() {
+ logger.debug("Authenticate to the Vera controller ...");
+
+ try {
+ if (controller.isConnected()) {
+ logger.debug("Vera controller successfully authenticated");
+ updateStatus(ThingStatus.ONLINE);
+
+ // 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");
+ }
+ } else {
+ logger.warn("Can't connect to Vera controller");
+ }
+ } catch (Exception e) {
+ logger.error("Error occurred when initialize bridge: {}", e);
+ if (getThing().getStatus() == ThingStatus.ONLINE) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
+ "Error occurred when initialize bridge: " + e.getMessage());
+ }
+ }
+ }
+ };
+
+ public VeraBridgeHandler(Bridge bridge) {
+ super(bridge);
+ bridgePolling = new BridgePolling();
+ }
+
+ @Override
+ public void initialize() {
+ logger.debug("Initializing Vera controller ...");
+ updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING, "Checking configuration...");
+ mConfig = getConfigAs(VeraBridgeConfiguration.class);
+ if (mConfig != null) {
+ controller = new Controller(mConfig);
+ scheduler.execute(new Initializer());
+ }
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Disposing Vera controller ...");
+ if (pollingJob != null && !pollingJob.isCancelled()) {
+ pollingJob.cancel(true);
+ pollingJob = null;
+ }
+ super.dispose();
+ }
+
+ @Override
+ public void handleConfigurationUpdate(Map configurationParameters) {
+ logger.debug("Handle Vera configuration update ...");
+ if (pollingJob != null) {
+ pollingJob.cancel(false);
+ }
+ initialize();
+ refreshAllThings();
+ super.handleConfigurationUpdate(configurationParameters);
+ }
+
+ private class BridgePolling implements Runnable {
+ @Override
+ public void run() {
+ getController().updateSdata();
+ if (getController().getSdata() != null) {
+ if (!getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ updateStatus(ThingStatus.ONLINE);
+ logger.debug("Connection to bridge {} restored.", getThing().getLabel());
+ }
+ refreshAllThings();
+ } else if (getThing().getStatus() == ThingStatus.ONLINE) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
+ "Error occurred when polling bridge.");
+ }
+ }
+ };
+
+ private void refreshAllThings() {
+ logger.debug("Handle bridge refresh command for all configured things ...");
+ for (Thing thing : getThing().getThings()) {
+ ThingHandler handler = thing.getHandler();
+ if (handler instanceof VeraDeviceHandler) {
+ logger.debug("Refreshing device: {}", thing.getLabel());
+ ((VeraDeviceHandler) handler).refreshAllChannels();
+ } else if (handler instanceof VeraSceneHandler) {
+ logger.debug("Refreshing scene: {}", thing.getLabel());
+ ((VeraSceneHandler) handler).refreshAllChannels();
+ }
+ }
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ 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)) {
+ refreshAllThings();
+ }
+ }
+ }
+
+ protected VeraBridgeConfiguration getVeraBridgeConfiguration() {
+ return mConfig;
+ }
+
+ public Controller getController() {
+ return controller;
+ }
+
+ public Sdata getData() {
+ return controller.getSdata();
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/handler/VeraDeviceHandler.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/handler/VeraDeviceHandler.java
new file mode 100644
index 0000000000000..3fb99669fe2e2
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/handler/VeraDeviceHandler.java
@@ -0,0 +1,468 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.handler;
+
+import static org.openhab.binding.vera.VeraBindingConstants.*;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.smarthome.core.library.types.DecimalType;
+import org.eclipse.smarthome.core.library.types.OnOffType;
+import org.eclipse.smarthome.core.library.types.OpenClosedType;
+import org.eclipse.smarthome.core.library.types.PercentType;
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.Channel;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.ThingStatusInfo;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder;
+import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder;
+import org.eclipse.smarthome.core.thing.type.ChannelTypeUID;
+import org.eclipse.smarthome.core.types.Command;
+import org.eclipse.smarthome.core.types.RefreshType;
+import org.openhab.binding.vera.config.VeraDeviceConfiguration;
+import org.openhab.binding.vera.controller.json.Device;
+import org.openhab.binding.vera.internal.converter.VeraDeviceStateConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link VeraDeviceHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraDeviceHandler extends BaseThingHandler {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+
+ private VeraDeviceConfiguration mConfig;
+
+ public VeraDeviceHandler(Thing thing) {
+ super(thing);
+ }
+
+ private class Initializer implements Runnable {
+ @Override
+ public void run() {
+ try {
+ VeraBridgeHandler veraBridgeHandler = getVeraBridgeHandler();
+ if (veraBridgeHandler != null && veraBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ ThingStatusInfo statusInfo = veraBridgeHandler.getThing().getStatusInfo();
+ logger.debug("Change device status to bridge status: {}", statusInfo);
+
+ // Set thing status to bridge status
+ updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
+
+ logger.debug("Add channels");
+ Device device = veraBridgeHandler.getController().getDevice(mConfig.getDeviceId());
+ if (device != null) {
+ logger.debug("Found {} device", device.getName());
+ updateLabelAndLocation(device.getName(), device.getRoomName());
+ addDeviceAsChannel(device,
+ veraBridgeHandler.getVeraBridgeConfiguration().getHomekitIntegration());
+ }
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
+ "Controller is not online");
+ }
+ } catch (Exception e) {
+ logger.error("Error occurred when adding device as channel: ", e);
+ if (getThing().getStatus() == ThingStatus.ONLINE) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
+ "Error occurred when adding device as channel: " + e.getMessage());
+ }
+ }
+ }
+ };
+
+ protected synchronized VeraBridgeHandler getVeraBridgeHandler() {
+ Bridge bridge = getBridge();
+ if (bridge == null) {
+ return null;
+ }
+ ThingHandler handler = bridge.getHandler();
+ if (handler instanceof VeraBridgeHandler) {
+ return (VeraBridgeHandler) handler;
+ } else {
+ return null;
+ }
+ }
+
+ private VeraDeviceConfiguration loadAndCheckConfiguration() {
+ VeraDeviceConfiguration config = getConfigAs(VeraDeviceConfiguration.class);
+ if (config.getDeviceId() == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "Couldn't create device, deviceId is missing.");
+ return null;
+ }
+ return config;
+ }
+
+ @Override
+ public void initialize() {
+ logger.debug("Initializing Vera device handler ...");
+ updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING,
+ "Checking configuration and bridge...");
+ mConfig = loadAndCheckConfiguration();
+ if (mConfig != null) {
+ logger.debug("Configuration complete: {}", mConfig);
+ scheduler.schedule(new Initializer(), 2, TimeUnit.SECONDS);
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "DeviceId required!");
+ }
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Dispose Vera device handler ...");
+ if (mConfig != null && mConfig.getDeviceId() != null) {
+ mConfig.setDeviceId(null);
+ }
+ super.dispose();
+ }
+
+ @Override
+ public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
+ // Only called if status ONLINE or OFFLINE
+ logger.debug("Vera 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
+ scheduler.execute(new Initializer());
+ }
+ }
+
+ private void updateLabelAndLocation(String label, String location) {
+ if (!label.equals(thing.getLabel()) || !location.equals(thing.getLocation())) {
+ logger.debug("Set location to {}", location);
+ ThingBuilder thingBuilder = editThing();
+ if (!label.equals(thing.getLabel())) {
+ thingBuilder.withLabel(thing.getLabel());
+ }
+ if (!location.equals(thing.getLocation())) {
+ thingBuilder.withLocation(location);
+ }
+ updateThing(thingBuilder.build());
+ }
+ }
+
+ private class DevicePolling implements Runnable {
+ @Override
+ public void run() {
+ for (Channel channel : getThing().getChannels()) {
+ if (isLinked(channel.getUID().getId())) {
+ refreshChannel(channel);
+ } else {
+ logger.debug("Polling for device: {} not possible (channel {} not linked", thing.getLabel(),
+ channel.getLabel());
+ }
+ }
+ }
+ };
+
+ protected void refreshAllChannels() {
+ scheduler.execute(new DevicePolling());
+ }
+
+ private void refreshChannel(Channel channel) {
+ VeraBridgeHandler veraBridgeHandler = getVeraBridgeHandler();
+ if (veraBridgeHandler == null || !veraBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ logger.debug("Vera bridge handler not found or not ONLINE.");
+ return;
+ }
+
+ // Check device id associated with channel
+ String deviceId = channel.getProperties().get(DEVICE_CONFIG_ID);
+ if (deviceId == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
+ "Not found deviceId for channel: " + channel.getChannelTypeUID());
+ logger.debug("Vera device disconnected: {}", deviceId);
+ return;
+ }
+
+ Device device = veraBridgeHandler.getController().getDevice(deviceId);
+ if (device == null) {
+ updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Channel refresh for device: " + deviceId
+ + " with channel: " + channel.getChannelTypeUID() + " failed!");
+ logger.debug("Vera device disconnected: {}", deviceId);
+ return;
+ }
+
+ updateLabelAndLocation(device.getName(), device.getRoomName());
+ updateState(channel.getUID(), VeraDeviceStateConverter.toState(device, channel, logger));
+ ThingStatusInfo statusInfo = veraBridgeHandler.getThing().getStatusInfo();
+ updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
+ }
+
+ @Override
+ public void channelLinked(ChannelUID channelUID) {
+ logger.debug("Vera device channel linked: {}", channelUID);
+ VeraBridgeHandler veraBridgeHandler = getVeraBridgeHandler();
+ if (veraBridgeHandler == null || !veraBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ logger.debug("Vera bridge handler not found or not ONLINE.");
+ return;
+ }
+ super.channelLinked(channelUID);
+ }
+
+ @Override
+ public void channelUnlinked(ChannelUID channelUID) {
+ logger.debug("Vera device channel unlinked: {}", channelUID);
+ VeraBridgeHandler veraBridgeHandler = getVeraBridgeHandler();
+ if (veraBridgeHandler == null || !veraBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ logger.debug("Vera 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());
+
+ VeraBridgeHandler veraBridgeHandler = getVeraBridgeHandler();
+ if (veraBridgeHandler == null || !veraBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ logger.debug("Vera bridge handler not found or not ONLINE.");
+ return;
+ }
+
+ final Channel channel = getThing().getChannel(channelUID.getId());
+ final String deviceId = channel.getProperties().get("deviceId");
+
+ if (deviceId != null) {
+ Device device = veraBridgeHandler.getController().getDevice(deviceId);
+ if (device != null) {
+ if (command instanceof RefreshType) {
+ logger.debug("Handle command: RefreshType");
+ refreshChannel(channel);
+ } else {
+ if (command instanceof PercentType) {
+ logger.debug("Handle command: PercentType");
+ veraBridgeHandler.getController().setDimLevel(device, ((PercentType) command).toString());
+ }
+ if (command instanceof DecimalType) {
+ logger.debug("Handle command: DecimalType");
+ veraBridgeHandler.getController().setDimLevel(device, ((DecimalType) command).toString());
+ }
+ if (command instanceof OnOffType) {
+ logger.debug("Handle command: OnOffType");
+ if (command.equals(OnOffType.ON)) {
+ veraBridgeHandler.getController().turnDeviceOn(device);
+ } else if (command.equals(OnOffType.OFF)) {
+ veraBridgeHandler.getController().turnDeviceOff(device);
+ }
+ } else if (command instanceof OpenClosedType) {
+ logger.debug("Handle command: OpenClosedType");
+ if (command.equals(OpenClosedType.CLOSED)) {
+ veraBridgeHandler.getController().turnDeviceOn(device);
+ } else if (command.equals(OpenClosedType.OPEN)) {
+ veraBridgeHandler.getController().turnDeviceOff(device);
+ }
+ } else {
+ logger.warn("Unknown command type: {}, {}, {}, {}", command, deviceId, device.getCategory(),
+ device.getCategoryType());
+ }
+ }
+ } else {
+ logger.warn("Device {} not loaded", deviceId);
+ }
+ }
+ }
+
+ protected synchronized void addDeviceAsChannel(Device device, boolean homekitIntegration) {
+ if (device != null) {
+ logger.debug("Add device as channel: {}", device.getName());
+
+ HashMap properties = new HashMap<>();
+ properties.put("deviceId", device.getId());
+
+ String id = null;
+ String acceptedItemType = "";
+ String tag = null;
+
+ int subcategory = Integer.parseInt(device.getSubcategory());
+ switch (device.getCategoryType()) {
+ case Controller:
+ case Interface:
+ break;
+ case DimmableLight:
+ switch (subcategory) {
+ case 1:
+ case 2:
+ case 3:
+ id = SWITCH_MULTILEVEL_CHANNEL;
+ acceptedItemType = "Dimmer";
+ tag = "Lighting";
+ break;
+ case 4:
+ id = SWITCH_COLOR_CHANNEL;
+ acceptedItemType = "Color";
+ tag = "Lighting";
+ break;
+ }
+ break;
+ case Switch:
+ id = SWITCH_BINARY_CHANNEL;
+ acceptedItemType = "Switch";
+ tag = "Switchable";
+ break;
+ case SecuritySensor:
+ switch (subcategory) {
+ case 1:
+ id = SENSOR_DOOR_WINDOW_CHANNEL;
+ acceptedItemType = "Contact";
+ break;
+ case 2:
+ id = SENSOR_FLOOD_CHANNEL;
+ acceptedItemType = "Switch";
+ case 3:
+ id = SENSOR_MOTION_CHANNEL;
+ acceptedItemType = "Switch";
+ break;
+ case 4:
+ id = SENSOR_SMOKE_CHANNEL;
+ acceptedItemType = "Switch";
+ break;
+ case 5:
+ id = SENSOR_CO_CHANNEL;
+ acceptedItemType = "Switch";
+ break;
+ case 6:
+ id = SENSOR_BINARY_CHANNEL;
+ acceptedItemType = "Switch";
+ break;
+ }
+ break;
+ case HVAC: // TODO
+ logger.warn("TODO: {}, {}", device, device.getCategoryType());
+ break;
+ case DoorLock:
+ id = DOORLOCK_CHANNEL;
+ acceptedItemType = "Switch";
+ tag = "Switchable";
+ break;
+ case WindowCovering:
+ id = SWITCH_ROLLERSHUTTER_CHANNEL;
+ acceptedItemType = "Rollershutter";
+ break;
+ case GenericSensor:
+ id = SENSOR_BINARY_CHANNEL;
+ acceptedItemType = "Switch";
+ break;
+ case SceneController:
+ id = SWITCH_BINARY_CHANNEL;
+ acceptedItemType = "Switch";
+ break;
+ case HumiditySensor:
+ id = SENSOR_HUMIDITY_CHANNEL;
+ acceptedItemType = "Number";
+ tag = "CurrentHumidity";
+ break;
+ case TemperatureSensor:
+ id = SENSOR_TEMPERATURE_CHANNEL;
+ acceptedItemType = "Number";
+ tag = "CurrentTemperature";
+ break;
+ case LightSensor:
+ id = SENSOR_LUMINOSITY_CHANNEL;
+ acceptedItemType = "Number";
+ break;
+ case PowerMeter:
+ id = SENSOR_ENERGY_CHANNEL;
+ acceptedItemType = "Number";
+ break;
+ case UVSensor:
+ id = SENSOR_ULTRAVIOLET_CHANNEL;
+ acceptedItemType = "Number";
+ break;
+ case Camera:
+ case RemoteControl:
+ case IRTransmitter:
+ case GenericIO:
+ case SerialPort:
+ case AV:
+ case ZWaveInterface:
+ case InsteonInterface:
+ case AlarmPanel:
+ case AlarmPartition:
+ case Siren:
+ case Weather:
+ case PhilipsController:
+ case Appliance:
+ logger.warn("TODO: {}, {}", device, device.getCategoryType());
+ break;
+ case Unknown:
+ logger.warn("Unknown device type: {}, {}", device, device.getCategory());
+ break;
+ }
+
+ if (id != null) {
+ addChannel(id, acceptedItemType, device.getName(), properties, homekitIntegration ? tag : null);
+
+ logger.debug("Channel for device added with channel id: {}, accepted item type: {} and title: {}", id,
+ acceptedItemType, device.getName());
+
+ if (device.getBatterylevel() != null && !device.getBatterylevel().isEmpty()) {
+ addChannel(BATTERY_CHANNEL, "Number", "Battery", properties, null);
+ }
+
+ if (device.getKwh() != null) {
+ addChannel(SENSOR_METER_KWH_CHANNEL, "Number", "Energy ALL", properties, null);
+ }
+
+ if (device.getWatts() != null) {
+ addChannel(SENSOR_METER_W_CHANNEL, "Number", "Energy Current", properties, null);
+ }
+ } else {
+ // Thing status will not be updated because thing could have more than one channel
+ logger.warn("No channel for device added: {}", device);
+ }
+ }
+ }
+
+ private synchronized void addChannel(String id, String acceptedItemType, String label,
+ HashMap properties, String tag) {
+ String channelId = id + "-" + properties.get("deviceId");
+ boolean channelExists = false;
+ // Check if a channel for this device exist.
+ List channels = getThing().getChannels();
+ for (Channel channel : channels) {
+ if (channelId.equals(channel.getUID().getId())) {
+ channelExists = true;
+ }
+ }
+ if (!channelExists) {
+ ThingBuilder thingBuilder = editThing();
+ ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, id);
+ ChannelBuilder channelBuilder = ChannelBuilder.create(new ChannelUID(getThing().getUID(), channelId),
+ acceptedItemType);
+ channelBuilder.withType(channelTypeUID);
+ channelBuilder.withLabel(label);
+ channelBuilder.withProperties(properties);
+ if (tag != null) {
+ Set tags = new HashSet();
+ tags.add(tag);
+ channelBuilder.withDefaultTags(tags);
+ }
+ thingBuilder.withChannel(channelBuilder.build());
+ thingBuilder.withLabel(thing.getLabel());
+ updateThing(thingBuilder.build());
+ }
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/handler/VeraSceneHandler.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/handler/VeraSceneHandler.java
new file mode 100644
index 0000000000000..2a14aeab8a2d7
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/handler/VeraSceneHandler.java
@@ -0,0 +1,294 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.handler;
+
+import static org.openhab.binding.vera.VeraBindingConstants.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.smarthome.core.library.types.OnOffType;
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.Channel;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.ThingStatusInfo;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder;
+import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder;
+import org.eclipse.smarthome.core.thing.type.ChannelTypeUID;
+import org.eclipse.smarthome.core.types.Command;
+import org.eclipse.smarthome.core.types.RefreshType;
+import org.openhab.binding.vera.config.VeraSceneConfiguration;
+import org.openhab.binding.vera.controller.json.Scene;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link VeraSceneHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraSceneHandler extends BaseThingHandler {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+
+ private VeraSceneConfiguration mConfig;
+
+ public VeraSceneHandler(Thing thing) {
+ super(thing);
+ }
+
+ private class Initializer implements Runnable {
+ @Override
+ public void run() {
+ try {
+ VeraBridgeHandler veraBridgeHandler = getVeraBridgeHandler();
+ if (veraBridgeHandler != null && veraBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ ThingStatusInfo statusInfo = veraBridgeHandler.getThing().getStatusInfo();
+ logger.debug("Change scene status to bridge status: {}", statusInfo);
+
+ // Set thing status to bridge status
+ updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
+
+ logger.debug("Add channels");
+ Scene scene = veraBridgeHandler.getController().getScene(mConfig.getSceneId());
+ if (scene != null) {
+ logger.debug("Found {} scene", scene.getName());
+ updateLabelAndLocation(scene.getName(), scene.getRoomName());
+ addSceneAsChannel(scene);
+ }
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
+ "Controller is not online");
+ }
+ } catch (Exception e) {
+ logger.error("Error occurred when adding scene as channel: {}", e);
+ if (getThing().getStatus() == ThingStatus.ONLINE) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
+ "Error occurred when adding scene as channel: " + e.getMessage());
+ }
+ }
+ }
+ };
+
+ protected synchronized VeraBridgeHandler getVeraBridgeHandler() {
+ Bridge bridge = getBridge();
+ if (bridge == null) {
+ return null;
+ }
+ ThingHandler handler = bridge.getHandler();
+ if (handler instanceof VeraBridgeHandler) {
+ return (VeraBridgeHandler) handler;
+ } else {
+ return null;
+ }
+ }
+
+ private VeraSceneConfiguration loadAndCheckConfiguration() {
+ VeraSceneConfiguration config = getConfigAs(VeraSceneConfiguration.class);
+ if (config.getSceneId() == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "Couldn't create scene, sceneId is missing.");
+ return null;
+ }
+ return config;
+ }
+
+ @Override
+ public void initialize() {
+ logger.debug("Initializing Vera scene handler ...");
+ updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING,
+ "Checking configuration and bridge...");
+ mConfig = loadAndCheckConfiguration();
+ if (mConfig != null) {
+ logger.debug("Configuration complete: {}", mConfig);
+ scheduler.schedule(new Initializer(), 2, TimeUnit.SECONDS);
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "SceneId required!");
+ }
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Dispose Vera scene handler ...");
+ if (mConfig.getSceneId() != null) {
+ mConfig.setSceneId(null);
+ }
+ super.dispose();
+ }
+
+ @Override
+ public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
+ // Only called if status ONLINE or OFFLINE
+ logger.debug("Vera 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 scene thing will be ONLINE
+ scheduler.execute(new Initializer());
+ }
+ }
+
+ private void updateLabelAndLocation(String label, String location) {
+ if (!label.equals(thing.getLabel()) || !location.equals(thing.getLocation())) {
+ logger.debug("Set location to {}", location);
+ ThingBuilder thingBuilder = editThing();
+ if (!label.equals(thing.getLabel())) {
+ thingBuilder.withLabel(thing.getLabel());
+ }
+ if (!location.equals(thing.getLocation())) {
+ thingBuilder.withLocation(location);
+ }
+ updateThing(thingBuilder.build());
+ }
+ }
+
+ private class ScenePolling implements Runnable {
+ @Override
+ public void run() {
+ for (Channel channel : getThing().getChannels()) {
+ if (isLinked(channel.getUID().getId())) {
+ refreshChannel(channel);
+ } else {
+ logger.debug("Polling for scene: {} not possible (channel {} not linked", thing.getLabel(),
+ channel.getLabel());
+ }
+ }
+ }
+ };
+
+ protected void refreshAllChannels() {
+ scheduler.execute(new ScenePolling());
+ }
+
+ private void refreshChannel(Channel channel) {
+ VeraBridgeHandler veraBridgeHandler = getVeraBridgeHandler();
+ if (veraBridgeHandler == null || !veraBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ logger.debug("Vera bridge handler not found or not ONLINE.");
+ return;
+ }
+
+ // Check scene id associated with channel
+ String sceneId = channel.getProperties().get(SCENE_CONFIG_ID);
+ if (sceneId == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
+ "Not found sceneId for channel: " + channel.getChannelTypeUID());
+ logger.debug("Vera scene disconnected: {}", thing.getLabel());
+ return;
+ }
+
+ Scene scene = veraBridgeHandler.getController().getScene(sceneId);
+ if (scene == null) {
+ updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Channel refresh for sceneId: " + sceneId
+ + " with channel: " + channel.getChannelTypeUID() + " failed!");
+ logger.debug("Vera scene disconnected: {}", sceneId);
+ return;
+ }
+
+ updateLabelAndLocation(scene.getName(), scene.getRoomName());
+ ThingStatusInfo statusInfo = veraBridgeHandler.getThing().getStatusInfo();
+ updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
+ }
+
+ @Override
+ public void channelLinked(ChannelUID channelUID) {
+ logger.debug("Vera scene channel linked: {}", channelUID);
+ VeraBridgeHandler veraBridgeHandler = getVeraBridgeHandler();
+ if (veraBridgeHandler == null || !veraBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ logger.debug("Vera bridge handler not found or not ONLINE.");
+ return;
+ }
+ super.channelLinked(channelUID);
+ }
+
+ @Override
+ public void channelUnlinked(ChannelUID channelUID) {
+ logger.debug("Vera scene channel unlinked: {}", channelUID);
+ VeraBridgeHandler veraBridgeHandler = getVeraBridgeHandler();
+ if (veraBridgeHandler == null || !veraBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ logger.debug("Vera 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());
+
+ VeraBridgeHandler veraBridgeHandler = getVeraBridgeHandler();
+ if (veraBridgeHandler == null || !veraBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ logger.debug("Vera bridge handler not found or not ONLINE.");
+ return;
+ }
+
+ final Channel channel = getThing().getChannel(channelUID.getId());
+ final String sceneId = channel.getProperties().get(SCENE_CONFIG_ID);
+ if (sceneId != null) {
+ if (command instanceof RefreshType) {
+ logger.debug("Handle command: RefreshType");
+ refreshChannel(channel);
+ } else {
+ if (command instanceof OnOffType) {
+ if (command == OnOffType.ON) {
+ logger.debug("Handle command: OnOffType");
+ veraBridgeHandler.getController().runScene(sceneId);
+ updateState(channelUID, OnOffType.OFF);
+ }
+ } else {
+ logger.warn("Unknown command type: {}, {}, {}, {}", command, sceneId);
+ }
+ }
+ } else {
+ logger.warn("Not found sceneId {}", sceneId);
+ }
+ }
+
+ protected synchronized void addSceneAsChannel(Scene scene) {
+ if (scene != null) {
+ logger.debug("Add scene as channel: {}", scene.getName());
+
+ HashMap properties = new HashMap<>();
+ properties.put(SCENE_CONFIG_ID, scene.getId());
+
+ addChannel("sceneButton", "Switch", "Run scene", properties);
+ }
+ }
+
+ private synchronized void addChannel(String id, String acceptedItemType, String label,
+ HashMap properties) {
+ String channelId = id + "-" + properties.get(SCENE_CONFIG_ID);
+ boolean channelExists = false;
+ // Check if a channel for this scene exist.
+ List channels = getThing().getChannels();
+ for (Channel channel : channels) {
+ if (channelId.equals(channel.getUID().getId())) {
+ channelExists = true;
+ }
+ }
+ if (!channelExists) {
+ ThingBuilder thingBuilder = editThing();
+ ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, id);
+ ChannelBuilder channelBuilder = ChannelBuilder.create(new ChannelUID(getThing().getUID(), channelId),
+ acceptedItemType);
+ channelBuilder.withType(channelTypeUID);
+ channelBuilder.withLabel(label);
+ channelBuilder.withProperties(properties);
+ thingBuilder.withChannel(channelBuilder.build());
+ thingBuilder.withLabel(thing.getLabel());
+ updateThing(thingBuilder.build());
+ }
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/VeraHandlerFactory.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/VeraHandlerFactory.java
new file mode 100644
index 0000000000000..3308112ef2549
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/VeraHandlerFactory.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.internal;
+
+import static org.openhab.binding.vera.VeraBindingConstants.*;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.eclipse.smarthome.config.discovery.DiscoveryService;
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.ThingUID;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.openhab.binding.vera.handler.VeraBridgeHandler;
+import org.openhab.binding.vera.handler.VeraDeviceHandler;
+import org.openhab.binding.vera.handler.VeraSceneHandler;
+import org.openhab.binding.vera.internal.discovery.VeraDeviceDiscoveryService;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * The {@link VeraHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraHandlerFactory extends BaseThingHandlerFactory {
+ private Map> discoveryServiceRegs = new HashMap<>();
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_DEVICE_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected ThingHandler createHandler(Thing thing) {
+ if (THING_TYPE_BRIDGE.equals(thing.getThingTypeUID())) {
+ VeraBridgeHandler handler = new VeraBridgeHandler((Bridge) thing);
+ registerDeviceDiscoveryService(handler);
+ return handler;
+ } else if (THING_TYPE_DEVICE.equals(thing.getThingTypeUID())) {
+ return new VeraDeviceHandler(thing);
+ } else if (THING_TYPE_SCENE.equals(thing.getThingTypeUID())) {
+ return new VeraSceneHandler(thing);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected synchronized void removeHandler(ThingHandler thingHandler) {
+ if (thingHandler instanceof VeraBridgeHandler) {
+ ServiceRegistration> serviceReg = this.discoveryServiceRegs.get(thingHandler.getThing().getUID());
+ if (serviceReg != null) {
+ serviceReg.unregister();
+ discoveryServiceRegs.remove(thingHandler.getThing().getUID());
+ }
+ }
+ }
+
+ private void registerDeviceDiscoveryService(VeraBridgeHandler handler) {
+ VeraDeviceDiscoveryService discoveryService = new VeraDeviceDiscoveryService(handler);
+ this.discoveryServiceRegs.put(handler.getThing().getUID(), bundleContext
+ .registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable()));
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/converter/VeraDeviceStateConverter.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/converter/VeraDeviceStateConverter.java
new file mode 100644
index 0000000000000..ac5ead6d65938
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/converter/VeraDeviceStateConverter.java
@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.internal.converter;
+
+import static org.openhab.binding.vera.VeraBindingConstants.*;
+
+import org.eclipse.smarthome.core.library.types.DecimalType;
+import org.eclipse.smarthome.core.library.types.OnOffType;
+import org.eclipse.smarthome.core.library.types.OpenClosedType;
+import org.eclipse.smarthome.core.library.types.PercentType;
+import org.eclipse.smarthome.core.thing.Channel;
+import org.eclipse.smarthome.core.types.State;
+import org.eclipse.smarthome.core.types.UnDefType;
+import org.openhab.binding.vera.controller.json.Device;
+import org.slf4j.Logger;
+
+/**
+ * The {@link VeraDeviceStateConverter} is responsible for converting Vera device level to openHAB states
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraDeviceStateConverter {
+ public static State toState(Device device, Channel channel, Logger logger) {
+
+ String channelType = channel.getUID().getId().split("-")[0];
+ if (BATTERY_CHANNEL.equals(channelType)) {
+ return getMultilevelState(device.getBatterylevel());
+ }
+ if (SENSOR_METER_KWH_CHANNEL.equals(channelType)) {
+ return getMultilevelState(device.getKwh());
+ }
+ if (SENSOR_METER_W_CHANNEL.equals(channelType)) {
+ return getMultilevelState(device.getWatts());
+ }
+
+ int subcategory = Integer.parseInt(device.getSubcategory());
+ switch (device.getCategoryType()) {
+ case Controller:
+ case Interface:
+ break;
+ case DimmableLight:
+ switch (subcategory) {
+ case 1:
+ case 2:
+ case 3:
+ return getPercentState(device.getLevel());
+ case 4:
+ return getPercentState(device.getLevel());// TODO getColorState(device.light);
+ }
+ break;
+ case Switch:
+ return getBinaryState(device.getStatus());
+ case SecuritySensor:
+ switch (subcategory) {
+ case 1:
+ return getDoorlockState("1".equals(device.getTripped()) ? "0" : "1");
+ default:
+ return getBinaryState(device.getTripped());
+ }
+ case HVAC: // TODO
+ logger.warn("TODO: {}, {}", device, device.getCategoryType());
+ break;
+ case DoorLock:
+ return getBinaryState(device.getLocked());
+ case WindowCovering:
+ return getPercentState(device.getLevel());
+ case GenericSensor:
+ return getMultilevelState(device.getLevel());
+ case SceneController:
+ return getBinaryState(device.getStatus());
+ case HumiditySensor:
+ return getMultilevelState(device.getHumidity());
+ case TemperatureSensor:
+ return getMultilevelState(device.getTemperature());
+ case LightSensor:
+ return getMultilevelState(device.getLight());
+ case PowerMeter:
+ return getMultilevelState(device.getLevel());
+ case UVSensor:
+ return getMultilevelState(device.getLevel());
+ case Camera:
+ case AV:
+ case ZWaveInterface:
+ case InsteonInterface:
+ case RemoteControl:
+ case IRTransmitter:
+ case GenericIO:
+ case SerialPort:
+ case AlarmPanel:
+ case AlarmPartition:
+ case Siren:
+ case Weather:
+ case PhilipsController:
+ case Appliance:
+ logger.warn("TODO: {}, {}", device, device.getCategoryType());
+ break;
+ case Unknown:
+ logger.warn("Unknown device type: {}, {}", device, device.getCategory());
+ break;
+ }
+ return UnDefType.UNDEF;
+ }
+
+ private static State getMultilevelState(String multilevelValue) {
+ if (multilevelValue != null) {
+ if (multilevelValue.isEmpty()) {
+ return new DecimalType("0");
+ }
+ return new DecimalType(multilevelValue);
+ }
+ return UnDefType.UNDEF;
+ }
+
+ private static State getPercentState(String multilevelValue) {
+ if (multilevelValue != null) {
+ if (multilevelValue.isEmpty()) {
+ return new PercentType("0");
+ }
+ return new PercentType(multilevelValue);
+ }
+ return UnDefType.UNDEF;
+ }
+
+ private static State getBinaryState(String status) {
+ if (status != null) {
+ if ("1".equals(status)) {
+ return OnOffType.ON;
+ } else if ("0".equals(status)) {
+ return OnOffType.OFF;
+ }
+ }
+ return UnDefType.UNDEF;
+ }
+
+ private static State getDoorlockState(String status) {
+ if (status != null) {
+ if ("1".equals(status)) {
+ return OpenClosedType.CLOSED;
+ } else if ("0".equals(status)) {
+ return OpenClosedType.OPEN;
+ }
+ }
+ return UnDefType.UNDEF;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/discovery/VeraBridgeDiscoveryService.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/discovery/VeraBridgeDiscoveryService.java
new file mode 100644
index 0000000000000..8664c1cc13a4f
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/discovery/VeraBridgeDiscoveryService.java
@@ -0,0 +1,154 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.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.eclipse.smarthome.config.discovery.AbstractDiscoveryService;
+import org.eclipse.smarthome.config.discovery.DiscoveryResult;
+import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder;
+import org.eclipse.smarthome.config.discovery.DiscoveryServiceCallback;
+import org.eclipse.smarthome.config.discovery.ExtendedDiscoveryService;
+import org.eclipse.smarthome.core.thing.ThingUID;
+import org.openhab.binding.vera.VeraBindingConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link VeraBridgeDiscoveryService} is responsible for device discovery.
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraBridgeDiscoveryService extends AbstractDiscoveryService implements ExtendedDiscoveryService {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private static final int SEARCH_TIME = 240;
+
+ private DiscoveryServiceCallback callback;
+
+ public VeraBridgeDiscoveryService() {
+ super(VeraBindingConstants.SUPPORTED_DEVICE_THING_TYPES_UIDS, SEARCH_TIME);
+ logger.debug("Initializing VeraBridgeDiscoveryService");
+ }
+
+ private void scan() {
+ logger.debug("Starting scan for Vera controller");
+
+ ValidateIPV4 validator = new ValidateIPV4();
+
+ try {
+ Enumeration 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 Vera Controller: {}", ipAddress);
+
+ String subnet = ipAddress + "/" + prefix;
+ SubnetUtils utils = new SubnetUtils(subnet);
+ String[] addresses = utils.getInfo().getAllAddresses();
+
+ for (String addressInSubnet : addresses) {
+ scheduler.execute(new VeraControllerScan(addressInSubnet));
+ }
+ }
+ }
+ }
+ }
+ } catch (SocketException e) {
+ logger.warn("Error occurred while searching Vera controller: ", e);
+ }
+ }
+
+ 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 VeraControllerScan implements Runnable {
+ private String ipAddress;
+
+ public VeraControllerScan(String ipAddress) {
+ this.ipAddress = ipAddress;
+ }
+
+ @Override
+ public void run() {
+ if (!pingHost(ipAddress, 3480, 100)) {
+ return;
+ }
+
+ try {
+ URL url = new URL("http://" + ipAddress + ":3480/data_request?id=sdata&output_format=json");
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+ if (connection.getResponseCode() == 200) {
+ ThingUID thingUID = new ThingUID(VeraBindingConstants.THING_TYPE_BRIDGE,
+ ipAddress.replaceAll("\\.", "_"));
+
+ if (callback != null && callback.getExistingDiscoveryResult(thingUID) == null
+ && callback.getExistingThing(thingUID) == null) {
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
+ .withProperty(VeraBindingConstants.BRIDGE_CONFIG_VERA_SERVER_IP_ADDRESS, ipAddress)
+ .withLabel("Vera controller " + ipAddress).build();
+ thingDiscovered(discoveryResult);
+ } else {
+ logger.debug("Thing or inbox entry already exists for UID={}, IP={}", thingUID, ipAddress);
+ }
+ }
+ } catch (IOException 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();
+ }
+ }
+
+ @Override
+ public void setDiscoveryServiceCallback(DiscoveryServiceCallback discoveryServiceCallback) {
+ callback = discoveryServiceCallback;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/discovery/VeraDeviceDiscoveryService.java b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/discovery/VeraDeviceDiscoveryService.java
new file mode 100644
index 0000000000000..e6313357588f2
--- /dev/null
+++ b/addons/binding/org.openhab.binding.vera/src/main/java/org/openhab/binding/vera/internal/discovery/VeraDeviceDiscoveryService.java
@@ -0,0 +1,140 @@
+/**
+ * Copyright (c) 2010-2017 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.vera.internal.discovery;
+
+import static org.openhab.binding.vera.VeraBindingConstants.*;
+
+import java.util.List;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService;
+import org.eclipse.smarthome.config.discovery.DiscoveryResult;
+import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder;
+import org.eclipse.smarthome.config.discovery.DiscoveryServiceCallback;
+import org.eclipse.smarthome.config.discovery.ExtendedDiscoveryService;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingUID;
+import org.openhab.binding.vera.controller.CategoryType;
+import org.openhab.binding.vera.controller.json.Device;
+import org.openhab.binding.vera.controller.json.Scene;
+import org.openhab.binding.vera.handler.VeraBridgeHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link VeraDeviceDiscoveryService} is responsible for device discovery.
+ *
+ * @author Dmitriy Ponomarev
+ */
+public class VeraDeviceDiscoveryService extends AbstractDiscoveryService implements ExtendedDiscoveryService {
+
+ 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 VeraBridgeHandler mBridgeHandler;
+ private VeraDeviceScan mVeraDeviceScanningRunnable;
+ private ScheduledFuture> mVeraDeviceScanningJob;
+
+ private DiscoveryServiceCallback callback;
+
+ public VeraDeviceDiscoveryService(VeraBridgeHandler bridgeHandler) {
+ super(SUPPORTED_DEVICE_THING_TYPES_UIDS, SEARCH_TIME);
+ logger.debug("Initializing VeraDeviceDiscoveryService");
+ mBridgeHandler = bridgeHandler;
+ mVeraDeviceScanningRunnable = new VeraDeviceScan();
+ activate(null);
+ }
+
+ private void scan() {
+ logger.debug("Starting scan on Vera Controller {}", mBridgeHandler.getThing().getUID());
+
+ if (mBridgeHandler == null || !mBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
+ logger.debug("Vera bridge handler not found or not ONLINE.");
+ return;
+ }
+
+ final ThingUID bridgeUID = mBridgeHandler.getThing().getUID();
+
+ List deviceList = mBridgeHandler.getData().getDevices();
+ for (Device device : deviceList) {
+ if (device.getCategoryType().equals(CategoryType.Controller)
+ || device.getCategoryType().equals(CategoryType.Interface)) {
+ continue;
+ }
+ ThingUID thingUID = new ThingUID(THING_TYPE_DEVICE, mBridgeHandler.getThing().getUID(), device.getId());
+ if (callback != null && callback.getExistingDiscoveryResult(thingUID) == null
+ && callback.getExistingThing(thingUID) == null) {
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(device.getName())
+ .withBridge(bridgeUID).withProperty(DEVICE_CONFIG_ID, device.getId())
+ .withProperty(DEVICE_PROP_CATEGORY, device.getCategory())
+ .withProperty(DEVICE_PROP_SUBCATEGORY, device.getSubcategory()).build();
+ thingDiscovered(discoveryResult);
+ logger.debug("Vera device found: {}, {}", device.getId(), device.getName());
+ }
+ }
+
+ List sceneList = mBridgeHandler.getData().getScenes();
+ for (Scene scene : sceneList) {
+ ThingUID thingUID = new ThingUID(THING_TYPE_SCENE, mBridgeHandler.getThing().getUID(), scene.getId());
+ if (callback != null && callback.getExistingDiscoveryResult(thingUID) == null
+ && callback.getExistingThing(thingUID) == null) {
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(scene.getName())
+ .withBridge(bridgeUID).withProperty(SCENE_CONFIG_ID, scene.getId()).build();
+ thingDiscovered(discoveryResult);
+ logger.debug("Vera scene found: {}, {}", scene.getId(), scene.getName());
+ }
+ }
+ }
+
+ @Override
+ protected void startScan() {
+ scan();
+ }
+
+ @Override
+ protected synchronized void stopScan() {
+ super.stopScan();
+ removeOlderResults(getTimestampOfLastScan());
+ }
+
+ @Override
+ protected void startBackgroundDiscovery() {
+ if (mVeraDeviceScanningJob == null || mVeraDeviceScanningJob.isCancelled()) {
+ logger.debug("Starting background scanning job");
+ mVeraDeviceScanningJob = AbstractDiscoveryService.scheduler.scheduleWithFixedDelay(
+ mVeraDeviceScanningRunnable, INITIAL_DELAY, SCAN_INTERVAL, TimeUnit.SECONDS);
+ } else {
+ logger.debug("Scanning job is allready active");
+ }
+ }
+
+ @Override
+ protected void stopBackgroundDiscovery() {
+ if (mVeraDeviceScanningJob != null && !mVeraDeviceScanningJob.isCancelled()) {
+ mVeraDeviceScanningJob.cancel(false);
+ mVeraDeviceScanningJob = null;
+ }
+ }
+
+ public class VeraDeviceScan implements Runnable {
+ @Override
+ public void run() {
+ scan();
+ }
+ }
+
+ @Override
+ public void setDiscoveryServiceCallback(DiscoveryServiceCallback discoveryServiceCallback) {
+ callback = discoveryServiceCallback;
+ }
+}
diff --git a/addons/binding/pom.xml b/addons/binding/pom.xml
index 93a46b50cc1f5..a9b8125eff8b6 100644
--- a/addons/binding/pom.xml
+++ b/addons/binding/pom.xml
@@ -95,6 +95,7 @@
org.openhab.binding.tplinksmarthomeorg.openhab.binding.tplinksmarthome.testorg.openhab.binding.urtsi
+ org.openhab.binding.veraorg.openhab.binding.vitotronicorg.openhab.binding.wifiledorg.openhab.binding.windcentrale
diff --git a/features/openhab-addons/src/main/feature/feature.xml b/features/openhab-addons/src/main/feature/feature.xml
index 51e981caf0c43..a2a106f749641 100644
--- a/features/openhab-addons/src/main/feature/feature.xml
+++ b/features/openhab-addons/src/main/feature/feature.xml
@@ -400,6 +400,11 @@
mvn:org.openhab.binding/org.openhab.binding.toon/${project.version}
+
+ openhab-runtime-base
+ mvn:org.openhab.binding/org.openhab.binding.vera/${project.version}
+
+
openhab-runtime-basemvn:org.openhab.binding/org.openhab.binding.tplinksmarthome/${project.version}