mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[touchwand] Touchwand Binding initial contribution - migration to OH3 (#8754)
* initial migration to OH 3 Signed-off-by: Roie Geron <roie.geron@gmail.com>
This commit is contained in:
parent
de6390501b
commit
7cd5510a72
@ -242,6 +242,7 @@
|
|||||||
/bundles/org.openhab.binding.tellstick/ @jarlebh
|
/bundles/org.openhab.binding.tellstick/ @jarlebh
|
||||||
/bundles/org.openhab.binding.tesla/ @kgoderis
|
/bundles/org.openhab.binding.tesla/ @kgoderis
|
||||||
/bundles/org.openhab.binding.tibber/ @kjoglum
|
/bundles/org.openhab.binding.tibber/ @kjoglum
|
||||||
|
/bundles/org.openhab.binding.touchwand /@roieg
|
||||||
/bundles/org.openhab.binding.tplinksmarthome/ @Hilbrand
|
/bundles/org.openhab.binding.tplinksmarthome/ @Hilbrand
|
||||||
/bundles/org.openhab.binding.tradfri/ @cweitkamp @kaikreuzer
|
/bundles/org.openhab.binding.tradfri/ @cweitkamp @kaikreuzer
|
||||||
/bundles/org.openhab.binding.unifi/ @mgbowman
|
/bundles/org.openhab.binding.unifi/ @mgbowman
|
||||||
|
13
bundles/org.openhab.binding.touchwand/NOTICE
Normal file
13
bundles/org.openhab.binding.touchwand/NOTICE
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
This content is produced and maintained by the openHAB project.
|
||||||
|
|
||||||
|
* Project home: https://www.openhab.org
|
||||||
|
|
||||||
|
== Declared Project Licenses
|
||||||
|
|
||||||
|
This program and the accompanying materials are made available under the terms
|
||||||
|
of the Eclipse Public License 2.0 which is available at
|
||||||
|
https://www.eclipse.org/legal/epl-2.0/.
|
||||||
|
|
||||||
|
== Source Code
|
||||||
|
|
||||||
|
https://github.com/openhab/openhab-addons
|
81
bundles/org.openhab.binding.touchwand/README.md
Normal file
81
bundles/org.openhab.binding.touchwand/README.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# TouchWand Binding
|
||||||
|
|
||||||
|
Touchwand Wanderfull™ Hub basic is a plug & play Z-Wave based controller that uses Wi-Fi and Bluetooth to easily connect all smart home components.
|
||||||
|
TouchWand products are compatible with most major Z-Wave products, IP controlled devices and KNX devices, providing the ideal solution for building all-inclusive full-featured smart homes.
|
||||||
|
[TouchWand.com](http://www.touchwand.com)
|
||||||
|
|
||||||
|
![Touchwand Wanderfull™ Hub](http://www.touchwand.com/wp-content/uploads/2017/12/hub-toch-1.png)
|
||||||
|
|
||||||
|
## Supported Things
|
||||||
|
|
||||||
|
This binding supports switches, shutters dimmers and wall controllers configured in Touchwand Wanderfull™ Hub Controller.
|
||||||
|
|
||||||
|
## Control
|
||||||
|
|
||||||
|
1. **switch** - control - ON/OFF
|
||||||
|
2. **shutter** - control - UP/DOWN/STOP
|
||||||
|
3. **dimmer** - control - ON/OFF/BRIGHTNESS
|
||||||
|
4. **wallcontroller** - control - LONG/SHORT
|
||||||
|
|
||||||
|
## Discovery
|
||||||
|
|
||||||
|
After adding TouchWand Hub the auto discovery will add all switches dimmers and shutters to the inbox.
|
||||||
|
|
||||||
|
## Bridge Configuration
|
||||||
|
|
||||||
|
**Touchwand Wanderfull™** Hub Controller need to be added manually by IP address. The controller requires **username** and **password**
|
||||||
|
|
||||||
|
| Parameter | Description | Units | required |
|
||||||
|
|-------------------|-----------------------------------------------------------------------|---------|----------|
|
||||||
|
| username | Touchwand hub username | string | yes |
|
||||||
|
| password | Touchwand hub password | string | yes |
|
||||||
|
| ipAddress | Touchwand hub hotname or IP address | string | yes |
|
||||||
|
| port | Management port (default 80) | integer | no |
|
||||||
|
| statusrefresh | Unit status refresh interval in seconds | integer | no |
|
||||||
|
| addSecondaryUnits | If the controller is primary, add secondary controllers units as well | bool | no |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Thing Configuration
|
||||||
|
|
||||||
|
No thing configuration is needed
|
||||||
|
|
||||||
|
## Full Example
|
||||||
|
|
||||||
|
### touchwand.things
|
||||||
|
|
||||||
|
Things can be defined manually
|
||||||
|
The syntax for touchwand this is
|
||||||
|
|
||||||
|
```xtend
|
||||||
|
Thing <binding_id>:<type_id>:<thing_id> "Label" @ "Location"
|
||||||
|
```
|
||||||
|
|
||||||
|
Where <thing_id> is the unit id in touchwand hub.
|
||||||
|
|
||||||
|
```
|
||||||
|
Bridge touchwand:bridge:1921681116 [ipAddress="192.168.1.116", username="username" , password="password"]{
|
||||||
|
Thing switch 408 "Strairs light"
|
||||||
|
Thing switch 411 "South Garden light"
|
||||||
|
Thing dimmer 415 "Living Room Ceiling dimmer"
|
||||||
|
Thing switch 418 "Kitchen light"
|
||||||
|
Thing shutter 345 "Living Room North shutter"
|
||||||
|
Thing shutter 346 "Living Room South shutter"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### touchwand.items
|
||||||
|
|
||||||
|
```
|
||||||
|
/* Shutters */
|
||||||
|
Rollershutter Rollershutter_345 "Living Room North shutter" {channel="touchwand:shutter:1921681116:345:shutter"}
|
||||||
|
Rollershutter Rollershutter_346 "Living Room South shutter" {channel="touchwand:shutter:1921681116:346:shutter"}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
/* Switches and Dimmers */
|
||||||
|
Switch Switch_408 "Strairs light" {channel="touchwand:switch:1921681116:408:switch"}
|
||||||
|
Switch Switch_411 "South Garden light" {channel="touchwand:switch:1921681116:411:switch"}
|
||||||
|
Dimmer Switch_415 "Living Room Ceiling dimmer" {channel="touchwand:switch:1921681116:415:switch"}
|
||||||
|
Switch Switch_418 "South Garden light" {channel="touchwand:switch:1921681116:418:switch"}
|
||||||
|
```
|
15
bundles/org.openhab.binding.touchwand/pom.xml
Normal file
15
bundles/org.openhab.binding.touchwand/pom.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||||
|
<version>3.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>org.openhab.binding.touchwand</artifactId>
|
||||||
|
<name>openHAB Add-ons :: Bundles :: TouchWand Binding</name>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<features name="org.openhab.binding.touchwand-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||||
|
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||||
|
|
||||||
|
<feature name="openhab-binding-touchwand" description="TouchWand Binding" version="${project.version}">
|
||||||
|
<feature>openhab-runtime-base</feature>
|
||||||
|
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.touchwand/${project.version}</bundle>
|
||||||
|
</feature>
|
||||||
|
</features>
|
@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.*;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
|
||||||
|
import org.openhab.core.thing.Bridge;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingStatus;
|
||||||
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.RefreshType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandBaseUnitHandler} is responsible for handling commands and status updates
|
||||||
|
* for TouchWand units. This is an abstract class , units should implement the specific command
|
||||||
|
* handling and status updates.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public abstract class TouchWandBaseUnitHandler extends BaseThingHandler implements TouchWandUnitUpdateListener {
|
||||||
|
|
||||||
|
protected final Logger logger = LoggerFactory.getLogger(TouchWandBaseUnitHandler.class);
|
||||||
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_SHUTTER, THING_TYPE_SWITCH,
|
||||||
|
THING_TYPE_WALLCONTROLLER, THING_TYPE_DIMMER);
|
||||||
|
protected String unitId = "";
|
||||||
|
|
||||||
|
protected @Nullable TouchWandBridgeHandler bridgeHandler;
|
||||||
|
|
||||||
|
public TouchWandBaseUnitHandler(Thing thing) {
|
||||||
|
super(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
if (command instanceof RefreshType) {
|
||||||
|
// updateTouchWandUnitState(getUnitState(unitId));
|
||||||
|
} else {
|
||||||
|
touchWandUnitHandleCommand(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
TouchWandBridgeHandler myTmpBridgeHandler = bridgeHandler;
|
||||||
|
if (myTmpBridgeHandler != null) {
|
||||||
|
myTmpBridgeHandler.unregisterUpdateListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
Bridge bridge = getBridge();
|
||||||
|
if (bridge == null || !(bridge.getHandler() instanceof TouchWandBridgeHandler)) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||||
|
logger.warn("Trying to initialize {} without a bridge", getThing().getUID());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bridgeHandler = (TouchWandBridgeHandler) bridge.getHandler();
|
||||||
|
|
||||||
|
unitId = getThing().getProperties().get(HANDLER_PROPERTIES_ID); // TouchWand unit id
|
||||||
|
|
||||||
|
TouchWandBridgeHandler myTmpBridgeHandler = bridgeHandler;
|
||||||
|
if (myTmpBridgeHandler != null) {
|
||||||
|
myTmpBridgeHandler.registerUpdateListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
|
scheduler.execute(() -> {
|
||||||
|
boolean thingReachable = false;
|
||||||
|
if (myTmpBridgeHandler != null) {
|
||||||
|
String response = myTmpBridgeHandler.touchWandClient.cmdGetUnitById(unitId);
|
||||||
|
thingReachable = !response.isEmpty();
|
||||||
|
if (thingReachable) {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused") // not used at the moment till touchWand state in hub will be fixed
|
||||||
|
private int getUnitState(String unitId) {
|
||||||
|
int status = 0;
|
||||||
|
|
||||||
|
TouchWandBridgeHandler touchWandBridgeHandler = bridgeHandler;
|
||||||
|
if (touchWandBridgeHandler == null) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
String response = touchWandBridgeHandler.touchWandClient.cmdGetUnitById(unitId);
|
||||||
|
if (!response.isEmpty()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonParser jsonParser = new JsonParser();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JsonObject unitObj = jsonParser.parse(response).getAsJsonObject();
|
||||||
|
status = unitObj.get("currStatus").getAsInt();
|
||||||
|
if (!this.getThing().getStatusInfo().getStatus().equals(ThingStatus.ONLINE)) {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
}
|
||||||
|
} catch (JsonParseException | IllegalStateException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Could not parse cmdGetUnitById:" + e.getMessage());
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract void touchWandUnitHandleCommand(Command command);
|
||||||
|
|
||||||
|
abstract void updateTouchWandUnitState(TouchWandUnitData unitData);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemStatusUpdate(TouchWandUnitData unitData) {
|
||||||
|
if (unitData.getStatus().equals("ALIVE")) {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
} else {
|
||||||
|
// updateStatus(ThingStatus.OFFLINE); // comment - OFFLINE status is not accurate at the moment
|
||||||
|
}
|
||||||
|
updateTouchWandUnitState(unitData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return unitId;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandBindingConstants} class defines common constants, which are
|
||||||
|
* used across the whole binding.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandBindingConstants {
|
||||||
|
|
||||||
|
public static final String BINDING_ID = "touchwand";
|
||||||
|
public static final String DISCOVERY_THREAD_ID = "OH-binding-touchwand-discovery";
|
||||||
|
|
||||||
|
// List of all Thing Type UIDs
|
||||||
|
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
|
||||||
|
public static final ThingTypeUID THING_TYPE_SWITCH = new ThingTypeUID(BINDING_ID, "switch");
|
||||||
|
public static final ThingTypeUID THING_TYPE_SHUTTER = new ThingTypeUID(BINDING_ID, "shutter");
|
||||||
|
public static final ThingTypeUID THING_TYPE_WALLCONTROLLER = new ThingTypeUID(BINDING_ID, "wallcontroller");
|
||||||
|
public static final ThingTypeUID THING_TYPE_DIMMER = new ThingTypeUID(BINDING_ID, "dimmer");
|
||||||
|
public static final ThingTypeUID THING_TYPE_ALARMSENSOR = new ThingTypeUID(BINDING_ID, "AlarmSensor"); // TBD
|
||||||
|
|
||||||
|
// List of all Channel ids
|
||||||
|
public static final String CHANNEL_SWITCH = "switch";
|
||||||
|
public static final String CHANNEL_SHUTTER = "shutter";
|
||||||
|
public static final String CHANNEL_DIMMER = "brightness";
|
||||||
|
public static final String CHANNEL_ALARM = "alarm";
|
||||||
|
public static final String CHANNEL_WALLCONTROLLER_ACTION = "wallaction";
|
||||||
|
public static final String CHANNEL_BATTERY_LEVEL = "battery_level";
|
||||||
|
public static final String CHANNEL_BATTERY_LOW = "battery_low";
|
||||||
|
|
||||||
|
// List of configuration parameters
|
||||||
|
|
||||||
|
public static final String HOST = "ipAddress";
|
||||||
|
public static final String PORT = "port";
|
||||||
|
public static final String USER = "username";
|
||||||
|
public static final String PASS = "password";
|
||||||
|
public static final String STATUS_REFRESH_TIME = "statusrefresh";
|
||||||
|
public static final String ADD_SECONDARY_UNITS = "addSecondaryUnits";
|
||||||
|
|
||||||
|
// Unit handler properties
|
||||||
|
|
||||||
|
public static final String HANDLER_PROPERTIES_ID = "id";
|
||||||
|
public static final String HANDLER_PROPERTIES_NAME = "name";
|
||||||
|
|
||||||
|
// Connectivity options
|
||||||
|
|
||||||
|
public static final String CONNECTIVITY_KNX = "knx";
|
||||||
|
public static final String CONNECTIVITY_ZWAVE = "zwave";
|
||||||
|
|
||||||
|
// commands
|
||||||
|
public static final String SWITCH_STATUS_ON = "255";
|
||||||
|
public static final String SWITCH_STATUS_OFF = "0";
|
||||||
|
|
||||||
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_SWITCH);
|
||||||
|
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_SHUTTER);
|
||||||
|
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_WALLCONTROLLER);
|
||||||
|
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_DIMMER);
|
||||||
|
// SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_ALARMSENSOR); // not implemented yet
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String TYPE_WALLCONTROLLER = "WallController";
|
||||||
|
public static final String TYPE_SWITCH = "Switch";
|
||||||
|
public static final String TYPE_SHUTTER = "shutter";
|
||||||
|
public static final String TYPE_DIMMER = "dimmer";
|
||||||
|
public static final String TYPE_ALARMSENSOR = "AlarmSensor";
|
||||||
|
|
||||||
|
public static final String[] SUPPORTED_TOUCHWAND_TYPES = { TYPE_WALLCONTROLLER, TYPE_SWITCH, TYPE_SHUTTER,
|
||||||
|
TYPE_DIMMER, TYPE_ALARMSENSOR };
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.THING_TYPE_BRIDGE;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.openhab.binding.touchwand.internal.config.TouchwandBridgeConfiguration;
|
||||||
|
import org.openhab.binding.touchwand.internal.discovery.TouchWandUnitDiscoveryService;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
|
||||||
|
import org.openhab.core.thing.Bridge;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.thing.ThingStatus;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.osgi.framework.BundleContext;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandBridgeHandler} is responsible for handling commands, which are
|
||||||
|
* sent to one of the channels TouchWand Wanderfull™ Hub channels .
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandBridgeHandler extends BaseBridgeHandler implements TouchWandUnitStatusUpdateListener {
|
||||||
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_BRIDGE);
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(TouchWandBridgeHandler.class);
|
||||||
|
private int statusRefreshRateSec;
|
||||||
|
private boolean addSecondaryUnits;
|
||||||
|
private @Nullable TouchWandWebSockets touchWandWebSockets;
|
||||||
|
private Map<String, TouchWandUnitUpdateListener> unitUpdateListeners = new ConcurrentHashMap<>();
|
||||||
|
private volatile boolean isRunning = false;
|
||||||
|
|
||||||
|
public TouchWandRestClient touchWandClient;
|
||||||
|
|
||||||
|
public TouchWandBridgeHandler(Bridge bridge, HttpClient httpClient, BundleContext bundleContext) {
|
||||||
|
super(bridge);
|
||||||
|
touchWandClient = new TouchWandRestClient(httpClient);
|
||||||
|
touchWandWebSockets = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void initialize() {
|
||||||
|
String host;
|
||||||
|
Integer port;
|
||||||
|
TouchwandBridgeConfiguration config;
|
||||||
|
|
||||||
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
|
|
||||||
|
config = getConfigAs(TouchwandBridgeConfiguration.class);
|
||||||
|
|
||||||
|
host = config.ipAddress;
|
||||||
|
port = config.port;
|
||||||
|
statusRefreshRateSec = config.statusrefresh;
|
||||||
|
addSecondaryUnits = config.addSecondaryUnits;
|
||||||
|
|
||||||
|
isRunning = true;
|
||||||
|
|
||||||
|
scheduler.execute(() -> {
|
||||||
|
boolean thingReachable = false;
|
||||||
|
String password = config.password;
|
||||||
|
String username = config.username;
|
||||||
|
thingReachable = touchWandClient.connect(username, password, host, port.toString());
|
||||||
|
if (thingReachable) {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
synchronized (this) {
|
||||||
|
if (isRunning) {
|
||||||
|
TouchWandWebSockets localSockets = touchWandWebSockets = new TouchWandWebSockets(host,
|
||||||
|
scheduler);
|
||||||
|
localSockets.registerListener(this);
|
||||||
|
localSockets.connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.OFFLINE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAddSecondaryControllerUnits() {
|
||||||
|
return addSecondaryUnits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStatusRefreshTime() {
|
||||||
|
return statusRefreshRateSec;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void dispose() {
|
||||||
|
isRunning = false;
|
||||||
|
TouchWandWebSockets myTouchWandWebSockets = touchWandWebSockets;
|
||||||
|
if (myTouchWandWebSockets != null) {
|
||||||
|
myTouchWandWebSockets.unregisterListener(this);
|
||||||
|
myTouchWandWebSockets.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean registerUpdateListener(TouchWandUnitUpdateListener listener) {
|
||||||
|
logger.debug("Adding Status update listener for device {}", listener.getId());
|
||||||
|
unitUpdateListeners.put(listener.getId(), listener);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean unregisterUpdateListener(TouchWandUnitUpdateListener listener) {
|
||||||
|
logger.debug("Remove Status update listener for device {}", listener.getId());
|
||||||
|
unitUpdateListeners.remove(listener.getId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDataReceived(TouchWandUnitData unitData) {
|
||||||
|
if (unitUpdateListeners.containsKey(unitData.getId().toString())) {
|
||||||
|
TouchWandUnitUpdateListener updateListener = unitUpdateListeners.get(unitData.getId().toString());
|
||||||
|
updateListener.onItemStatusUpdate(unitData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||||
|
return Collections.singleton(TouchWandUnitDiscoveryService.class);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.CHANNEL_DIMMER;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandShutterSwitchUnitData;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
|
||||||
|
import org.openhab.core.library.types.OnOffType;
|
||||||
|
import org.openhab.core.library.types.PercentType;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandDimmerHandler} is responsible for handling commands for Dimmer units
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandDimmerHandler extends TouchWandBaseUnitHandler {
|
||||||
|
|
||||||
|
public TouchWandDimmerHandler(Thing thing) {
|
||||||
|
super(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void touchWandUnitHandleCommand(Command command) {
|
||||||
|
TouchWandBridgeHandler touchWandBridgeHandler = bridgeHandler;
|
||||||
|
if (touchWandBridgeHandler != null) {
|
||||||
|
if (command instanceof OnOffType) {
|
||||||
|
touchWandBridgeHandler.touchWandClient.cmdSwitchOnOff(unitId, (OnOffType) command);
|
||||||
|
} else {
|
||||||
|
touchWandBridgeHandler.touchWandClient.cmdDimmerPosition(unitId, command.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void updateTouchWandUnitState(TouchWandUnitData unitData) {
|
||||||
|
if (unitData instanceof TouchWandShutterSwitchUnitData) {
|
||||||
|
int status = ((TouchWandShutterSwitchUnitData) unitData).getCurrStatus();
|
||||||
|
PercentType state = PercentType.ZERO;
|
||||||
|
int convertStatus = status;
|
||||||
|
state = new PercentType(convertStatus);
|
||||||
|
updateState(CHANNEL_DIMMER, state);
|
||||||
|
} else {
|
||||||
|
logger.warn("updateTouchWandUnitState incompatible TouchWandUnitData instance");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.*;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
|
import org.openhab.core.thing.Bridge;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandHandlerFactory} is responsible for creating things and thing
|
||||||
|
* handlers.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Component(configurationPid = "binding.touchwand", service = ThingHandlerFactory.class)
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
|
||||||
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
|
||||||
|
.unmodifiableSet(Stream.concat(TouchWandBridgeHandler.SUPPORTED_THING_TYPES.stream(),
|
||||||
|
TouchWandBaseUnitHandler.SUPPORTED_THING_TYPES.stream()).collect(Collectors.toSet()));
|
||||||
|
|
||||||
|
private @NonNullByDefault({}) HttpClient httpClient;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
|
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||||
|
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||||
|
|
||||||
|
if (THING_TYPE_BRIDGE.equals(thingTypeUID)) {
|
||||||
|
return new TouchWandBridgeHandler((Bridge) thing, httpClient, bundleContext);
|
||||||
|
} else if (THING_TYPE_SWITCH.equals(thingTypeUID)) {
|
||||||
|
return new TouchWandSwitchHandler(thing);
|
||||||
|
} else if (THING_TYPE_SHUTTER.equals(thingTypeUID)) {
|
||||||
|
return new TouchWandShutterHandler(thing);
|
||||||
|
} else if (THING_TYPE_WALLCONTROLLER.equals(thingTypeUID)) {
|
||||||
|
return new TouchWandWallControllerHandler(thing);
|
||||||
|
} else if (THING_TYPE_DIMMER.equals(thingTypeUID)) {
|
||||||
|
return new TouchWandDimmerHandler(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Reference
|
||||||
|
protected void setHttpClientFactory(HttpClientFactory httpClientFactory) {
|
||||||
|
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void unsetHttpClientFactory(HttpClientFactory httpClientFactory) {
|
||||||
|
this.httpClient = null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.*;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.CookieManager;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.api.ContentProvider;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
import org.eclipse.jetty.http.MimeTypes;
|
||||||
|
import org.openhab.core.library.types.OnOffType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandRestClient} is responsible for handling low level commands units TouchWand WonderFull hub
|
||||||
|
* REST API interface
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandRestClient {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(TouchWandRestClient.class);
|
||||||
|
|
||||||
|
static CookieManager cookieManager = new CookieManager();
|
||||||
|
|
||||||
|
private static final HttpMethod METHOD_GET = HttpMethod.GET;
|
||||||
|
private static final HttpMethod METHOD_POST = HttpMethod.POST;
|
||||||
|
|
||||||
|
private static final String CMD_LOGIN = "login";
|
||||||
|
private static final String CMD_LIST_UNITS = "listunits";
|
||||||
|
private static final String CMD_LIST_SCENARIOS = "listsencarios";
|
||||||
|
private static final String CMD_UNIT_ACTION = "action";
|
||||||
|
private static final String CMD_GET_UNIT_BY_ID = "getunitbyid";
|
||||||
|
|
||||||
|
private static final String ACTION_SWITCH_OFF = "{\"id\":%s,\"value\":" + SWITCH_STATUS_OFF + "}";
|
||||||
|
private static final String ACTION_SWITCH_ON = "{\"id\":%s,\"value\":" + SWITCH_STATUS_ON + "}";
|
||||||
|
private static final String ACTION_SHUTTER_DOWN = "{\"id\":%s,\"value\":0,\"type\":\"height\"}";
|
||||||
|
private static final String ACTION_SHUTTER_UP = "{\"id\":%s,\"value\":255,\"type\":\"height\"}";
|
||||||
|
private static final String ACTION_SHUTTER_STOP = "{\"id\":%s,\"value\":0,\"type\":\"stop\"}";
|
||||||
|
private static final String ACTION_SHUTTER_POSITION = "{\"id\":%s,\"value\":%s}";
|
||||||
|
private static final String ACTION_DIMMER_POSITION = "{\"id\":%s,\"value\":%s}";
|
||||||
|
|
||||||
|
private static final String CONTENT_TYPE_APPLICATION_JSON = MimeTypes.Type.APPLICATION_JSON.asString();
|
||||||
|
|
||||||
|
private static final int REQUEST_TIMEOUT_SEC = 10;
|
||||||
|
|
||||||
|
private static final Map<String, String> COMMAND_MAP = new HashMap<String, String>();
|
||||||
|
static {
|
||||||
|
COMMAND_MAP.put(CMD_LOGIN, "/auth/login?");
|
||||||
|
COMMAND_MAP.put(CMD_LIST_UNITS, "/units/listUnits");
|
||||||
|
COMMAND_MAP.put(CMD_LIST_SCENARIOS, "/scenarios/listScenarios");
|
||||||
|
COMMAND_MAP.put(CMD_UNIT_ACTION, "/units/action");
|
||||||
|
COMMAND_MAP.put(CMD_GET_UNIT_BY_ID, "/units/getUnitByID?");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String touchWandIpAddr = "";
|
||||||
|
private String touchWandPort = "";
|
||||||
|
private boolean isConnected = false;
|
||||||
|
private HttpClient httpClient;
|
||||||
|
|
||||||
|
public TouchWandRestClient(HttpClient httpClient) {
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean connect(String user, String pass, String ipAddr, String port) {
|
||||||
|
touchWandIpAddr = ipAddr;
|
||||||
|
touchWandPort = port;
|
||||||
|
isConnected = cmdLogin(user, pass, ipAddr);
|
||||||
|
|
||||||
|
return isConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final boolean cmdLogin(String user, String pass, String ipAddr) {
|
||||||
|
String encodedUser;
|
||||||
|
String encodedPass;
|
||||||
|
String response = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
encodedUser = URLEncoder.encode(user, StandardCharsets.UTF_8.toString());
|
||||||
|
encodedPass = URLEncoder.encode(pass, StandardCharsets.UTF_8.toString());
|
||||||
|
String command = buildUrl(CMD_LOGIN) + "user=" + encodedUser + "&" + "psw=" + encodedPass;
|
||||||
|
response = sendCommand(command, METHOD_GET, "");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
logger.warn("Error url encoding username or password : {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return !response.equals("Unauthorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String cmdListUnits() {
|
||||||
|
String command = buildUrl(CMD_LIST_UNITS);
|
||||||
|
String response = sendCommand(command, METHOD_GET, "");
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String cmdGetUnitById(String id) {
|
||||||
|
String command = buildUrl(CMD_GET_UNIT_BY_ID) + "id=" + id;
|
||||||
|
String response = sendCommand(command, METHOD_GET, "");
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cmdSwitchOnOff(String id, OnOffType onoff) {
|
||||||
|
String action;
|
||||||
|
|
||||||
|
if (OnOffType.OFF.equals(onoff)) {
|
||||||
|
action = String.format(ACTION_SWITCH_OFF, id);
|
||||||
|
} else {
|
||||||
|
action = String.format(ACTION_SWITCH_ON, id);
|
||||||
|
}
|
||||||
|
cmdUnitAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cmdShutterUp(String id) {
|
||||||
|
String action = String.format(ACTION_SHUTTER_UP, id);
|
||||||
|
cmdUnitAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cmdShutterDown(String id) {
|
||||||
|
String action = String.format(ACTION_SHUTTER_DOWN, id);
|
||||||
|
cmdUnitAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cmdShutterPosition(String id, String position) {
|
||||||
|
String action = String.format(ACTION_SHUTTER_POSITION, id, position);
|
||||||
|
cmdUnitAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cmdShutterStop(String id) {
|
||||||
|
String action = String.format(ACTION_SHUTTER_STOP, id);
|
||||||
|
cmdUnitAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cmdDimmerPosition(String id, String position) {
|
||||||
|
String action = String.format(ACTION_DIMMER_POSITION, id, position);
|
||||||
|
cmdUnitAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String cmdUnitAction(String action) {
|
||||||
|
String command = buildUrl(CMD_UNIT_ACTION);
|
||||||
|
String response = sendCommand(command, METHOD_POST, action);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildUrl(String command) {
|
||||||
|
String url = "http://" + touchWandIpAddr + ":" + touchWandPort + COMMAND_MAP.get(command);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized String sendCommand(String command, HttpMethod method, String content) {
|
||||||
|
ContentResponse response;
|
||||||
|
Request request;
|
||||||
|
|
||||||
|
URL url = null;
|
||||||
|
try {
|
||||||
|
url = new URL(command);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
logger.warn("Error building URL {} : {}", command, e.getMessage());
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
request = httpClient.newRequest(url.toString()).timeout(REQUEST_TIMEOUT_SEC, TimeUnit.SECONDS).method(method);
|
||||||
|
if (method.equals(METHOD_POST) && (!content.isEmpty())) {
|
||||||
|
ContentProvider contentProvider = new StringContentProvider(CONTENT_TYPE_APPLICATION_JSON, content,
|
||||||
|
StandardCharsets.UTF_8);
|
||||||
|
request = request.content(contentProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
response = request.send();
|
||||||
|
return response.getContentAsString();
|
||||||
|
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||||
|
logger.warn("Error opening connecton to {} : {} ", touchWandIpAddr, e.getMessage());
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.CHANNEL_SHUTTER;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandShutterSwitchUnitData;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
|
||||||
|
import org.openhab.core.library.types.PercentType;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandShutterHandler} is responsible for handling commands for Shutter units
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandShutterHandler extends TouchWandBaseUnitHandler {
|
||||||
|
|
||||||
|
public TouchWandShutterHandler(Thing thing) {
|
||||||
|
super(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void touchWandUnitHandleCommand(Command command) {
|
||||||
|
TouchWandBridgeHandler touchWandBridgeHandler = bridgeHandler;
|
||||||
|
if (touchWandBridgeHandler != null) {
|
||||||
|
switch (command.toString()) {
|
||||||
|
case "OFF":
|
||||||
|
case "DOWN":
|
||||||
|
touchWandBridgeHandler.touchWandClient.cmdShutterDown(unitId);
|
||||||
|
break;
|
||||||
|
case "ON":
|
||||||
|
case "UP":
|
||||||
|
touchWandBridgeHandler.touchWandClient.cmdShutterUp(unitId);
|
||||||
|
break;
|
||||||
|
case "STOP":
|
||||||
|
touchWandBridgeHandler.touchWandClient.cmdShutterStop(unitId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
touchWandBridgeHandler.touchWandClient.cmdShutterPosition(unitId, command.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void updateTouchWandUnitState(TouchWandUnitData unitData) {
|
||||||
|
if (unitData instanceof TouchWandShutterSwitchUnitData) {
|
||||||
|
int status = ((TouchWandShutterSwitchUnitData) unitData).getCurrStatus();
|
||||||
|
PercentType state = PercentType.ZERO;
|
||||||
|
int convertStatus = 100 - status;
|
||||||
|
state = new PercentType(convertStatus);
|
||||||
|
updateState(CHANNEL_SHUTTER, state);
|
||||||
|
} else {
|
||||||
|
logger.warn("updateTouchWandUnitState incompatible TouchWandUnitData instance");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.*;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandShutterSwitchUnitData;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
|
||||||
|
import org.openhab.core.library.types.OnOffType;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandSwitchHandler} is responsible for handling command for Switch unit
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandSwitchHandler extends TouchWandBaseUnitHandler {
|
||||||
|
|
||||||
|
public TouchWandSwitchHandler(Thing thing) {
|
||||||
|
super(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void updateTouchWandUnitState(TouchWandUnitData unitData) {
|
||||||
|
if (unitData instanceof TouchWandShutterSwitchUnitData) {
|
||||||
|
OnOffType state;
|
||||||
|
int status = ((TouchWandShutterSwitchUnitData) unitData).getCurrStatus();
|
||||||
|
String sStatus = Integer.toString(status);
|
||||||
|
|
||||||
|
if (sStatus.equals(SWITCH_STATUS_OFF)) {
|
||||||
|
state = OnOffType.OFF;
|
||||||
|
} else if ((status >= 1) && (status <= 255)) {
|
||||||
|
state = OnOffType.ON;
|
||||||
|
} else {
|
||||||
|
logger.warn("updateTouchWandUnitState illegal update value {}", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateState(CHANNEL_SWITCH, state);
|
||||||
|
} else {
|
||||||
|
logger.warn("updateTouchWandUnitState incompatible TouchWandUnitData instance");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void touchWandUnitHandleCommand(Command command) {
|
||||||
|
if (command instanceof OnOffType) {
|
||||||
|
TouchWandBridgeHandler touchWandBridgeHandler = bridgeHandler;
|
||||||
|
if (touchWandBridgeHandler != null) {
|
||||||
|
touchWandBridgeHandler.touchWandClient.cmdSwitchOnOff(unitId, (OnOffType) command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for a listener on the {@link TouchWandWebSocket}.
|
||||||
|
* When it is registered on the socket, it gets called back when {@link TouchWandWebSocket} receives data.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface TouchWandUnitStatusUpdateListener {
|
||||||
|
|
||||||
|
void onDataReceived(TouchWandUnitData unitData);
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for Unit updates.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface TouchWandUnitUpdateListener {
|
||||||
|
|
||||||
|
void onItemStatusUpdate(TouchWandUnitData unitData);
|
||||||
|
|
||||||
|
String getId();
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.CHANNEL_WALLCONTROLLER_ACTION;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitDataWallController;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandWallControllerHandler} is responsible for handling commands and triggers
|
||||||
|
*
|
||||||
|
* for WallController units
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandWallControllerHandler extends TouchWandBaseUnitHandler {
|
||||||
|
|
||||||
|
private long timeSinceLastEventMs;
|
||||||
|
private static final int ADJUSTENT_EVENT_FILTER_TIME_MILLISEC = 2000; // 2 seconds
|
||||||
|
|
||||||
|
public TouchWandWallControllerHandler(Thing thing) {
|
||||||
|
super(thing);
|
||||||
|
timeSinceLastEventMs = Instant.now().toEpochMilli();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void touchWandUnitHandleCommand(Command command) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void updateTouchWandUnitState(TouchWandUnitData unitData) {
|
||||||
|
int status = ((TouchWandUnitDataWallController) unitData).getCurrStatus();
|
||||||
|
long timeDiff = Instant.now().toEpochMilli() - timeSinceLastEventMs;
|
||||||
|
if ((timeDiff) > ADJUSTENT_EVENT_FILTER_TIME_MILLISEC) {
|
||||||
|
String action = status <= 100 ? "SHORT" : "LONG";
|
||||||
|
triggerChannel(CHANNEL_WALLCONTROLLER_ACTION, action);
|
||||||
|
}
|
||||||
|
timeSinceLastEventMs = Instant.now().toEpochMilli();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.touchwand.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.SUPPORTED_TOUCHWAND_TYPES;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||||
|
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||||
|
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitFromJson;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandWebSockets} class implements WebSockets API to TouchWand controller
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandWebSockets {
|
||||||
|
|
||||||
|
private static final int CONNECT_TIMEOUT_SEC = 10;
|
||||||
|
private static final int WEBSOCKET_RECONNECT_INTERVAL_SEC = CONNECT_TIMEOUT_SEC * 2;
|
||||||
|
private static final int WEBSOCKET_IDLE_TIMEOUT_MS = CONNECT_TIMEOUT_SEC * 10 * 1000;
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(TouchWandWebSockets.class);
|
||||||
|
private static final String WS_ENDPOINT_TOUCHWAND = "/async";
|
||||||
|
|
||||||
|
private WebSocketClient client;
|
||||||
|
private String controllerAddress;
|
||||||
|
private TouchWandSocket touchWandSocket;
|
||||||
|
private boolean isShutDown = false;
|
||||||
|
private CopyOnWriteArraySet<TouchWandUnitStatusUpdateListener> listeners = new CopyOnWriteArraySet<>();
|
||||||
|
private @Nullable ScheduledFuture<?> socketReconnect;
|
||||||
|
private @Nullable URI uri;
|
||||||
|
|
||||||
|
private ScheduledExecutorService scheduler;
|
||||||
|
|
||||||
|
public TouchWandWebSockets(String ipAddress, ScheduledExecutorService scheduler) {
|
||||||
|
client = new WebSocketClient();
|
||||||
|
touchWandSocket = new TouchWandSocket();
|
||||||
|
this.controllerAddress = ipAddress;
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
socketReconnect = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect() {
|
||||||
|
try {
|
||||||
|
uri = new URI("ws://" + controllerAddress + WS_ENDPOINT_TOUCHWAND);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
logger.warn("URI not valid {} message {}", uri, e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.setConnectTimeout(CONNECT_TIMEOUT_SEC);
|
||||||
|
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||||
|
request.setSubProtocols("relay_protocol");
|
||||||
|
|
||||||
|
try {
|
||||||
|
client.start();
|
||||||
|
client.connect(touchWandSocket, uri, request);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Could not connect webSocket URI {} message {}", uri, e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
isShutDown = true;
|
||||||
|
try {
|
||||||
|
client.stop();
|
||||||
|
ScheduledFuture<?> mySocketReconnect = socketReconnect;
|
||||||
|
if (mySocketReconnect != null) {
|
||||||
|
mySocketReconnect.cancel(true);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Could not stop webSocketClient, message {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerListener(TouchWandUnitStatusUpdateListener listener) {
|
||||||
|
if (!listeners.contains(listener)) {
|
||||||
|
logger.debug("Adding TouchWandWebSocket listener {}", listener);
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterListener(TouchWandUnitStatusUpdateListener listener) {
|
||||||
|
logger.debug("Removing TouchWandWebSocket listener {}", listener);
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@WebSocket(maxIdleTime = WEBSOCKET_IDLE_TIMEOUT_MS)
|
||||||
|
public class TouchWandSocket {
|
||||||
|
|
||||||
|
@OnWebSocketClose
|
||||||
|
public void onClose(int statusCode, String reason) {
|
||||||
|
logger.debug("Connection closed: {} - {}", statusCode, reason);
|
||||||
|
if (!isShutDown) {
|
||||||
|
logger.debug("weSocket Closed - reconnecting");
|
||||||
|
asyncWeb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketConnect
|
||||||
|
public void onConnect(Session session) {
|
||||||
|
logger.debug("TouchWandWebSockets connected to {}", session.getRemoteAddress().toString());
|
||||||
|
try {
|
||||||
|
session.getRemote().sendString("{\"myopenhab\": \"myopenhab\"}");
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.warn("sendString : {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketMessage
|
||||||
|
public void onMessage(String msg) {
|
||||||
|
TouchWandUnitData touchWandUnit;
|
||||||
|
JsonParser jsonParser = new JsonParser();
|
||||||
|
try {
|
||||||
|
JsonObject unitObj = jsonParser.parse(msg).getAsJsonObject();
|
||||||
|
boolean eventUnitChanged = unitObj.get("type").getAsString().equals("UNIT_CHANGED");
|
||||||
|
if (!eventUnitChanged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
touchWandUnit = TouchWandUnitFromJson.parseResponse(unitObj.get("unit").getAsJsonObject());
|
||||||
|
if (!touchWandUnit.getStatus().equals("ALIVE")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean supportedUnitType = Arrays.asList(SUPPORTED_TOUCHWAND_TYPES).contains(touchWandUnit.getType());
|
||||||
|
if (!supportedUnitType) {
|
||||||
|
logger.debug("UNIT_CHANGED for unsupported unit type {}", touchWandUnit.getType());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.debug("UNIT_CHANGED: name {} id {} status {}", touchWandUnit.getName(), touchWandUnit.getId(),
|
||||||
|
touchWandUnit.getCurrStatus());
|
||||||
|
|
||||||
|
for (TouchWandUnitStatusUpdateListener listener : listeners) {
|
||||||
|
listener.onDataReceived(touchWandUnit);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
logger.warn("jsonParser.parse {} ", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketError
|
||||||
|
public void onError(Throwable cause) {
|
||||||
|
logger.warn("WebSocket Error: {}", cause.getMessage());
|
||||||
|
if (!isShutDown) {
|
||||||
|
logger.debug("WebSocket onError - reconnecting");
|
||||||
|
asyncWeb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void asyncWeb() {
|
||||||
|
ScheduledFuture<?> mySocketReconnect = socketReconnect;
|
||||||
|
if (mySocketReconnect == null || mySocketReconnect.isDone()) {
|
||||||
|
socketReconnect = scheduler.schedule(TouchWandWebSockets.this::connect,
|
||||||
|
WEBSOCKET_RECONNECT_INTERVAL_SEC, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.touchwand.internal.config;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration class for {@link TouchwandBridgeHandler}.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchwandBridgeConfiguration {
|
||||||
|
public String username = "";
|
||||||
|
public String password = "";
|
||||||
|
public String ipAddress = "";
|
||||||
|
public int port;
|
||||||
|
public int statusrefresh;
|
||||||
|
public boolean addSecondaryUnits;
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal.discovery;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.THING_TYPE_BRIDGE;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.touchwand.internal.TouchWandBindingConstants;
|
||||||
|
import org.openhab.binding.touchwand.internal.TouchWandBridgeHandler;
|
||||||
|
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||||
|
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||||
|
import org.openhab.core.config.discovery.DiscoveryService;
|
||||||
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandControllerDiscoveryService} Discovery service for Touchwand Controllers.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
@Component(service = DiscoveryService.class, configurationPid = "discovery.touchwand")
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandControllerDiscoveryService extends AbstractDiscoveryService {
|
||||||
|
|
||||||
|
private static final int SEARCH_TIME_SEC = 2;
|
||||||
|
private static final int TOUCHWAND_BCAST_PORT = 35000;
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(TouchWandControllerDiscoveryService.class);
|
||||||
|
|
||||||
|
private @Nullable Thread socketReceiveThread = null;
|
||||||
|
private DatagramSocket listenSocket;
|
||||||
|
|
||||||
|
public TouchWandControllerDiscoveryService() throws SocketException {
|
||||||
|
super(TouchWandBridgeHandler.SUPPORTED_THING_TYPES, SEARCH_TIME_SEC, true);
|
||||||
|
|
||||||
|
listenSocket = new DatagramSocket(TOUCHWAND_BCAST_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startScan() {
|
||||||
|
DatagramSocket localListenSocket = listenSocket;
|
||||||
|
runReceiveThread(localListenSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stopScan() {
|
||||||
|
super.stopScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void activate(@Nullable Map<String, @Nullable Object> configProperties) {
|
||||||
|
removeOlderResults(getTimestampOfLastScan());
|
||||||
|
super.activate(configProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deactivate() {
|
||||||
|
Thread mySocketReceiveThread = socketReceiveThread;
|
||||||
|
if (mySocketReceiveThread != null) {
|
||||||
|
mySocketReceiveThread.interrupt();
|
||||||
|
socketReceiveThread = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
listenSocket.close();
|
||||||
|
super.deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDeviceDiscoveryResult(String label, String ip) {
|
||||||
|
String id = ip.replaceAll("\\.", "");
|
||||||
|
ThingUID thingUID = new ThingUID(THING_TYPE_BRIDGE, id);
|
||||||
|
Map<String, Object> properties = new HashMap<>();
|
||||||
|
properties.put("label", label);
|
||||||
|
properties.put("ipAddress", ip);
|
||||||
|
// @formatter:off
|
||||||
|
logger.debug("Add new Bridge label:{} id {} ",label, id);
|
||||||
|
thingDiscovered(DiscoveryResultBuilder.create(thingUID)
|
||||||
|
.withThingType(THING_TYPE_BRIDGE)
|
||||||
|
.withLabel(label)
|
||||||
|
.withProperties(properties)
|
||||||
|
.withRepresentationProperty("ipAddress")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void runReceiveThread(DatagramSocket socket) {
|
||||||
|
Thread localSocketReceivedThread = socketReceiveThread = new ReceiverThread(socket);
|
||||||
|
localSocketReceivedThread.setName(TouchWandBindingConstants.DISCOVERY_THREAD_ID);
|
||||||
|
localSocketReceivedThread.setDaemon(true);
|
||||||
|
localSocketReceivedThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReceiverThread extends Thread {
|
||||||
|
|
||||||
|
private static final int BUFFER_LENGTH = 256;
|
||||||
|
private DatagramPacket dgram = new DatagramPacket(new byte[BUFFER_LENGTH], BUFFER_LENGTH);
|
||||||
|
private DatagramSocket mySocket;
|
||||||
|
|
||||||
|
public ReceiverThread(DatagramSocket socket) {
|
||||||
|
mySocket = socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
receiveData(dgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void receiveData(DatagramPacket datagram) {
|
||||||
|
try {
|
||||||
|
while (!isInterrupted()) {
|
||||||
|
mySocket.receive(datagram);
|
||||||
|
InetAddress address = datagram.getAddress();
|
||||||
|
String sentence = new String(dgram.getData(), 0, dgram.getLength(), StandardCharsets.US_ASCII);
|
||||||
|
addDeviceDiscoveryResult(sentence, address.getHostAddress().toString());
|
||||||
|
logger.debug("Received Datagram from {}:{} on Port {} message {}", address.getHostAddress(),
|
||||||
|
dgram.getPort(), mySocket.getLocalPort(), sentence);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (!isInterrupted()) {
|
||||||
|
logger.warn("Error while receiving {}", e.getMessage());
|
||||||
|
} else {
|
||||||
|
logger.debug("Receiver thread was interrupted {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,223 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal.discovery;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.*;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.touchwand.internal.TouchWandBridgeHandler;
|
||||||
|
import org.openhab.binding.touchwand.internal.TouchWandUnitStatusUpdateListener;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
|
||||||
|
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitFromJson;
|
||||||
|
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||||
|
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||||
|
import org.openhab.core.config.discovery.DiscoveryService;
|
||||||
|
import org.openhab.core.thing.ThingStatus;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandUnitDiscoveryService} Discovery service for TouchWand units.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandUnitDiscoveryService extends AbstractDiscoveryService
|
||||||
|
implements DiscoveryService, ThingHandlerService {
|
||||||
|
|
||||||
|
private static final int SEARCH_TIME_SEC = 10;
|
||||||
|
private static final int SCAN_INTERVAL_SEC = 60;
|
||||||
|
private static final int LINK_DISCOVERY_SERVICE_INITIAL_DELAY_SEC = 5;
|
||||||
|
private static final String[] CONNECTIVITY_OPTIONS = { CONNECTIVITY_KNX, CONNECTIVITY_ZWAVE };
|
||||||
|
private @NonNullByDefault({}) TouchWandBridgeHandler touchWandBridgeHandler;
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(TouchWandUnitDiscoveryService.class);
|
||||||
|
|
||||||
|
private @Nullable ScheduledFuture<?> scanningJob;
|
||||||
|
private CopyOnWriteArraySet<TouchWandUnitStatusUpdateListener> listeners = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
|
public TouchWandUnitDiscoveryService() {
|
||||||
|
super(SUPPORTED_THING_TYPES_UIDS, SEARCH_TIME_SEC, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startScan() {
|
||||||
|
if (touchWandBridgeHandler.getThing().getStatus() != ThingStatus.ONLINE) {
|
||||||
|
logger.warn("Could not scan units while bridge offline");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Starting TouchWand discovery on bridge {}", touchWandBridgeHandler.getThing().getUID());
|
||||||
|
String response = touchWandBridgeHandler.touchWandClient.cmdListUnits();
|
||||||
|
if (response.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonParser jsonParser = new JsonParser();
|
||||||
|
try {
|
||||||
|
JsonArray jsonArray = jsonParser.parse(response).getAsJsonArray();
|
||||||
|
if (jsonArray.isJsonArray()) {
|
||||||
|
try {
|
||||||
|
for (JsonElement unit : jsonArray) {
|
||||||
|
TouchWandUnitData touchWandUnit;
|
||||||
|
touchWandUnit = TouchWandUnitFromJson.parseResponse(unit.getAsJsonObject());
|
||||||
|
if (touchWandUnit == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!touchWandBridgeHandler.isAddSecondaryControllerUnits()) {
|
||||||
|
if (!Arrays.asList(CONNECTIVITY_OPTIONS).contains(touchWandUnit.getConnectivity())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String type = touchWandUnit.getType();
|
||||||
|
if (!Arrays.asList(SUPPORTED_TOUCHWAND_TYPES).contains(type)) {
|
||||||
|
logger.debug("Unit discovery skipping unsupported unit type : {} ", type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case TYPE_WALLCONTROLLER:
|
||||||
|
addDeviceDiscoveryResult(touchWandUnit, THING_TYPE_WALLCONTROLLER);
|
||||||
|
break;
|
||||||
|
case TYPE_SWITCH:
|
||||||
|
addDeviceDiscoveryResult(touchWandUnit, THING_TYPE_SWITCH);
|
||||||
|
notifyListeners(touchWandUnit);
|
||||||
|
break;
|
||||||
|
case TYPE_DIMMER:
|
||||||
|
addDeviceDiscoveryResult(touchWandUnit, THING_TYPE_DIMMER);
|
||||||
|
notifyListeners(touchWandUnit);
|
||||||
|
break;
|
||||||
|
case TYPE_SHUTTER:
|
||||||
|
addDeviceDiscoveryResult(touchWandUnit, THING_TYPE_SHUTTER);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
logger.warn("Could not parse unit {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (JsonSyntaxException msg) {
|
||||||
|
logger.warn("Could not parse list units response {}", msg.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyListeners(TouchWandUnitData touchWandUnit) {
|
||||||
|
for (TouchWandUnitStatusUpdateListener listener : listeners) {
|
||||||
|
listener.onDataReceived(touchWandUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stopScan() {
|
||||||
|
removeOlderResults(getTimestampOfLastScan());
|
||||||
|
super.stopScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void activate() {
|
||||||
|
super.activate(null);
|
||||||
|
removeOlderResults(new Date().getTime(), touchWandBridgeHandler.getThing().getUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deactivate() {
|
||||||
|
removeOlderResults(new Date().getTime(), touchWandBridgeHandler.getThing().getUID());
|
||||||
|
super.deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startBackgroundDiscovery() {
|
||||||
|
ScheduledFuture<?> localScanningJob = scanningJob;
|
||||||
|
if (localScanningJob == null || localScanningJob.isCancelled()) {
|
||||||
|
scanningJob = scheduler.scheduleWithFixedDelay(this::startScan, LINK_DISCOVERY_SERVICE_INITIAL_DELAY_SEC,
|
||||||
|
SCAN_INTERVAL_SEC, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stopBackgroundDiscovery() {
|
||||||
|
ScheduledFuture<?> myScanningJob = scanningJob;
|
||||||
|
if (myScanningJob != null) {
|
||||||
|
myScanningJob.cancel(true);
|
||||||
|
scanningJob = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerListener(TouchWandUnitStatusUpdateListener listener) {
|
||||||
|
if (!listeners.contains(listener)) {
|
||||||
|
logger.debug("Adding TouchWandWebSocket listener {}", listener);
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterListener(TouchWandUnitStatusUpdateListener listener) {
|
||||||
|
logger.debug("Removing TouchWandWebSocket listener {}", listener);
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getScanTimeout() {
|
||||||
|
return SEARCH_TIME_SEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDeviceDiscoveryResult(TouchWandUnitData unit, ThingTypeUID typeUID) {
|
||||||
|
ThingUID bridgeUID = touchWandBridgeHandler.getThing().getUID();
|
||||||
|
ThingUID thingUID = new ThingUID(typeUID, bridgeUID, unit.getId().toString());
|
||||||
|
Map<String, Object> properties = new HashMap<>();
|
||||||
|
properties.put(HANDLER_PROPERTIES_ID, unit.getId().toString());
|
||||||
|
properties.put(HANDLER_PROPERTIES_NAME, unit.getName());
|
||||||
|
// @formatter:off
|
||||||
|
thingDiscovered(DiscoveryResultBuilder.create(thingUID)
|
||||||
|
.withThingType(typeUID)
|
||||||
|
.withLabel(unit.getName())
|
||||||
|
.withBridge(bridgeUID)
|
||||||
|
.withProperties(properties)
|
||||||
|
.withRepresentationProperty(HANDLER_PROPERTIES_ID)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setThingHandler(@NonNullByDefault({}) ThingHandler handler) {
|
||||||
|
if (handler instanceof TouchWandBridgeHandler) {
|
||||||
|
touchWandBridgeHandler = (TouchWandBridgeHandler) handler;
|
||||||
|
registerListener(touchWandBridgeHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull ThingHandler getThingHandler() {
|
||||||
|
return touchWandBridgeHandler;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link Csc} implements Csc data class.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
public class Csc {
|
||||||
|
|
||||||
|
private int sceneNo;
|
||||||
|
private int ts;
|
||||||
|
private int keyAttr;
|
||||||
|
|
||||||
|
public int getSceneNo() {
|
||||||
|
return sceneNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSceneNo(int sceneNo) {
|
||||||
|
this.sceneNo = sceneNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTs() {
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTs(int ts) {
|
||||||
|
this.ts = ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeyAttr() {
|
||||||
|
return keyAttr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyAttr(int keyAttr) {
|
||||||
|
this.keyAttr = keyAttr;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link CurrStatus} implements CurrStatus data class.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class CurrStatus {
|
||||||
|
|
||||||
|
private Csc csc;
|
||||||
|
|
||||||
|
public Csc getCsc() {
|
||||||
|
return csc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCsc(Csc csc) {
|
||||||
|
this.csc = csc;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal.dto;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandShutterSwitchUnitData} implements Shutter and Switch units property.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TouchWandShutterSwitchUnitData extends TouchWandUnitData {
|
||||||
|
|
||||||
|
private Integer currStatus = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getCurrStatus() {
|
||||||
|
return currStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrStatus(int currStatus) {
|
||||||
|
this.currStatus = currStatus;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,199 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal.dto;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandUnitData} implements unit property.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public abstract class TouchWandUnitData {
|
||||||
|
|
||||||
|
private Integer id = 0;
|
||||||
|
private String name = "";
|
||||||
|
private String type = "";
|
||||||
|
private Integer nodeId = 0;
|
||||||
|
private Integer epId = 0;
|
||||||
|
private String icon = "";
|
||||||
|
private String connectivity = "";
|
||||||
|
private String status = "";
|
||||||
|
private boolean isFavorite = false;
|
||||||
|
private Integer errorCode = 0;
|
||||||
|
private boolean hasPowerMeter;
|
||||||
|
private boolean hasBattery;
|
||||||
|
private Object config = "";
|
||||||
|
private Object association = "";
|
||||||
|
private String customOp = "";
|
||||||
|
private boolean isHidden = false;
|
||||||
|
private String createdAt = "";
|
||||||
|
private String updatedAt = "";
|
||||||
|
private Integer roomId = 0;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNodeId() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeId(int nodeId) {
|
||||||
|
this.nodeId = nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEpId() {
|
||||||
|
return epId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEpId(int epId) {
|
||||||
|
this.epId = epId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(String icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConnectivity() {
|
||||||
|
return connectivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConnectivity(String connectivity) {
|
||||||
|
this.connectivity = connectivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getIsFavorite() {
|
||||||
|
return isFavorite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsFavorite(boolean isFavorite) {
|
||||||
|
this.isFavorite = isFavorite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getErrorCode() {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorCode(Integer errorCode) {
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getHasPowerMeter() {
|
||||||
|
return hasPowerMeter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasPowerMeter(boolean hasPowerMeter) {
|
||||||
|
this.hasPowerMeter = hasPowerMeter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getHasBattery() {
|
||||||
|
return hasBattery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasBattery(boolean hasBattery) {
|
||||||
|
this.hasBattery = hasBattery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfig(Object config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getAssociation() {
|
||||||
|
return association;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAssociation(Object association) {
|
||||||
|
this.association = association;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCustomOp() {
|
||||||
|
return customOp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomOp(String customOp) {
|
||||||
|
this.customOp = customOp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getIsHidden() {
|
||||||
|
return isHidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsHidden(boolean isHidden) {
|
||||||
|
this.isHidden = isHidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCreatedAt() {
|
||||||
|
return createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedAt(String createdAt) {
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUpdatedAt() {
|
||||||
|
return updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdatedAt(String updatedAt) {
|
||||||
|
this.updatedAt = updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getRoomId() {
|
||||||
|
return roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoomId(Integer roomId) {
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Integer getCurrStatus();
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandUnitDataWallController} implements WallController unit
|
||||||
|
* property.
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
public class TouchWandUnitDataWallController extends TouchWandUnitData {
|
||||||
|
|
||||||
|
private CurrStatus currStatus;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getCurrStatus() {
|
||||||
|
if (currStatus != null) {
|
||||||
|
return currStatus.getCsc().getKeyAttr();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrStatus(CurrStatus currStatus) {
|
||||||
|
this.currStatus = currStatus;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openhab.binding.touchwand.internal.dto;
|
||||||
|
|
||||||
|
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.*;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link TouchWandUnitFromJson} parse Json unit data
|
||||||
|
*
|
||||||
|
* @author Roie Geron - Initial contribution
|
||||||
|
*/
|
||||||
|
public class TouchWandUnitFromJson {
|
||||||
|
|
||||||
|
public TouchWandUnitFromJson() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TouchWandUnitData parseResponse(JsonObject jsonUnit) {
|
||||||
|
final Gson gson = new Gson();
|
||||||
|
TouchWandUnitData touchWandUnit;
|
||||||
|
String type = jsonUnit.get("type").getAsString();
|
||||||
|
if (!Arrays.asList(SUPPORTED_TOUCHWAND_TYPES).contains(type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jsonUnit.has("currStatus") || (jsonUnit.get("currStatus") == null)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case TYPE_WALLCONTROLLER:
|
||||||
|
touchWandUnit = gson.fromJson(jsonUnit, TouchWandUnitDataWallController.class);
|
||||||
|
break;
|
||||||
|
case TYPE_SWITCH:
|
||||||
|
touchWandUnit = gson.fromJson(jsonUnit, TouchWandShutterSwitchUnitData.class);
|
||||||
|
break;
|
||||||
|
case TYPE_DIMMER:
|
||||||
|
touchWandUnit = gson.fromJson(jsonUnit, TouchWandShutterSwitchUnitData.class);
|
||||||
|
break;
|
||||||
|
case TYPE_SHUTTER:
|
||||||
|
touchWandUnit = gson.fromJson(jsonUnit, TouchWandShutterSwitchUnitData.class);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return touchWandUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TouchWandUnitData parseResponse(String JsonUnit) {
|
||||||
|
final JsonParser jsonParser = new JsonParser();
|
||||||
|
JsonObject unitObj = jsonParser.parse(JsonUnit).getAsJsonObject();
|
||||||
|
return parseResponse(unitObj);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<binding:binding id="touchwand" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||||
|
|
||||||
|
<name>TouchWand Binding</name>
|
||||||
|
<description>Connect to TouchWand Wanderfull™ Hub and communicate with units connected to the controller.</description>
|
||||||
|
<author>Roie Geron</author>
|
||||||
|
|
||||||
|
</binding:binding>
|
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="touchwand"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||||
|
<bridge-type id="bridge">
|
||||||
|
<label>TouchWand Wanderfull Hub Bridge</label>
|
||||||
|
<description>Multifunctional Gateway - a bridge to units and scenarios</description>
|
||||||
|
<properties>
|
||||||
|
<property name="vendor">TouchWand</property>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<config-description>
|
||||||
|
<parameter name="username" type="text">
|
||||||
|
<label>Username</label>
|
||||||
|
<description>Username for TouchWand Wanderfull Hub</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="password" type="text" required="true">
|
||||||
|
<context>password</context>
|
||||||
|
<label>Password</label>
|
||||||
|
<description>Password for TouchWand Wanderfull Hub </description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="ipAddress" type="text" required="true">
|
||||||
|
<context>network-address</context>
|
||||||
|
<label>Network Address</label>
|
||||||
|
<description>Network address of this TouchWand Wanderfull Hub </description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="port" type="integer" min="80" max="65535" required="true">
|
||||||
|
<label>Port</label>
|
||||||
|
<description>Port of the TouchWand Wanderfull Hub communication channel</description>
|
||||||
|
<default>80</default>
|
||||||
|
<advanced>true</advanced>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="statusrefresh" type="integer" unit="s">
|
||||||
|
<default>120</default>
|
||||||
|
<description>Unit status refresh interval (seconds)</description>
|
||||||
|
<label>Refresh</label>
|
||||||
|
<advanced>true</advanced>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="addSecondaryUnits" type="boolean">
|
||||||
|
<default>false</default>
|
||||||
|
<description>If the controller is primary, add secondary controllers units as well</description>
|
||||||
|
<label>Add Secondary Controllers</label>
|
||||||
|
<advanced>true</advanced>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
</bridge-type>
|
||||||
|
</thing:thing-descriptions>
|
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="touchwand"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||||
|
|
||||||
|
<thing-type id="dimmer">
|
||||||
|
<supported-bridge-type-refs>
|
||||||
|
<bridge-type-ref id="bridge"></bridge-type-ref>
|
||||||
|
</supported-bridge-type-refs>
|
||||||
|
<label>TouchWand Dimmer Unit</label>
|
||||||
|
<channels>
|
||||||
|
<channel id="brightness" typeId="system.brightness">
|
||||||
|
<description>light brightness control</description>
|
||||||
|
</channel>
|
||||||
|
</channels>
|
||||||
|
</thing-type>
|
||||||
|
</thing:thing-descriptions>
|
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="touchwand"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||||
|
|
||||||
|
<thing-type id="shutter">
|
||||||
|
<supported-bridge-type-refs>
|
||||||
|
<bridge-type-ref id="bridge"></bridge-type-ref>
|
||||||
|
</supported-bridge-type-refs>
|
||||||
|
<label>TouchWand Shutter Unit</label>
|
||||||
|
<channels>
|
||||||
|
<channel id="shutter" typeId="shutter">
|
||||||
|
<description>Shutter control</description>
|
||||||
|
</channel>
|
||||||
|
</channels>
|
||||||
|
</thing-type>
|
||||||
|
<channel-type id="shutter">
|
||||||
|
<item-type>Rollershutter</item-type>
|
||||||
|
<label>Shutter</label>
|
||||||
|
<description>The Shutter channel allows control shutters</description>
|
||||||
|
<category>Blinds</category>
|
||||||
|
</channel-type>
|
||||||
|
</thing:thing-descriptions>
|
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="touchwand"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||||
|
|
||||||
|
<thing-type id="switch">
|
||||||
|
<supported-bridge-type-refs>
|
||||||
|
<bridge-type-ref id="bridge"></bridge-type-ref>
|
||||||
|
</supported-bridge-type-refs>
|
||||||
|
<label>TouchWand Switch Unit</label>
|
||||||
|
<channels>
|
||||||
|
<channel id="switch" typeId="switch">
|
||||||
|
<description>Unit on off switch</description>
|
||||||
|
</channel>
|
||||||
|
</channels>
|
||||||
|
</thing-type>
|
||||||
|
<channel-type id="switch">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Switch</label>
|
||||||
|
<description>The switch channel allows to switch the light on and off.
|
||||||
|
</description>
|
||||||
|
<category>Light</category>
|
||||||
|
<tags>
|
||||||
|
<tag>Lighting</tag>
|
||||||
|
</tags>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
</thing:thing-descriptions>
|
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="touchwand"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||||
|
|
||||||
|
<thing-type id="wallcontroller">
|
||||||
|
<supported-bridge-type-refs>
|
||||||
|
<bridge-type-ref id="bridge"></bridge-type-ref>
|
||||||
|
</supported-bridge-type-refs>
|
||||||
|
<label>TouchWand WallController Unit</label>
|
||||||
|
<channels>
|
||||||
|
<channel id="wallaction" typeId="wallaction">
|
||||||
|
<description>WallController action</description>
|
||||||
|
</channel>
|
||||||
|
</channels>
|
||||||
|
</thing-type>
|
||||||
|
<channel-type id="wallaction">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>WallController action</label>
|
||||||
|
<event>
|
||||||
|
<options>
|
||||||
|
<option value="LONG">long</option>
|
||||||
|
<option value="SHORT">short</option>
|
||||||
|
</options>
|
||||||
|
</event>
|
||||||
|
</channel-type>
|
||||||
|
</thing:thing-descriptions>
|
@ -274,6 +274,7 @@
|
|||||||
<module>org.openhab.binding.tellstick</module>
|
<module>org.openhab.binding.tellstick</module>
|
||||||
<module>org.openhab.binding.tesla</module>
|
<module>org.openhab.binding.tesla</module>
|
||||||
<module>org.openhab.binding.tibber</module>
|
<module>org.openhab.binding.tibber</module>
|
||||||
|
<module>org.openhab.binding.touchwand</module>
|
||||||
<module>org.openhab.binding.tplinksmarthome</module>
|
<module>org.openhab.binding.tplinksmarthome</module>
|
||||||
<module>org.openhab.binding.tradfri</module>
|
<module>org.openhab.binding.tradfri</module>
|
||||||
<module>org.openhab.binding.unifi</module>
|
<module>org.openhab.binding.unifi</module>
|
||||||
|
Loading…
Reference in New Issue
Block a user