diff --git a/base.yaml b/base.yaml index 8f89f5c..0e3d07c 100644 --- a/base.yaml +++ b/base.yaml @@ -22,6 +22,24 @@ ratgdo: title: "${friendly_name} sync failed" message: "Failed to communicate with garage opener on startup; Check the ${friendly_name} Rolling code counter number entity history and set the entity to one number larger than the largest value in history. [ESPHome devices](/config/devices/dashboard?domain=esphome)" notification_id: "esphome_ratgdo_${id_prefix}_sync_failed" +api: + services: + - service: wipe_devices_from_gdo_memory + variables: + devices_to_wipe: string + then: + - lambda: !lambda |- + if(devices_to_wipe.compare("all") == 0) { + id($id_prefix).clear_paired_devices(0); + } else if (devices_to_wipe.compare("remote") == 0) { + id($id_prefix).clear_paired_devices(1); + } else if (devices_to_wipe.compare("keypad") == 0) { + id($id_prefix).clear_paired_devices(2); + } else if (devices_to_wipe.compare("wall") == 0) { + id($id_prefix).clear_paired_devices(3); + } else if (devices_to_wipe.compare("accessory") == 0) { + id($id_prefix).clear_paired_devices(4); + } sensor: - platform: ratgdo @@ -32,6 +50,45 @@ sensor: name: "Openings" unit_of_measurement: "openings" icon: mdi:open-in-app + - platform: ratgdo + id: ${id_prefix}_paired_devices_total + type: paired_devices_total + entity_category: diagnostic + ratgdo_id: ${id_prefix} + name: "Paired Devices" + icon: mdi:remote + - platform: ratgdo + id: ${id_prefix}_paired_devices_remotes + type: paired_devices_remotes + entity_category: diagnostic + ratgdo_id: ${id_prefix} + name: "Paired Remotes" + disabled_by_default: true + icon: mdi:remote + - platform: ratgdo + id: ${id_prefix}_paired_devices_keypads + type: paired_devices_keypads + entity_category: diagnostic + ratgdo_id: ${id_prefix} + name: "Paired Keypads" + disabled_by_default: true + icon: mdi:dialpad + - platform: ratgdo + id: ${id_prefix}_paired_devices_wall_controls + type: paired_devices_wall_controls + entity_category: diagnostic + ratgdo_id: ${id_prefix} + name: "Paired Wall Controls" + disabled_by_default: true + icon: mdi:pan-vertical + - platform: ratgdo + id: ${id_prefix}_paired_devices_accessories + type: paired_devices_accessories + entity_category: diagnostic + ratgdo_id: ${id_prefix} + name: "Paired Accessories" + disabled_by_default: true + icon: mdi:cloud-cancel-outline lock: - platform: ratgdo diff --git a/components/ratgdo/ratgdo.cpp b/components/ratgdo/ratgdo.cpp index 056782d..3d81554 100644 --- a/components/ratgdo/ratgdo.cpp +++ b/components/ratgdo/ratgdo.cpp @@ -264,9 +264,22 @@ namespace ratgdo { ESP_LOGD(TAG, "Time to close (TTC): %ds", seconds); } if (cmd == Command::LEARN) { - if (nibble == 1) { // LEARN sent from wall control, it will poll status every second + if (nibble == 1) { // LEARN sent from wall control, it will poll status every second. + // otherwise if ratgdo or gdo initiated ratgdo needs to poll learn_poll_status_ = false; } + } else if (cmd == Command::PAIRED_DEVICES) { + if (nibble == 0x0) { + this->paired_total = byte2; + } else if (nibble == 0x1) { + this->paired_remotes = byte2; + } else if (nibble == 0x2) { + this->paired_keypads = byte2; + } else if (nibble == 0x3) { + this->paired_wall_controls = byte2; + } else if (nibble == 0x4) { + this->paired_accessories = byte2; + } } return cmd; @@ -471,6 +484,36 @@ namespace ratgdo { send_command(Command::GET_OPENINGS); } + void RATGDOComponent::query_paired_devices() + { + ESP_LOGD(TAG, "start query_paired_devices"); + set_timeout(200, [=] { this->send_command(Command::GET_PAIRED_DEVICES, 0); }); // total + set_timeout(400, [=] { this->send_command(Command::GET_PAIRED_DEVICES, 1); }); // wireless + set_timeout(600, [=] { this->send_command(Command::GET_PAIRED_DEVICES, 2); }); // keypads + set_timeout(800, [=] { this->send_command(Command::GET_PAIRED_DEVICES, 3); }); // wall controls + set_timeout(1000, [=] { this->send_command(Command::GET_PAIRED_DEVICES, 4); }); // accessories + } + + // wipe devices from memory based on get paired devices nibble values + void RATGDOComponent::clear_paired_devices(uint16_t wipe) + { + if (wipe < 5) { + ESP_LOGW(TAG, "clear_paired_devices: %d", wipe); + if (wipe == 0) { + set_timeout(200, [=] { this->send_command(Command::CLEAR_PAIRED_DEVICES, 0); }); // wireless + set_timeout(400, [=] { this->send_command(Command::CLEAR_PAIRED_DEVICES, 1); }); // keypads + set_timeout(600, [=] { this->send_command(Command::CLEAR_PAIRED_DEVICES, 2); }); // wall controls + set_timeout(800, [=] { this->send_command(Command::CLEAR_PAIRED_DEVICES, 3); }); // accessories + set_timeout(1000, [=] { this->query_status(); }); + set_timeout(1200, [=] { this->query_paired_devices(); }); + } else { + this->send_command(Command::CLEAR_PAIRED_DEVICES, wipe - 1); // just requested device + set_timeout(200, [=] { this->query_status(); }); + set_timeout(400, [=] { this->query_paired_devices(); }); + } + } + } + void RATGDOComponent::send_command(Command command, uint32_t data, bool increment) { ESP_LOG1(TAG, "Send command: %s, data: %08" PRIx32, Command_to_string(command), data); @@ -545,6 +588,7 @@ namespace ratgdo { this->send_command(Command::GET_OPENINGS); return RetryResult::RETRY; } + query_paired_devices(); return RetryResult::DONE; }; @@ -777,6 +821,26 @@ namespace ratgdo { { this->openings.subscribe([=](uint16_t state) { defer("openings", [=] { f(state); }); }); } + void RATGDOComponent::subscribe_paired_devices_total(std::function&& f) + { + this->paired_total.subscribe([=](uint16_t state) { defer("paired_total", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_paired_remotes(std::function&& f) + { + this->paired_remotes.subscribe([=](uint16_t state) { defer("paired_remotes", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_paired_keypads(std::function&& f) + { + this->paired_keypads.subscribe([=](uint16_t state) { defer("paired_keypads", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_paired_wall_controls(std::function&& f) + { + this->paired_wall_controls.subscribe([=](uint16_t state) { defer("paired_wall_controls", [=] { f(state); }); }); + } + void RATGDOComponent::subscribe_paired_accessories(std::function&& f) + { + this->paired_accessories.subscribe([=](uint16_t state) { defer("paired_accessories", [=] { f(state); }); }); + } void RATGDOComponent::subscribe_door_state(std::function&& f) { this->door_state.subscribe([=](DoorState state) { diff --git a/components/ratgdo/ratgdo.h b/components/ratgdo/ratgdo.h index ff8b488..3934b5d 100644 --- a/components/ratgdo/ratgdo.h +++ b/components/ratgdo/ratgdo.h @@ -71,6 +71,10 @@ namespace ratgdo { (MOTOR_ON, 0x284), (MOTION, 0x285), + (GET_PAIRED_DEVICES, 0x307), // nibble 0 for total, 1 wireless, 2 keypads, 3 wall, 4 accessories. + (PAIRED_DEVICES, 0x308), // byte2 holds number of paired devices + (CLEAR_PAIRED_DEVICES, 0x30D), // nibble 0 to clear remotes, 1 keypads, 2 wall, 3 accessories (offset from above) + (LEARN_1, 0x391), (PING, 0x392), (PING_RESP, 0x393), @@ -110,6 +114,11 @@ namespace ratgdo { observable closing_duration { 0 }; observable openings { 0 }; // number of times the door has been opened + observable paired_total { 0xFF }; + observable paired_remotes { 0xFF }; + observable paired_keypads { 0xFF }; + observable paired_wall_controls { 0xFF }; + observable paired_accessories { 0xFF }; observable door_state { DoorState::UNKNOWN }; observable door_position { DOOR_POSITION_UNKNOWN }; @@ -174,8 +183,11 @@ namespace ratgdo { void lock(); void unlock(); + // Learn & Paired void activate_learn(); void inactivate_learn(); + void query_paired_devices(); + void clear_paired_devices(uint16_t wipe); // button functionality void query_status(); @@ -187,6 +199,11 @@ namespace ratgdo { void subscribe_opening_duration(std::function&& f); void subscribe_closing_duration(std::function&& f); void subscribe_openings(std::function&& f); + void subscribe_paired_devices_total(std::function&& f); + void subscribe_paired_remotes(std::function&& f); + void subscribe_paired_keypads(std::function&& f); + void subscribe_paired_wall_controls(std::function&& f); + void subscribe_paired_accessories(std::function&& f); void subscribe_door_state(std::function&& f); void subscribe_light_state(std::function&& f); void subscribe_lock_state(std::function&& f); diff --git a/components/ratgdo/sensor/__init__.py b/components/ratgdo/sensor/__init__.py index 981c16f..a2f9758 100644 --- a/components/ratgdo/sensor/__init__.py +++ b/components/ratgdo/sensor/__init__.py @@ -13,6 +13,11 @@ RATGDOSensorType = ratgdo_ns.enum("RATGDOSensorType") CONF_TYPE = "type" TYPES = { "openings": RATGDOSensorType.RATGDO_OPENINGS, + "paired_devices_total": RATGDOSensorType.RATGDO_PAIRED_DEVICES_TOTAL, + "paired_devices_remotes": RATGDOSensorType.RATGDO_PAIRED_REMOTES, + "paired_devices_keypads": RATGDOSensorType.RATGDO_PAIRED_KEYPADS, + "paired_devices_wall_controls": RATGDOSensorType.RATGDO_PAIRED_WALL_CONTROLS, + "paired_devices_accessories": RATGDOSensorType.RATGDO_PAIRED_ACCESSORIES, } @@ -33,3 +38,4 @@ async def to_code(config): await cg.register_component(var, config) cg.add(var.set_ratgdo_sensor_type(config[CONF_TYPE])) await register_ratgdo_child(var, config) + diff --git a/components/ratgdo/sensor/ratgdo_sensor.cpp b/components/ratgdo/sensor/ratgdo_sensor.cpp index c2312b9..e28290a 100644 --- a/components/ratgdo/sensor/ratgdo_sensor.cpp +++ b/components/ratgdo/sensor/ratgdo_sensor.cpp @@ -7,17 +7,51 @@ namespace ratgdo { static const char* const TAG = "ratgdo.sensor"; + void RATGDOSensor::setup() + { + if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_OPENINGS) { + this->parent_->subscribe_openings([=](uint16_t value) { + this->publish_state(value); + }); + } else if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_PAIRED_DEVICES_TOTAL) { + this->parent_->subscribe_paired_devices_total([=](uint16_t value) { + this->publish_state(value); + }); + } else if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_PAIRED_REMOTES) { + this->parent_->subscribe_paired_remotes([=](uint16_t value) { + this->publish_state(value); + }); + } else if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_PAIRED_KEYPADS) { + this->parent_->subscribe_paired_keypads([=](uint16_t value) { + this->publish_state(value); + }); + } else if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_PAIRED_WALL_CONTROLS) { + this->parent_->subscribe_paired_wall_controls([=](uint16_t value) { + this->publish_state(value); + }); + } else if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_PAIRED_ACCESSORIES) { + this->parent_->subscribe_paired_accessories([=](uint16_t value) { + this->publish_state(value); + }); + } + } + void RATGDOSensor::dump_config() { LOG_SENSOR("", "RATGDO Sensor", this); - ESP_LOGCONFIG(TAG, " Type: Openings"); - } - - void RATGDOSensor::setup() - { - this->parent_->subscribe_openings([=](uint16_t value) { - this->publish_state(value); - }); + if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_OPENINGS) { + ESP_LOGCONFIG(TAG, " Type: Openings"); + } else if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_PAIRED_DEVICES_TOTAL) { + ESP_LOGCONFIG(TAG, " Type: Paired Devices"); + } else if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_PAIRED_REMOTES) { + ESP_LOGCONFIG(TAG, " Type: Paired Remotes"); + } else if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_PAIRED_KEYPADS) { + ESP_LOGCONFIG(TAG, " Type: Paired Keypads"); + } else if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_PAIRED_WALL_CONTROLS) { + ESP_LOGCONFIG(TAG, " Type: Paired Wall Controls"); + } else if (this->ratgdo_sensor_type_ == RATGDOSensorType::RATGDO_PAIRED_ACCESSORIES) { + ESP_LOGCONFIG(TAG, " Type: Paired Accessories"); + } } } // namespace ratgdo diff --git a/components/ratgdo/sensor/ratgdo_sensor.h b/components/ratgdo/sensor/ratgdo_sensor.h index 26f2618..3a3517c 100644 --- a/components/ratgdo/sensor/ratgdo_sensor.h +++ b/components/ratgdo/sensor/ratgdo_sensor.h @@ -9,7 +9,12 @@ namespace esphome { namespace ratgdo { enum RATGDOSensorType { - RATGDO_OPENINGS + RATGDO_OPENINGS, + RATGDO_PAIRED_DEVICES_TOTAL, + RATGDO_PAIRED_REMOTES, + RATGDO_PAIRED_KEYPADS, + RATGDO_PAIRED_WALL_CONTROLS, + RATGDO_PAIRED_ACCESSORIES }; class RATGDOSensor : public sensor::Sensor, public RATGDOClient, public Component { diff --git a/components/ratgdo/switch/ratgdo_switch.cpp b/components/ratgdo/switch/ratgdo_switch.cpp index 9ab103a..9872e65 100644 --- a/components/ratgdo/switch/ratgdo_switch.cpp +++ b/components/ratgdo/switch/ratgdo_switch.cpp @@ -29,6 +29,9 @@ namespace ratgdo { bool value = state == LearnState::ACTIVE; this->state = value; this->publish_state(value); + if (value == false) { + this->parent_->query_paired_devices(); + } } void RATGDOSwitch::write_state(bool state)