[elroconnects] New binding for Elro Connects (#11189)
* Adjustments after review. Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Fix pom.xml formatting. Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Some fixes and removed redundant null checks. Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Proper thread naming. Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Adjust brand name capitalization. Some README adjustments. Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Fix format issue. Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Fix threadname. Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Update development cycle Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Review fixes Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Avoid communication restart when disposing Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Update CODEOWNERS Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Code review. Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Background discovery Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Fix formatting Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Create i18n properties file Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Add IP Adress parameter Signed-off-by: Mark Herwege <mark.herwege@telenet.be> * Moved hostname resolving out of initialize Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.elroconnects-${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-elroconnects" description="ELRO Connects Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.elroconnects/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "elroconnects";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final String TYPE_CONNECTOR = "connector";
|
||||
public static final ThingTypeUID THING_TYPE_CONNECTOR = new ThingTypeUID(BINDING_ID, TYPE_CONNECTOR);
|
||||
|
||||
public static final String TYPE_SMOKEALARM = "smokealarm";
|
||||
public static final String TYPE_COALARM = "coalarm";
|
||||
public static final String TYPE_HEATALARM = "heatalarm";
|
||||
public static final String TYPE_WATERALARM = "wateralarm";
|
||||
public static final String TYPE_ENTRYSENSOR = "entrysensor";
|
||||
public static final String TYPE_MOTIONSENSOR = "motionsensor";
|
||||
public static final String TYPE_THSENSOR = "temperaturesensor";
|
||||
public static final String TYPE_POWERSOCKET = "powersocket";
|
||||
public static final ThingTypeUID THING_TYPE_SMOKEALARM = new ThingTypeUID(BINDING_ID, TYPE_SMOKEALARM);
|
||||
public static final ThingTypeUID THING_TYPE_COALARM = new ThingTypeUID(BINDING_ID, TYPE_COALARM);
|
||||
public static final ThingTypeUID THING_TYPE_HEATALARM = new ThingTypeUID(BINDING_ID, TYPE_HEATALARM);
|
||||
public static final ThingTypeUID THING_TYPE_WATERALARM = new ThingTypeUID(BINDING_ID, TYPE_WATERALARM);
|
||||
public static final ThingTypeUID THING_TYPE_ENTRYSENSOR = new ThingTypeUID(BINDING_ID, TYPE_ENTRYSENSOR);
|
||||
public static final ThingTypeUID THING_TYPE_MOTIONSENSOR = new ThingTypeUID(BINDING_ID, TYPE_MOTIONSENSOR);
|
||||
public static final ThingTypeUID THING_TYPE_THSENSOR = new ThingTypeUID(BINDING_ID, TYPE_THSENSOR);
|
||||
public static final ThingTypeUID THING_TYPE_POWERSOCKET = new ThingTypeUID(BINDING_ID, TYPE_POWERSOCKET);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_BRIDGE_TYPES_UIDS = Set.of(THING_TYPE_CONNECTOR);
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_CONNECTOR,
|
||||
THING_TYPE_SMOKEALARM, THING_TYPE_COALARM, THING_TYPE_HEATALARM, THING_TYPE_WATERALARM,
|
||||
THING_TYPE_ENTRYSENSOR, THING_TYPE_MOTIONSENSOR, THING_TYPE_THSENSOR, THING_TYPE_POWERSOCKET);
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String SCENE = "scene";
|
||||
|
||||
public static final String BATTERY_LEVEL = "battery";
|
||||
public static final String LOW_BATTERY = "lowBattery";
|
||||
public static final String MUTE_ALARM = "muteAlarm";
|
||||
public static final String TEST_ALARM = "testAlarm";
|
||||
|
||||
public static final String ENTRY = "entry";
|
||||
public static final String MOTION = "motion";
|
||||
|
||||
public static final String POWER_STATE = "powerState";
|
||||
|
||||
public static final String TEMPERATURE = "temperature";
|
||||
public static final String HUMIDITY = "humidity";
|
||||
|
||||
public static final String SMOKE_ALARM = "smokeAlarm";
|
||||
public static final String CO_ALARM = "coAlarm";
|
||||
public static final String HEAT_ALARM = "heatAlarm";
|
||||
public static final String WATER_ALARM = "waterAlarm";
|
||||
public static final String ENTRY_ALARM = "entryAlarm";
|
||||
public static final String MOTION_ALARM = "motionAlarm";
|
||||
|
||||
// Config properties
|
||||
public static final String CONFIG_CONNECTOR_ID = "connectorId";
|
||||
public static final String CONFIG_REFRESH_INTERVAL_S = "refreshInterval";
|
||||
public static final String CONFIG_DEVICE_ID = "deviceId";
|
||||
public static final String CONFIG_DEVICE_TYPE = "deviceType";
|
||||
|
||||
// ELRO cmd constants
|
||||
public static final int ELRO_DEVICE_CONTROL = 101;
|
||||
public static final int ELRO_GET_DEVICE_NAME = 14;
|
||||
public static final int ELRO_GET_DEVICE_STATUSES = 15;
|
||||
public static final int ELRO_REC_DEVICE_NAME = 17;
|
||||
public static final int ELRO_REC_DEVICE_STATUS = 19;
|
||||
public static final int ELRO_SYNC_DEVICES = 29;
|
||||
|
||||
public static final int ELRO_SELECT_SCENE = 106;
|
||||
public static final int ELRO_GET_SCENE = 18;
|
||||
public static final int ELRO_REC_SCENE = 28;
|
||||
public static final int ELRO_REC_SCENE_NAME = 26;
|
||||
public static final int ELRO_REC_SCENE_TYPE = 27;
|
||||
public static final int ELRO_SYNC_SCENES = 131;
|
||||
|
||||
public static final int ELRO_REC_ALARM = 25;
|
||||
|
||||
public static final int ELRO_IGNORE_YES_NO = 11;
|
||||
|
||||
// ELRO device types
|
||||
public static enum ElroDeviceType {
|
||||
ENTRY_SENSOR,
|
||||
CO_ALARM,
|
||||
CXSM_ALARM,
|
||||
MOTION_SENSOR,
|
||||
SM_ALARM,
|
||||
POWERSOCKET,
|
||||
THERMAL_ALARM,
|
||||
TH_SENSOR,
|
||||
WT_ALARM,
|
||||
DEFAULT
|
||||
}
|
||||
|
||||
public static final Map<ElroDeviceType, ThingTypeUID> THING_TYPE_MAP = Map.ofEntries(
|
||||
Map.entry(ElroDeviceType.ENTRY_SENSOR, THING_TYPE_ENTRYSENSOR),
|
||||
Map.entry(ElroDeviceType.CO_ALARM, THING_TYPE_COALARM),
|
||||
Map.entry(ElroDeviceType.CXSM_ALARM, THING_TYPE_SMOKEALARM),
|
||||
Map.entry(ElroDeviceType.MOTION_SENSOR, THING_TYPE_MOTIONSENSOR),
|
||||
Map.entry(ElroDeviceType.SM_ALARM, THING_TYPE_SMOKEALARM),
|
||||
Map.entry(ElroDeviceType.THERMAL_ALARM, THING_TYPE_HEATALARM),
|
||||
Map.entry(ElroDeviceType.WT_ALARM, THING_TYPE_WATERALARM),
|
||||
Map.entry(ElroDeviceType.TH_SENSOR, THING_TYPE_THSENSOR),
|
||||
Map.entry(ElroDeviceType.POWERSOCKET, THING_TYPE_POWERSOCKET));
|
||||
|
||||
public static final Set<String> T_ENTRY_SENSOR = Set.of("0101", "1101", "2101");
|
||||
public static final Set<String> T_POWERSOCKET = Set.of("0200", "1200", "2200");
|
||||
public static final Set<String> T_MOTION_SENSOR = Set.of("0100", "1100", "2100");
|
||||
public static final Set<String> T_CO_ALARM = Set.of("0000", "1000", "2000", "0008", "1008", "2008", "000E", "100E",
|
||||
"200E");
|
||||
public static final Set<String> T_SM_ALARM = Set.of("0001", "1001", "2001", "0009", "1009", "2009", "000F", "100F",
|
||||
"200F");
|
||||
public static final Set<String> T_WT_ALARM = Set.of("0004", "1004", "2004", "000C", "100C", "200C", "0012", "1012",
|
||||
"2012");
|
||||
public static final Set<String> T_TH_SENSOR = Set.of("0102", "1102", "2102");
|
||||
public static final Set<String> T_CXSM_ALARM = Set.of("0005", "1109", "2109", "000D", "100D", "200D", "0013",
|
||||
"1013", "2013");
|
||||
public static final Set<String> T_THERMAL_ALARM = Set.of("0003", "1003", "2003", "000B", "100B", "200B", "0011",
|
||||
"1011", "2011");
|
||||
|
||||
public static final Map<ElroDeviceType, Set<String>> DEVICE_TYPE_MAP = Map.ofEntries(
|
||||
Map.entry(ElroDeviceType.ENTRY_SENSOR, T_ENTRY_SENSOR), Map.entry(ElroDeviceType.CO_ALARM, T_CO_ALARM),
|
||||
Map.entry(ElroDeviceType.CXSM_ALARM, T_CXSM_ALARM),
|
||||
Map.entry(ElroDeviceType.MOTION_SENSOR, T_MOTION_SENSOR), Map.entry(ElroDeviceType.SM_ALARM, T_SM_ALARM),
|
||||
Map.entry(ElroDeviceType.POWERSOCKET, T_POWERSOCKET),
|
||||
Map.entry(ElroDeviceType.THERMAL_ALARM, T_THERMAL_ALARM), Map.entry(ElroDeviceType.TH_SENSOR, T_TH_SENSOR),
|
||||
Map.entry(ElroDeviceType.WT_ALARM, T_WT_ALARM));
|
||||
|
||||
public static final Map<String, ElroDeviceType> TYPE_MAP = DEVICE_TYPE_MAP.entrySet().stream()
|
||||
.flatMap(e -> e.getValue().stream().map(v -> Map.entry(v, e.getKey())))
|
||||
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
// ELRO device status
|
||||
public static enum ElroDeviceStatus {
|
||||
NORMAL,
|
||||
TRIGGERED,
|
||||
TEST,
|
||||
SILENCE,
|
||||
OPEN,
|
||||
FAULT,
|
||||
UNDEF
|
||||
}
|
||||
|
||||
// Listener threadname prefix
|
||||
public static final String THREAD_NAME_PREFIX = "binding-";
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
||||
import org.openhab.core.types.StateDescription;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@Component(service = { DynamicStateDescriptionProvider.class, ElroConnectsDynamicStateDescriptionProvider.class })
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsDynamicStateDescriptionProvider implements DynamicStateDescriptionProvider {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ElroConnectsDynamicStateDescriptionProvider.class);
|
||||
|
||||
private final Map<ChannelUID, @Nullable StateDescription> descriptions = new ConcurrentHashMap<>();
|
||||
|
||||
public void setDescription(ChannelUID channelUID, @Nullable StateDescription description) {
|
||||
logger.debug("Adding command description for channel {}", channelUID);
|
||||
descriptions.put(channelUID, description);
|
||||
}
|
||||
|
||||
public void removeAllDescriptions() {
|
||||
logger.debug("Removing all command descriptions");
|
||||
descriptions.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable StateDescription getStateDescription(Channel channel,
|
||||
@Nullable StateDescription originalStateDescription, @Nullable Locale locale) {
|
||||
StateDescription description = descriptions.get(channel.getUID());
|
||||
return description;
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
public void deactivate() {
|
||||
descriptions.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsBridgeHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsCOAlarmHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsDeviceHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsEntrySensorHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsHeatAlarmHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsMotionSensorHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsPowerSocketHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsSmokeAlarmHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsWaterAlarmHandler;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
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 ElroConnectsHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.elroconnects", service = ThingHandlerFactory.class)
|
||||
public class ElroConnectsHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private @NonNullByDefault({}) NetworkAddressService networkAddressService;
|
||||
private @NonNullByDefault({}) ElroConnectsDynamicStateDescriptionProvider dynamicStateDescriptionProvider;
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
switch (thing.getThingTypeUID().getId()) {
|
||||
case TYPE_CONNECTOR:
|
||||
return new ElroConnectsBridgeHandler((Bridge) thing, networkAddressService,
|
||||
dynamicStateDescriptionProvider);
|
||||
case TYPE_SMOKEALARM:
|
||||
return new ElroConnectsSmokeAlarmHandler(thing);
|
||||
case TYPE_WATERALARM:
|
||||
return new ElroConnectsWaterAlarmHandler(thing);
|
||||
case TYPE_COALARM:
|
||||
return new ElroConnectsCOAlarmHandler(thing);
|
||||
case TYPE_HEATALARM:
|
||||
return new ElroConnectsHeatAlarmHandler(thing);
|
||||
case TYPE_ENTRYSENSOR:
|
||||
return new ElroConnectsEntrySensorHandler(thing);
|
||||
case TYPE_MOTIONSENSOR:
|
||||
return new ElroConnectsMotionSensorHandler(thing);
|
||||
case TYPE_POWERSOCKET:
|
||||
return new ElroConnectsPowerSocketHandler(thing);
|
||||
case TYPE_THSENSOR:
|
||||
return new ElroConnectsDeviceHandler(thing);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Reference
|
||||
protected void setNetworkAddressService(NetworkAddressService networkAddressService) {
|
||||
this.networkAddressService = networkAddressService;
|
||||
}
|
||||
|
||||
protected void unsetNetworkAddressService(NetworkAddressService networkAddressService) {
|
||||
this.networkAddressService = null;
|
||||
}
|
||||
|
||||
@Reference
|
||||
protected void setDynamicStateDescriptionProvider(
|
||||
ElroConnectsDynamicStateDescriptionProvider dynamicStateDescriptionProver) {
|
||||
this.dynamicStateDescriptionProvider = dynamicStateDescriptionProver;
|
||||
}
|
||||
|
||||
protected void unsetDynamicStateDescriptionProvider(
|
||||
ElroConnectsDynamicStateDescriptionProvider dynamicStateDescriptionProver) {
|
||||
this.dynamicStateDescriptionProvider = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.elroconnects.internal.util.ElroConnectsUtil;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsMessage} represents the JSON messages exchanged with the ELRO Connects K1 Connector. This
|
||||
* class is used to serialize/deserialize the messages.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@SuppressWarnings("unused") // Suppress warning on serialized fields
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsMessage {
|
||||
|
||||
private static class Data {
|
||||
private int cmdId;
|
||||
|
||||
@SerializedName(value = "device_ID")
|
||||
private @Nullable Integer deviceId;
|
||||
|
||||
@SerializedName(value = "device_name")
|
||||
private @Nullable String deviceName;
|
||||
|
||||
@SerializedName(value = "device_status")
|
||||
private @Nullable String deviceStatus;
|
||||
|
||||
@SerializedName(value = "answer_content")
|
||||
private @Nullable String answerContent;
|
||||
|
||||
@SerializedName(value = "sence_group")
|
||||
private @Nullable Integer sceneGroup;
|
||||
|
||||
@SerializedName(value = "scene_type")
|
||||
private @Nullable Integer sceneType;
|
||||
|
||||
@SerializedName(value = "scene_content")
|
||||
private @Nullable String sceneContent;
|
||||
}
|
||||
|
||||
private static class Params {
|
||||
private String devTid = "";
|
||||
private String ctrlKey = "";
|
||||
private Data data = new Data();
|
||||
}
|
||||
|
||||
private int msgId;
|
||||
private String action = "appSend";
|
||||
private Params params = new Params();
|
||||
|
||||
public ElroConnectsMessage(int msgId, String devTid, String ctrlKey, int cmdId) {
|
||||
this.msgId = msgId;
|
||||
params.devTid = devTid;
|
||||
params.ctrlKey = ctrlKey;
|
||||
params.data.cmdId = cmdId;
|
||||
}
|
||||
|
||||
public ElroConnectsMessage(int msgId) {
|
||||
this.msgId = msgId;
|
||||
action = "heartbeat";
|
||||
}
|
||||
|
||||
public ElroConnectsMessage withDeviceStatus(String deviceStatus) {
|
||||
params.data.deviceStatus = deviceStatus;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElroConnectsMessage withDeviceId(int deviceId) {
|
||||
params.data.deviceId = deviceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElroConnectsMessage withSceneType(int sceneType) {
|
||||
params.data.sceneType = sceneType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElroConnectsMessage withSceneGroup(int sceneGroup) {
|
||||
params.data.sceneGroup = sceneGroup;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElroConnectsMessage withSceneContent(String sceneContent) {
|
||||
params.data.sceneContent = sceneContent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElroConnectsMessage withAnswerContent(String answerContent) {
|
||||
params.data.answerContent = answerContent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public int getCmdId() {
|
||||
return params.data.cmdId;
|
||||
}
|
||||
|
||||
public String getDeviceStatus() {
|
||||
return ElroConnectsUtil.stringOrEmpty(params.data.deviceStatus);
|
||||
}
|
||||
|
||||
public int getSceneGroup() {
|
||||
return ElroConnectsUtil.intOrZero(params.data.sceneGroup);
|
||||
}
|
||||
|
||||
public int getSceneType() {
|
||||
return ElroConnectsUtil.intOrZero(params.data.sceneType);
|
||||
}
|
||||
|
||||
public String getSceneContent() {
|
||||
return ElroConnectsUtil.stringOrEmpty(params.data.sceneContent);
|
||||
}
|
||||
|
||||
public String getAnswerContent() {
|
||||
return ElroConnectsUtil.stringOrEmpty(params.data.answerContent);
|
||||
}
|
||||
|
||||
public int getDeviceId() {
|
||||
return ElroConnectsUtil.intOrZero(params.data.deviceId);
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return ElroConnectsUtil.stringOrEmpty(params.data.deviceName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.devices;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.ElroDeviceStatus;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsBridgeHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsDeviceHandler;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsDevice} is an abstract class representing all basic properties for ELRO Connects devices.
|
||||
* Concrete subclasses will contain specific logic for each device type.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class ElroConnectsDevice {
|
||||
|
||||
// minimum data to create an instance of the class
|
||||
protected int deviceId;
|
||||
protected ElroConnectsBridgeHandler bridge;
|
||||
|
||||
protected volatile String deviceName = "";
|
||||
protected volatile String deviceType = "";
|
||||
protected volatile String deviceStatus = "";
|
||||
|
||||
protected volatile Map<String, ElroDeviceStatus> statusMap = Map.of();
|
||||
|
||||
/**
|
||||
* Create a new instance of a subclass of {@link ElroConnectsDevice}. These instances get created by an instance
|
||||
* {@link ElroConnectsBridgeHandler}. The deviceId will be set on creation. Other fields will be set as and when the
|
||||
* information is received from the K1 hub.
|
||||
*
|
||||
* @param deviceId
|
||||
* @param bridge
|
||||
*/
|
||||
public ElroConnectsDevice(int deviceId, ElroConnectsBridgeHandler bridge) {
|
||||
this.deviceId = deviceId;
|
||||
this.bridge = bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current status of the device.
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
protected ElroDeviceStatus getStatus() {
|
||||
String deviceStatus = this.deviceStatus;
|
||||
ElroDeviceStatus elroStatus = ElroDeviceStatus.UNDEF;
|
||||
|
||||
if (deviceStatus.length() >= 6) {
|
||||
elroStatus = statusMap.getOrDefault(deviceStatus.substring(4, 6), ElroDeviceStatus.UNDEF);
|
||||
}
|
||||
|
||||
return elroStatus;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
|
||||
public void setDeviceType(String deviceType) {
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public void setDeviceStatus(String deviceStatus) {
|
||||
this.deviceStatus = deviceStatus;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public String getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the {@link ElroConnectsDeviceHandler} for device.
|
||||
*
|
||||
* @return handler for the device.
|
||||
*/
|
||||
protected @Nullable ElroConnectsDeviceHandler getHandler() {
|
||||
return bridge.getDeviceHandler(deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all {@link ElroConnectsDeviceHandler} channel states with information received from the device. This
|
||||
* method needs to be implemented in the concrete subclass when any state updates are received from the device.
|
||||
*/
|
||||
public abstract void updateState();
|
||||
|
||||
/**
|
||||
* Send alarm test message to the device. This method is called from the {@link ElroConnectsDeviceHandler}. The
|
||||
* method needs to be implemented in the concrete subclass when test alarms are supported.
|
||||
*/
|
||||
public abstract void testAlarm();
|
||||
|
||||
/**
|
||||
* Send alarm mute message to the device. This method is called from the {@link ElroConnectsDeviceHandler}. The
|
||||
* method needs to be implemented in the concrete subclass when alarm muting is supported.
|
||||
*/
|
||||
public abstract void muteAlarm();
|
||||
|
||||
/**
|
||||
* Send state switch message to the device. This method is called from the {@link ElroConnectsDeviceHandler}. The
|
||||
* method needs to be implemented in the concrete subclass when switching the state on/off is supported.
|
||||
*/
|
||||
public abstract void switchState(boolean state);
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.devices;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.ElroDeviceStatus;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsBridgeHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsDeviceHandler;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsDeviceCxsmAlarm} is representing an ELRO Connects Cxsm Alarm device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsDeviceCxsmAlarm extends ElroConnectsDevice {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ElroConnectsDeviceCxsmAlarm.class);
|
||||
|
||||
// device states
|
||||
private static final String STAT_ALARM = "19";
|
||||
private static final String STAT_TEST = "17";
|
||||
private static final String STAT_FAULT = "12";
|
||||
private static final String STAT_SILENCE_1 = "1B";
|
||||
private static final String STAT_SILENCE_2 = "15";
|
||||
private static final String STAT_NORMAL = "AA";
|
||||
|
||||
private static final Set<String> T_ALARM = Set.of(STAT_ALARM);
|
||||
private static final Set<String> T_TEST = Set.of(STAT_TEST);
|
||||
private static final Set<String> T_FAULT = Set.of(STAT_FAULT);
|
||||
private static final Set<String> T_SILENCE = Set.of(STAT_SILENCE_1, STAT_SILENCE_2);
|
||||
private static final Set<String> T_NORMAL = Set.of(STAT_NORMAL);
|
||||
|
||||
private static final Map<ElroDeviceStatus, Set<String>> DEVICE_STATUS_MAP = Map.ofEntries(
|
||||
Map.entry(ElroDeviceStatus.NORMAL, T_NORMAL), Map.entry(ElroDeviceStatus.TRIGGERED, T_ALARM),
|
||||
Map.entry(ElroDeviceStatus.TEST, T_TEST), Map.entry(ElroDeviceStatus.SILENCE, T_SILENCE),
|
||||
Map.entry(ElroDeviceStatus.FAULT, T_FAULT));
|
||||
|
||||
// device commands
|
||||
private static final String CMD_TEST = "17000000";
|
||||
private static final String CMD_SILENCE_1 = "1B000000";
|
||||
private static final String CMD_SILENCE_2 = "15000000";
|
||||
|
||||
public ElroConnectsDeviceCxsmAlarm(int deviceId, ElroConnectsBridgeHandler bridge) {
|
||||
super(deviceId, bridge);
|
||||
statusMap = DEVICE_STATUS_MAP.entrySet().stream()
|
||||
.flatMap(e -> e.getValue().stream().map(v -> Map.entry(v, e.getKey())))
|
||||
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void muteAlarm() {
|
||||
try {
|
||||
ElroDeviceStatus elroStatus = getStatus();
|
||||
if (ElroDeviceStatus.FAULT.equals(elroStatus)) {
|
||||
bridge.deviceControl(deviceId, CMD_SILENCE_2);
|
||||
} else {
|
||||
bridge.deviceControl(deviceId, CMD_SILENCE_1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Failed to control device: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAlarm() {
|
||||
try {
|
||||
ElroDeviceStatus elroStatus = getStatus();
|
||||
if (!(ElroDeviceStatus.TRIGGERED.equals(elroStatus) || ElroDeviceStatus.TEST.equals(elroStatus))) {
|
||||
bridge.deviceControl(deviceId, CMD_TEST);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Failed to control device: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState() {
|
||||
ElroConnectsDeviceHandler handler = getHandler();
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ElroDeviceStatus elroStatus = getStatus();
|
||||
int batteryLevel = 0;
|
||||
String deviceStatus = this.deviceStatus;
|
||||
if (deviceStatus.length() >= 6) {
|
||||
batteryLevel = Integer.parseInt(deviceStatus.substring(2, 4), 16);
|
||||
} else {
|
||||
elroStatus = ElroDeviceStatus.FAULT;
|
||||
logger.debug("Could not decode device status: {}", deviceStatus);
|
||||
}
|
||||
|
||||
switch (elroStatus) {
|
||||
case UNDEF:
|
||||
handler.updateState(BATTERY_LEVEL, UnDefType.UNDEF);
|
||||
handler.updateState(LOW_BATTERY, UnDefType.UNDEF);
|
||||
handler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Device " + deviceId + " is not syncing with K1 hub");
|
||||
break;
|
||||
case FAULT:
|
||||
handler.updateState(BATTERY_LEVEL, UnDefType.UNDEF);
|
||||
handler.updateState(LOW_BATTERY, UnDefType.UNDEF);
|
||||
handler.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "Device " + deviceId + " has a fault");
|
||||
break;
|
||||
default:
|
||||
handler.updateState(BATTERY_LEVEL, new DecimalType(batteryLevel));
|
||||
handler.updateState(LOW_BATTERY, (batteryLevel < 15) ? OnOffType.ON : OnOffType.OFF);
|
||||
handler.updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchState(boolean state) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.devices;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.ElroDeviceStatus;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsBridgeHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsDeviceHandler;
|
||||
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.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsDeviceEntrySensor} is representing a generic ELRO Connects Entry Sensor device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsDeviceEntrySensor extends ElroConnectsDevice {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ElroConnectsDeviceEntrySensor.class);
|
||||
|
||||
// device states
|
||||
private static final String STAT_OPEN = "55";
|
||||
private static final String STAT_STILL_OPEN = "66";
|
||||
private static final String STAT_FAULT = "11";
|
||||
private static final String STAT_NORMAL = "AA";
|
||||
|
||||
private static final Set<String> T_OPEN = Set.of(STAT_OPEN, STAT_STILL_OPEN);
|
||||
private static final Set<String> T_FAULT = Set.of(STAT_FAULT);
|
||||
private static final Set<String> T_NORMAL = Set.of(STAT_NORMAL);
|
||||
|
||||
private static final Map<ElroDeviceStatus, Set<String>> DEVICE_STATUS_MAP = Map.ofEntries(
|
||||
Map.entry(ElroDeviceStatus.NORMAL, T_NORMAL), Map.entry(ElroDeviceStatus.OPEN, T_OPEN),
|
||||
Map.entry(ElroDeviceStatus.FAULT, T_FAULT));
|
||||
|
||||
public ElroConnectsDeviceEntrySensor(int deviceId, ElroConnectsBridgeHandler bridge) {
|
||||
super(deviceId, bridge);
|
||||
statusMap = DEVICE_STATUS_MAP.entrySet().stream()
|
||||
.flatMap(e -> e.getValue().stream().map(v -> Map.entry(v, e.getKey())))
|
||||
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState() {
|
||||
ElroConnectsDeviceHandler handler = getHandler();
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ElroDeviceStatus elroStatus = getStatus();
|
||||
int batteryLevel = 0;
|
||||
String deviceStatus = this.deviceStatus;
|
||||
if (deviceStatus.length() >= 6) {
|
||||
batteryLevel = Integer.parseInt(deviceStatus.substring(2, 4), 16);
|
||||
} else {
|
||||
elroStatus = ElroDeviceStatus.FAULT;
|
||||
logger.debug("Could not decode device status: {}", deviceStatus);
|
||||
}
|
||||
|
||||
switch (elroStatus) {
|
||||
case UNDEF:
|
||||
handler.updateState(ENTRY, UnDefType.UNDEF);
|
||||
handler.updateState(BATTERY_LEVEL, UnDefType.UNDEF);
|
||||
handler.updateState(LOW_BATTERY, UnDefType.UNDEF);
|
||||
handler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Device " + deviceId + " is not syncing with K1 hub");
|
||||
break;
|
||||
case FAULT:
|
||||
handler.updateState(ENTRY, UnDefType.UNDEF);
|
||||
handler.updateState(BATTERY_LEVEL, UnDefType.UNDEF);
|
||||
handler.updateState(LOW_BATTERY, UnDefType.UNDEF);
|
||||
handler.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "Device " + deviceId + " has a fault");
|
||||
break;
|
||||
default:
|
||||
handler.updateState(ENTRY,
|
||||
ElroDeviceStatus.OPEN.equals(elroStatus) ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
|
||||
handler.updateState(BATTERY_LEVEL, new DecimalType(batteryLevel));
|
||||
handler.updateState(LOW_BATTERY, (batteryLevel < 15) ? OnOffType.ON : OnOffType.OFF);
|
||||
handler.updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAlarm() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void muteAlarm() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchState(boolean state) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.devices;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.ElroDeviceStatus;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsBridgeHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsDeviceHandler;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsDeviceGenericAlarm} is representing a generic ELRO Connects Alarm device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsDeviceGenericAlarm extends ElroConnectsDevice {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ElroConnectsDeviceGenericAlarm.class);
|
||||
|
||||
// device states
|
||||
private static final String STAT_ALARM = "55";
|
||||
private static final String STAT_TEST = "BB";
|
||||
private static final String STAT_FAULT = "11";
|
||||
private static final String STAT_SILENCE = "50";
|
||||
private static final String STAT_NORMAL = "AA";
|
||||
|
||||
private static final Set<String> T_ALARM = Set.of(STAT_ALARM);
|
||||
private static final Set<String> T_TEST = Set.of(STAT_TEST);
|
||||
private static final Set<String> T_FAULT = Set.of(STAT_FAULT);
|
||||
private static final Set<String> T_SILENCE = Set.of(STAT_SILENCE);
|
||||
private static final Set<String> T_NORMAL = Set.of(STAT_NORMAL);
|
||||
|
||||
private static final Map<ElroDeviceStatus, Set<String>> DEVICE_STATUS_MAP = Map.ofEntries(
|
||||
Map.entry(ElroDeviceStatus.NORMAL, T_NORMAL), Map.entry(ElroDeviceStatus.TRIGGERED, T_ALARM),
|
||||
Map.entry(ElroDeviceStatus.TEST, T_TEST), Map.entry(ElroDeviceStatus.SILENCE, T_SILENCE),
|
||||
Map.entry(ElroDeviceStatus.FAULT, T_FAULT));
|
||||
|
||||
// device commands
|
||||
protected static final String CMD_TEST = "BB000000";
|
||||
protected static final String CMD_SILENCE = "50000000";
|
||||
|
||||
public ElroConnectsDeviceGenericAlarm(int deviceId, ElroConnectsBridgeHandler bridge) {
|
||||
super(deviceId, bridge);
|
||||
statusMap = DEVICE_STATUS_MAP.entrySet().stream()
|
||||
.flatMap(e -> e.getValue().stream().map(v -> Map.entry(v, e.getKey())))
|
||||
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void muteAlarm() {
|
||||
try {
|
||||
bridge.deviceControl(deviceId, CMD_SILENCE);
|
||||
} catch (IOException e) {
|
||||
logger.debug("Failed to control device: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAlarm() {
|
||||
try {
|
||||
ElroDeviceStatus elroStatus = getStatus();
|
||||
if (!(ElroDeviceStatus.TRIGGERED.equals(elroStatus) || ElroDeviceStatus.TEST.equals(elroStatus))) {
|
||||
bridge.deviceControl(deviceId, CMD_TEST);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Failed to control device: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState() {
|
||||
ElroConnectsDeviceHandler handler = getHandler();
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ElroDeviceStatus elroStatus = getStatus();
|
||||
int batteryLevel = 0;
|
||||
String deviceStatus = this.deviceStatus;
|
||||
if (deviceStatus.length() >= 6) {
|
||||
batteryLevel = Integer.parseInt(deviceStatus.substring(2, 4), 16);
|
||||
} else {
|
||||
elroStatus = ElroDeviceStatus.FAULT;
|
||||
logger.debug("Could not decode device status: {}", deviceStatus);
|
||||
}
|
||||
|
||||
switch (elroStatus) {
|
||||
case UNDEF:
|
||||
handler.updateState(BATTERY_LEVEL, UnDefType.UNDEF);
|
||||
handler.updateState(LOW_BATTERY, UnDefType.UNDEF);
|
||||
handler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Device " + deviceId + " is not syncing with K1 hub");
|
||||
break;
|
||||
case FAULT:
|
||||
handler.updateState(BATTERY_LEVEL, UnDefType.UNDEF);
|
||||
handler.updateState(LOW_BATTERY, UnDefType.UNDEF);
|
||||
handler.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "Device " + deviceId + " has a fault");
|
||||
break;
|
||||
default:
|
||||
handler.updateState(BATTERY_LEVEL, new DecimalType(batteryLevel));
|
||||
handler.updateState(LOW_BATTERY, (batteryLevel < 15) ? OnOffType.ON : OnOffType.OFF);
|
||||
handler.updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchState(boolean state) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.devices;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.ElroDeviceStatus;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsBridgeHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsDeviceHandler;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsDeviceMotionSensor} is representing an ELRO Connects Motion Sensor device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsDeviceMotionSensor extends ElroConnectsDevice {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ElroConnectsDeviceMotionSensor.class);
|
||||
|
||||
// device states
|
||||
private static final String STAT_TRIGGERED = "55";
|
||||
private static final String STAT_TEARED = "A0";
|
||||
private static final String STAT_FAULT = "11";
|
||||
private static final String STAT_NORMAL = "AA";
|
||||
|
||||
private static final Set<String> T_TRIGGERED = Set.of(STAT_TEARED, STAT_TRIGGERED);
|
||||
private static final Set<String> T_FAULT = Set.of(STAT_FAULT);
|
||||
private static final Set<String> T_NORMAL = Set.of(STAT_NORMAL);
|
||||
|
||||
private static final Map<ElroDeviceStatus, Set<String>> DEVICE_STATUS_MAP = Map.ofEntries(
|
||||
Map.entry(ElroDeviceStatus.NORMAL, T_NORMAL), Map.entry(ElroDeviceStatus.TRIGGERED, T_TRIGGERED),
|
||||
Map.entry(ElroDeviceStatus.FAULT, T_FAULT));
|
||||
|
||||
public ElroConnectsDeviceMotionSensor(int deviceId, ElroConnectsBridgeHandler bridge) {
|
||||
super(deviceId, bridge);
|
||||
statusMap = DEVICE_STATUS_MAP.entrySet().stream()
|
||||
.flatMap(e -> e.getValue().stream().map(v -> Map.entry(v, e.getKey())))
|
||||
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState() {
|
||||
ElroConnectsDeviceHandler handler = getHandler();
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ElroDeviceStatus elroStatus = getStatus();
|
||||
int batteryLevel = 0;
|
||||
String deviceStatus = this.deviceStatus;
|
||||
if (deviceStatus.length() >= 6) {
|
||||
batteryLevel = Integer.parseInt(deviceStatus.substring(2, 4), 16);
|
||||
} else {
|
||||
elroStatus = ElroDeviceStatus.FAULT;
|
||||
logger.debug("Could not decode device status: {}", deviceStatus);
|
||||
}
|
||||
|
||||
switch (elroStatus) {
|
||||
case UNDEF:
|
||||
handler.updateState(MOTION, UnDefType.UNDEF);
|
||||
handler.updateState(BATTERY_LEVEL, UnDefType.UNDEF);
|
||||
handler.updateState(LOW_BATTERY, UnDefType.UNDEF);
|
||||
handler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Device " + deviceId + " is not syncing with K1 hub");
|
||||
break;
|
||||
case FAULT:
|
||||
handler.updateState(MOTION, UnDefType.UNDEF);
|
||||
handler.updateState(BATTERY_LEVEL, UnDefType.UNDEF);
|
||||
handler.updateState(LOW_BATTERY, UnDefType.UNDEF);
|
||||
handler.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "Device " + deviceId + " has a fault");
|
||||
break;
|
||||
default:
|
||||
handler.updateState(MOTION,
|
||||
ElroDeviceStatus.TRIGGERED.equals(elroStatus) ? OnOffType.ON : OnOffType.OFF);
|
||||
handler.updateState(BATTERY_LEVEL, new DecimalType(batteryLevel));
|
||||
handler.updateState(LOW_BATTERY, (batteryLevel < 15) ? OnOffType.ON : OnOffType.OFF);
|
||||
handler.updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAlarm() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void muteAlarm() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchState(boolean state) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.devices;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.POWER_STATE;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsBridgeHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsDeviceHandler;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsDevicePowerSocket} is representing an ELRO Connects power socket device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsDevicePowerSocket extends ElroConnectsDevice {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ElroConnectsDevicePowerSocket.class);
|
||||
|
||||
// device states
|
||||
private static final String STAT_ON = "00";
|
||||
private static final String STAT_OFF = "01";
|
||||
|
||||
// device commands
|
||||
protected static final String CMD_OFF = "0101FFFF";
|
||||
protected static final String CMD_ON = "0100FFFF";
|
||||
|
||||
public ElroConnectsDevicePowerSocket(int deviceId, ElroConnectsBridgeHandler bridge) {
|
||||
super(deviceId, bridge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchState(boolean state) {
|
||||
try {
|
||||
bridge.deviceControl(deviceId, state ? CMD_ON : CMD_OFF);
|
||||
} catch (IOException e) {
|
||||
logger.debug("Failed to control device: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState() {
|
||||
ElroConnectsDeviceHandler handler = getHandler();
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String deviceStatus = this.deviceStatus;
|
||||
if (deviceStatus.length() < 6) {
|
||||
logger.debug("Could not decode device status: {}", deviceStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
String status = deviceStatus.substring(4, 6);
|
||||
State state = STAT_ON.equals(status) ? OnOffType.ON
|
||||
: (STAT_OFF.equals(status) ? OnOffType.OFF : UnDefType.UNDEF);
|
||||
handler.updateState(POWER_STATE, state);
|
||||
if (UnDefType.UNDEF.equals(state)) {
|
||||
handler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Device " + deviceId + " is not syncing with K1 hub");
|
||||
} else {
|
||||
handler.updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAlarm() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void muteAlarm() {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.devices;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.ElroDeviceStatus;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsBridgeHandler;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsDeviceHandler;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsDeviceTemperatureSensor} is representing an ELRO Connects temperature and humidity sensor
|
||||
* device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsDeviceTemperatureSensor extends ElroConnectsDevice {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ElroConnectsDeviceTemperatureSensor.class);
|
||||
|
||||
public ElroConnectsDeviceTemperatureSensor(int deviceId, ElroConnectsBridgeHandler bridge) {
|
||||
super(deviceId, bridge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState() {
|
||||
ElroConnectsDeviceHandler handler = getHandler();
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ElroDeviceStatus elroStatus = ElroDeviceStatus.NORMAL;
|
||||
int batteryLevel = 0;
|
||||
int temperature = 0;
|
||||
int humidity = 0;
|
||||
String deviceStatus = this.deviceStatus;
|
||||
if (deviceStatus.length() >= 8) {
|
||||
batteryLevel = Integer.parseInt(deviceStatus.substring(2, 4), 16);
|
||||
temperature = Byte.parseByte(deviceStatus.substring(4, 6), 16);
|
||||
humidity = Integer.parseInt(deviceStatus.substring(6, 8));
|
||||
} else {
|
||||
elroStatus = ElroDeviceStatus.FAULT;
|
||||
logger.debug("Could not decode device status: {}", deviceStatus);
|
||||
}
|
||||
|
||||
switch (elroStatus) {
|
||||
case FAULT:
|
||||
handler.updateState(BATTERY_LEVEL, UnDefType.UNDEF);
|
||||
handler.updateState(LOW_BATTERY, UnDefType.UNDEF);
|
||||
handler.updateState(TEMPERATURE, UnDefType.UNDEF);
|
||||
handler.updateState(HUMIDITY, UnDefType.UNDEF);
|
||||
handler.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "Device " + deviceId + " has a fault");
|
||||
break;
|
||||
default:
|
||||
handler.updateState(BATTERY_LEVEL, new DecimalType(batteryLevel));
|
||||
handler.updateState(LOW_BATTERY, (batteryLevel < 15) ? OnOffType.ON : OnOffType.OFF);
|
||||
handler.updateState(TEMPERATURE, new QuantityType<>(temperature, CELSIUS));
|
||||
handler.updateState(HUMIDITY, new QuantityType<>(humidity, Units.PERCENT));
|
||||
handler.updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAlarm() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void muteAlarm() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchState(boolean state) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.ElroDeviceType;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDevice;
|
||||
import org.openhab.binding.elroconnects.internal.handler.ElroConnectsBridgeHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
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;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsDiscoveryService} discovers devices connected to the ELRO Connects K1 Controller.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ElroConnectsDiscoveryService.class);
|
||||
|
||||
private @Nullable ElroConnectsBridgeHandler bridgeHandler;
|
||||
|
||||
private static final int TIMEOUT_SECONDS = 5;
|
||||
private static final int REFRESH_INTERVAL_SECONDS = 60;
|
||||
|
||||
private @Nullable ScheduledFuture<?> discoveryJob;
|
||||
|
||||
public ElroConnectsDiscoveryService() {
|
||||
super(ElroConnectsBindingConstants.SUPPORTED_THING_TYPES_UIDS, TIMEOUT_SECONDS);
|
||||
logger.debug("Bridge discovery service started");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
discoverDevices();
|
||||
}
|
||||
|
||||
private void discoverDevices() {
|
||||
logger.debug("Starting device discovery scan");
|
||||
ElroConnectsBridgeHandler bridge = bridgeHandler;
|
||||
if (bridge != null) {
|
||||
Map<Integer, ElroConnectsDevice> devices = bridge.getDevices();
|
||||
ThingUID bridgeUID = bridge.getThing().getUID();
|
||||
devices.entrySet().forEach(e -> {
|
||||
String deviceId = e.getKey().toString();
|
||||
String deviceName = e.getValue().getDeviceName();
|
||||
String deviceType = e.getValue().getDeviceType();
|
||||
if (!deviceType.isEmpty()) {
|
||||
ElroDeviceType type = TYPE_MAP.get(deviceType);
|
||||
if (type != null) {
|
||||
ThingTypeUID thingTypeUID = THING_TYPE_MAP.get(type);
|
||||
if (thingTypeUID != null) {
|
||||
thingDiscovered(DiscoveryResultBuilder
|
||||
.create(new ThingUID(thingTypeUID, bridgeUID, deviceId)).withLabel(deviceName)
|
||||
.withBridge(bridgeUID).withProperty(CONFIG_DEVICE_ID, deviceId)
|
||||
.withRepresentationProperty(CONFIG_DEVICE_ID).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
super.stopScan();
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
logger.debug("Start device background discovery");
|
||||
ScheduledFuture<?> job = discoveryJob;
|
||||
if (job == null || job.isCancelled()) {
|
||||
discoveryJob = scheduler.scheduleWithFixedDelay(this::discoverDevices, 0, REFRESH_INTERVAL_SECONDS,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
logger.debug("Stop device background discovery");
|
||||
ScheduledFuture<?> job = discoveryJob;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
discoveryJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof ElroConnectsBridgeHandler) {
|
||||
bridgeHandler = (ElroConnectsBridgeHandler) handler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return bridgeHandler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsBridgeConfiguration} class contains fields mapping bridge configuration parameters.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsBridgeConfiguration {
|
||||
|
||||
public String connectorId = "";
|
||||
public String ipAddress = "";
|
||||
public int refreshInterval = 60;
|
||||
}
|
||||
@@ -0,0 +1,864 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.ElroDeviceType;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsDynamicStateDescriptionProvider;
|
||||
import org.openhab.binding.elroconnects.internal.ElroConnectsMessage;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDevice;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDeviceCxsmAlarm;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDeviceEntrySensor;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDeviceGenericAlarm;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDeviceMotionSensor;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDevicePowerSocket;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDeviceTemperatureSensor;
|
||||
import org.openhab.binding.elroconnects.internal.discovery.ElroConnectsDiscoveryService;
|
||||
import org.openhab.binding.elroconnects.internal.util.ElroConnectsUtil;
|
||||
import org.openhab.core.common.NamedThreadFactory;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.StateDescription;
|
||||
import org.openhab.core.types.StateDescriptionFragmentBuilder;
|
||||
import org.openhab.core.types.StateOption;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsBridgeHandler} is the bridge handler responsible to for handling all communication with the
|
||||
* ELRO Connects K1 Hub. All individual device communication passes through the hub.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsBridgeHandler extends BaseBridgeHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ElroConnectsBridgeHandler.class);
|
||||
|
||||
private static final int PORT = 1025; // UDP port for UDP socket communication with K1 hub
|
||||
private static final int RESPONSE_TIMEOUT_MS = 5000; // max time to wait for receiving all responses on a request
|
||||
|
||||
// Default scene names are not received from K1 hub, so kept here
|
||||
private static final Map<Integer, String> DEFAULT_SCENES = Map.ofEntries(Map.entry(0, "Home"), Map.entry(1, "Away"),
|
||||
Map.entry(2, "Sleep"));
|
||||
private static final int MAX_DEFAULT_SCENE = 2;
|
||||
|
||||
// Command filter when syncing devices and scenes, other values would filter what gets received
|
||||
private static final String SYNC_COMMAND = "0002";
|
||||
|
||||
// Regex for valid connectorId
|
||||
private static final Pattern CONNECTOR_ID_PATTERN = Pattern.compile("^ST_([0-9a-f]){12}$");
|
||||
|
||||
// Message string for acknowledging receipt of data
|
||||
private static final String ACK_STRING = "{\"answer\": \"APP_answer_OK\"}";
|
||||
private static final byte[] ACK = ACK_STRING.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
// Connector expects to receive messages with an increasing id for each message
|
||||
// Max msgId is 65536, therefore use short and convert to unsigned Integer when using it
|
||||
private short msgId;
|
||||
|
||||
private NetworkAddressService networkAddressService;
|
||||
|
||||
// Used when restarting connection, delay restart for 1s to avoid high network traffic
|
||||
private volatile boolean restart;
|
||||
static final int RESTART_DELAY_MS = 1000;
|
||||
|
||||
private volatile String connectorId = "";
|
||||
// Used for getting IP address and keep connection alive messages
|
||||
private static final String QUERY_BASE_STRING = "IOT_KEY?";
|
||||
private volatile String queryString = QUERY_BASE_STRING + connectorId;
|
||||
// Regex to retrieve ctrlKey from response on IP address message
|
||||
private static final Pattern CTRL_KEY_PATTERN = Pattern.compile("KEY:([0-9a-f]*)");
|
||||
|
||||
private int refreshInterval = 60;
|
||||
private volatile @Nullable InetAddress addr;
|
||||
private volatile String ctrlKey = "";
|
||||
|
||||
private volatile @Nullable DatagramSocket socket;
|
||||
private volatile @Nullable DatagramPacket ackPacket;
|
||||
|
||||
private volatile @Nullable ScheduledFuture<?> syncFuture;
|
||||
private volatile @Nullable CompletableFuture<Boolean> awaitResponse;
|
||||
|
||||
private ElroConnectsDynamicStateDescriptionProvider stateDescriptionProvider;
|
||||
|
||||
private final Map<Integer, String> scenes = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, ElroConnectsDevice> devices = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, ElroConnectsDeviceHandler> deviceHandlers = new ConcurrentHashMap<>();
|
||||
|
||||
private int currentScene;
|
||||
|
||||
// We only keep 2 gson adapters used to serialize and deserialize all messages sent and received
|
||||
private final Gson gsonOut = new Gson();
|
||||
private Gson gsonIn = new Gson();
|
||||
|
||||
public ElroConnectsBridgeHandler(Bridge bridge, NetworkAddressService networkAddressService,
|
||||
ElroConnectsDynamicStateDescriptionProvider stateDescriptionProvider) {
|
||||
super(bridge);
|
||||
this.networkAddressService = networkAddressService;
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
|
||||
resetScenes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
ElroConnectsBridgeConfiguration config = getConfigAs(ElroConnectsBridgeConfiguration.class);
|
||||
connectorId = config.connectorId;
|
||||
refreshInterval = config.refreshInterval;
|
||||
|
||||
if (connectorId.isEmpty()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Device ID not set");
|
||||
return;
|
||||
} else if (!CONNECTOR_ID_PATTERN.matcher(connectorId).matches()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Device ID not of format ST_xxxxxxxxxxxx with xxxxxxxxxxxx the lowercase MAC address of the connector");
|
||||
return;
|
||||
}
|
||||
|
||||
queryString = QUERY_BASE_STRING + connectorId;
|
||||
|
||||
scheduler.submit(this::startCommunication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
stopCommunication();
|
||||
}
|
||||
|
||||
private synchronized void startCommunication() {
|
||||
ElroConnectsBridgeConfiguration config = getConfigAs(ElroConnectsBridgeConfiguration.class);
|
||||
InetAddress addr = this.addr;
|
||||
|
||||
String ipAddress = config.ipAddress;
|
||||
if (!ipAddress.isEmpty()) {
|
||||
try {
|
||||
addr = InetAddress.getByName(ipAddress);
|
||||
this.addr = addr;
|
||||
} catch (UnknownHostException e) {
|
||||
addr = null;
|
||||
logger.warn("Unknown host for {}, trying to discover address", ipAddress);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
addr = getAddr(addr == null);
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Error trying to find IP address for connector ID " + connectorId + ".");
|
||||
stopCommunication();
|
||||
return;
|
||||
}
|
||||
if (addr == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Error trying to find IP address for connector ID " + connectorId + ".");
|
||||
stopCommunication();
|
||||
return;
|
||||
}
|
||||
|
||||
String ctrlKey = this.ctrlKey;
|
||||
if (ctrlKey.isEmpty()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Communication data error while starting communication.");
|
||||
stopCommunication();
|
||||
return;
|
||||
}
|
||||
|
||||
DatagramSocket socket;
|
||||
try {
|
||||
socket = createSocket(false);
|
||||
this.socket = socket;
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Socket error while starting communication: " + e.getMessage());
|
||||
stopCommunication();
|
||||
return;
|
||||
}
|
||||
|
||||
ackPacket = new DatagramPacket(ACK, ACK.length, addr, PORT);
|
||||
|
||||
logger.debug("Connected to connector {} at {}:{}", connectorId, addr, PORT);
|
||||
|
||||
try {
|
||||
// Start ELRO Connects listener. This listener will act on all messages coming from ELRO K1 Connector.
|
||||
(new NamedThreadFactory(THREAD_NAME_PREFIX + thing.getUID().getAsString()).newThread(this::runElroEvents))
|
||||
.start();
|
||||
|
||||
keepAlive();
|
||||
|
||||
// First get status, then name. The status response contains the device types needed to instantiate correct
|
||||
// classes.
|
||||
getDeviceStatuses();
|
||||
getDeviceNames();
|
||||
|
||||
syncScenes();
|
||||
getCurrentScene();
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
updateState(SCENE, new StringType(String.valueOf(currentScene)));
|
||||
} catch (IOException e) {
|
||||
restartCommunication("Error in communication getting initial data: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleSyncStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IP address and ctrl key of the connector by broadcasting message with connectorId. This should be used
|
||||
* when initializing the connection. the ctrlKey field is set.
|
||||
*
|
||||
* @param broadcast, if true find address by broadcast, otherwise simply send to configured address to retrieve key
|
||||
* only
|
||||
* @return IP address of connector
|
||||
* @throws IOException
|
||||
*/
|
||||
private @Nullable InetAddress getAddr(boolean broadcast) throws IOException {
|
||||
try (DatagramSocket socket = createSocket(true)) {
|
||||
String response = sendAndReceive(socket, queryString, broadcast);
|
||||
Matcher keyMatcher = CTRL_KEY_PATTERN.matcher(response);
|
||||
ctrlKey = keyMatcher.find() ? keyMatcher.group(1) : "";
|
||||
logger.debug("Key: {}", ctrlKey);
|
||||
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send keep alive message.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void keepAlive() throws IOException {
|
||||
DatagramSocket socket = this.socket;
|
||||
if (socket != null) {
|
||||
logger.trace("Keep alive");
|
||||
// Sending query string, so the connection with the K1 hub stays alive
|
||||
awaitResponse(true);
|
||||
send(socket, queryString, false);
|
||||
} else {
|
||||
restartCommunication("Error in communication, no socket to send keep alive");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup socket when the communication with ELRO Connects connector is closed.
|
||||
*
|
||||
*/
|
||||
private synchronized void stopCommunication() {
|
||||
ScheduledFuture<?> sync = syncFuture;
|
||||
if (sync != null) {
|
||||
sync.cancel(true);
|
||||
}
|
||||
syncFuture = null;
|
||||
|
||||
stopAwaitResponse();
|
||||
|
||||
DatagramSocket socket = this.socket;
|
||||
if (socket != null && !socket.isClosed()) {
|
||||
socket.close();
|
||||
}
|
||||
this.socket = null;
|
||||
|
||||
logger.debug("Communication stopped");
|
||||
}
|
||||
|
||||
/**
|
||||
* Close and restart communication with ELRO Connects system, to be called after error in communication.
|
||||
*
|
||||
* @param offlineMessage message for thing status
|
||||
*/
|
||||
private synchronized void restartCommunication(String offlineMessage) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, offlineMessage);
|
||||
|
||||
stopCommunication();
|
||||
|
||||
if (!restart) {
|
||||
logger.debug("Restart communication");
|
||||
|
||||
restart = true;
|
||||
scheduler.schedule(this::startFromRestart, RESTART_DELAY_MS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void startFromRestart() {
|
||||
restart = false;
|
||||
if (ThingStatus.OFFLINE.equals(thing.getStatus())) {
|
||||
startCommunication();
|
||||
}
|
||||
}
|
||||
|
||||
private DatagramSocket createSocket(boolean timeout) throws SocketException {
|
||||
DatagramSocket socket = new DatagramSocket();
|
||||
socket.setBroadcast(true);
|
||||
if (timeout) {
|
||||
socket.setSoTimeout(1000);
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read messages received through UDP socket.
|
||||
*
|
||||
* @param socket
|
||||
*/
|
||||
private void runElroEvents() {
|
||||
DatagramSocket socket = this.socket;
|
||||
|
||||
if (socket != null) {
|
||||
logger.debug("Listening for messages");
|
||||
|
||||
try {
|
||||
byte[] buffer = new byte[4096];
|
||||
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
|
||||
while (!Thread.interrupted()) {
|
||||
String response = receive(socket, buffer, packet);
|
||||
processMessage(socket, response);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
restartCommunication("Communication error in listener: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
restartCommunication("Error in communication, no socket to start listener");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule regular queries to sync devices and scenes.
|
||||
*/
|
||||
private void scheduleSyncStatus() {
|
||||
syncFuture = scheduler.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
keepAlive();
|
||||
syncDevices();
|
||||
syncScenes();
|
||||
getCurrentScene();
|
||||
} catch (IOException e) {
|
||||
restartCommunication("Error in communication refreshing device status: " + e.getMessage());
|
||||
}
|
||||
}, refreshInterval, refreshInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process response received from K1 Hub and send acknowledgement through open socket.
|
||||
*
|
||||
* @param socket
|
||||
* @param response
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processMessage(DatagramSocket socket, String response) throws IOException {
|
||||
if (!response.startsWith("{")) {
|
||||
// Not a Json to interpret, just ignore
|
||||
stopAwaitResponse();
|
||||
return;
|
||||
}
|
||||
ElroConnectsMessage message;
|
||||
String json = "";
|
||||
try {
|
||||
json = response.split("\\R")[0];
|
||||
message = gsonIn.fromJson(json, ElroConnectsMessage.class);
|
||||
sendAck(socket);
|
||||
} catch (JsonSyntaxException ignore) {
|
||||
logger.debug("Cannot decode, not a valid json: {}", json);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message.getCmdId()) {
|
||||
case ELRO_IGNORE_YES_NO:
|
||||
break;
|
||||
case ELRO_REC_DEVICE_NAME:
|
||||
processDeviceNameMessage(message);
|
||||
break;
|
||||
case ELRO_REC_DEVICE_STATUS:
|
||||
processDeviceStatusMessage(message);
|
||||
break;
|
||||
case ELRO_REC_ALARM:
|
||||
processAlarmTriggerMessage(message);
|
||||
break;
|
||||
case ELRO_REC_SCENE_NAME:
|
||||
processSceneNameMessage(message);
|
||||
break;
|
||||
case ELRO_REC_SCENE_TYPE:
|
||||
processSceneTypeMessage(message);
|
||||
break;
|
||||
case ELRO_REC_SCENE:
|
||||
processSceneMessage(message);
|
||||
break;
|
||||
default:
|
||||
logger.debug("CmdId not implemented: {}", message.getCmdId());
|
||||
}
|
||||
}
|
||||
|
||||
private void processDeviceStatusMessage(ElroConnectsMessage message) {
|
||||
int deviceId = message.getDeviceId();
|
||||
String deviceStatus = message.getDeviceStatus();
|
||||
if ("OVER".equals(deviceStatus)) {
|
||||
// last message in series received
|
||||
stopAwaitResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
ElroConnectsDevice device = devices.get(deviceId);
|
||||
device = (device == null) ? addDevice(message) : device;
|
||||
if (device == null) {
|
||||
// device type not recognized, could not be added
|
||||
return;
|
||||
}
|
||||
device.setDeviceStatus(deviceStatus);
|
||||
|
||||
device.updateState();
|
||||
}
|
||||
|
||||
private void processDeviceNameMessage(ElroConnectsMessage message) {
|
||||
String answerContent = message.getAnswerContent();
|
||||
if ("NAME_OVER".equals(answerContent)) {
|
||||
// last message in series received
|
||||
stopAwaitResponse();
|
||||
return;
|
||||
}
|
||||
if (answerContent.length() <= 4) {
|
||||
logger.debug("Could not decode answer {}", answerContent);
|
||||
return;
|
||||
}
|
||||
|
||||
int deviceId = Integer.parseInt(answerContent.substring(0, 4), 16);
|
||||
String deviceName = (new String(HexUtils.hexToBytes(answerContent.substring(4)))).replaceAll("[@$]*", "");
|
||||
ElroConnectsDevice device = devices.get(deviceId);
|
||||
if (device != null) {
|
||||
device.setDeviceName(deviceName);
|
||||
logger.debug("Device ID {} name: {}", deviceId, deviceName);
|
||||
}
|
||||
}
|
||||
|
||||
private void processSceneNameMessage(ElroConnectsMessage message) {
|
||||
int sceneId = message.getSceneGroup();
|
||||
String answerContent = message.getAnswerContent();
|
||||
String sceneName;
|
||||
if (sceneId > MAX_DEFAULT_SCENE) {
|
||||
if (answerContent.length() < 44) {
|
||||
logger.debug("Could not decode answer {}", answerContent);
|
||||
return;
|
||||
}
|
||||
sceneName = (new String(HexUtils.hexToBytes(answerContent.substring(6, 38)))).replaceAll("[@$]*", "");
|
||||
scenes.put(sceneId, sceneName);
|
||||
logger.debug("Scene ID {} name: {}", sceneId, sceneName);
|
||||
}
|
||||
}
|
||||
|
||||
private void processSceneTypeMessage(ElroConnectsMessage message) {
|
||||
String sceneContent = message.getSceneContent();
|
||||
if ("OVER".equals(sceneContent)) {
|
||||
// last message in series received
|
||||
stopAwaitResponse();
|
||||
|
||||
updateSceneOptions();
|
||||
}
|
||||
}
|
||||
|
||||
private void processSceneMessage(ElroConnectsMessage message) {
|
||||
int sceneId = message.getSceneGroup();
|
||||
|
||||
currentScene = sceneId;
|
||||
|
||||
updateState(SCENE, new StringType(String.valueOf(currentScene)));
|
||||
}
|
||||
|
||||
private void processAlarmTriggerMessage(ElroConnectsMessage message) {
|
||||
String answerContent = message.getAnswerContent();
|
||||
if (answerContent.length() < 10) {
|
||||
logger.debug("Could not decode answer {}", answerContent);
|
||||
return;
|
||||
}
|
||||
|
||||
int deviceId = Integer.parseInt(answerContent.substring(6, 10), 16);
|
||||
|
||||
ElroConnectsDeviceHandler handler = deviceHandlers.get(deviceId);
|
||||
if (handler != null) {
|
||||
handler.triggerAlarm();
|
||||
}
|
||||
logger.debug("Device ID {} alarm", deviceId);
|
||||
}
|
||||
|
||||
private @Nullable ElroConnectsDevice addDevice(ElroConnectsMessage message) {
|
||||
int deviceId = message.getDeviceId();
|
||||
String deviceType = message.getDeviceName();
|
||||
ElroDeviceType type = TYPE_MAP.getOrDefault(deviceType, ElroDeviceType.DEFAULT);
|
||||
|
||||
ElroConnectsDevice device;
|
||||
switch (type) {
|
||||
case CO_ALARM:
|
||||
case SM_ALARM:
|
||||
case WT_ALARM:
|
||||
case THERMAL_ALARM:
|
||||
device = new ElroConnectsDeviceGenericAlarm(deviceId, this);
|
||||
break;
|
||||
case CXSM_ALARM:
|
||||
device = new ElroConnectsDeviceCxsmAlarm(deviceId, this);
|
||||
break;
|
||||
case POWERSOCKET:
|
||||
device = new ElroConnectsDevicePowerSocket(deviceId, this);
|
||||
break;
|
||||
case ENTRY_SENSOR:
|
||||
device = new ElroConnectsDeviceEntrySensor(deviceId, this);
|
||||
break;
|
||||
case MOTION_SENSOR:
|
||||
device = new ElroConnectsDeviceMotionSensor(deviceId, this);
|
||||
break;
|
||||
case TH_SENSOR:
|
||||
device = new ElroConnectsDeviceTemperatureSensor(deviceId, this);
|
||||
break;
|
||||
default:
|
||||
logger.debug("Device type {} not supported", deviceType);
|
||||
return null;
|
||||
}
|
||||
device.setDeviceType(deviceType);
|
||||
devices.put(deviceId, device);
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just before sending message, this method should be called to make sure we wait for all responses that are still
|
||||
* expected to be received. The last response will be indicated by a token in the last response message.
|
||||
*
|
||||
* @param waitResponse true if we want to wait for response for next message to be sent before allowing subsequent
|
||||
* message
|
||||
*/
|
||||
private void awaitResponse(boolean waitResponse) {
|
||||
CompletableFuture<Boolean> waiting = awaitResponse;
|
||||
if (waiting != null) {
|
||||
try {
|
||||
logger.trace("Waiting for previous response before sending");
|
||||
waiting.get(RESPONSE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException ignore) {
|
||||
logger.trace("Wait for previous response timed out");
|
||||
}
|
||||
}
|
||||
awaitResponse = waitResponse ? new CompletableFuture<>() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when all responses on a request have been received.
|
||||
*/
|
||||
private void stopAwaitResponse() {
|
||||
CompletableFuture<Boolean> future = awaitResponse;
|
||||
if (future != null) {
|
||||
future.complete(true);
|
||||
}
|
||||
awaitResponse = null;
|
||||
}
|
||||
|
||||
private void sendAck(DatagramSocket socket) throws IOException {
|
||||
logger.debug("Send Ack: {}", ACK_STRING);
|
||||
socket.send(ackPacket);
|
||||
}
|
||||
|
||||
private String sendAndReceive(DatagramSocket socket, String query, boolean broadcast)
|
||||
throws UnknownHostException, IOException {
|
||||
send(socket, query, broadcast);
|
||||
byte[] buffer = new byte[4096];
|
||||
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
|
||||
return receive(socket, buffer, packet);
|
||||
}
|
||||
|
||||
private void send(DatagramSocket socket, String query, boolean broadcast) throws IOException {
|
||||
final InetAddress address = broadcast
|
||||
? InetAddress.getByName(networkAddressService.getConfiguredBroadcastAddress())
|
||||
: addr;
|
||||
if (address == null) {
|
||||
if (broadcast) {
|
||||
restartCommunication("No broadcast address, check network configuration");
|
||||
} else {
|
||||
restartCommunication("Failed sending, hub address was not set");
|
||||
}
|
||||
return;
|
||||
}
|
||||
logger.debug("Send: {}", query);
|
||||
final byte[] queryBuffer = query.getBytes(StandardCharsets.UTF_8);
|
||||
DatagramPacket queryPacket = new DatagramPacket(queryBuffer, queryBuffer.length, address, PORT);
|
||||
socket.send(queryPacket);
|
||||
}
|
||||
|
||||
private String receive(DatagramSocket socket, byte[] buffer, DatagramPacket packet) throws IOException {
|
||||
socket.receive(packet);
|
||||
String response = new String(packet.getData(), packet.getOffset(), packet.getLength());
|
||||
logger.debug("Received: {}", response);
|
||||
addr = packet.getAddress();
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic method to send an {@link ElroConnectsMessage} to the K1 hub.
|
||||
*
|
||||
* @param elroMessage
|
||||
* @param waitResponse true if no new messages should be allowed to be sent before receiving the full response
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized void sendElroMessage(ElroConnectsMessage elroMessage, boolean waitResponse)
|
||||
throws IOException {
|
||||
DatagramSocket socket = this.socket;
|
||||
if (socket != null) {
|
||||
String message = gsonOut.toJson(elroMessage);
|
||||
awaitResponse(waitResponse);
|
||||
send(socket, message, false);
|
||||
} else {
|
||||
throw new IOException("No socket");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send device control command. The device command string various by device type. The calling method is responsible
|
||||
* for creating the appropriate command string.
|
||||
*
|
||||
* @param deviceId
|
||||
* @param deviceCommand ELRO Connects device command string
|
||||
* @throws IOException
|
||||
*/
|
||||
public void deviceControl(int deviceId, String deviceCommand) throws IOException {
|
||||
String connectorId = this.connectorId;
|
||||
String ctrlKey = this.ctrlKey;
|
||||
logger.debug("Device control {}, status {}", deviceId, deviceCommand);
|
||||
ElroConnectsMessage elroMessage = new ElroConnectsMessage(msgIdIncrement(), connectorId, ctrlKey,
|
||||
ELRO_DEVICE_CONTROL).withDeviceId(ElroConnectsUtil.encode(deviceId)).withDeviceStatus(deviceCommand);
|
||||
sendElroMessage(elroMessage, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request to receive all device names.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void getDeviceNames() throws IOException {
|
||||
String connectorId = this.connectorId;
|
||||
String ctrlKey = this.ctrlKey;
|
||||
logger.debug("Get device names");
|
||||
ElroConnectsMessage elroMessage = new ElroConnectsMessage(msgIdIncrement(), connectorId, ctrlKey,
|
||||
ELRO_GET_DEVICE_NAME).withDeviceId(0);
|
||||
sendElroMessage(elroMessage, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request to receive all device statuses.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void getDeviceStatuses() throws IOException {
|
||||
String connectorId = this.connectorId;
|
||||
String ctrlKey = this.ctrlKey;
|
||||
logger.debug("Get all equipment status");
|
||||
ElroConnectsMessage elroMessage = new ElroConnectsMessage(msgIdIncrement(), connectorId, ctrlKey,
|
||||
ELRO_GET_DEVICE_STATUSES);
|
||||
sendElroMessage(elroMessage, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request to sync all devices statuses.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void syncDevices() throws IOException {
|
||||
String connectorId = this.connectorId;
|
||||
String ctrlKey = this.ctrlKey;
|
||||
logger.debug("Sync device status");
|
||||
ElroConnectsMessage elroMessage = new ElroConnectsMessage(msgIdIncrement(), connectorId, ctrlKey,
|
||||
ELRO_SYNC_DEVICES).withDeviceStatus(SYNC_COMMAND);
|
||||
sendElroMessage(elroMessage, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request to get the currently selected scene.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void getCurrentScene() throws IOException {
|
||||
String connectorId = this.connectorId;
|
||||
String ctrlKey = this.ctrlKey;
|
||||
logger.debug("Get current scene");
|
||||
ElroConnectsMessage elroMessage = new ElroConnectsMessage(msgIdIncrement(), connectorId, ctrlKey,
|
||||
ELRO_GET_SCENE);
|
||||
sendElroMessage(elroMessage, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message to set the current scene.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void selectScene(int scene) throws IOException {
|
||||
String connectorId = this.connectorId;
|
||||
String ctrlKey = this.ctrlKey;
|
||||
logger.debug("Select scene {}", scene);
|
||||
ElroConnectsMessage elroMessage = new ElroConnectsMessage(msgIdIncrement(), connectorId, ctrlKey,
|
||||
ELRO_SELECT_SCENE).withSceneType(ElroConnectsUtil.encode(scene));
|
||||
sendElroMessage(elroMessage, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request to sync all scenes.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void syncScenes() throws IOException {
|
||||
String connectorId = this.connectorId;
|
||||
String ctrlKey = this.ctrlKey;
|
||||
logger.debug("Sync scenes");
|
||||
ElroConnectsMessage elroMessage = new ElroConnectsMessage(msgIdIncrement(), connectorId, ctrlKey,
|
||||
ELRO_SYNC_SCENES).withSceneGroup(ElroConnectsUtil.encode(0)).withSceneContent(SYNC_COMMAND)
|
||||
.withAnswerContent(SYNC_COMMAND);
|
||||
sendElroMessage(elroMessage, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("Channel {}, command {}, type {}", channelUID, command, command.getClass());
|
||||
if (SCENE.equals(channelUID.getId())) {
|
||||
if (command instanceof RefreshType) {
|
||||
updateState(SCENE, new StringType(String.valueOf(currentScene)));
|
||||
} else if (command instanceof DecimalType) {
|
||||
try {
|
||||
selectScene(((DecimalType) command).intValue());
|
||||
} catch (IOException e) {
|
||||
restartCommunication("Error in communication while setting scene: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We do not get scene delete messages, therefore call this method before requesting list of scenes to clear list of
|
||||
* scenes.
|
||||
*/
|
||||
private void resetScenes() {
|
||||
scenes.clear();
|
||||
scenes.putAll(DEFAULT_SCENES);
|
||||
|
||||
updateSceneOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state option list for scene selection channel.
|
||||
*/
|
||||
private void updateSceneOptions() {
|
||||
// update the command scene command options
|
||||
List<StateOption> stateOptionList = new ArrayList<>();
|
||||
scenes.forEach((id, scene) -> {
|
||||
StateOption option = new StateOption(Integer.toString(id), scene);
|
||||
stateOptionList.add(option);
|
||||
});
|
||||
logger.trace("Scenes: {}", stateOptionList);
|
||||
|
||||
Channel channel = thing.getChannel(SCENE);
|
||||
if (channel != null) {
|
||||
ChannelUID channelUID = channel.getUID();
|
||||
StateDescription stateDescription = StateDescriptionFragmentBuilder.create().withReadOnly(false)
|
||||
.withOptions(stateOptionList).build().toStateDescription();
|
||||
stateDescriptionProvider.setDescription(channelUID, stateDescription);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages need to be sent with consecutive id's. Increment the msgId field and rotate at max unsigned short.
|
||||
*
|
||||
* @return new message id
|
||||
*/
|
||||
private int msgIdIncrement() {
|
||||
return Short.toUnsignedInt(msgId++);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ElroConnectsDeviceHandler} for the device with deviceId, should be called from the thing handler
|
||||
* when initializing the thing.
|
||||
*
|
||||
* @param deviceId
|
||||
* @param handler
|
||||
*/
|
||||
public void setDeviceHandler(int deviceId, ElroConnectsDeviceHandler handler) {
|
||||
deviceHandlers.put(deviceId, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset the {@link ElroConnectsDeviceHandler} for the device with deviceId, should be called from the thing handler
|
||||
* when disposing the thing.
|
||||
*
|
||||
* @param deviceId
|
||||
* @param handler
|
||||
*/
|
||||
public void unsetDeviceHandler(int deviceId, ElroConnectsDeviceHandler handler) {
|
||||
deviceHandlers.remove(deviceId, handler);
|
||||
}
|
||||
|
||||
public @Nullable ElroConnectsDeviceHandler getDeviceHandler(int deviceId) {
|
||||
return deviceHandlers.get(deviceId);
|
||||
}
|
||||
|
||||
public @Nullable ElroConnectsDevice getDevice(int deviceId) {
|
||||
return devices.get(deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full list of devices connected to the K1 hub. This can be used by the {@link ElroConnectsDiscoveryService} to
|
||||
* scan for devices connected to the K1 hub.
|
||||
*
|
||||
* @return devices
|
||||
*/
|
||||
public Map<Integer, ElroConnectsDevice> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(ElroConnectsDiscoveryService.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDevice;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsCOAlarmHandler} represents the thing handler for an ELRO Connects CO alarm device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsCOAlarmHandler extends ElroConnectsDeviceHandler {
|
||||
|
||||
public ElroConnectsCOAlarmHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
Integer id = deviceId;
|
||||
ElroConnectsBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
ElroConnectsDevice device = bridgeHandler.getDevice(id);
|
||||
if (device != null) {
|
||||
switch (channelUID.getId()) {
|
||||
case MUTE_ALARM:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
device.muteAlarm();
|
||||
}
|
||||
break;
|
||||
case TEST_ALARM:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
device.testAlarm();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.handleCommand(channelUID, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerAlarm() {
|
||||
triggerChannel(CO_ALARM);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsDeviceConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsDeviceConfiguration {
|
||||
|
||||
public int deviceId = 0;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDevice;
|
||||
import org.openhab.binding.elroconnects.internal.util.ElroConnectsUtil;
|
||||
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.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsDeviceHandler} represents the thing handler for an ELRO Connects device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsDeviceHandler extends BaseThingHandler {
|
||||
|
||||
protected int deviceId;
|
||||
|
||||
public ElroConnectsDeviceHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
ElroConnectsDeviceConfiguration config = getConfigAs(ElroConnectsDeviceConfiguration.class);
|
||||
deviceId = config.deviceId;
|
||||
|
||||
ElroConnectsBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
|
||||
if (bridgeHandler != null) {
|
||||
bridgeHandler.setDeviceHandler(deviceId, this);
|
||||
updateProperties(bridgeHandler);
|
||||
refreshChannels(bridgeHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ElroConnectsBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
|
||||
if (bridgeHandler != null) {
|
||||
bridgeHandler.unsetDeviceHandler(deviceId, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bridge handler for this thing handler.
|
||||
*
|
||||
* @return {@link ElroConnectsBridgeHandler}, null if no bridge handler set
|
||||
*/
|
||||
protected @Nullable ElroConnectsBridgeHandler getBridgeHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"No bridge defined for device " + String.valueOf(deviceId));
|
||||
return null;
|
||||
}
|
||||
|
||||
ElroConnectsBridgeHandler bridgeHandler = (ElroConnectsBridgeHandler) bridge.getHandler();
|
||||
if (bridgeHandler == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"No bridge handler defined for device " + String.valueOf(deviceId));
|
||||
return null;
|
||||
}
|
||||
|
||||
return bridgeHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
ElroConnectsBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
if (command instanceof RefreshType) {
|
||||
refreshChannels(bridgeHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update thing properties.
|
||||
*
|
||||
* @param bridgeHandler
|
||||
*/
|
||||
protected void updateProperties(ElroConnectsBridgeHandler bridgeHandler) {
|
||||
ElroConnectsDevice device = bridgeHandler.getDevice(deviceId);
|
||||
if (device != null) {
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put("deviceType", ElroConnectsUtil.stringOrEmpty(device.getDeviceType()));
|
||||
thing.setProperties(properties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh all thing channels.
|
||||
*
|
||||
* @param bridgeHandler
|
||||
*/
|
||||
protected void refreshChannels(ElroConnectsBridgeHandler bridgeHandler) {
|
||||
ElroConnectsDevice device = bridgeHandler.getDevice(deviceId);
|
||||
if (device != null) {
|
||||
device.updateState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(String channelID, State state) {
|
||||
super.updateState(channelID, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStatus(ThingStatus thingStatus, ThingStatusDetail thingStatusDetail,
|
||||
@Nullable String description) {
|
||||
super.updateStatus(thingStatus, thingStatusDetail, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStatus(ThingStatus thingStatus) {
|
||||
super.updateStatus(thingStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to be called when an alarm event is received from the K1 hub. This should trigger a trigger channel. The
|
||||
* method should be implemented in subclasses for the appropriate trigger channel if it applies.
|
||||
*/
|
||||
public void triggerAlarm() {
|
||||
// nothing by default
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.ENTRY_ALARM;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsEntrySensorHandler} represents the thing handler for an ELRO Connects entry sensor device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsEntrySensorHandler extends ElroConnectsDeviceHandler {
|
||||
|
||||
public ElroConnectsEntrySensorHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerAlarm() {
|
||||
triggerChannel(ENTRY_ALARM);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDevice;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsHeatAlarmHandler} represents the thing handler for an ELRO Connects heat alarm device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsHeatAlarmHandler extends ElroConnectsDeviceHandler {
|
||||
|
||||
public ElroConnectsHeatAlarmHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
Integer id = deviceId;
|
||||
ElroConnectsBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
ElroConnectsDevice device = bridgeHandler.getDevice(id);
|
||||
if (device != null) {
|
||||
switch (channelUID.getId()) {
|
||||
case MUTE_ALARM:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
device.muteAlarm();
|
||||
}
|
||||
break;
|
||||
case TEST_ALARM:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
device.testAlarm();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.handleCommand(channelUID, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerAlarm() {
|
||||
triggerChannel(HEAT_ALARM);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.MOTION_ALARM;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsMotionSensorHandler} represents the thing handler for an ELRO Connects motion sensor device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsMotionSensorHandler extends ElroConnectsDeviceHandler {
|
||||
|
||||
public ElroConnectsMotionSensorHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerAlarm() {
|
||||
triggerChannel(MOTION_ALARM);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.POWER_STATE;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDevice;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsPowerSocketHandler} represents the thing handler for an ELRO Connects power socket device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsPowerSocketHandler extends ElroConnectsDeviceHandler {
|
||||
|
||||
public ElroConnectsPowerSocketHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
Integer id = deviceId;
|
||||
ElroConnectsBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
ElroConnectsDevice device = bridgeHandler.getDevice(id);
|
||||
if (device != null) {
|
||||
switch (channelUID.getId()) {
|
||||
case POWER_STATE:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
device.switchState(OnOffType.ON.equals(command) ? true : false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.handleCommand(channelUID, command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDevice;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsSmokeAlarmHandler} represents the thing handler for an ELRO Connects smoke alarm device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsSmokeAlarmHandler extends ElroConnectsDeviceHandler {
|
||||
|
||||
public ElroConnectsSmokeAlarmHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
Integer id = deviceId;
|
||||
ElroConnectsBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
ElroConnectsDevice device = bridgeHandler.getDevice(id);
|
||||
if (device != null) {
|
||||
switch (channelUID.getId()) {
|
||||
case MUTE_ALARM:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
device.muteAlarm();
|
||||
}
|
||||
break;
|
||||
case TEST_ALARM:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
device.testAlarm();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.handleCommand(channelUID, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerAlarm() {
|
||||
triggerChannel(SMOKE_ALARM);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.handler;
|
||||
|
||||
import static org.openhab.binding.elroconnects.internal.ElroConnectsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.elroconnects.internal.devices.ElroConnectsDevice;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsWaterAlarmHandler} represents the thing handler for an ELRO Connects water alarm device.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ElroConnectsWaterAlarmHandler extends ElroConnectsDeviceHandler {
|
||||
|
||||
public ElroConnectsWaterAlarmHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
Integer id = deviceId;
|
||||
ElroConnectsBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
ElroConnectsDevice device = bridgeHandler.getDevice(id);
|
||||
if (device != null) {
|
||||
switch (channelUID.getId()) {
|
||||
case MUTE_ALARM:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
device.muteAlarm();
|
||||
}
|
||||
break;
|
||||
case TEST_ALARM:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
device.testAlarm();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.handleCommand(channelUID, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerAlarm() {
|
||||
triggerChannel(WATER_ALARM);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 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.elroconnects.internal.util;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link ElroConnectsUtil} contains a few utility methods for the ELRO Connects binding.
|
||||
*
|
||||
* @author Mark Herwege - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class ElroConnectsUtil {
|
||||
|
||||
public static int encode(int value) {
|
||||
return (((value ^ 0xFFFFFFFF) + 0x10000) ^ 0x123) ^ 0x1234;
|
||||
}
|
||||
|
||||
public static String stringOrEmpty(@Nullable String data) {
|
||||
return (data == null ? "" : data);
|
||||
}
|
||||
|
||||
public static int intOrZero(@Nullable Integer data) {
|
||||
return (data == null ? 0 : data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="elroconnects" 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>ELRO Connects Binding</name>
|
||||
<description>This is the binding for the ELRO Connects smart home system.</description>
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,75 @@
|
||||
# binding
|
||||
|
||||
binding.elroconnects.name = ELRO Connects Binding
|
||||
binding.elroconnects.description = This is the binding for the ELRO Connects smart home system.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.elroconnects.coalarm.label = CO Alarm
|
||||
thing-type.elroconnects.coalarm.description = ELRO Connects CO alarm
|
||||
thing-type.elroconnects.connector.label = ELRO Connects Connector
|
||||
thing-type.elroconnects.connector.description = This bridge represents an ELRO Connects K1 Connector
|
||||
thing-type.elroconnects.entrysensor.label = Door/Window Sensor
|
||||
thing-type.elroconnects.entrysensor.description = ELRO Connects door/window contact sensor
|
||||
thing-type.elroconnects.heatalarm.label = Heat Alarm
|
||||
thing-type.elroconnects.heatalarm.description = ELRO Connects heat alarm
|
||||
thing-type.elroconnects.motionsensor.label = Motion Sensor
|
||||
thing-type.elroconnects.motionsensor.description = ELRO Connects motion sensor
|
||||
thing-type.elroconnects.powersocket.label = Power Socket
|
||||
thing-type.elroconnects.powersocket.description = ELRO Connects power socket
|
||||
thing-type.elroconnects.smokealarm.label = Smoke Alarm
|
||||
thing-type.elroconnects.smokealarm.description = ELRO Connects smoke alarm
|
||||
thing-type.elroconnects.temperaturesensor.label = Temperature Sensor
|
||||
thing-type.elroconnects.temperaturesensor.description = ELRO Connects motion sensor
|
||||
thing-type.elroconnects.wateralarm.label = Water Alarm
|
||||
thing-type.elroconnects.wateralarm.description = ELRO Connects water alarm
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.elroconnects.coalarm.deviceId.label = Device ID
|
||||
thing-type.config.elroconnects.coalarm.deviceId.description = ID of the ELRO Connects Device.
|
||||
thing-type.config.elroconnects.connector.connectorId.label = Connector ID
|
||||
thing-type.config.elroconnects.connector.connectorId.description = ID of the ELRO Connects K1 Connector, should be ST_xxxxxxxxxxxx with xxxxxxxxxxxx the lowercase MAC address of the connector.
|
||||
thing-type.config.elroconnects.connector.ipAddress.label = IP Address
|
||||
thing-type.config.elroconnects.connector.ipAddress.description = IP address of the ELRO Connects K1 Connector, not required if connector and openHAB server in same subnet.
|
||||
thing-type.config.elroconnects.connector.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.elroconnects.connector.refreshInterval.description = Heartbeat device refresh interval for communication with ELRO Connects K1 Connector in seconds, default 60s.
|
||||
thing-type.config.elroconnects.entrysensor.deviceId.label = Device ID
|
||||
thing-type.config.elroconnects.entrysensor.deviceId.description = ID of the ELRO Connects Device.
|
||||
thing-type.config.elroconnects.heatalarm.deviceId.label = Device ID
|
||||
thing-type.config.elroconnects.heatalarm.deviceId.description = ID of the ELRO Connects Device.
|
||||
thing-type.config.elroconnects.motionsensor.deviceId.label = Device ID
|
||||
thing-type.config.elroconnects.motionsensor.deviceId.description = ID of the ELRO Connects Device.
|
||||
thing-type.config.elroconnects.powersocket.deviceId.label = Device ID
|
||||
thing-type.config.elroconnects.powersocket.deviceId.description = ID of the ELRO Connects Device.
|
||||
thing-type.config.elroconnects.smokealarm.deviceId.label = Device ID
|
||||
thing-type.config.elroconnects.smokealarm.deviceId.description = ID of the ELRO Connects Device.
|
||||
thing-type.config.elroconnects.temperaturesensor.deviceId.label = Device ID
|
||||
thing-type.config.elroconnects.temperaturesensor.deviceId.description = ID of the ELRO Connects Device.
|
||||
thing-type.config.elroconnects.wateralarm.deviceId.label = Device ID
|
||||
thing-type.config.elroconnects.wateralarm.deviceId.description = ID of the ELRO Connects Device.
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.elroconnects.coalarm.label = CO Alarm
|
||||
channel-type.elroconnects.coalarm.description = CO alarm triggered
|
||||
channel-type.elroconnects.entry.label = Entry Contact
|
||||
channel-type.elroconnects.entry.description = Door/window contact open/closed
|
||||
channel-type.elroconnects.entryalarm.label = Entry Alarm
|
||||
channel-type.elroconnects.entryalarm.description = Entry alarm triggered
|
||||
channel-type.elroconnects.heatalarm.label = Heat Alarm
|
||||
channel-type.elroconnects.heatalarm.description = Heat alarm triggered
|
||||
channel-type.elroconnects.motionalarm.label = Motion Alarm
|
||||
channel-type.elroconnects.motionalarm.description = Motion alarm triggered
|
||||
channel-type.elroconnects.mutealarm.label = Mute Alarm
|
||||
channel-type.elroconnects.mutealarm.description = Mute
|
||||
channel-type.elroconnects.scene.label = Scene
|
||||
channel-type.elroconnects.scene.description = Scene selection from scenes configured in the ELRO Connects app, enables configuring alarm modes
|
||||
channel-type.elroconnects.smokealarm.label = Smoke Alarm
|
||||
channel-type.elroconnects.smokealarm.description = Smoke alarm triggered
|
||||
channel-type.elroconnects.temperature.label = Temperature
|
||||
channel-type.elroconnects.temperature.description = Current temperature
|
||||
channel-type.elroconnects.testalarm.label = Test Alarm
|
||||
channel-type.elroconnects.testalarm.description = Trigger alarm test sound
|
||||
channel-type.elroconnects.wateralarm.label = Water Alarm
|
||||
channel-type.elroconnects.wateralarm.description = Water alarm triggered
|
||||
@@ -0,0 +1,303 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="elroconnects"
|
||||
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="connector">
|
||||
<label>ELRO Connects Connector</label>
|
||||
<description>This bridge represents an ELRO Connects K1 Connector</description>
|
||||
<channels>
|
||||
<channel id="scene" typeId="scene"/>
|
||||
</channels>
|
||||
<representation-property>connectorId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="connectorId" type="text" required="true">
|
||||
<label>Connector ID</label>
|
||||
<description>ID of the ELRO Connects K1 Connector, should be ST_xxxxxxxxxxxx with xxxxxxxxxxxx the lowercase MAC
|
||||
address of the connector.</description>
|
||||
</parameter>
|
||||
<parameter name="ipAddress" type="text">
|
||||
<label>IP Address</label>
|
||||
<description>IP address of the ELRO Connects K1 Connector, not required if connector and openHAB server in same
|
||||
subnet.</description>
|
||||
<context>network-address</context>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Heartbeat device refresh interval for communication with ELRO Connects K1 Connector in seconds, default
|
||||
60s.</description>
|
||||
<default>60</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<thing-type id="smokealarm">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="connector"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Smoke Alarm</label>
|
||||
<description>ELRO Connects smoke alarm</description>
|
||||
<category>SmokeDetector</category>
|
||||
<channels>
|
||||
<channel id="smokeAlarm" typeId="smokealarm"/>
|
||||
<channel id="muteAlarm" typeId="mutealarm"/>
|
||||
<channel id="testAlarm" typeId="testalarm"/>
|
||||
<channel id="battery" typeId="system.battery-level"/>
|
||||
<channel id="lowBattery" typeId="system.low-battery"/>
|
||||
</channels>
|
||||
<representation-property>deviceId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="integer" required="true">
|
||||
<label>Device ID</label>
|
||||
<description>ID of the ELRO Connects Device.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<thing-type id="coalarm">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="connector"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>CO Alarm</label>
|
||||
<description>ELRO Connects CO alarm</description>
|
||||
<channels>
|
||||
<channel id="coAlarm" typeId="coalarm"/>
|
||||
<channel id="muteAlarm" typeId="mutealarm"/>
|
||||
<channel id="testAlarm" typeId="testalarm"/>
|
||||
<channel id="battery" typeId="system.battery-level"/>
|
||||
<channel id="lowBattery" typeId="system.low-battery"/>
|
||||
</channels>
|
||||
<representation-property>deviceId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="integer" required="true">
|
||||
<label>Device ID</label>
|
||||
<description>ID of the ELRO Connects Device.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<thing-type id="heatalarm">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="connector"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Heat Alarm</label>
|
||||
<description>ELRO Connects heat alarm</description>
|
||||
<channels>
|
||||
<channel id="heatAlarm" typeId="heatalarm"/>
|
||||
<channel id="muteAlarm" typeId="mutealarm"/>
|
||||
<channel id="testAlarm" typeId="testalarm"/>
|
||||
<channel id="battery" typeId="system.battery-level"/>
|
||||
<channel id="lowBattery" typeId="system.low-battery"/>
|
||||
</channels>
|
||||
<representation-property>deviceId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="integer" required="true">
|
||||
<label>Device ID</label>
|
||||
<description>ID of the ELRO Connects Device.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<thing-type id="wateralarm">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="connector"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Water Alarm</label>
|
||||
<description>ELRO Connects water alarm</description>
|
||||
<channels>
|
||||
<channel id="waterAlarm" typeId="wateralarm"/>
|
||||
<channel id="muteAlarm" typeId="mutealarm"/>
|
||||
<channel id="testAlarm" typeId="testalarm"/>
|
||||
<channel id="battery" typeId="system.battery-level"/>
|
||||
<channel id="lowBattery" typeId="system.low-battery"/>
|
||||
</channels>
|
||||
<representation-property>deviceId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="integer" required="true">
|
||||
<label>Device ID</label>
|
||||
<description>ID of the ELRO Connects Device.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<thing-type id="entrysensor">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="connector"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Door/Window Sensor</label>
|
||||
<description>ELRO Connects door/window contact sensor</description>
|
||||
<category>Door</category>
|
||||
<channels>
|
||||
<channel id="entryAlarm" typeId="entryalarm"/>
|
||||
<channel id="entry" typeId="entry"/>
|
||||
<channel id="battery" typeId="system.battery-level"/>
|
||||
<channel id="lowBattery" typeId="system.low-battery"/>
|
||||
</channels>
|
||||
<representation-property>deviceId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="integer" required="true">
|
||||
<label>Device ID</label>
|
||||
<description>ID of the ELRO Connects Device.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<thing-type id="motionsensor">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="connector"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Motion Sensor</label>
|
||||
<description>ELRO Connects motion sensor</description>
|
||||
<category>MotionDetector</category>
|
||||
<channels>
|
||||
<channel id="motionAlarm" typeId="motionalarm"/>
|
||||
<channel id="motion" typeId="system.motion"/>
|
||||
<channel id="battery" typeId="system.battery-level"/>
|
||||
<channel id="lowBattery" typeId="system.low-battery"/>
|
||||
</channels>
|
||||
<representation-property>deviceId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="integer" required="true">
|
||||
<label>Device ID</label>
|
||||
<description>ID of the ELRO Connects Device.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<thing-type id="temperaturesensor">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="connector"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Temperature Sensor</label>
|
||||
<description>ELRO Connects motion sensor</description>
|
||||
<category>Sensor</category>
|
||||
<channels>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="humidity" typeId="system.atmospheric-humidity"/>
|
||||
<channel id="battery" typeId="system.battery-level"/>
|
||||
<channel id="lowBattery" typeId="system.low-battery"/>
|
||||
</channels>
|
||||
<representation-property>deviceId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="integer" required="true">
|
||||
<label>Device ID</label>
|
||||
<description>ID of the ELRO Connects Device.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<thing-type id="powersocket">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="connector"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Power Socket</label>
|
||||
<description>ELRO Connects power socket</description>
|
||||
<category>PowerOutlet</category>
|
||||
<channels>
|
||||
<channel id="powerState" typeId="system.power"/>
|
||||
</channels>
|
||||
<representation-property>deviceId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="integer" required="true">
|
||||
<label>Device ID</label>
|
||||
<description>ID of the ELRO Connects Device.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="scene">
|
||||
<item-type>String</item-type>
|
||||
<label>Scene</label>
|
||||
<description>Scene selection from scenes configured in the ELRO Connects app, enables configuring alarm modes</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="smokealarm">
|
||||
<kind>trigger</kind>
|
||||
<label>Smoke Alarm</label>
|
||||
<description>Smoke alarm triggered</description>
|
||||
<category>Alarm</category>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
<tag>Smoke</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
<channel-type id="coalarm">
|
||||
<kind>trigger</kind>
|
||||
<label>CO Alarm</label>
|
||||
<description>CO alarm triggered</description>
|
||||
<category>Alarm</category>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
<tag>CO</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
<channel-type id="heatalarm">
|
||||
<kind>trigger</kind>
|
||||
<label>Heat Alarm</label>
|
||||
<description>Heat alarm triggered</description>
|
||||
<category>Fire</category>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
<channel-type id="wateralarm">
|
||||
<kind>trigger</kind>
|
||||
<label>Water Alarm</label>
|
||||
<description>Water alarm triggered</description>
|
||||
<category>Alarm</category>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
<tag>Water</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
<channel-type id="entryalarm">
|
||||
<kind>trigger</kind>
|
||||
<label>Entry Alarm</label>
|
||||
<description>Entry alarm triggered</description>
|
||||
<category>Alarm</category>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
<tag>OpenState</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
<channel-type id="motionalarm">
|
||||
<kind>trigger</kind>
|
||||
<label>Motion Alarm</label>
|
||||
<description>Motion alarm triggered</description>
|
||||
<category>Alarm</category>
|
||||
<tags>
|
||||
<tag>Alarm</tag>
|
||||
<tag>Presence</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
<channel-type id="entry">
|
||||
<item-type>Contact</item-type>
|
||||
<label>Entry Contact</label>
|
||||
<description>Door/window contact open/closed</description>
|
||||
<tags>
|
||||
<tag>OpenState</tag>
|
||||
</tags>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="mutealarm">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Mute Alarm</label>
|
||||
<description>Mute</description>
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel-type>
|
||||
<channel-type id="testalarm">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Test Alarm</label>
|
||||
<description>Trigger alarm test sound</description>
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel-type>
|
||||
<channel-type id="temperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Current temperature</description>
|
||||
<category>Temperature</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user