[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:
Roie Geron 2020-10-18 03:19:40 +03:00 committed by GitHub
parent de6390501b
commit 7cd5510a72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 2308 additions and 0 deletions

View File

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

View 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

View 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"}
```

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

View File

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

View File

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

View File

@ -0,0 +1,91 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.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 };
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,223 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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