Skip to content

Commit

Permalink
[insteon] added support for motion sensor II (2844-222) (#8014)
Browse files Browse the repository at this point in the history
Signed-off-by: Rob Nielsen <[email protected]>
  • Loading branch information
robnielsen authored Jul 1, 2020
1 parent 4479472 commit 5872892
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 50 deletions.
27 changes: 24 additions & 3 deletions bundles/org.openhab.binding.insteon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ These have been tested and should work out of the box:
| 2413U | PowerLinc 2413U USB modem | 0x000045 | Bernd Pfrommer |
| 2843-222 | Wireless Open/Close Sensor | 0x000049 | Josenivaldo Benito |
| 2842-222 | Motion Sensor | 0x00004A | Bernd Pfrommer |
| 2844-222 | Motion Sensor II | F00.00.24 | Rob Nielsen |
| 2486DWH8 | KeypadLinc Dimmer | 0x000051 | Chris Graham |
| 2472D | OutletLincDimmer | 0x000068 | Chris Graham |
| X10 switch | generic X10 switch | X00.00.01 | Bernd Pfrommer |
Expand All @@ -118,6 +119,7 @@ In order to determine which channels a device supports, you can look at the devi
| acDelay | Number | AC Delay |
| backlightDuration | Number | Back Light Duration |
| batteryLevel | Number | Battery Level |
| batteryPercent | Number:Dimensionless | Battery Percent |
| batteryWatermarkLevel | Number | Battery Watermark Level |
| beep | Switch | Beep |
| bottomOutlet | Switch | Bottom Outlet |
Expand Down Expand Up @@ -179,7 +181,9 @@ In order to determine which channels a device supports, you can look at the devi
| stage1Duration | Number | Stage 1 Duration |
| switch | Switch | Switch |
| systemMode | Number | System Mode |
| tamperSwitch | Contact | Tamper Switch |
| temperature | Number:Temperature | Temperature |
| temperatureLevel | Number | Temperature Level |
| topOutlet | Switch | Top Outlet |
| update | Switch | Update |
| watts | Number:Power | Watts |
Expand Down Expand Up @@ -388,12 +392,29 @@ Then create entries in the .items file like this:

```
Contact motionSensor "motion sensor [MAP(contact.map):%s]" { channel="insteon:device:home:AABBCC:contact"}
Number motionSensorBatteryLevel "motion sensor battery level [%.1f]" { channel="insteon:device:home:AABBCC:batteryLevel" }
Number motionSensorLightLevel "motion sensor light level [%.1f]" { channel="insteon:device:home:AABBCC:lightLevel" }
Number motionSensorBatteryLevel "motion sensor battery level" { channel="insteon:device:home:AABBCC:batteryLevel" }
Number motionSensorLightLevel "motion sensor light level" { channel="insteon:device:home:AABBCC:lightLevel" }
```

This will give you a contact, the battery level, and the light level.
Note that battery and light level are only updated when either there is motion, or the sensor battery runs low.
Note that battery and light level are only updated when either there is motion, light level above/below threshold, tamper switch activated, or the sensor battery runs low.

The motion sensor II includes three additional channels:

**Items**

```
Number motionSensorBatteryPercent "motion sensor battery percent" { channel="insteon:device:home:AABBCC:batteryPercent" }
Contact motionSensorTamperSwitch "motion sensor tamper switch [MAP(contact.map):%s]" { channel="insteon:device:home:AABBCC:tamperSwitch"}
Number motionSensorTemperatureLevel "motion sensor temperature level" { channel="insteon:device:home:AABBCC:temperatureLevel" }
```

The temperature can be calculated in Fahrenheit using the following formulas:

* If the device is battery powered: `temperature = 0.73 * motionSensorTemperatureLevel - 20.53`
* If the device is USB powered: `temperature = 0.72 * motionSensorTemperatureLevel - 24.61`

Since the motion sensor II might not be calibrated correctly, the values `20.53` and `24.61` can be adjusted as necessary to produce the correct temperature.

### Hidden Door Sensors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ public InsteonDevice makeNewDevice(InsteonAddress addr, String productKey) {
DeviceType dt = DeviceTypeLoader.instance().getDeviceType(productKey);
InsteonDevice dev = InsteonDevice.makeDevice(dt);
dev.setAddress(addr);
dev.setProductKey(productKey);
dev.setDriver(driver);
if (!dev.hasValidPollingInterval()) {
dev.setPollInterval(devicePollIntervalMilliseconds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class InsteonBindingConstants {
public static final String AC_DELAY = "acDelay";
public static final String BACKLIGHT_DURATION = "backlightDuration";
public static final String BATTERY_LEVEL = "batteryLevel";
public static final String BATTERY_PERCENT = "batteryPercent";
public static final String BATTERY_WATERMARK_LEVEL = "batteryWatermarkLevel";
public static final String BEEP = "beep";
public static final String BOTTOM_OUTLET = "bottomOutlet";
Expand Down Expand Up @@ -102,7 +103,9 @@ public class InsteonBindingConstants {
public static final String STAGE1_DURATION = "stage1Duration";
public static final String SWITCH = "switch";
public static final String SYSTEM_MODE = "systemMode";
public static final String TAMPER_SWITCH = "tamperSwitch";
public static final String TEMPERATURE = "temperature";
public static final String TEMPERATURE_LEVEL = "temperatureLevel";
public static final String TOP_OUTLET = "topOutlet";
public static final String UPDATE = "update";
public static final String WATTS = "watts";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.smarthome.core.types.Command;
import org.openhab.binding.insteon.internal.config.InsteonChannelConfiguration;
import org.openhab.binding.insteon.internal.device.DeviceFeatureListener.StateChangeType;
import org.openhab.binding.insteon.internal.handler.InsteonDeviceHandler;
import org.openhab.binding.insteon.internal.message.FieldException;
import org.openhab.binding.insteon.internal.message.InvalidMessageTypeException;
import org.openhab.binding.insteon.internal.message.Msg;
Expand Down Expand Up @@ -688,23 +689,25 @@ public static class PowerMeterCommandHandler extends CommandHandler {

@Override
public void handleCommand(InsteonChannelConfiguration conf, Command cmd, InsteonDevice dev) {
String cmdParam = conf.getParameters().get("cmd");
String cmdParam = conf.getParameters().get(InsteonDeviceHandler.CMD);
if (cmdParam == null) {
logger.warn("{} ignoring cmd {} because no cmd= is configured!", nm(), cmd);
return;
}
try {
if (cmd == OnOffType.ON) {
if (cmdParam.equals("reset")) {
if (cmdParam.equals(InsteonDeviceHandler.CMD_RESET)) {
Msg m = dev.makeStandardMessage((byte) 0x0f, (byte) 0x80, (byte) 0x00);
dev.enqueueMessage(m, feature);
logger.debug("{}: sent reset msg to power meter {}", nm(), dev.getAddress());
feature.publish(OnOffType.OFF, StateChangeType.ALWAYS, "cmd", "reset");
} else if (cmdParam.equals("update")) {
feature.publish(OnOffType.OFF, StateChangeType.ALWAYS, InsteonDeviceHandler.CMD,
InsteonDeviceHandler.CMD_RESET);
} else if (cmdParam.equals(InsteonDeviceHandler.CMD_UPDATE)) {
Msg m = dev.makeStandardMessage((byte) 0x0f, (byte) 0x82, (byte) 0x00);
dev.enqueueMessage(m, feature);
logger.debug("{}: sent update msg to power meter {}", nm(), dev.getAddress());
feature.publish(OnOffType.OFF, StateChangeType.ALWAYS, "cmd", "update");
feature.publish(OnOffType.OFF, StateChangeType.ALWAYS, InsteonDeviceHandler.CMD,
InsteonDeviceHandler.CMD_UPDATE);
} else {
logger.warn("{}: ignoring unknown cmd {} for power meter {}", nm(), cmdParam, dev.getAddress());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.eclipse.smarthome.core.types.State;
import org.openhab.binding.insteon.internal.device.DeviceFeatureListener.StateChangeType;
import org.openhab.binding.insteon.internal.device.GroupMessageStateMachine.GroupMessage;
import org.openhab.binding.insteon.internal.handler.InsteonDeviceHandler;
import org.openhab.binding.insteon.internal.message.FieldException;
import org.openhab.binding.insteon.internal.message.InvalidMessageTypeException;
import org.openhab.binding.insteon.internal.message.Msg;
Expand Down Expand Up @@ -715,15 +716,46 @@ public void handleMessage(int group, byte cmd1, Msg msg, DeviceFeature f) {
}
try {
int cmd2 = msg.getByte("command2") & 0xff;
int batteryLevel;
int lightLevel;
int temperatureLevel;
switch (cmd2) {
case 0x00: // this is a product data response message
int batteryLevel = msg.getByte("userData12") & 0xff;
int lightLevel = msg.getByte("userData11") & 0xff;
batteryLevel = msg.getByte("userData12") & 0xff;
lightLevel = msg.getByte("userData11") & 0xff;
logger.debug("{}: {} got light level: {}, battery level: {}", nm(), dev.getAddress(),
lightLevel, batteryLevel);
feature.publish(new DecimalType(lightLevel), StateChangeType.CHANGED, "field", "light_level");
feature.publish(new DecimalType(batteryLevel), StateChangeType.CHANGED, "field",
"battery_level");
feature.publish(new DecimalType(lightLevel), StateChangeType.CHANGED,
InsteonDeviceHandler.FIELD, InsteonDeviceHandler.FIELD_LIGHT_LEVEL);
feature.publish(new DecimalType(batteryLevel), StateChangeType.CHANGED,
InsteonDeviceHandler.FIELD, InsteonDeviceHandler.FIELD_BATTERY_LEVEL);
break;
case 0x03: // this is the 2844-222 data response message
batteryLevel = msg.getByte("userData6") & 0xff;
lightLevel = msg.getByte("userData7") & 0xff;
temperatureLevel = msg.getByte("userData8") & 0xff;
logger.debug("{}: {} got light level: {}, battery level: {}, temperature level: {}", nm(),
dev.getAddress(), lightLevel, batteryLevel, temperatureLevel);
feature.publish(new DecimalType(lightLevel), StateChangeType.CHANGED,
InsteonDeviceHandler.FIELD, InsteonDeviceHandler.FIELD_LIGHT_LEVEL);
feature.publish(new DecimalType(batteryLevel), StateChangeType.CHANGED,
InsteonDeviceHandler.FIELD, InsteonDeviceHandler.FIELD_BATTERY_LEVEL);
feature.publish(new DecimalType(temperatureLevel), StateChangeType.CHANGED,
InsteonDeviceHandler.FIELD, InsteonDeviceHandler.FIELD_TEMPERATURE_LEVEL);

// per 2844-222 dev doc: working battery level range is 0xd2 - 0x70
int batteryPercentage;
if (batteryLevel >= 0xd2) {
batteryPercentage = 100;
} else if (batteryLevel <= 0x70) {
batteryPercentage = 0;
} else {
batteryPercentage = (batteryLevel - 0x70) * 100 / (0xd2 - 0x70);
}
logger.debug("{}: {} battery percentage: {}", nm(), dev.getAddress(), batteryPercentage);
feature.publish(new QuantityType<>(batteryPercentage, SmartHomeUnits.PERCENT),
StateChangeType.CHANGED, InsteonDeviceHandler.FIELD,
InsteonDeviceHandler.FIELD_BATTERY_PERCENTAGE);
break;
default:
logger.warn("unknown cmd2 = {} in info reply message {}", cmd2, msg);
Expand Down Expand Up @@ -756,10 +788,10 @@ public void handleMessage(int group, byte cmd1, Msg msg, DeviceFeature f) {
int batteryWatermark = msg.getByte("userData7") & 0xff;
logger.debug("{}: {} got light level: {}, battery level: {}", nm(), dev.getAddress(),
batteryWatermark, batteryLevel);
feature.publish(new DecimalType(batteryWatermark), StateChangeType.CHANGED, "field",
"battery_watermark_level");
feature.publish(new DecimalType(batteryLevel), StateChangeType.CHANGED, "field",
"battery_level");
feature.publish(new DecimalType(batteryWatermark), StateChangeType.CHANGED,
InsteonDeviceHandler.FIELD, InsteonDeviceHandler.FIELD_BATTERY_WATERMARK_LEVEL);
feature.publish(new DecimalType(batteryLevel), StateChangeType.CHANGED,
InsteonDeviceHandler.FIELD, InsteonDeviceHandler.FIELD_BATTERY_LEVEL);
break;
default:
logger.warn("unknown cmd2 = {} in info reply message {}", cmd2, msg);
Expand Down Expand Up @@ -801,9 +833,9 @@ public void handleMessage(int group, byte cmd1, Msg msg, DeviceFeature f) {

logger.debug("{}:{} watts: {} kwh: {} ", nm(), f.getDevice().getAddress(), watts, kwh);
feature.publish(new QuantityType<>(kwh, SmartHomeUnits.KILOWATT_HOUR), StateChangeType.CHANGED,
"field", "kwh");
feature.publish(new QuantityType<>(watts, SmartHomeUnits.WATT), StateChangeType.CHANGED, "field",
"watts");
InsteonDeviceHandler.FIELD, InsteonDeviceHandler.FIELD_KWH);
feature.publish(new QuantityType<>(watts, SmartHomeUnits.WATT), StateChangeType.CHANGED,
InsteonDeviceHandler.FIELD, InsteonDeviceHandler.FIELD_WATTS);
} catch (FieldException e) {
logger.warn("error parsing {}: ", msg, e);
}
Expand Down Expand Up @@ -941,7 +973,11 @@ public static class ClosedSleepingContactHandler extends MessageHandler {
@Override
public void handleMessage(int group, byte cmd1, Msg msg, DeviceFeature f) {
feature.publish(OpenClosedType.CLOSED, StateChangeType.ALWAYS);
sendExtendedQuery(f, (byte) 0x2e, (byte) 00);
if (f.getDevice().hasProductKey(InsteonDeviceHandler.MOTION_SENSOR_II_PRODUCT_KEY)) {
sendExtendedQuery(f, (byte) 0x2e, (byte) 03);
} else {
sendExtendedQuery(f, (byte) 0x2e, (byte) 00);
}
}
}

Expand All @@ -954,7 +990,11 @@ public static class OpenedSleepingContactHandler extends MessageHandler {
@Override
public void handleMessage(int group, byte cmd1, Msg msg, DeviceFeature f) {
feature.publish(OpenClosedType.OPEN, StateChangeType.ALWAYS);
sendExtendedQuery(f, (byte) 0x2e, (byte) 00);
if (f.getDevice().hasProductKey(InsteonDeviceHandler.MOTION_SENSOR_II_PRODUCT_KEY)) {
sendExtendedQuery(f, (byte) 0x2e, (byte) 03);
} else {
sendExtendedQuery(f, (byte) 0x2e, (byte) 00);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.openhab.binding.insteon.internal.device.InsteonAddress;
import org.openhab.binding.insteon.internal.device.InsteonDevice;
import org.openhab.binding.insteon.internal.device.ModemDBBuilder;
import org.openhab.binding.insteon.internal.handler.InsteonDeviceHandler;
import org.openhab.binding.insteon.internal.message.FieldException;
import org.openhab.binding.insteon.internal.message.InvalidMessageTypeException;
import org.openhab.binding.insteon.internal.message.Msg;
Expand Down Expand Up @@ -509,14 +510,14 @@ public void msg(Msg msg) {
if (msg.getByte("Cmd") == 0x60) {
// add the modem to the device list
InsteonAddress a = new InsteonAddress(msg.getAddress("IMAddress"));
String prodKey = "0x000045";
DeviceType dt = DeviceTypeLoader.instance().getDeviceType(prodKey);
DeviceType dt = DeviceTypeLoader.instance().getDeviceType(InsteonDeviceHandler.PLM_PRODUCT_KEY);
if (dt == null) {
logger.warn("unknown modem product key: {} for modem: {}.", prodKey, a);
logger.warn("unknown modem product key: {} for modem: {}.",
InsteonDeviceHandler.PLM_PRODUCT_KEY, a);
} else {
device = InsteonDevice.makeDevice(dt);
device.setAddress(a);
device.setProductKey(prodKey);
device.setProductKey(InsteonDeviceHandler.PLM_PRODUCT_KEY);
device.setDriver(driver);
device.setIsModem(true);
logger.debug("found modem {} in device_types: {}", a, device.toString());
Expand Down
Loading

0 comments on commit 5872892

Please sign in to comment.