mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[insteon] Update remote device support (#17540)
* [insteon] Fix remote device not polled when awake Signed-off-by: jsetton <jeremy.setton@gmail.com> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
4307003c1c
commit
cce5d6d3a5
@ -13,10 +13,13 @@
|
||||
package org.openhab.binding.insteon.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.RemoteSceneButtonConfig;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.RemoteSwitchButtonConfig;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.VenstarSystemMode;
|
||||
import org.openhab.core.OpenHAB;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
@ -77,7 +80,6 @@ public class InsteonBindingConstants {
|
||||
public static final String FEATURE_RAMP_RATE = "rampRate";
|
||||
public static final String FEATURE_SCENE_ON_OFF = "sceneOnOff";
|
||||
public static final String FEATURE_STAY_AWAKE = "stayAwake";
|
||||
public static final String FEATURE_SYSTEM_MODE = "systemMode";
|
||||
public static final String FEATURE_TEMPERATURE_SCALE = "temperatureScale";
|
||||
public static final String FEATURE_TWO_GROUPS = "2Groups";
|
||||
|
||||
@ -90,6 +92,8 @@ public class InsteonBindingConstants {
|
||||
public static final String FEATURE_TYPE_KEYPAD_BUTTON_ON_MASK = "KeypadButtonOnMask";
|
||||
public static final String FEATURE_TYPE_KEYPAD_BUTTON_TOGGLE_MODE = "KeypadButtonToggleMode";
|
||||
public static final String FEATURE_TYPE_OUTLET_SWITCH = "OutletSwitch";
|
||||
public static final String FEATURE_TYPE_REMOTE_SCENE_BUTTON_CONFIG = "RemoteSceneButtonConfig";
|
||||
public static final String FEATURE_TYPE_REMOTE_SWITCH_BUTTON_CONFIG = "RemoteSwitchButtonConfig";
|
||||
public static final String FEATURE_TYPE_THERMOSTAT_FAN_MODE = "ThermostatFanMode";
|
||||
public static final String FEATURE_TYPE_THERMOSTAT_SYSTEM_MODE = "ThermostatSystemMode";
|
||||
public static final String FEATURE_TYPE_THERMOSTAT_COOL_SETPOINT = "ThermostatCoolSetpoint";
|
||||
@ -99,12 +103,9 @@ public class InsteonBindingConstants {
|
||||
public static final String FEATURE_TYPE_VENSTAR_COOL_SETPOINT = "VenstarCoolSetpoint";
|
||||
public static final String FEATURE_TYPE_VENSTAR_HEAT_SETPOINT = "VenstarHeatSetpoint";
|
||||
|
||||
// List of specific device types
|
||||
public static final String DEVICE_TYPE_CLIMATE_CONTROL_VENSTAR_THERMOSTAT = "ClimateControl_VenstarThermostat";
|
||||
|
||||
// Map of custom state description options
|
||||
public static final Map<String, String[]> CUSTOM_STATE_DESCRIPTION_OPTIONS = Map.ofEntries(
|
||||
// Venstar Thermostat System Mode
|
||||
Map.entry(DEVICE_TYPE_CLIMATE_CONTROL_VENSTAR_THERMOSTAT + ":" + FEATURE_SYSTEM_MODE,
|
||||
VenstarSystemMode.names().toArray(String[]::new)));
|
||||
public static final Map<String, List<String>> CUSTOM_STATE_DESCRIPTION_OPTIONS = Map.ofEntries(
|
||||
Map.entry(FEATURE_TYPE_REMOTE_SCENE_BUTTON_CONFIG, RemoteSceneButtonConfig.names()),
|
||||
Map.entry(FEATURE_TYPE_REMOTE_SWITCH_BUTTON_CONFIG, RemoteSwitchButtonConfig.names()),
|
||||
Map.entry(FEATURE_TYPE_VENSTAR_SYSTEM_MODE, VenstarSystemMode.names()));
|
||||
}
|
||||
|
@ -40,10 +40,11 @@ import org.openhab.binding.insteon.internal.device.database.ModemDB;
|
||||
import org.openhab.binding.insteon.internal.device.database.ModemDBChange;
|
||||
import org.openhab.binding.insteon.internal.device.database.ModemDBEntry;
|
||||
import org.openhab.binding.insteon.internal.device.database.ModemDBRecord;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.DeviceTypeRenamer;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.KeypadButtonToggleMode;
|
||||
import org.openhab.binding.insteon.internal.handler.InsteonDeviceHandler;
|
||||
import org.openhab.binding.insteon.internal.transport.message.FieldException;
|
||||
import org.openhab.binding.insteon.internal.transport.message.GroupMessageStateMachine;
|
||||
import org.openhab.binding.insteon.internal.transport.message.GroupMessageStateMachine.GroupMessageType;
|
||||
import org.openhab.binding.insteon.internal.transport.message.Msg;
|
||||
import org.openhab.binding.insteon.internal.utils.BinaryUtils;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
@ -219,49 +220,32 @@ public class InsteonDevice extends BaseDevice<InsteonAddress, InsteonDeviceHandl
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a broadcast message is duplicate
|
||||
* Returns if an incoming message is duplicate
|
||||
*
|
||||
* @param cmd1 the cmd1 from the broadcast message received
|
||||
* @param timestamp the timestamp from the broadcast message received
|
||||
* @return true if the broadcast message is duplicate
|
||||
* @param msg the message received
|
||||
* @return true if group or broadcast message is duplicate
|
||||
*/
|
||||
public boolean isDuplicateBroadcastMsg(byte cmd1, long timestamp) {
|
||||
synchronized (lastBroadcastReceived) {
|
||||
long timelapse = timestamp - lastBroadcastReceived.getOrDefault(cmd1, timestamp);
|
||||
if (timelapse > 0 && timelapse < BCAST_STATE_TIMEOUT) {
|
||||
return true;
|
||||
} else {
|
||||
lastBroadcastReceived.put(cmd1, timestamp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a group message is duplicate
|
||||
*
|
||||
* @param cmd1 cmd1 from the group message received
|
||||
* @param timestamp the timestamp from the broadcast message received
|
||||
* @param group the broadcast group
|
||||
* @param type the group message type that was received
|
||||
* @return true if the group message is duplicate
|
||||
*/
|
||||
public boolean isDuplicateGroupMsg(byte cmd1, long timestamp, int group, GroupMessageType type) {
|
||||
public boolean isDuplicateMsg(Msg msg) {
|
||||
try {
|
||||
if (msg.isAllLinkBroadcastOrCleanup()) {
|
||||
synchronized (groupState) {
|
||||
GroupMessageStateMachine stateMachine = groupState.get(group);
|
||||
if (stateMachine == null) {
|
||||
stateMachine = new GroupMessageStateMachine();
|
||||
groupState.put(group, stateMachine);
|
||||
logger.trace("{} created group {} state", address, group);
|
||||
int group = msg.getGroup();
|
||||
GroupMessageStateMachine stateMachine = groupState.computeIfAbsent(group,
|
||||
k -> new GroupMessageStateMachine());
|
||||
return stateMachine != null && stateMachine.isDuplicate(msg);
|
||||
}
|
||||
if (stateMachine.getLastCommand() == cmd1 && stateMachine.getLastTimestamp() == timestamp) {
|
||||
logger.trace("{} using previous group {} state for {}", address, group, type);
|
||||
return stateMachine.isDuplicate();
|
||||
} else {
|
||||
logger.trace("{} updating group {} state to {}", address, group, type);
|
||||
return stateMachine.update(address, group, cmd1, timestamp, type);
|
||||
} else if (msg.isBroadcast()) {
|
||||
synchronized (lastBroadcastReceived) {
|
||||
byte cmd1 = msg.getByte("command1");
|
||||
long timestamp = msg.getTimestamp();
|
||||
Long lastTimestamp = lastBroadcastReceived.put(cmd1, timestamp);
|
||||
return lastTimestamp != null && Math.abs(timestamp - lastTimestamp) <= BCAST_STATE_TIMEOUT;
|
||||
}
|
||||
}
|
||||
} catch (FieldException e) {
|
||||
logger.warn("error parsing msg: {}", msg, e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -494,6 +478,13 @@ public class InsteonDevice extends BaseDevice<InsteonAddress, InsteonDeviceHandl
|
||||
getFeatures().stream().filter(DeviceFeature::isStatusFeature)
|
||||
.forEach(feature -> feature.handleMessage(msg));
|
||||
}
|
||||
// poll battery powered device while awake if non-duplicate all link or broadcast message
|
||||
if ((msg.isAllLinkBroadcastOrCleanup() || msg.isBroadcast()) && isBatteryPowered() && isAwake()
|
||||
&& !isDuplicateMsg(msg)) {
|
||||
// add poll delay for non-replayed all link broadcast allowing cleanup msg to be be processed beforehand
|
||||
long delay = msg.isAllLinkBroadcast() && !msg.isAllLinkSuccessReport() && !msg.isReplayed() ? 1500L : 0L;
|
||||
doPoll(delay);
|
||||
}
|
||||
// notify if responding state changed
|
||||
if (isPrevResponding != isResponding()) {
|
||||
statusChanged();
|
||||
@ -596,12 +587,21 @@ public class InsteonDevice extends BaseDevice<InsteonAddress, InsteonDeviceHandl
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this device type
|
||||
*
|
||||
* @param renamer the device type renamer
|
||||
*/
|
||||
public void updateType(DeviceTypeRenamer renamer) {
|
||||
Optional.ofNullable(getType()).map(DeviceType::getName).map(renamer::getNewDeviceType)
|
||||
.map(name -> DeviceTypeRegistry.getInstance().getDeviceType(name)).ifPresent(this::updateType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this device type
|
||||
*
|
||||
* @param newType the new device type to use
|
||||
*/
|
||||
|
||||
public void updateType(DeviceType newType) {
|
||||
ProductData productData = getProductData();
|
||||
DeviceType currentType = getType();
|
||||
|
@ -40,6 +40,8 @@ import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.IOLincRe
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.KeypadButtonConfig;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.KeypadButtonToggleMode;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.MicroModuleOpMode;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.RemoteSceneButtonConfig;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.RemoteSwitchButtonConfig;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.SirenAlertType;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.ThermostatFanMode;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.ThermostatSystemMode;
|
||||
@ -1154,7 +1156,8 @@ public abstract class CommandHandler extends BaseFeatureHandler {
|
||||
protected int getOpFlagCommand(Command cmd) {
|
||||
try {
|
||||
String config = ((StringType) cmd).toString();
|
||||
return KeypadButtonConfig.valueOf(config).getValue();
|
||||
return KeypadButtonConfig.valueOf(config).shouldSetFlag() ? getParameterAsInteger("on", -1)
|
||||
: getParameterAsInteger("off", -1);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("{}: got unexpected button config command: {}, ignoring request", nm(), cmd);
|
||||
return -1;
|
||||
@ -1845,6 +1848,74 @@ public abstract class CommandHandler extends BaseFeatureHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote scene button config command handler
|
||||
*/
|
||||
public static class RemoteSceneButtonConfigCommandHandler extends MultiOpFlagsCommandHandler {
|
||||
RemoteSceneButtonConfigCommandHandler(DeviceFeature feature) {
|
||||
super(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<Integer, String> getOpFlagCommands(Command cmd) {
|
||||
Map<Integer, String> commands = new HashMap<>();
|
||||
try {
|
||||
String mode = ((StringType) cmd).toString();
|
||||
switch (RemoteSceneButtonConfig.valueOf(mode)) {
|
||||
case BUTTON_4:
|
||||
commands.put(0x0F, "grouped ON");
|
||||
commands.put(0x09, "toggle off ON");
|
||||
break;
|
||||
case BUTTON_8_ALWAYS_ON:
|
||||
commands.put(0x0E, "grouped OFF");
|
||||
commands.put(0x09, "toggle off ON");
|
||||
break;
|
||||
case BUTTON_8_TOGGLE:
|
||||
commands.put(0x0E, "grouped OFF");
|
||||
commands.put(0x08, "toggle off OFF");
|
||||
break;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("{}: got unexpected button config command: {}, ignoring request", nm(), cmd);
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote switch button config command handler
|
||||
*/
|
||||
public static class RemoteSwitchButtonConfigCommandHandler extends MultiOpFlagsCommandHandler {
|
||||
RemoteSwitchButtonConfigCommandHandler(DeviceFeature feature) {
|
||||
super(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<Integer, String> getOpFlagCommands(Command cmd) {
|
||||
Map<Integer, String> commands = new HashMap<>();
|
||||
try {
|
||||
String mode = ((StringType) cmd).toString();
|
||||
switch (RemoteSwitchButtonConfig.valueOf(mode)) {
|
||||
case BUTTON_1:
|
||||
commands.put(0x0F, "grouped ON");
|
||||
commands.put(0x09, "toggle off ON");
|
||||
break;
|
||||
case BUTTON_2_ALWAYS_ON:
|
||||
commands.put(0x0E, "grouped OFF");
|
||||
commands.put(0x09, "toggle off ON");
|
||||
break;
|
||||
case BUTTON_2_TOGGLE:
|
||||
commands.put(0x0E, "grouped OFF");
|
||||
commands.put(0x08, "toggle off OFF");
|
||||
break;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("{}: got unexpected button config command: {}, ignoring request", nm(), cmd);
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprinkler valve on/off command handler
|
||||
*/
|
||||
|
@ -16,6 +16,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@ -111,24 +112,27 @@ public class FeatureEnums {
|
||||
}
|
||||
}
|
||||
|
||||
public static enum KeypadButtonConfig {
|
||||
BUTTON_6(0x07, 6),
|
||||
BUTTON_8(0x06, 8);
|
||||
public static enum KeypadButtonConfig implements DeviceTypeRenamer {
|
||||
BUTTON_6(false, "KeypadButton6"),
|
||||
BUTTON_8(true, "KeypadButton8");
|
||||
|
||||
private int value;
|
||||
private int count;
|
||||
private static final Pattern DEVICE_TYPE_NAME_PATTERN = Pattern.compile("KeypadButton[68]$");
|
||||
|
||||
private KeypadButtonConfig(int value, int count) {
|
||||
this.value = value;
|
||||
this.count = count;
|
||||
private boolean setFlag;
|
||||
private String replacement;
|
||||
|
||||
private KeypadButtonConfig(boolean setFlag, String replacement) {
|
||||
this.setFlag = setFlag;
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
@Override
|
||||
public String getNewDeviceType(String deviceType) {
|
||||
return DEVICE_TYPE_NAME_PATTERN.matcher(deviceType).replaceAll(replacement);
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
public boolean shouldSetFlag() {
|
||||
return setFlag;
|
||||
}
|
||||
|
||||
public static KeypadButtonConfig from(boolean is8Button) {
|
||||
@ -194,6 +198,78 @@ public class FeatureEnums {
|
||||
}
|
||||
}
|
||||
|
||||
public static enum RemoteSceneButtonConfig implements DeviceTypeRenamer {
|
||||
BUTTON_4("MiniRemoteScene4"),
|
||||
BUTTON_8_ALWAYS_ON("MiniRemoteScene8"),
|
||||
BUTTON_8_TOGGLE("MiniRemoteScene8");
|
||||
|
||||
private static final Pattern DEVICE_TYPE_NAME_PATTERN = Pattern.compile("MiniRemoteScene[48]$");
|
||||
|
||||
private String replacement;
|
||||
|
||||
private RemoteSceneButtonConfig(String replacement) {
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNewDeviceType(String deviceType) {
|
||||
return DEVICE_TYPE_NAME_PATTERN.matcher(deviceType).replaceAll(replacement);
|
||||
}
|
||||
|
||||
public static RemoteSceneButtonConfig valueOf(int value) {
|
||||
if (BinaryUtils.isBitSet(value, 6)) {
|
||||
// return button 4, when grouped op flag (6) is on
|
||||
return RemoteSceneButtonConfig.BUTTON_4;
|
||||
} else if (BinaryUtils.isBitSet(value, 4)) {
|
||||
// return button 8 always on, when toggle off op flag (5) is on
|
||||
return RemoteSceneButtonConfig.BUTTON_8_ALWAYS_ON;
|
||||
} else {
|
||||
// return button 8 toggle, otherwise
|
||||
return RemoteSceneButtonConfig.BUTTON_8_TOGGLE;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> names() {
|
||||
return Arrays.stream(values()).map(String::valueOf).toList();
|
||||
}
|
||||
}
|
||||
|
||||
public static enum RemoteSwitchButtonConfig implements DeviceTypeRenamer {
|
||||
BUTTON_1("MiniRemoteSwitch"),
|
||||
BUTTON_2_ALWAYS_ON("MiniRemoteSwitch2"),
|
||||
BUTTON_2_TOGGLE("MiniRemoteSwitch2");
|
||||
|
||||
private static final Pattern DEVICE_TYPE_NAME_PATTERN = Pattern.compile("MiniRemoteSwitch[2]?$");
|
||||
|
||||
private String replacement;
|
||||
|
||||
private RemoteSwitchButtonConfig(String replacement) {
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNewDeviceType(String deviceType) {
|
||||
return DEVICE_TYPE_NAME_PATTERN.matcher(deviceType).replaceAll(replacement);
|
||||
}
|
||||
|
||||
public static RemoteSwitchButtonConfig valueOf(int value) {
|
||||
if (BinaryUtils.isBitSet(value, 6)) {
|
||||
// return button 1, when grouped op flag (6) is on
|
||||
return RemoteSwitchButtonConfig.BUTTON_1;
|
||||
} else if (BinaryUtils.isBitSet(value, 4)) {
|
||||
// return button 2 always on, when toggle off op flag (5) is on
|
||||
return RemoteSwitchButtonConfig.BUTTON_2_ALWAYS_ON;
|
||||
} else {
|
||||
// return button 2 toggle, otherwise
|
||||
return RemoteSwitchButtonConfig.BUTTON_2_TOGGLE;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> names() {
|
||||
return Arrays.stream(values()).map(String::valueOf).toList();
|
||||
}
|
||||
}
|
||||
|
||||
public static enum SirenAlertType {
|
||||
CHIME(0x00),
|
||||
LOUD_SIREN(0x01);
|
||||
@ -401,4 +477,8 @@ public class FeatureEnums {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
public interface DeviceTypeRenamer {
|
||||
String getNewDeviceType(String deviceType);
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,6 @@ import javax.measure.quantity.Time;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.insteon.internal.device.DeviceFeature;
|
||||
import org.openhab.binding.insteon.internal.device.DeviceType;
|
||||
import org.openhab.binding.insteon.internal.device.DeviceTypeRegistry;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonEngine;
|
||||
import org.openhab.binding.insteon.internal.device.RampRate;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.ButtonEvent;
|
||||
@ -44,6 +42,8 @@ import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.IOLincRe
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.KeypadButtonConfig;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.KeypadButtonToggleMode;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.MicroModuleOpMode;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.RemoteSceneButtonConfig;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.RemoteSwitchButtonConfig;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.SirenAlertType;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.ThermostatFanMode;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.ThermostatSystemMode;
|
||||
@ -52,7 +52,6 @@ import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.Thermost
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.ThermostatTimeFormat;
|
||||
import org.openhab.binding.insteon.internal.device.feature.FeatureEnums.VenstarSystemMode;
|
||||
import org.openhab.binding.insteon.internal.transport.message.FieldException;
|
||||
import org.openhab.binding.insteon.internal.transport.message.GroupMessageStateMachine.GroupMessageType;
|
||||
import org.openhab.binding.insteon.internal.transport.message.Msg;
|
||||
import org.openhab.binding.insteon.internal.utils.BinaryUtils;
|
||||
import org.openhab.binding.insteon.internal.utils.HexUtils;
|
||||
@ -146,29 +145,11 @@ public abstract class MessageHandler extends BaseFeatureHandler {
|
||||
* Returns if an incoming message is a duplicate
|
||||
*
|
||||
* @param msg the received message
|
||||
* @return true if the broadcast message is a duplicate
|
||||
* @return true if group or broadcast message is duplicate
|
||||
*/
|
||||
protected boolean isDuplicate(Msg msg) {
|
||||
try {
|
||||
if (msg.isAllLinkBroadcastOrCleanup()) {
|
||||
byte cmd1 = msg.getByte("command1");
|
||||
long timestamp = msg.getTimestamp();
|
||||
int group = msg.getGroup();
|
||||
GroupMessageType type = msg.isAllLinkBroadcast() ? GroupMessageType.BCAST : GroupMessageType.CLEAN;
|
||||
if (msg.isAllLinkSuccessReport()) {
|
||||
cmd1 = msg.getInsteonAddress("toAddress").getHighByte();
|
||||
type = GroupMessageType.SUCCESS;
|
||||
}
|
||||
return getInsteonDevice().isDuplicateGroupMsg(cmd1, timestamp, group, type);
|
||||
} else if (msg.isBroadcast()) {
|
||||
byte cmd1 = msg.getByte("command1");
|
||||
long timestamp = msg.getTimestamp();
|
||||
return getInsteonDevice().isDuplicateBroadcastMsg(cmd1, timestamp);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("cannot parse msg: {}", msg, e);
|
||||
} catch (FieldException e) {
|
||||
logger.warn("cannot parse msg: {}", msg, e);
|
||||
if (msg.isAllLinkBroadcastOrCleanup() || msg.isBroadcast()) {
|
||||
return getInsteonDevice().isDuplicateMsg(msg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -236,13 +217,9 @@ public abstract class MessageHandler extends BaseFeatureHandler {
|
||||
* @throws FieldException if field not there
|
||||
*/
|
||||
private boolean matchesParameter(Msg msg, String field, String param) throws FieldException {
|
||||
int mp = getParameterAsInteger(param, -1);
|
||||
int value = getParameterAsInteger(param, -1);
|
||||
// parameter not filtered for, declare this a match!
|
||||
if (mp == -1) {
|
||||
return true;
|
||||
}
|
||||
byte value = msg.getByte(field);
|
||||
return value == mp;
|
||||
return value == -1 || msg.getInt(field) == value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -993,11 +970,16 @@ public abstract class MessageHandler extends BaseFeatureHandler {
|
||||
public void handleMessage(byte cmd1, Msg msg) {
|
||||
// trigger poll if is my command reply message (0x20)
|
||||
if (feature.getQueryCommand() == 0x20) {
|
||||
feature.triggerPoll(0L);
|
||||
long delay = getPollDelay();
|
||||
feature.triggerPoll(delay);
|
||||
} else {
|
||||
super.handleMessage(cmd1, msg);
|
||||
}
|
||||
}
|
||||
|
||||
protected long getPollDelay() {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1043,26 +1025,11 @@ public abstract class MessageHandler extends BaseFeatureHandler {
|
||||
@Override
|
||||
protected State getBitState(boolean is8Button) {
|
||||
KeypadButtonConfig config = KeypadButtonConfig.from(is8Button);
|
||||
// update device type based on button count
|
||||
updateDeviceType(config.getCount());
|
||||
// update device type based on button config
|
||||
getInsteonDevice().updateType(config);
|
||||
// return button config state
|
||||
return new StringType(config.toString());
|
||||
}
|
||||
|
||||
private void updateDeviceType(int buttonCount) {
|
||||
DeviceType deviceType = getInsteonDevice().getType();
|
||||
if (deviceType == null) {
|
||||
logger.warn("{}: unknown device type for {}", nm(), getInsteonDevice().getAddress());
|
||||
} else {
|
||||
String name = deviceType.getName().replaceAll(".$", String.valueOf(buttonCount));
|
||||
DeviceType newType = DeviceTypeRegistry.getInstance().getDeviceType(name);
|
||||
if (newType == null) {
|
||||
logger.warn("{}: unknown device type {}", nm(), name);
|
||||
} else {
|
||||
getInsteonDevice().updateType(newType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1107,13 +1074,6 @@ public abstract class MessageHandler extends BaseFeatureHandler {
|
||||
@Override
|
||||
public void handleMessage(byte cmd1, Msg msg) {
|
||||
super.handleMessage(cmd1, msg);
|
||||
// poll battery powered sensor device while awake
|
||||
if (getInsteonDevice().isBatteryPowered()) {
|
||||
// no delay for all link cleanup, all link success report or replayed messages
|
||||
// otherise, 1500ms for all link broadcast message allowing cleanup msg to be be processed beforehand
|
||||
long delay = msg.isAllLinkCleanup() || msg.isAllLinkSuccessReport() || msg.isReplayed() ? 0L : 1500L;
|
||||
getInsteonDevice().doPoll(delay);
|
||||
}
|
||||
// poll related devices
|
||||
feature.pollRelatedDevices(0L);
|
||||
}
|
||||
@ -1339,19 +1299,14 @@ public abstract class MessageHandler extends BaseFeatureHandler {
|
||||
/**
|
||||
* I/O linc relay mode reply message handler
|
||||
*/
|
||||
public static class IOLincRelayModeReplyHandler extends CustomMsgHandler {
|
||||
public static class IOLincRelayModeReplyHandler extends OpFlagsReplyHandler {
|
||||
IOLincRelayModeReplyHandler(DeviceFeature feature) {
|
||||
super(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(byte cmd1, Msg msg) {
|
||||
// trigger poll if is my command reply message (0x20)
|
||||
if (feature.getQueryCommand() == 0x20) {
|
||||
feature.triggerPoll(5000L); // 5000ms delay to allow all op flag commands to be processed
|
||||
} else {
|
||||
super.handleMessage(cmd1, msg);
|
||||
}
|
||||
protected long getPollDelay() {
|
||||
return 5000L; // delay to allow all op flag commands to be processed
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1364,19 +1319,14 @@ public abstract class MessageHandler extends BaseFeatureHandler {
|
||||
/**
|
||||
* Micro module operation mode reply message handler
|
||||
*/
|
||||
public static class MicroModuleOpModeReplyHandler extends CustomMsgHandler {
|
||||
public static class MicroModuleOpModeReplyHandler extends OpFlagsReplyHandler {
|
||||
MicroModuleOpModeReplyHandler(DeviceFeature feature) {
|
||||
super(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(byte cmd1, Msg msg) {
|
||||
// trigger poll if is my command reply message (0x20)
|
||||
if (feature.getQueryCommand() == 0x20) {
|
||||
feature.triggerPoll(2000L); // 2000ms delay to allow all op flag commands to be processed
|
||||
} else {
|
||||
super.handleMessage(cmd1, msg);
|
||||
}
|
||||
protected long getPollDelay() {
|
||||
return 2000L; // delay to allow all op flag commands to be processed
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1445,6 +1395,52 @@ public abstract class MessageHandler extends BaseFeatureHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote scene button config reply message handler
|
||||
*/
|
||||
public static class RemoteSceneButtonConfigReplyHandler extends OpFlagsReplyHandler {
|
||||
RemoteSceneButtonConfigReplyHandler(DeviceFeature feature) {
|
||||
super(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getPollDelay() {
|
||||
return 2000L; // delay to allow all op flag commands to be processed
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable State getState(byte cmd1, double value) {
|
||||
RemoteSceneButtonConfig config = RemoteSceneButtonConfig.valueOf((int) value);
|
||||
// update device type based on button config
|
||||
getInsteonDevice().updateType(config);
|
||||
// return button config state
|
||||
return new StringType(config.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote switch button config reply message handler
|
||||
*/
|
||||
public static class RemoteSwitchButtonConfigReplyHandler extends OpFlagsReplyHandler {
|
||||
RemoteSwitchButtonConfigReplyHandler(DeviceFeature feature) {
|
||||
super(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getPollDelay() {
|
||||
return 2000L; // delay to allow all op flag commands to be processed
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable State getState(byte cmd1, double value) {
|
||||
RemoteSwitchButtonConfig config = RemoteSwitchButtonConfig.valueOf((int) value);
|
||||
// update device type based on button config
|
||||
getInsteonDevice().updateType(config);
|
||||
// return button config state
|
||||
return new StringType(config.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Siren request reply message handler
|
||||
*/
|
||||
|
@ -19,7 +19,6 @@ import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@ -28,7 +27,7 @@ import org.openhab.binding.insteon.internal.config.InsteonBridgeConfiguration;
|
||||
import org.openhab.binding.insteon.internal.config.InsteonDeviceConfiguration;
|
||||
import org.openhab.binding.insteon.internal.device.Device;
|
||||
import org.openhab.binding.insteon.internal.device.DeviceCache;
|
||||
import org.openhab.binding.insteon.internal.device.DeviceType;
|
||||
import org.openhab.binding.insteon.internal.device.DeviceFeature;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonAddress;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonDevice;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonEngine;
|
||||
@ -142,30 +141,30 @@ public class InsteonDeviceHandler extends InsteonBaseThingHandler {
|
||||
|
||||
@Override
|
||||
protected void initializeChannels(Device device) {
|
||||
DeviceType deviceType = device.getType();
|
||||
if (deviceType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.initializeChannels(device);
|
||||
|
||||
getThing().getChannels().forEach(channel -> setChannelCustomSettings(channel, deviceType.getName()));
|
||||
getThing().getChannels().forEach(channel -> setChannelCustomSettings(channel, device));
|
||||
}
|
||||
|
||||
private void setChannelCustomSettings(Channel channel, String deviceTypeName) {
|
||||
private void setChannelCustomSettings(Channel channel, Device device) {
|
||||
ChannelUID channelUID = channel.getUID();
|
||||
ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
|
||||
if (channelTypeUID == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String key = deviceTypeName + ":" + channelIdToFeatureName(channelTypeUID.getId());
|
||||
String[] stateDescriptionOptions = CUSTOM_STATE_DESCRIPTION_OPTIONS.get(key);
|
||||
String featureName = channelIdToFeatureName(channelTypeUID.getId());
|
||||
DeviceFeature feature = device.getFeature(featureName);
|
||||
if (feature == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> stateDescriptionOptions = CUSTOM_STATE_DESCRIPTION_OPTIONS.get(feature.getType());
|
||||
if (stateDescriptionOptions == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<StateOption> options = Stream.of(stateDescriptionOptions).map(value -> new StateOption(value,
|
||||
List<StateOption> options = stateDescriptionOptions.stream().map(value -> new StateOption(value,
|
||||
StringUtils.capitalizeByWhitespace(value.replace("_", " ").toLowerCase()))).toList();
|
||||
|
||||
logger.trace("setting state options for {} to {}", channelUID, options);
|
||||
|
@ -13,9 +13,6 @@
|
||||
package org.openhab.binding.insteon.internal.transport.message;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Ideally, Insteon ALL LINK messages are received in this order, and
|
||||
@ -87,7 +84,7 @@ public class GroupMessageStateMachine {
|
||||
* IN:Cmd:0x50|fromAddress:20.AC.99|toAddress:13.03.01|messageFlags:0xCB=ALL_LINK_BROADCAST:3:2|command1:0x06|
|
||||
* command2:0x00|
|
||||
*/
|
||||
public static enum GroupMessageType {
|
||||
private enum GroupMessageType {
|
||||
BCAST,
|
||||
CLEAN,
|
||||
SUCCESS
|
||||
@ -97,97 +94,89 @@ public class GroupMessageStateMachine {
|
||||
* The state of the machine (i.e. what message we are expecting next).
|
||||
* The usual state should be EXPECT_BCAST
|
||||
*/
|
||||
private static enum State {
|
||||
private enum State {
|
||||
EXPECT_BCAST,
|
||||
EXPECT_CLEAN,
|
||||
EXPECT_SUCCESS
|
||||
}
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GroupMessageStateMachine.class);
|
||||
|
||||
private State state = State.EXPECT_BCAST;
|
||||
private boolean duplicate = false;
|
||||
private byte lastCmd1 = 0;
|
||||
private long lastTimestamp = 0;
|
||||
|
||||
public boolean isDuplicate() {
|
||||
/**
|
||||
* Returns if group message is duplicate
|
||||
*
|
||||
* @param msg the group message
|
||||
* @return true if the group message is duplicate
|
||||
* @throws FieldException
|
||||
*/
|
||||
public boolean isDuplicate(Msg msg) throws FieldException {
|
||||
byte cmd1 = msg.isAllLinkSuccessReport() ? msg.getInsteonAddress("toAddress").getHighByte()
|
||||
: msg.getByte("command1");
|
||||
long timestamp = msg.getTimestamp();
|
||||
|
||||
if (cmd1 != lastCmd1 || timestamp != lastTimestamp) {
|
||||
GroupMessageType type = msg.isAllLinkSuccessReport() ? GroupMessageType.SUCCESS
|
||||
: msg.isAllLinkCleanup() ? GroupMessageType.CLEAN : GroupMessageType.BCAST;
|
||||
|
||||
update(cmd1, timestamp, type);
|
||||
}
|
||||
|
||||
return duplicate;
|
||||
}
|
||||
|
||||
public byte getLastCommand() {
|
||||
return lastCmd1;
|
||||
}
|
||||
|
||||
public long getLastTimestamp() {
|
||||
return lastTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state machine and determine if not duplicate
|
||||
* Updates the state machine
|
||||
*
|
||||
* @param address the address of the device that this state machine belongs to
|
||||
* @param group the group that this state machine belongs to
|
||||
* @param cmd1 cmd1 from the message received
|
||||
* @param timestamp timestamp from the message received
|
||||
* @param type the group message type that was received
|
||||
* @return true if the group message is duplicate
|
||||
*/
|
||||
public boolean update(InsteonAddress address, int group, byte cmd1, long timestamp, GroupMessageType type) {
|
||||
boolean isNewGroupMsg = cmd1 != lastCmd1 || timestamp > lastTimestamp + GROUP_STATE_TIMEOUT;
|
||||
private void update(byte cmd1, long timestamp, GroupMessageType type) {
|
||||
boolean isNewGroupMsg = cmd1 != lastCmd1 || Math.abs(timestamp - lastTimestamp) > GROUP_STATE_TIMEOUT;
|
||||
|
||||
switch (type) {
|
||||
case BCAST:
|
||||
switch (state) {
|
||||
case EXPECT_BCAST:
|
||||
switch (type) {
|
||||
case BCAST:
|
||||
case EXPECT_SUCCESS:
|
||||
duplicate = false;
|
||||
break;
|
||||
case CLEAN:
|
||||
case SUCCESS:
|
||||
duplicate = !isNewGroupMsg;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EXPECT_CLEAN:
|
||||
switch (type) {
|
||||
case BCAST:
|
||||
duplicate = !isNewGroupMsg;
|
||||
break;
|
||||
case CLEAN:
|
||||
case SUCCESS:
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EXPECT_SUCCESS:
|
||||
switch (type) {
|
||||
case BCAST:
|
||||
duplicate = false;
|
||||
break;
|
||||
case CLEAN:
|
||||
case SUCCESS:
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case BCAST:
|
||||
state = State.EXPECT_CLEAN;
|
||||
break;
|
||||
case CLEAN:
|
||||
switch (state) {
|
||||
case EXPECT_BCAST:
|
||||
duplicate = !isNewGroupMsg;
|
||||
break;
|
||||
case EXPECT_CLEAN:
|
||||
case EXPECT_SUCCESS:
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
state = State.EXPECT_SUCCESS;
|
||||
break;
|
||||
case SUCCESS:
|
||||
switch (state) {
|
||||
case EXPECT_BCAST:
|
||||
duplicate = !isNewGroupMsg;
|
||||
break;
|
||||
case EXPECT_CLEAN:
|
||||
case EXPECT_SUCCESS:
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
state = State.EXPECT_BCAST;
|
||||
break;
|
||||
}
|
||||
|
||||
lastCmd1 = cmd1;
|
||||
lastTimestamp = timestamp;
|
||||
|
||||
logger.debug("{} group:{} type:{} state:{} duplicate:{}", address, group, type, state, duplicate);
|
||||
|
||||
return duplicate;
|
||||
}
|
||||
}
|
||||
|
@ -190,8 +190,14 @@ channel-type.insteon.button-beep.label = Button Beep
|
||||
channel-type.insteon.button-beep.description = Enable beep on button press.
|
||||
channel-type.insteon.button-config.label = Button Config
|
||||
channel-type.insteon.button-config.description = Configure the button/scene mode.
|
||||
channel-type.insteon.button-config.state.option.BUTTON_1 = 1-Button
|
||||
channel-type.insteon.button-config.state.option.BUTTON_2_ALWAYS_ON = 2-Button Always On
|
||||
channel-type.insteon.button-config.state.option.BUTTON_2_TOGGLE = 2-Button Toggle
|
||||
channel-type.insteon.button-config.state.option.BUTTON_4 = 4-Button
|
||||
channel-type.insteon.button-config.state.option.BUTTON_6 = 6-Button
|
||||
channel-type.insteon.button-config.state.option.BUTTON_8 = 8-Button
|
||||
channel-type.insteon.button-config.state.option.BUTTON_8_ALWAYS_ON = 8-Button Always On
|
||||
channel-type.insteon.button-config.state.option.BUTTON_8_TOGGLE = 8-Button Toggle
|
||||
channel-type.insteon.button-lock.label = Button Lock
|
||||
channel-type.insteon.button-lock.description = Disable the front button press.
|
||||
channel-type.insteon.carbon-monoxide-alarm.label = Carbon Monoxide Alarm
|
||||
@ -308,6 +314,9 @@ channel-type.insteon.system-mode.state.option.HEAT = Heat
|
||||
channel-type.insteon.system-mode.state.option.COOL = Cool
|
||||
channel-type.insteon.system-mode.state.option.AUTO = Auto
|
||||
channel-type.insteon.system-mode.state.option.PROGRAM = Program
|
||||
channel-type.insteon.system-mode.state.option.PROGRAM_HEAT = Program Heat
|
||||
channel-type.insteon.system-mode.state.option.PROGRAM_COOL = Program Cool
|
||||
channel-type.insteon.system-mode.state.option.PROGRAM_AUTO = Program Heat
|
||||
channel-type.insteon.system-state.label = System State
|
||||
channel-type.insteon.system-state.state.option.OFF = Off
|
||||
channel-type.insteon.system-state.state.option.COOLING = Cooling
|
||||
|
@ -235,7 +235,7 @@
|
||||
<message-dispatcher>DefaultDispatcher</message-dispatcher>
|
||||
<message-handler command="0x19">IOLincRelayModeReplyHandler</message-handler>
|
||||
<message-handler default="true">NoOpMsgHandler</message-handler>
|
||||
<command-handler command="OnOffType">IOLincRelayModeCommandHandler</command-handler>
|
||||
<command-handler command="StringType">IOLincRelayModeCommandHandler</command-handler>
|
||||
<command-handler command="RefreshType">RefreshCommandHandler</command-handler>
|
||||
<poll-handler>NoPollHandler</poll-handler> <!-- polled by OpFlagsGroup -->
|
||||
</feature-type>
|
||||
@ -525,6 +525,34 @@
|
||||
<poll-handler>NoPollHandler</poll-handler> <!-- polled by OutletStatusGroup -->
|
||||
</feature-type>
|
||||
|
||||
<feature-type name="RemoteBatteryLevel">
|
||||
<message-dispatcher>DefaultDispatcher</message-dispatcher>
|
||||
<!-- battery level range 0xA0 => 0xB4 (undocumented) -->
|
||||
<!-- message field data1 0x01 (documented); 0x00 (observed) -->
|
||||
<message-handler command="0x2E" ext="1" cmd1="0x2E" cmd2="0x00" d1="0x00" d2="0x01" field="userData10"
|
||||
min="0xA0" max="0xB4">CustomDimensionlessMsgHandler</message-handler>
|
||||
<message-handler default="true">NoOpMsgHandler</message-handler>
|
||||
<command-handler command="RefreshType">RefreshCommandHandler</command-handler>
|
||||
<command-handler default="true">NoOpCommandHandler</command-handler>
|
||||
<poll-handler>NoPollHandler</poll-handler> <!-- polled by ExtDataGroup -->
|
||||
</feature-type>
|
||||
<feature-type name="RemoteSceneButtonConfig">
|
||||
<message-dispatcher>DefaultDispatcher</message-dispatcher>
|
||||
<message-handler command="0x19">RemoteSceneButtonConfigReplyHandler</message-handler>
|
||||
<message-handler default="true">NoOpMsgHandler</message-handler>
|
||||
<command-handler command="StringType">RemoteSceneButtonConfigCommandHandler</command-handler>
|
||||
<command-handler command="RefreshType">RefreshCommandHandler</command-handler>
|
||||
<poll-handler>NoPollHandler</poll-handler> <!-- polled by OpFlagsGroup -->
|
||||
</feature-type>
|
||||
<feature-type name="RemoteSwitchButtonConfig">
|
||||
<message-dispatcher>DefaultDispatcher</message-dispatcher>
|
||||
<message-handler command="0x19">RemoteSwitchButtonConfigReplyHandler</message-handler>
|
||||
<message-handler default="true">NoOpMsgHandler</message-handler>
|
||||
<command-handler command="StringType">RemoteSwitchButtonConfigCommandHandler</command-handler>
|
||||
<command-handler command="RefreshType">RefreshCommandHandler</command-handler>
|
||||
<poll-handler>NoPollHandler</poll-handler> <!-- polled by OpFlagsGroup -->
|
||||
</feature-type>
|
||||
|
||||
<feature-type name="PowerMeterDataGroup">
|
||||
<message-dispatcher>PollGroupDispatcher</message-dispatcher>
|
||||
<poll-handler ext="0" cmd1="0x82" cmd2="0x00">FlexPollHandler</poll-handler>
|
||||
|
@ -49,11 +49,15 @@
|
||||
<feature name="eventButtonB" group="2">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonC" group="3">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonD" group="4">GenericButtonEvent</feature>
|
||||
<feature-group name="extDataGroup" type="ExtDataGroup">
|
||||
<feature name="batteryLevel">RemoteBatteryLevel</feature>
|
||||
</feature-group>
|
||||
<feature-group name="opFlagsGroup" type="OpFlagsGroup">
|
||||
<feature name="programLock" bit="0" on="0x00" off="0x01">OpFlags</feature>
|
||||
<feature name="ledOnOff" bit="1" on="0x02" off="0x03" inverted="true">OpFlags</feature>
|
||||
<feature name="buttonBeep" bit="2" on="0x04" off="0x05">OpFlags</feature>
|
||||
<feature name="stayAwake" bit="3" on="0x06" off="0x07">OpFlags</feature>
|
||||
<feature name="buttonConfig">RemoteSceneButtonConfig</feature>
|
||||
</feature-group>
|
||||
<default-link name="buttonA" type="controller" group="1" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonB" type="controller" group="2" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
@ -62,41 +66,66 @@
|
||||
</device-type>
|
||||
|
||||
<device-type name="GeneralizedController_MiniRemoteScene8" batteryPowered="true">
|
||||
<feature name="eventButtonA" group="1">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonB" group="2">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonC" group="3">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonD" group="4">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonE" group="5">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonF" group="6">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonG" group="7">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonH" group="8">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonA" group="2">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonB" group="1">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonC" group="4">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonD" group="3">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonE" group="6">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonF" group="5">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonG" group="8">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonH" group="7">GenericButtonEvent</feature>
|
||||
<feature-group name="extDataGroup" type="ExtDataGroup">
|
||||
<feature name="batteryLevel">RemoteBatteryLevel</feature>
|
||||
</feature-group>
|
||||
<feature-group name="opFlagsGroup" type="OpFlagsGroup">
|
||||
<feature name="programLock" bit="0" on="0x00" off="0x01">OpFlags</feature>
|
||||
<feature name="ledOnOff" bit="1" on="0x02" off="0x03" inverted="true">OpFlags</feature>
|
||||
<feature name="buttonBeep" bit="2" on="0x04" off="0x05">OpFlags</feature>
|
||||
<feature name="stayAwake" bit="3" on="0x06" off="0x07">OpFlags</feature>
|
||||
<feature name="buttonConfig">RemoteSceneButtonConfig</feature>
|
||||
</feature-group>
|
||||
<default-link name="buttonA" type="controller" group="1" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonB" type="controller" group="2" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonC" type="controller" group="3" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonD" type="controller" group="4" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonE" type="controller" group="5" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonF" type="controller" group="6" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonG" type="controller" group="7" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonH" type="controller" group="8" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonA" type="controller" group="2" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonB" type="controller" group="1" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonC" type="controller" group="4" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonD" type="controller" group="3" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonE" type="controller" group="6" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonF" type="controller" group="5" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonG" type="controller" group="8" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonH" type="controller" group="7" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
</device-type>
|
||||
|
||||
<device-type name="GeneralizedController_MiniRemoteSwitch" batteryPowered="true">
|
||||
<feature name="eventButton">GenericButtonEvent</feature>
|
||||
<feature-group name="extDataGroup" type="ExtDataGroup">
|
||||
<feature name="batteryLevel">RemoteBatteryLevel</feature>
|
||||
</feature-group>
|
||||
<feature-group name="opFlagsGroup" type="OpFlagsGroup">
|
||||
<feature name="programLock" bit="0" on="0x00" off="0x01">OpFlags</feature>
|
||||
<feature name="ledOnOff" bit="1" on="0x02" off="0x03" inverted="true">OpFlags</feature>
|
||||
<feature name="buttonBeep" bit="2" on="0x04" off="0x05">OpFlags</feature>
|
||||
<feature name="stayAwake" bit="3" on="0x06" off="0x07">OpFlags</feature>
|
||||
<feature name="buttonConfig">RemoteSwitchButtonConfig</feature>
|
||||
</feature-group>
|
||||
<default-link name="button" type="controller" group="1" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
</device-type>
|
||||
|
||||
<device-type name="GeneralizedController_MiniRemoteSwitch2" batteryPowered="true">
|
||||
<feature name="eventButtonA" group="1">GenericButtonEvent</feature>
|
||||
<feature name="eventButtonB" group="2">GenericButtonEvent</feature>
|
||||
<feature-group name="extDataGroup" type="ExtDataGroup">
|
||||
<feature name="batteryLevel">RemoteBatteryLevel</feature>
|
||||
</feature-group>
|
||||
<feature-group name="opFlagsGroup" type="OpFlagsGroup">
|
||||
<feature name="programLock" bit="0" on="0x00" off="0x01">OpFlags</feature>
|
||||
<feature name="ledOnOff" bit="1" on="0x02" off="0x03" inverted="true">OpFlags</feature>
|
||||
<feature name="buttonBeep" bit="2" on="0x04" off="0x05">OpFlags</feature>
|
||||
<feature name="stayAwake" bit="3" on="0x06" off="0x07">OpFlags</feature>
|
||||
<feature name="buttonConfig">RemoteSwitchButtonConfig</feature>
|
||||
</feature-group>
|
||||
<default-link name="buttonA" type="controller" group="1" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
<default-link name="buttonB" type="controller" group="2" data1="0x03" data2="0x00" data3="0x00"/>
|
||||
</device-type>
|
||||
|
||||
<!-- Dimmable Lighting Control -->
|
||||
|
||||
<device-type name="DimmableLightingControl">
|
||||
|
Loading…
Reference in New Issue
Block a user