From 6d9c6093dee9ff66911435f3d7065dc0366a84e8 Mon Sep 17 00:00:00 2001 From: ebaauw Date: Thu, 27 Feb 2020 22:45:33 +0100 Subject: [PATCH] Bitron Remote, Hue smart button - Add support for Bitron Remote, see #2392; - Add support (untested!) for Hue smart button, see #2077. --- bindings.cpp | 25 ++++++++++++--- database.cpp | 24 +++++++++++++-- de_web_plugin.cpp | 78 ++++++++++++++++++++++++++++++++++------------- sensor.cpp | 18 +++++++++-- 4 files changed, 115 insertions(+), 30 deletions(-) diff --git a/bindings.cpp b/bindings.cpp index 1f6a57602d..a264f6d658 100644 --- a/bindings.cpp +++ b/bindings.cpp @@ -1112,7 +1112,8 @@ bool DeRestPluginPrivate::sendConfigureReportingRequest(BindingTask &bt) rq.maxInterval = 7200; // value used by Hue bridge rq.reportableChange8bit = 0; // value used by Hue bridge } - else if (sensor && sensor->modelId().startsWith(QLatin1String("RWL02"))) // Hue dimmer switch + else if (sensor && (sensor->modelId().startsWith(QLatin1String("RWL02")) || // Hue dimmer switch + sensor->modelId().startsWith(QLatin1String("ROM00")))) // Hue smart button { rq.minInterval = 300; // value used by Hue bridge rq.maxInterval = 300; // value used by Hue bridge @@ -1124,7 +1125,8 @@ bool DeRestPluginPrivate::sendConfigureReportingRequest(BindingTask &bt) rq.maxInterval = 900; // value used by Hue bridge rq.reportableChange8bit = 4; // value used by Hue bridge } - else if (sensor && sensor->manufacturer().startsWith(QLatin1String("Climax"))) + else if (sensor && (sensor->manufacturer().startsWith(QLatin1String("Climax")) || + sensor->modelId().startsWith(QLatin1String("902010/23")))) { rq.attributeId = 0x0035; // battery alarm mask rq.dataType = deCONZ::Zcl8BitBitMap; @@ -1533,6 +1535,7 @@ bool DeRestPluginPrivate::sendConfigureReportingRequest(BindingTask &bt) Sensor *sensor = dynamic_cast(bt.restNode); if (sensor && (sensor->modelId().startsWith(QLatin1String("RWL02")) || // Hue dimmer switch + sensor->modelId().startsWith(QLatin1String("ROM00")) || // Hue smart button sensor->modelId().startsWith(QLatin1String("Z3-1BRL")))) // Lutron Aurora Friends-of-Hue dimmer switch { deCONZ::NumericUnion val; @@ -1854,6 +1857,7 @@ bool DeRestPluginPrivate::checkSensorBindingsForAttributeReporting(Sensor *senso // Philips sensor->modelId().startsWith(QLatin1String("SML00")) || sensor->modelId().startsWith(QLatin1String("RWL02")) || + sensor->modelId().startsWith(QLatin1String("ROM00")) || // Lutron Aurora Friends-of-Hue dimmer switch sensor->modelId().startsWith(QLatin1String("Z3-1BRL")) || // ubisys @@ -2057,7 +2061,8 @@ bool DeRestPluginPrivate::checkSensorBindingsForAttributeReporting(Sensor *senso //This device don't support report attribute continue; } - if (sensor->manufacturer().startsWith(QLatin1String("Climax"))) + if (sensor->manufacturer().startsWith(QLatin1String("Climax")) || + sensor->modelId().startsWith(QLatin1String("902010/23"))) { val = sensor->getZclValue(*i, 0x0035); // battery alarm mask } @@ -2116,6 +2121,7 @@ bool DeRestPluginPrivate::checkSensorBindingsForAttributeReporting(Sensor *senso else if (*i == VENDOR_CLUSTER_ID) { if (sensor->modelId().startsWith(QLatin1String("RWL02")) || // Hue dimmer switch + sensor->modelId().startsWith(QLatin1String("ROM00")) || // Hue smart button sensor->modelId().startsWith(QLatin1String("Z3-1BRL"))) // Lutron Aurora Friends-of-Hue dimmer switch { val = sensor->getZclValue(*i, 0x0000); // button event @@ -2325,7 +2331,8 @@ bool DeRestPluginPrivate::checkSensorBindingsForClientClusters(Sensor *sensor) //quint8 srcEndpoint = sensor->fingerPrint().endpoint; std::vector clusters; - if (sensor->modelId().startsWith(QLatin1String("RWL02"))) // Hue dimmer switch + if (sensor->modelId().startsWith(QLatin1String("RWL02")) || // Hue dimmer switch + sensor->modelId().startsWith(QLatin1String("ROM00"))) // Hue smart button { srcEndpoints.push_back(0x01); clusters.push_back(ONOFF_CLUSTER_ID); @@ -2480,6 +2487,13 @@ bool DeRestPluginPrivate::checkSensorBindingsForClientClusters(Sensor *sensor) clusters.push_back(COLOR_CLUSTER_ID); srcEndpoints.push_back(sensor->fingerPrint().endpoint); } + // Bitron remote control + else if (sensor->modelId().startsWith(QLatin1String("902010/23"))) + { + clusters.push_back(ONOFF_CLUSTER_ID); + clusters.push_back(LEVEL_CLUSTER_ID); + srcEndpoints.push_back(sensor->fingerPrint().endpoint); + } else { return false; @@ -2593,7 +2607,8 @@ void DeRestPluginPrivate::checkSensorGroup(Sensor *sensor) } } - if (sensor->modelId().startsWith(QLatin1String("RWL02"))) // Hue dimmer switch + if (sensor->modelId().startsWith(QLatin1String("RWL02")) || // Hue dimmer switch + sensor->modelId().startsWith(QLatin1String("ROM00"))) // Hue smart button { if (!group) { diff --git a/database.cpp b/database.cpp index cf59f3e22b..2cb9156ba2 100644 --- a/database.cpp +++ b/database.cpp @@ -2930,7 +2930,9 @@ static int sqliteLoadAllSensorsCallback(void *user, int ncols, char **colval , c { sensor.addItem(DataTypeInt32, RStateGesture); } - else if (sensor.modelId().startsWith(QLatin1String("RWL02"))) // || sensor.modelId().startsWith(QLatin1String("Z3-1BRL"))) + else if (sensor.modelId().startsWith(QLatin1String("RWL02")) || + // sensor.modelId().startsWith(QLatin1String("Z3-1BRL")) || + sensor.modelId().startsWith(QLatin1String("ROM00"))) { sensor.addItem(DataTypeUInt16, RStateEventDuration); } @@ -3289,6 +3291,23 @@ static int sqliteLoadAllSensorsCallback(void *user, int ncols, char **colval , c sensor.setNeedSaveDatabase(true); } } + else if (sensor.modelId().startsWith(QLatin1String("ROM00"))) // Hue smart button + { + clusterId = VENDOR_CLUSTER_ID; + endpoint = 1; + + if (!sensor.fingerPrint().hasInCluster(POWER_CONFIGURATION_CLUSTER_ID)) + { + sensor.fingerPrint().inClusters.push_back(POWER_CONFIGURATION_CLUSTER_ID); + sensor.setNeedSaveDatabase(true); + } + + if (!sensor.fingerPrint().hasInCluster(VENDOR_CLUSTER_ID)) // for realtime button feedback + { + sensor.fingerPrint().inClusters.push_back(VENDOR_CLUSTER_ID); + sensor.setNeedSaveDatabase(true); + } + } else if (sensor.modelId().startsWith(QLatin1String("SML00"))) // Hue motion sensor { if (!sensor.fingerPrint().hasInCluster(BASIC_CLUSTER_ID)) @@ -3377,7 +3396,8 @@ static int sqliteLoadAllSensorsCallback(void *user, int ncols, char **colval , c if (sensor.fingerPrint().hasInCluster(POWER_CONFIGURATION_CLUSTER_ID)) { - if (sensor.manufacturer().startsWith(QLatin1String("Climax"))) + if (sensor.manufacturer().startsWith(QLatin1String("Climax")) || + sensor.modelId().startsWith(QLatin1String("902010/23"))) { // climax non IAS reports state/lowbattery via battery alarm mask attribute item = sensor.addItem(DataTypeBool, RStateLowBattery); diff --git a/de_web_plugin.cpp b/de_web_plugin.cpp index 3cf6796074..584adaeb8b 100644 --- a/de_web_plugin.cpp +++ b/de_web_plugin.cpp @@ -127,6 +127,7 @@ static const SupportedDevice supportedDevices[] = { { VENDOR_NONE, "OJB-IR715-Z", tiMacPrefix }, { VENDOR_NONE, "902010/21", tiMacPrefix }, // Bitron: door/window sensor { VENDOR_NONE, "902010/22", tiMacPrefix }, // Bitron: motion sensor + { VENDOR_NONE, "902010/23", tiMacPrefix }, // Bitron: remote control { VENDOR_NONE, "902010/24", tiMacPrefix }, // Bitron: smoke detector { VENDOR_NONE, "902010/25", tiMacPrefix }, // Bitron: smart plug { VENDOR_BITRON, "902010/32", emberMacPrefix }, // Bitron: thermostat @@ -151,8 +152,8 @@ static const SupportedDevice supportedDevices[] = { { VENDOR_NYCE, "3011", emberMacPrefix }, // NYCE door/window sensor { VENDOR_NYCE, "3014", emberMacPrefix }, // NYCE garage door/tilt sensor { VENDOR_NYCE, "3043", emberMacPrefix }, // NYCE ceiling motion sensor - { VENDOR_PHILIPS, "RWL020", philipsMacPrefix }, // Hue dimmer switch - { VENDOR_PHILIPS, "RWL021", philipsMacPrefix }, // Hue dimmer switch + { VENDOR_PHILIPS, "RWL02", philipsMacPrefix }, // Hue dimmer switch + { VENDOR_PHILIPS, "ROM00", philipsMacPrefix }, // Hue smart button { VENDOR_PHILIPS, "SML00", philipsMacPrefix }, // Hue motion sensor { VENDOR_SAMJIN, "motion", samjinMacPrefix }, // Smarthings GP-U999SJVLBAA (Samjin) Motion Sensor { VENDOR_SAMJIN, "multi", samjinMacPrefix }, // Smarthings (Samjin) Multipurpose Sensor @@ -3238,6 +3239,7 @@ void DeRestPluginPrivate::checkSensorButtonEvent(Sensor *sensor, const deCONZ::A checkClientCluster = true; } else if (sensor->modelId().startsWith(QLatin1String("RWL02")) || // Hue dimmer switch + sensor->modelId().startsWith(QLatin1String("ROM00")) || // Hue smart button sensor->modelId().startsWith(QLatin1String("Z3-1BRL"))) // Lutron Aurora Friends-of-Hue dimmer switch { checkReporting = true; @@ -3653,6 +3655,7 @@ void DeRestPluginPrivate::checkSensorButtonEvent(Sensor *sensor, const deCONZ::A #if 0 // check if hue dimmer switch is configured if (sensor->modelId().startsWith(QLatin1String("RWL02")) || // Hue dimmer switch + sensor->modelId().startsWith(QLatin1String("ROM00")) || // Hue smart button sensor->modelId().startsWith(QLatin1String("Z3-1BRL"))) // Lutron Aurora Friends-of-Hue dimmer switch { bool ok = true; @@ -4717,7 +4720,8 @@ void DeRestPluginPrivate::addSensorNode(const deCONZ::Node *node, const SensorFi if (sensorNode.fingerPrint().hasInCluster(POWER_CONFIGURATION_CLUSTER_ID)) { - if (manufacturer.startsWith(QLatin1String("Climax"))) + if (manufacturer.startsWith(QLatin1String("Climax")) || + sensorNode.modelId().startsWith(QLatin1String("902010/23"))) { // climax non IAS reports state/lowbattery via battery alarm mask attribute sensorNode.addItem(DataTypeBool, RStateLowBattery); @@ -4767,7 +4771,9 @@ void DeRestPluginPrivate::addSensorNode(const deCONZ::Node *node, const SensorFi { sensorNode.addItem(DataTypeInt32, RStateGesture); } - else if (modelId.startsWith(QLatin1String("RWL02"))) // || modelId.startsWith(QLatin1String("Z3-1BRL"))) + else if (modelId.startsWith(QLatin1String("RWL02")) || // Hue dimmer switch + // modelId.startsWith(QLatin1String("Z3-1BRL")) || // Lutron Aurora Firends-of-Hue dimmer switch + modelId.startsWith(QLatin1String("ROM00"))) // Hue smart button { sensorNode.addItem(DataTypeUInt16, RStateEventDuration); } @@ -5112,6 +5118,10 @@ void DeRestPluginPrivate::addSensorNode(const deCONZ::Node *node, const SensorFi sensorNode.fingerPrint().inClusters.push_back(VENDOR_CLUSTER_ID); } } + else if (modelId.startsWith(QLatin1String("ROM00"))) // Hue smart button) + { + clusterId = VENDOR_CLUSTER_ID; + } else if (modelId.startsWith(QLatin1String("SML00"))) // Hue motion sensor { if (type == QLatin1String("ZHASwitch")) @@ -12597,6 +12607,7 @@ void DeRestPluginPrivate::handleCommissioningClusterIndication(TaskItem &task, c (ind.srcAddress().hasNwk() && ind.srcAddress().nwk() == s.address().nwk())) { if (s.modelId().startsWith(QLatin1String("RWL02")) || // Hue dimmer switch + s.modelId().startsWith(QLatin1String("ROM00")) || // Hue smart button s.modelId().startsWith(QLatin1String("Z3-1BRL"))) // Lutron Aurora Friends-of-Hue dimmer switch { sensorNode = &s; @@ -14131,6 +14142,7 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve } } else if (sensor->modelId().startsWith(QLatin1String("RWL02")) || // Hue dimmer switch + sensor->modelId().startsWith(QLatin1String("ROM00")) || // Hue smart button sensor->modelId().startsWith(QLatin1String("Z3-1BRL"))) // Lutron Aurora Friends-of-Hue dimmer switch { @@ -14262,30 +14274,54 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve } } } - else if (sensor->modelId().startsWith(QLatin1String("ICZB-RM"))) // icasa remote - { - checkSensorGroup(sensor); - checkSensorBindingsForClientClusters(sensor); - } - else if (sensor->modelId().startsWith(QLatin1String("RC 110"))) // innr Remote - { - checkSensorGroup(sensor); - checkSensorBindingsForClientClusters(sensor); - } - else if (sensor->modelId() == QLatin1String("Remote switch"))// Legrand switch + else if (sensor->modelId().startsWith(QLatin1String("ICZB-RM")) || // icasa remote + sensor->modelId().startsWith(QLatin1String("RC 110")) || // innr Remote + sensor->modelId() == QLatin1String("Remote switch") || // Legrand switch + sensor->modelId().contains(QLatin1String("86opcn01")) || // Aqara Opple + sensor->modelId() == QLatin1String("Shutters central remote switch")) // Legrand switch { checkSensorGroup(sensor); checkSensorBindingsForClientClusters(sensor); } - else if (sensor->modelId().contains(QLatin1String("86opcn01"))) // Aqara Opple - { - checkSensorGroup(sensor); - checkSensorBindingsForClientClusters(sensor); - } - else if (sensor->modelId() == QLatin1String("Shutters central remote switch"))// Legrand switch + else if (sensor->modelId().startsWith(QLatin1String("902010/23"))) // Bitron remote control { checkSensorGroup(sensor); checkSensorBindingsForClientClusters(sensor); + + ResourceItem *item = sensor->item(RStateButtonEvent); + + if (!item || !item->lastSet().isValid()) + { + BindingTask bindingTask, bindingTask2; + + bindingTask.state = BindingTask::StateIdle; + bindingTask.action = BindingTask::ActionBind; + bindingTask.restNode = sensor; + Binding &bnd = bindingTask.binding; + bnd.srcAddress = sensor->address().ext(); + bnd.dstAddrMode = deCONZ::ApsExtAddress; + bnd.srcEndpoint = sensor->fingerPrint().endpoint; + bnd.clusterId = ONOFF_CLUSTER_ID; + bnd.dstAddress.ext = apsCtrl->getParameter(deCONZ::ParamMacAddress); + bnd.dstEndpoint = endpoint(); + + bindingTask2.state = BindingTask::StateIdle; + bindingTask2.action = BindingTask::ActionBind; + bindingTask2.restNode = sensor; + Binding &bnd2 = bindingTask2.binding; + bnd2.srcAddress = sensor->address().ext(); + bnd2.dstAddrMode = deCONZ::ApsExtAddress; + bnd2.srcEndpoint = sensor->fingerPrint().endpoint; + bnd2.clusterId = LEVEL_CLUSTER_ID; + bnd2.dstAddress.ext = apsCtrl->getParameter(deCONZ::ParamMacAddress); + bnd2.dstEndpoint = endpoint(); + + if (bnd.dstEndpoint > 0) // valid gateway endpoint? + { + queueBindingTask(bindingTask); + queueBindingTask(bindingTask2); + } + } } for (auto &s : sensors) diff --git a/sensor.cpp b/sensor.cpp index 86dee70a67..3a3a2f7fe1 100644 --- a/sensor.cpp +++ b/sensor.cpp @@ -682,6 +682,16 @@ static const Sensor::ButtonMap legrandShutterSwitchRemote[] = { { Sensor::ModeNone, 0x00, 0x0000, 0x00, 0, 0, nullptr } }; +static const Sensor::ButtonMap bitronRemoteMap[] = { +// mode ep cluster cmd param button name + { Sensor::ModeScenes, 0x01, 0x0008, 0x06, 0, S_BUTTON_1 + S_BUTTON_ACTION_SHORT_RELEASED, "Step up (with on/off)" }, + { Sensor::ModeScenes, 0x01, 0x0006, 0x01, 0, S_BUTTON_2 + S_BUTTON_ACTION_SHORT_RELEASED, "On" }, + { Sensor::ModeScenes, 0x01, 0x0006, 0x00, 0, S_BUTTON_3 + S_BUTTON_ACTION_SHORT_RELEASED, "Off" }, + { Sensor::ModeScenes, 0x01, 0x0008, 0x06, 1, S_BUTTON_4 + S_BUTTON_ACTION_SHORT_RELEASED, "Step down (with on/off)" }, + // end + { Sensor::ModeNone, 0x00, 0x0000, 0x00, 0, 0, nullptr } +}; + /*! Returns a fingerprint as JSON string. */ QString SensorFingerprint::toString() const @@ -1241,14 +1251,18 @@ const Sensor::ButtonMap *Sensor::buttonMap() } else if (manufacturer == QLatin1String("Legrand")) { - if (modelid == QLatin1String("Remote switch")) { m_buttonMap = legrandSwitchRemote; } - else if (modelid == QLatin1String("Shutters central remote switch")) { m_buttonMap = legrandShutterSwitchRemote; } + if (modelid == QLatin1String("Remote switch")) { m_buttonMap = legrandSwitchRemote; } + else if (modelid == QLatin1String("Shutters central remote switch")) { m_buttonMap = legrandShutterSwitchRemote; } } else if (manufacturer == QLatin1String("Sunricher")) { if (modelid.startsWith(QLatin1String("ZGRC-KEY"))) { m_buttonMap = sunricherCCTMap; } else if (modelid.startsWith(QLatin1String("ZG2833K"))) { m_buttonMap = sunricherMap; } } + else if (manufacturer == QLatin1String("Bitron Home")) + { + if (modelid.startsWith(QLatin1String("902010/23"))) { m_buttonMap = bitronRemoteMap; } + } } return m_buttonMap;