[touchwand]- Added support for AlarmSensor and WallController (#8933)

* Added support for AlarmSensor and WallController

Signed-off-by: Roie Geron <roie.geron@gmail.com>
pull/9058/head
Roie Geron 2020-11-17 01:44:03 +02:00 committed by GitHub
parent 1f4053538f
commit 387b9675fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 675 additions and 77 deletions

View File

@ -8,18 +8,19 @@ TouchWand products are compatible with most major Z-Wave products, IP controlled
## Supported Things
This binding supports switches, shutters dimmers and wall controllers configured in Touchwand Wanderfull™ Hub Controller.
This binding supports switches, shutters dimmers alarm sensors and wall controllers configured in Touchwand Wanderfull™ Hub Controller.
## Control
## Control and Status
1. **switch** - control - ON/OFF
2. **shutter** - control - UP/DOWN/STOP
3. **dimmer** - control - ON/OFF/BRIGHTNESS
4. **wallcontroller** - control - LONG/SHORT
5. **alarmsensor** - status channels depend on alarm sensor type
## Discovery
After adding TouchWand Hub the auto discovery will add all switches dimmers and shutters to the inbox.
After adding TouchWand Hub the auto discovery will add all switches dimmers alarm sensors and shutters to the inbox.
## Bridge Configuration
@ -29,7 +30,7 @@ After adding TouchWand Hub the auto discovery will add all switches dimmers and
|-------------------|-----------------------------------------------------------------------|---------|----------|
| username | Touchwand hub username | string | yes |
| password | Touchwand hub password | string | yes |
| ipAddress | Touchwand hub hotname or IP address | string | yes |
| ipAddress | Touchwand hub hostname 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 |
@ -40,6 +41,29 @@ After adding TouchWand Hub the auto discovery will add all switches dimmers and
No thing configuration is needed
## Channels
note **Touchwand Wanderfull™** supports various types of alarm sensors such as water leak, door/window sensor and motion sensor.
Alarm Sensor thing represents a generic sensor, relevant sensor channels will be displayed once a sensor is added as a Thing.
| Channel Type ID | Item Type | Description
|-------------------|--------------------|-----------------------------------------------------------------------|
| switch | Switch | This channel supports switching the device on and off. |
| shutter | Rollershutter | This channel controls the shutter position |
| brightness | Dimmer | This channel supports adjusting the brightness value. |
| illumination | Number:Illuminance | This channel shows the current illuminance measured by the sensor. |
| temperature | Number:Temperature | This channel shows the current temperature measured by the sensor. |
| leak | Switch | This channel alert when water leak is detected by the sensor |
| motion | Switch | This channel alert when motion detected by the sensor. |
| isOpen | Contact | This channel shows the status of Door/Window sensor. |
| battery_level | Number | This channel shows the battery level. |
| battery_low | Switch | This channel indicates whether the battery is low or not. |
| wallaction | String | This channel indicate SHORT or LONG wallcontroller button pressed |
## Full Example
### touchwand.things

View File

@ -0,0 +1,173 @@
/**
* 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.ArrayList;
import javax.measure.quantity.Illuminance;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.touchwand.internal.dto.TouchWandAlarmSensorCurrentStatus.BinarySensorEvent;
import org.openhab.binding.touchwand.internal.dto.TouchWandAlarmSensorCurrentStatus.Sensor;
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitDataAlarmSensor;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.types.Command;
/**
* The {@link TouchWandAlarmSensorHandler} is responsible for handling command for Alarm Sensor unit
*
*
* @author Roie Geron - Initial contribution
*
*/
@NonNullByDefault
public class TouchWandAlarmSensorHandler extends TouchWandBaseUnitHandler {
private static final int BATT_LEVEL_LOW = 20;
private static final int BATT_LEVEL_LOW_HYS = 5;
private boolean isBatteryLow = false;
private boolean isFirstUpdateTouchWandUnitState = true;
public TouchWandAlarmSensorHandler(Thing thing) {
super(thing);
}
@Override
void updateTouchWandUnitState(TouchWandUnitData unitData) {
if (unitData instanceof TouchWandUnitDataAlarmSensor) {
TouchWandUnitDataAlarmSensor sensor = (TouchWandUnitDataAlarmSensor) unitData;
if (isFirstUpdateTouchWandUnitState) {
removeUnsupportedChannels(sensor);
isFirstUpdateTouchWandUnitState = false;
}
updateBatteryLevel(sensor);
updateIllumination(sensor);
updateChannelLeak(sensor);
updateChannelDoorWindow(sensor);
updateChannelMotion(sensor);
updateChannelTemperature(sensor);
} else {
logger.warn("updateTouchWandUnitState incompatible TouchWandUnitData instance");
}
}
@Override
void touchWandUnitHandleCommand(Command command) {
}
void updateBatteryLevel(TouchWandUnitDataAlarmSensor unitData) {
Integer battLevel = unitData.getCurrStatus().getBatt();
updateState(CHANNEL_BATTERY_LEVEL, new DecimalType(battLevel));
int lowThreshold = isBatteryLow ? BATT_LEVEL_LOW + BATT_LEVEL_LOW_HYS : BATT_LEVEL_LOW;
boolean lowBattery = (battLevel <= lowThreshold);
updateState(CHANNEL_BATTERY_LOW, OnOffType.from(lowBattery));
isBatteryLow = lowBattery;
}
void updateIllumination(TouchWandUnitDataAlarmSensor unitData) {
for (Sensor sensor : unitData.getCurrStatus().getSensorsStatus()) {
if (sensor.type == SENSOR_TYPE_LUMINANCE) {
updateState(CHANNEL_ILLUMINATION, new QuantityType<Illuminance>(sensor.value, SmartHomeUnits.LUX));
}
}
}
void updateChannelLeak(TouchWandUnitDataAlarmSensor unitData) {
for (BinarySensorEvent bSensor : unitData.getCurrStatus().getbSensorsStatus()) {
if (bSensor.sensorType == SENSOR_TYPE_LEAK) {
boolean isLeak = bSensor.sensor.state;
updateState(CHANNEL_LEAK, OnOffType.from(isLeak));
}
}
}
void updateChannelDoorWindow(TouchWandUnitDataAlarmSensor unitData) {
for (BinarySensorEvent bSensor : unitData.getCurrStatus().getbSensorsStatus()) {
if (bSensor.sensorType == SENSOR_TYPE_DOOR_WINDOW) {
boolean isOpen = bSensor.sensor.state;
OpenClosedType myOpenClose;
myOpenClose = isOpen ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
updateState(CHANNEL_DOORWINDOW, myOpenClose);
}
}
}
void updateChannelMotion(TouchWandUnitDataAlarmSensor unitData) {
for (BinarySensorEvent bSensor : unitData.getCurrStatus().getbSensorsStatus()) {
if (bSensor.sensorType == SENSOR_TYPE_MOTION) {
boolean hasMotion = bSensor.sensor.state;
updateState(CHANNEL_MOTION, OnOffType.from(hasMotion));
}
}
}
void updateChannelTemperature(TouchWandUnitDataAlarmSensor unitData) {
for (Sensor sensor : unitData.getCurrStatus().getSensorsStatus()) {
if (sensor.type == SENSOR_TYPE_TEMPERATURE) {
updateState(CHANNEL_TEMPERATURE, new QuantityType<Temperature>(sensor.value, SIUnits.CELSIUS));
}
}
}
void removeUnsupportedChannels(TouchWandUnitDataAlarmSensor unitData) {
ArrayList<Channel> toBeRemovedChannels = new ArrayList<>(thing.getChannels());
for (BinarySensorEvent bSensor : unitData.getCurrStatus().getbSensorsStatus()) {
switch (bSensor.sensorType) {
case SENSOR_TYPE_MOTION:
toBeRemovedChannels.remove(thing.getChannel(CHANNEL_MOTION));
break;
case SENSOR_TYPE_DOOR_WINDOW:
toBeRemovedChannels.remove(thing.getChannel(CHANNEL_DOORWINDOW));
break;
case SENSOR_TYPE_LEAK:
Channel channel = thing.getChannel(CHANNEL_LEAK);
toBeRemovedChannels.remove(channel);
break;
}
}
for (Sensor sensor : unitData.getCurrStatus().getSensorsStatus()) {
switch (sensor.type) {
case SENSOR_TYPE_TEMPERATURE:
toBeRemovedChannels.remove(thing.getChannel(CHANNEL_TEMPERATURE));
break;
case SENSOR_TYPE_LUMINANCE:
toBeRemovedChannels.remove(thing.getChannel(CHANNEL_ILLUMINATION));
break;
}
}
if (unitData.getHasBattery()) {
toBeRemovedChannels.remove(thing.getChannel(CHANNEL_BATTERY_LEVEL));
toBeRemovedChannels.remove(thing.getChannel(CHANNEL_BATTERY_LOW));
}
ThingBuilder thingBuilder = editThing();
thingBuilder.withoutChannels(toBeRemovedChannels);
updateThing(thingBuilder.build());
}
}

View File

@ -15,10 +15,12 @@ package org.openhab.binding.touchwand.internal;
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.*;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitFromJson;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
@ -31,10 +33,6 @@ 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
@ -46,9 +44,10 @@ import com.google.gson.JsonParser;
@NonNullByDefault
public abstract class TouchWandBaseUnitHandler extends BaseThingHandler implements TouchWandUnitUpdateListener {
private static final int UNITS_STATUS_UPDATE_DELAY_SEC = 1;
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);
THING_TYPE_WALLCONTROLLER, THING_TYPE_DIMMER, THING_TYPE_ALARMSENSOR);
protected String unitId = "";
protected @Nullable TouchWandBridgeHandler bridgeHandler;
@ -60,7 +59,10 @@ public abstract class TouchWandBaseUnitHandler extends BaseThingHandler implemen
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
// updateTouchWandUnitState(getUnitState(unitId));
TouchWandUnitData myUnitData = getUnitState(unitId);
if (myUnitData != null) {
updateTouchWandUnitState(myUnitData);
}
} else {
touchWandUnitHandleCommand(command);
}
@ -98,47 +100,34 @@ public abstract class TouchWandBaseUnitHandler extends BaseThingHandler implemen
}
updateStatus(ThingStatus.UNKNOWN);
scheduler.execute(() -> {
scheduler.schedule(() -> {
boolean thingReachable = false;
if (myTmpBridgeHandler != null) {
String response = myTmpBridgeHandler.touchWandClient.cmdGetUnitById(unitId);
thingReachable = !response.isEmpty();
if (thingReachable) {
updateStatus(ThingStatus.ONLINE);
updateTouchWandUnitState(TouchWandUnitFromJson.parseResponse(response));
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
}
});
}, UNITS_STATUS_UPDATE_DELAY_SEC, TimeUnit.SECONDS);
}
@SuppressWarnings("unused") // not used at the moment till touchWand state in hub will be fixed
private int getUnitState(String unitId) {
int status = 0;
private @Nullable TouchWandUnitData getUnitState(String unitId) {
TouchWandBridgeHandler touchWandBridgeHandler = bridgeHandler;
if (touchWandBridgeHandler == null) {
return status;
return null;
}
String response = touchWandBridgeHandler.touchWandClient.cmdGetUnitById(unitId);
if (!response.isEmpty()) {
return status;
if (response.isEmpty()) {
return null;
}
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;
return TouchWandUnitFromJson.parseResponse(response);
}
abstract void touchWandUnitHandleCommand(Command command);

View File

@ -36,7 +36,7 @@ public class TouchWandBindingConstants {
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
public static final ThingTypeUID THING_TYPE_ALARMSENSOR = new ThingTypeUID(BINDING_ID, "alarmsensor");
// List of all Channel ids
public static final String CHANNEL_SWITCH = "switch";
@ -46,6 +46,11 @@ public class TouchWandBindingConstants {
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";
public static final String CHANNEL_LEAK = "leak";
public static final String CHANNEL_MOTION = "motion";
public static final String CHANNEL_ILLUMINATION = "illumination";
public static final String CHANNEL_DOORWINDOW = "isOpen";
public static final String CHANNEL_TEMPERATURE = "temperature";
// List of configuration parameters
@ -77,7 +82,7 @@ public class TouchWandBindingConstants {
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
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_ALARMSENSOR);
}
public static final String TYPE_WALLCONTROLLER = "WallController";
@ -85,6 +90,13 @@ public class TouchWandBindingConstants {
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 TYPE_UNKNOWN = "unknown";
public static final int SENSOR_TYPE_TEMPERATURE = 1;
public static final int SENSOR_TYPE_LUMINANCE = 3;
public static final int SENSOR_TYPE_LEAK = 6;
public static final int SENSOR_TYPE_DOOR_WINDOW = 10;
public static final int SENSOR_TYPE_MOTION = 12;
public static final String[] SUPPORTED_TOUCHWAND_TYPES = { TYPE_WALLCONTROLLER, TYPE_SWITCH, TYPE_SHUTTER,
TYPE_DIMMER, TYPE_ALARMSENSOR };

View File

@ -68,6 +68,8 @@ public class TouchWandHandlerFactory extends BaseThingHandlerFactory {
return new TouchWandWallControllerHandler(thing);
} else if (THING_TYPE_DIMMER.equals(thingTypeUID)) {
return new TouchWandDimmerHandler(thing);
} else if (THING_TYPE_ALARMSENSOR.equals(thingTypeUID)) {
return new TouchWandAlarmSensorHandler(thing);
}
return null;

View File

@ -117,16 +117,21 @@ public class TouchWandRestClient {
}
public String cmdListUnits() {
String command = buildUrl(CMD_LIST_UNITS);
String response = sendCommand(command, METHOD_GET, "");
String response = "";
if (isConnected) {
String command = buildUrl(CMD_LIST_UNITS);
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, "");
String response = "";
if (isConnected) {
String command = buildUrl(CMD_GET_UNIT_BY_ID) + "id=" + id;
response = sendCommand(command, METHOD_GET, "");
}
return response;
}
@ -167,9 +172,11 @@ public class TouchWandRestClient {
}
private String cmdUnitAction(String action) {
String command = buildUrl(CMD_UNIT_ACTION);
String response = sendCommand(command, METHOD_POST, action);
String response = "";
if (isConnected) {
String command = buildUrl(CMD_UNIT_ACTION);
response = sendCommand(command, METHOD_POST, action);
}
return response;
}

View File

@ -17,6 +17,7 @@ import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.C
import java.time.Instant;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.touchwand.internal.dto.Csc;
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
import org.openhab.binding.touchwand.internal.dto.TouchWandUnitDataWallController;
import org.openhab.core.thing.Thing;
@ -33,12 +34,12 @@ import org.openhab.core.types.Command;
@NonNullByDefault
public class TouchWandWallControllerHandler extends TouchWandBaseUnitHandler {
private long timeSinceLastEventMs;
private static final int ADJUSTENT_EVENT_FILTER_TIME_MILLISEC = 2000; // 2 seconds
private long timeLastEventMs;
private static final int ADJACENT_EVENT_FILTER_TIME_MILLISEC = 2000; // 2 seconds
public TouchWandWallControllerHandler(Thing thing) {
super(thing);
timeSinceLastEventMs = Instant.now().toEpochMilli();
timeLastEventMs = Instant.now().toEpochMilli();
}
@Override
@ -47,12 +48,16 @@ public class TouchWandWallControllerHandler extends TouchWandBaseUnitHandler {
@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);
if (unitData instanceof TouchWandUnitDataWallController) {
Csc status = ((TouchWandUnitDataWallController) unitData).getCurrStatus();
long ts = status.getTs();
long timeDiff = ts - timeLastEventMs;
if ((timeDiff) > ADJACENT_EVENT_FILTER_TIME_MILLISEC) {
int value = status.getKeyAttr();
String action = (value <= 100) ? "SHORT" : "LONG";
triggerChannel(CHANNEL_WALLCONTROLLER_ACTION, action);
}
timeLastEventMs = status.getTs();
}
timeSinceLastEventMs = Instant.now().toEpochMilli();
}
}

View File

@ -50,7 +50,8 @@ import com.google.gson.JsonSyntaxException;
@NonNullByDefault
public class TouchWandWebSockets {
private static final int CONNECT_TIMEOUT_SEC = 10;
private static final int CONNECT_TIMEOUT_SEC = 15;
private static final int CONNECT_TIMEOUT_MS = CONNECT_TIMEOUT_SEC * 1000;
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);
@ -82,7 +83,7 @@ public class TouchWandWebSockets {
return;
}
client.setConnectTimeout(CONNECT_TIMEOUT_SEC);
client.setConnectTimeout(CONNECT_TIMEOUT_MS);
ClientUpgradeRequest request = new ClientUpgradeRequest();
request.setSubProtocols("relay_protocol");
@ -136,7 +137,9 @@ public class TouchWandWebSockets {
public void onConnect(Session session) {
logger.debug("TouchWandWebSockets connected to {}", session.getRemoteAddress().toString());
try {
session.getRemote().sendString("{\"myopenhab\": \"myopenhab\"}");
long timestamp = System.currentTimeMillis(); // need unique id
String controllerIdStr = String.format("{\"contId\": \"openhab%d\"}", timestamp);
session.getRemote().sendString(controllerIdStr);
} catch (IOException e) {
logger.warn("sendString : {}", e.getMessage());
}

View File

@ -90,9 +90,7 @@ public class TouchWandUnitDiscoveryService extends AbstractDiscoveryService
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;
@ -118,6 +116,9 @@ public class TouchWandUnitDiscoveryService extends AbstractDiscoveryService
case TYPE_SHUTTER:
addDeviceDiscoveryResult(touchWandUnit, THING_TYPE_SHUTTER);
break;
case TYPE_ALARMSENSOR:
addDeviceDiscoveryResult(touchWandUnit, THING_TYPE_ALARMSENSOR);
break;
default:
continue;
}

View File

@ -0,0 +1,122 @@
/**
* 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 java.lang.reflect.Type;
import java.util.Map.Entry;
import org.openhab.binding.touchwand.internal.dto.TouchWandAlarmSensorCurrentStatus.Alarm;
import org.openhab.binding.touchwand.internal.dto.TouchWandAlarmSensorCurrentStatus.BinarySensor;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
/**
* The {@link AlarmSensorUnitDataDeserializer} implements AlarmSensorUnitData unit
* Json De-serializer.
*
* @author Roie Geron - Initial contribution
*/
public class AlarmSensorUnitDataDeserializer implements JsonDeserializer<TouchWandUnitDataAlarmSensor> {
static final Gson gson = new Gson();
static GsonBuilder builder = new GsonBuilder();
@Override
public TouchWandUnitDataAlarmSensor deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
TouchWandUnitDataAlarmSensor touchWandUnitDataAlarmSensor = new TouchWandUnitDataAlarmSensor();
JsonObject jsonObject = json.getAsJsonObject();
touchWandUnitDataAlarmSensor.setId(jsonObject.get("id").getAsInt());
touchWandUnitDataAlarmSensor.setName(jsonObject.get("name").getAsString());
touchWandUnitDataAlarmSensor.setConnectivity(jsonObject.get("connectivity").getAsString());
touchWandUnitDataAlarmSensor.setType(jsonObject.get("type").getAsString());
touchWandUnitDataAlarmSensor.setHasBattery(jsonObject.get("hasBattery").getAsBoolean());
JsonElement powerMeterElement = jsonObject.get("hasPowerMeter");
if (powerMeterElement != null && !powerMeterElement.isJsonNull()) {
touchWandUnitDataAlarmSensor.setHasPowerMeter(powerMeterElement.getAsBoolean());
} else {
touchWandUnitDataAlarmSensor.setHasPowerMeter(false);
}
JsonElement status = jsonObject.get("status");
if (status != null && !status.isJsonNull()) { // Sometimes status is null
touchWandUnitDataAlarmSensor.setStatus(jsonObject.get("status").getAsString());
}
JsonObject currentStatusObj = builder.create().fromJson(jsonObject.get("currStatus").getAsJsonObject(),
JsonObject.class);
if (currentStatusObj != null) {
TouchWandAlarmSensorCurrentStatus touchWandUnitDataAlarmSensorCurrentStatus = touchWandUnitDataAlarmSensor
.getCurrStatus();
for (Entry<String, JsonElement> entry : currentStatusObj.entrySet()) {
String key = entry.getKey();
String splits[] = key.split("_"); // the key is xxxx_n where xxx is sensor type and n is
String keyName = splits[0];
int index = 0;
if (splits.length > 1 && !splits[1].isEmpty()) {
try {
index = Integer.parseInt(splits[1]);
} catch (final NumberFormatException e) {
index = 0;
}
}
switch (keyName) {
case "batt":
touchWandUnitDataAlarmSensorCurrentStatus.setBatt(entry.getValue().getAsInt());
break;
case "alarm":
Alarm alarm = gson.fromJson(entry.getValue().getAsJsonObject(), Alarm.class);
TouchWandAlarmSensorCurrentStatus.AlarmEvent alarmEvent = new TouchWandAlarmSensorCurrentStatus.AlarmEvent();
if (alarm != null) {
alarmEvent.alarm = alarm;
alarmEvent.alarmType = index;
}
touchWandUnitDataAlarmSensor.getCurrStatus().getAlarmsStatus().add(alarmEvent);
break;
case "sensor":
TouchWandAlarmSensorCurrentStatus.Sensor sensor = new TouchWandAlarmSensorCurrentStatus.Sensor();
sensor.value = entry.getValue().getAsFloat();
sensor.type = index;
touchWandUnitDataAlarmSensor.getCurrStatus().getSensorsStatus().add(sensor);
break;
case "bsensor":
BinarySensor bsensor = gson.fromJson(entry.getValue().getAsJsonObject(), BinarySensor.class);
TouchWandAlarmSensorCurrentStatus.BinarySensorEvent bsensorevent = new TouchWandAlarmSensorCurrentStatus.BinarySensorEvent();
if (bsensor != null) {
bsensorevent.sensor = bsensor;
bsensorevent.sensorType = index;
}
touchWandUnitDataAlarmSensor.getCurrStatus().getbSensorsStatus().add(bsensorevent);
break;
default:
break;
}
}
}
return touchWandUnitDataAlarmSensor;
}
}

View File

@ -13,16 +13,19 @@
package org.openhab.binding.touchwand.internal.dto;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Csc} implements Csc data class.
*
* @author Roie Geron - Initial contribution
*/
@NonNullByDefault
public class Csc {
private int sceneNo;
private int ts;
private int keyAttr;
private int sceneNo = 0;
private long ts = 0;
private int keyAttr = 0;
public int getSceneNo() {
return sceneNo;
@ -32,11 +35,11 @@ public class Csc {
this.sceneNo = sceneNo;
}
public int getTs() {
public long getTs() {
return ts;
}
public void setTs(int ts) {
public void setTs(long ts) {
this.ts = ts;
}

View File

@ -13,15 +13,17 @@
package org.openhab.binding.touchwand.internal.dto;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link CurrStatus} implements CurrStatus data class.
*
* @author Roie Geron - Initial contribution
*/
@NonNullByDefault
public class CurrStatus {
private Csc csc;
private Csc csc = new Csc();
public Csc getCsc() {
return csc;

View File

@ -0,0 +1,92 @@
/**
* 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 java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link TouchWandAlarmSensorCurrentStatus} implements Alarm Sensor unit
* CurrentStatus data property.
*
* @author Roie Geron - Initial contribution
*/
@NonNullByDefault
public class TouchWandAlarmSensorCurrentStatus {
private int batt;
private List<Sensor> sensorsStatus = new ArrayList<Sensor>();
private List<AlarmEvent> alarmsStatus = new ArrayList<AlarmEvent>();
private List<BinarySensorEvent> bSensorsStatus = new ArrayList<BinarySensorEvent>();
public void setBatt(Integer batt) {
this.batt = batt;
}
public int getBatt() {
return batt;
}
public void setSensorsStatus(List<Sensor> sensorsStatus) {
this.sensorsStatus = sensorsStatus;
}
public List<Sensor> getSensorsStatus() {
return sensorsStatus;
}
public List<BinarySensorEvent> getbSensorsStatus() {
return bSensorsStatus;
}
public void setbSensorsStatus(List<BinarySensorEvent> bSensorsStatus) {
this.bSensorsStatus = bSensorsStatus;
}
public List<AlarmEvent> getAlarmsStatus() {
return alarmsStatus;
}
public void setAlarmsStatus(List<AlarmEvent> alarmsStatus) {
this.alarmsStatus = alarmsStatus;
}
public static class Alarm {
public int event;
public long ts;
}
public static class AlarmEvent {
int alarmType;
Alarm alarm = new Alarm();
}
public static class Sensor {
public int type;
public float value;
}
public static class BinarySensor {
public long ts;
public boolean state;
}
public static class BinarySensorEvent {
public int sensorType;
public BinarySensor sensor = new BinarySensor();
}
}

View File

@ -195,5 +195,5 @@ public abstract class TouchWandUnitData {
this.roomId = roomId;
}
public abstract Integer getCurrStatus();
public abstract Object getCurrStatus();
}

View File

@ -0,0 +1,43 @@
/**
* 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 TouchWandUnitDataAlarmSensor} implements Alarm Sensor unit
* data property.
*
* @author Roie Geron - Initial contribution
*/
@NonNullByDefault
public class TouchWandUnitDataAlarmSensor extends TouchWandUnitData {
private TouchWandAlarmSensorCurrentStatus currStatus = new TouchWandAlarmSensorCurrentStatus();
@Override
public TouchWandAlarmSensorCurrentStatus getCurrStatus() {
return this.currStatus;
}
public class AlarmEventType {
public int eventsNum;
public String description = "";
}
public class SensorType {
public int type;
public String description = "";
}
}

View File

@ -13,23 +13,22 @@
package org.openhab.binding.touchwand.internal.dto;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link TouchWandUnitDataWallController} implements WallController unit
* property.
*
* @author Roie Geron - Initial contribution
*/
@NonNullByDefault
public class TouchWandUnitDataWallController extends TouchWandUnitData {
private CurrStatus currStatus;
private CurrStatus currStatus = new CurrStatus();
@Override
public Integer getCurrStatus() {
if (currStatus != null) {
return currStatus.getCsc().getKeyAttr();
} else {
return 0;
}
public Csc getCurrStatus() {
return currStatus.getCsc();
}
public void setCurrStatus(CurrStatus currStatus) {

View File

@ -17,8 +17,14 @@ import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.*
import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
/**
@ -26,8 +32,11 @@ import com.google.gson.JsonParser;
*
* @author Roie Geron - Initial contribution
*/
@NonNullByDefault
public class TouchWandUnitFromJson {
private final static Logger logger = LoggerFactory.getLogger(TouchWandUnitFromJson.class);
public TouchWandUnitFromJson() {
}
@ -36,11 +45,11 @@ public class TouchWandUnitFromJson {
TouchWandUnitData touchWandUnit;
String type = jsonUnit.get("type").getAsString();
if (!Arrays.asList(SUPPORTED_TOUCHWAND_TYPES).contains(type)) {
return null;
type = TYPE_UNKNOWN;
}
if (!jsonUnit.has("currStatus") || (jsonUnit.get("currStatus") == null)) {
return null;
type = TYPE_UNKNOWN;
}
switch (type) {
@ -56,8 +65,21 @@ public class TouchWandUnitFromJson {
case TYPE_SHUTTER:
touchWandUnit = gson.fromJson(jsonUnit, TouchWandShutterSwitchUnitData.class);
break;
case TYPE_ALARMSENSOR:
Gson builder = new GsonBuilder()
.registerTypeAdapter(TouchWandUnitDataAlarmSensor.class, new AlarmSensorUnitDataDeserializer())
.create();
touchWandUnit = builder.fromJson(jsonUnit, TouchWandUnitDataAlarmSensor.class);
break;
case TYPE_UNKNOWN:
touchWandUnit = new TouchWandUnknownTypeUnitData();
break;
default:
return null;
touchWandUnit = new TouchWandUnknownTypeUnitData();
}
if (touchWandUnit == null) {
touchWandUnit = new TouchWandUnknownTypeUnitData();
}
return touchWandUnit;
@ -65,7 +87,15 @@ public class TouchWandUnitFromJson {
public static TouchWandUnitData parseResponse(String JsonUnit) {
final JsonParser jsonParser = new JsonParser();
JsonObject unitObj = jsonParser.parse(JsonUnit).getAsJsonObject();
return parseResponse(unitObj);
TouchWandUnitData myTouchWandUnitData;
JsonObject unitObj;
try {
unitObj = jsonParser.parse(JsonUnit).getAsJsonObject();
myTouchWandUnitData = parseResponse(unitObj);
} catch (JsonParseException | IllegalStateException e) {
logger.warn("Could not parse response {}", JsonUnit);
myTouchWandUnitData = new TouchWandUnknownTypeUnitData(); // Return unknown type
}
return myTouchWandUnitData;
}
}

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;
import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.TYPE_UNKNOWN;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link TouchWandUnknownTypeUnitData} implements unknown unit data
* property.
* It makes the code generic in case parsing error or unknown types
*
* @author Roie Geron - Initial contribution
*/
@NonNullByDefault
public class TouchWandUnknownTypeUnitData extends TouchWandUnitData {
public TouchWandUnknownTypeUnitData() {
this.setType(TYPE_UNKNOWN);
}
@Override
public Integer getCurrStatus() {
return 0;
}
}

View File

@ -0,0 +1,53 @@
<?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="alarmsensor">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"></bridge-type-ref>
</supported-bridge-type-refs>
<label>TouchWand Alarm Sensor Unit</label>
<channels>
<channel id="battery_level" typeId="system.battery-level"/>
<channel id="battery_low" typeId="system.low-battery"/>
<channel id="leak" typeId="leak"></channel>
<channel id="motion" typeId="motion"/>
<channel id="illumination" typeId="illumination"/>
<channel id="isOpen" typeId="isOpen"/>
<channel id="temperature" typeId="temperature"/>
</channels>
</thing-type>
<channel-type id="leak">
<item-type>Switch</item-type>
<label>Leak Detected</label>
<state readOnly="true"></state>
</channel-type>
<channel-type id="temperature">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<category>Temperature</category>
<state pattern="%.1f %unit%" readOnly="true">
</state>
</channel-type>
<channel-type id="illumination">
<item-type>Number:Illuminance</item-type>
<label>Illumination</label>
<description>
This channel shows the brightness of the environment in Lux.
</description>
<state pattern="%d %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="isOpen">
<item-type>Contact</item-type>
<label>Open Status</label>
<category>Contact</category>
<state readOnly="true"></state>
</channel-type>
<channel-type id="motion">
<item-type>Switch</item-type>
<label>Motion Status</label>
<category>Motion</category>
<state readOnly="true"></state>
</channel-type>
</thing:thing-descriptions>