[ld6002b] Add select and button platforms (4/5) (#17822)

Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
Mustafa KURU
2026-08-07 14:33:37 -04:00
committed by GitHub
co-authored by Jonathan Swoboda
parent 715b14aeba
commit 950cfc4da3
12 changed files with 571 additions and 1 deletions
@@ -0,0 +1,101 @@
import esphome.codegen as cg
from esphome.components import button
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_WAKEUP_PIN,
ENTITY_CATEGORY_CONFIG,
ENTITY_CATEGORY_DIAGNOSTIC,
)
import esphome.final_validate as fv
from .. import LD6002BComponent, ld6002b_ns
from ..const import (
CONF_GET_DELAY,
CONF_GET_INSTALLATION,
CONF_GET_LOW_POWER_MODE,
CONF_GET_LOW_POWER_SLEEP_TIME,
CONF_GET_SENSITIVITY,
CONF_GET_TRIGGER_SPEED,
CONF_GET_Z_RANGE,
CONF_LD6002B_ID,
CONF_RESET_UNATTENDED,
CONF_WAKE,
)
DEPENDENCIES = ["ld6002b"]
LD6002BButton = ld6002b_ns.class_("LD6002BButton", button.Button)
ButtonType = ld6002b_ns.enum("ButtonType", is_class=True)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_LD6002B_ID): cv.use_id(LD6002BComponent),
cv.Optional(CONF_GET_DELAY): button.button_schema(
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
cv.Optional(CONF_GET_SENSITIVITY): button.button_schema(
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
cv.Optional(CONF_GET_TRIGGER_SPEED): button.button_schema(
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
cv.Optional(CONF_GET_Z_RANGE): button.button_schema(
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
cv.Optional(CONF_GET_INSTALLATION): button.button_schema(
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
cv.Optional(CONF_GET_LOW_POWER_MODE): button.button_schema(
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
cv.Optional(CONF_GET_LOW_POWER_SLEEP_TIME): button.button_schema(
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
cv.Optional(CONF_RESET_UNATTENDED): button.button_schema(
LD6002BButton, entity_category=ENTITY_CATEGORY_CONFIG
),
cv.Optional(CONF_WAKE): button.button_schema(
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
),
}
)
def final_validate(config):
full_config = fv.full_config.get()
hub_id = config[CONF_LD6002B_ID]
if config.get(CONF_WAKE):
hub_path = full_config.get_path_for_id(hub_id)
hub_config = full_config.get_config_for_path(hub_path[:-1])
if hub_config.get(CONF_WAKEUP_PIN) is None:
raise cv.Invalid(
f"{CONF_WAKE} requires {CONF_WAKEUP_PIN} on the parent ld6002b component",
path=[CONF_WAKE],
)
return config
FINAL_VALIDATE_SCHEMA = final_validate
BUTTON_MAP = {
CONF_GET_DELAY: ButtonType.GET_DELAY,
CONF_GET_SENSITIVITY: ButtonType.GET_SENSITIVITY,
CONF_GET_TRIGGER_SPEED: ButtonType.GET_TRIGGER_SPEED,
CONF_GET_Z_RANGE: ButtonType.GET_Z_RANGE,
CONF_GET_INSTALLATION: ButtonType.GET_INSTALLATION,
CONF_GET_LOW_POWER_MODE: ButtonType.GET_LOW_POWER_MODE,
CONF_GET_LOW_POWER_SLEEP_TIME: ButtonType.GET_LOW_POWER_SLEEP_TIME,
CONF_RESET_UNATTENDED: ButtonType.RESET_UNATTENDED,
CONF_WAKE: ButtonType.WAKE,
}
async def to_code(config):
for key, button_type in BUTTON_MAP.items():
if button_config := config.get(key):
b = cg.new_Pvariable(button_config[CONF_ID], button_type)
await button.register_button(b, button_config)
await cg.register_parented(b, config[CONF_LD6002B_ID])
@@ -0,0 +1,7 @@
#include "ld6002b_button.h"
namespace esphome::ld6002b {
void LD6002BButton::press_action() { this->parent_->press_button(this->type_); }
} // namespace esphome::ld6002b
@@ -0,0 +1,18 @@
#pragma once
#include "esphome/components/button/button.h"
#include "../ld6002b.h"
namespace esphome::ld6002b {
class LD6002BButton : public button::Button, public Parented<LD6002BComponent> {
public:
explicit LD6002BButton(ButtonType type) : type_(type) {}
protected:
void press_action() override;
ButtonType type_;
};
} // namespace esphome::ld6002b
+11
View File
@@ -1,14 +1,25 @@
CONF_AUTO_WAKE = "auto_wake"
CONF_CLUSTER_ID = "cluster_id"
CONF_DOPPLER_INDEX = "doppler_index"
CONF_GET_DELAY = "get_delay"
CONF_GET_INSTALLATION = "get_installation"
CONF_GET_LOW_POWER_MODE = "get_low_power_mode"
CONF_GET_LOW_POWER_SLEEP_TIME = "get_low_power_sleep_time"
CONF_GET_SENSITIVITY = "get_sensitivity"
CONF_GET_TRIGGER_SPEED = "get_trigger_speed"
CONF_GET_Z_RANGE = "get_z_range"
CONF_HOLD_DELAY = "hold_delay"
CONF_INSTALLATION_MODE = "installation_mode"
CONF_LD6002B_ID = "ld6002b_id"
CONF_LOW_POWER = "low_power"
CONF_LOW_POWER_SLEEP_TIME = "low_power_sleep_time"
CONF_OTA_VERSION = "ota_version"
CONF_POINT_CLOUD = "point_cloud"
CONF_POINT_COUNT = "point_count"
CONF_RESET_UNATTENDED = "reset_unattended"
CONF_TARGET_DISPLAY = "target_display"
CONF_TRIGGER_SPEED = "trigger_speed"
CONF_WAKE = "wake"
CONF_WAKEUP_PULSE = "wakeup_pulse"
CONF_WORK_MODE = "work_mode"
CONF_Z = "z"
+190
View File
@@ -22,7 +22,10 @@ static constexpr uint16_t TYPE_SET_LOW_POWER_SLEEP = 0x0205;
static constexpr uint16_t TYPE_REPORT_TARGET = 0x0A04;
static constexpr uint16_t TYPE_REPORT_POINT_CLOUD = 0x0A08;
static constexpr uint16_t TYPE_REPORT_DELAY = 0x0A0D;
static constexpr uint16_t TYPE_REPORT_SENSITIVITY = 0x0A0E;
static constexpr uint16_t TYPE_REPORT_TRIGGER = 0x0A0F;
static constexpr uint16_t TYPE_REPORT_Z_RANGE = 0x0A10;
static constexpr uint16_t TYPE_REPORT_INSTALLATION = 0x0A11;
static constexpr uint16_t TYPE_REPORT_LOW_POWER = 0x0A12;
static constexpr uint16_t TYPE_REPORT_LOW_POWER_SLEEP = 0x0A13;
static constexpr uint16_t TYPE_REPORT_WORK_MODE = 0x0A14;
@@ -34,11 +37,23 @@ static constexpr uint32_t CMD_POINT_CLOUD_ON = 0x06;
static constexpr uint32_t CMD_POINT_CLOUD_OFF = 0x07;
static constexpr uint32_t CMD_TARGET_DISPLAY_ON = 0x08;
static constexpr uint32_t CMD_TARGET_DISPLAY_OFF = 0x09;
static constexpr uint32_t CMD_SENSITIVITY_LOW = 0x0A;
static constexpr uint32_t CMD_SENSITIVITY_MEDIUM = 0x0B;
static constexpr uint32_t CMD_SENSITIVITY_HIGH = 0x0C;
static constexpr uint32_t CMD_GET_SENSITIVITY = 0x0D;
static constexpr uint32_t CMD_TRIGGER_SLOW = 0x0E;
static constexpr uint32_t CMD_TRIGGER_MEDIUM = 0x0F;
static constexpr uint32_t CMD_TRIGGER_FAST = 0x10;
static constexpr uint32_t CMD_GET_TRIGGER = 0x11;
static constexpr uint32_t CMD_GET_Z_RANGE = 0x12;
static constexpr uint32_t CMD_INSTALL_TOP = 0x13;
static constexpr uint32_t CMD_INSTALL_SIDE = 0x14;
static constexpr uint32_t CMD_GET_INSTALLATION = 0x15;
static constexpr uint32_t CMD_LOW_POWER_ON = 0x16;
static constexpr uint32_t CMD_LOW_POWER_OFF = 0x17;
static constexpr uint32_t CMD_GET_LOW_POWER = 0x18;
static constexpr uint32_t CMD_GET_LOW_POWER_SLEEP = 0x19;
static constexpr uint32_t CMD_RESET_UNATTENDED = 0x1A;
static constexpr uint16_t TARGET_DATA_LEN = 20; // x,y,z,dop_idx,cluster_id
@@ -57,8 +72,30 @@ static const char *control_command_name(uint32_t command) {
return "target_display_on";
case CMD_TARGET_DISPLAY_OFF:
return "target_display_off";
case CMD_SENSITIVITY_LOW:
return "sensitivity_low";
case CMD_SENSITIVITY_MEDIUM:
return "sensitivity_medium";
case CMD_SENSITIVITY_HIGH:
return "sensitivity_high";
case CMD_GET_SENSITIVITY:
return "get_sensitivity";
case CMD_TRIGGER_SLOW:
return "trigger_slow";
case CMD_TRIGGER_MEDIUM:
return "trigger_medium";
case CMD_TRIGGER_FAST:
return "trigger_fast";
case CMD_GET_TRIGGER:
return "get_trigger";
case CMD_GET_Z_RANGE:
return "get_z_range";
case CMD_INSTALL_TOP:
return "install_top";
case CMD_INSTALL_SIDE:
return "install_side";
case CMD_GET_INSTALLATION:
return "get_installation";
case CMD_LOW_POWER_ON:
return "low_power_on";
case CMD_LOW_POWER_OFF:
@@ -67,6 +104,8 @@ static const char *control_command_name(uint32_t command) {
return "get_low_power";
case CMD_GET_LOW_POWER_SLEEP:
return "get_low_power_sleep";
case CMD_RESET_UNATTENDED:
return "reset_unattended";
default:
return "unknown";
}
@@ -88,8 +127,14 @@ static const char *frame_type_name(uint16_t type) {
return "report_point_cloud";
case TYPE_REPORT_DELAY:
return "report_delay";
case TYPE_REPORT_SENSITIVITY:
return "report_sensitivity";
case TYPE_REPORT_TRIGGER:
return "report_trigger";
case TYPE_REPORT_Z_RANGE:
return "report_z_range";
case TYPE_REPORT_INSTALLATION:
return "report_installation";
case TYPE_REPORT_LOW_POWER:
return "report_low_power";
case TYPE_REPORT_LOW_POWER_SLEEP:
@@ -107,8 +152,14 @@ static bool is_expected_control_report(uint32_t command, uint16_t type) {
switch (command) {
case CMD_GET_DELAY:
return type == TYPE_REPORT_DELAY;
case CMD_GET_SENSITIVITY:
return type == TYPE_REPORT_SENSITIVITY;
case CMD_GET_TRIGGER:
return type == TYPE_REPORT_TRIGGER;
case CMD_GET_Z_RANGE:
return type == TYPE_REPORT_Z_RANGE;
case CMD_GET_INSTALLATION:
return type == TYPE_REPORT_INSTALLATION;
case CMD_GET_LOW_POWER:
case CMD_LOW_POWER_ON:
case CMD_LOW_POWER_OFF:
@@ -259,6 +310,18 @@ void LD6002BComponent::setup() {
this->send_control_command_(want_point_cloud ? CMD_POINT_CLOUD_ON : CMD_POINT_CLOUD_OFF);
this->point_cloud_enabled_ = want_point_cloud;
}
#ifdef USE_SELECT
if (this->sensitivity_select_ != nullptr) {
this->send_control_command_(CMD_GET_SENSITIVITY);
}
if (this->trigger_speed_select_ != nullptr) {
this->send_control_command_(CMD_GET_TRIGGER);
}
if (this->installation_select_ != nullptr) {
this->send_control_command_(CMD_GET_INSTALLATION);
}
#endif
#ifdef USE_NUMBER
if (this->z_min_number_ != nullptr || this->z_max_number_ != nullptr) {
this->send_control_command_(CMD_GET_Z_RANGE);
@@ -345,6 +408,11 @@ void LD6002BComponent::dump_config() {
LOG_SWITCH(" ", "Point Cloud", this->point_cloud_switch_);
LOG_SWITCH(" ", "Target Display", this->target_display_switch_);
#endif
#ifdef USE_SELECT
LOG_SELECT(" ", "Sensitivity", this->sensitivity_select_);
LOG_SELECT(" ", "Trigger Speed", this->trigger_speed_select_);
LOG_SELECT(" ", "Installation Mode", this->installation_select_);
#endif
}
void LD6002BComponent::loop() {
@@ -492,9 +560,18 @@ void LD6002BComponent::handle_frame_(uint16_t type, const uint8_t *data, uint16_
case TYPE_REPORT_DELAY:
this->handle_delay_report_(data, len);
break;
case TYPE_REPORT_SENSITIVITY:
this->handle_sensitivity_report_(data, len);
break;
case TYPE_REPORT_TRIGGER:
this->handle_trigger_speed_report_(data, len);
break;
case TYPE_REPORT_Z_RANGE:
this->handle_z_range_report_(data, len);
break;
case TYPE_REPORT_INSTALLATION:
this->handle_installation_report_(data, len);
break;
case TYPE_REPORT_LOW_POWER:
this->handle_low_power_report_(data, len);
break;
@@ -660,6 +737,32 @@ void LD6002BComponent::handle_delay_report_(const uint8_t *data, uint16_t len) {
#endif
}
void LD6002BComponent::handle_sensitivity_report_(const uint8_t *data, uint16_t len) {
if (len < 1)
return;
#ifdef USE_SELECT
if (this->sensitivity_select_ == nullptr)
return;
uint8_t value = data[0];
if (value <= 2) {
this->sensitivity_select_->publish_state(value);
}
#endif
}
void LD6002BComponent::handle_trigger_speed_report_(const uint8_t *data, uint16_t len) {
if (len < 1)
return;
#ifdef USE_SELECT
if (this->trigger_speed_select_ == nullptr)
return;
uint8_t value = data[0];
if (value <= 2) {
this->trigger_speed_select_->publish_state(value);
}
#endif
}
void LD6002BComponent::handle_z_range_report_(const uint8_t *data, uint16_t len) {
if (len < 8)
return;
@@ -673,6 +776,19 @@ void LD6002BComponent::handle_z_range_report_(const uint8_t *data, uint16_t len)
#endif
}
void LD6002BComponent::handle_installation_report_(const uint8_t *data, uint16_t len) {
if (len < 1)
return;
#ifdef USE_SELECT
if (this->installation_select_ == nullptr)
return;
uint8_t value = data[0];
if (value <= 1) {
this->installation_select_->publish_state(value);
}
#endif
}
void LD6002BComponent::handle_low_power_report_(const uint8_t *data, uint16_t len) {
if (len < 1)
return;
@@ -889,6 +1005,8 @@ void LD6002BComponent::send_command_internal_(uint16_t type, const uint8_t *data
if (len > 0 && data != nullptr) {
std::memcpy(this->wake_scratch_.data(), data, len);
}
// A button pulse must not raise the pin in the middle of this one.
this->cancel_timeout(WAKE_BUTTON_TIMEOUT);
this->wake_pulse_pending_ = true;
this->wakeup_pin_->digital_write(false);
const uint8_t generation = this->send_generation_;
@@ -972,6 +1090,16 @@ void LD6002BComponent::send_z_range_() {
this->queue_command_(TYPE_SET_Z_RANGE, data, sizeof(data));
}
void LD6002BComponent::wake_() {
// A command's own pulse raises the pin and writes after it, so ride along instead of
// claiming the flag: claiming it would send that command down the immediate-write path
// with the pin still low.
if (this->wakeup_pin_ == nullptr || this->wake_pulse_pending_)
return;
this->wakeup_pin_->digital_write(false);
this->set_timeout(WAKE_BUTTON_TIMEOUT, this->wakeup_pulse_ms_, [this]() { this->wakeup_pin_->digital_write(true); });
}
void LD6002BComponent::set_number_value(NumberType type, float value) {
switch (type) {
case NumberType::HOLD_DELAY: {
@@ -999,6 +1127,36 @@ void LD6002BComponent::set_number_value(NumberType type, float value) {
}
}
void LD6002BComponent::set_select_value(SelectType type, size_t index) {
switch (type) {
case SelectType::SENSITIVITY:
if (index == 0) {
this->send_control_command_(CMD_SENSITIVITY_LOW);
} else if (index == 1) {
this->send_control_command_(CMD_SENSITIVITY_MEDIUM);
} else if (index == 2) {
this->send_control_command_(CMD_SENSITIVITY_HIGH);
}
break;
case SelectType::TRIGGER_SPEED:
if (index == 0) {
this->send_control_command_(CMD_TRIGGER_SLOW);
} else if (index == 1) {
this->send_control_command_(CMD_TRIGGER_MEDIUM);
} else if (index == 2) {
this->send_control_command_(CMD_TRIGGER_FAST);
}
break;
case SelectType::INSTALLATION_MODE:
if (index == 0) {
this->send_control_command_(CMD_INSTALL_TOP);
} else if (index == 1) {
this->send_control_command_(CMD_INSTALL_SIDE);
}
break;
}
}
void LD6002BComponent::init_version_pref_() {
#ifdef USE_TEXT_SENSOR
if (this->ota_version_text_sensor_ == nullptr) {
@@ -1124,4 +1282,36 @@ void LD6002BComponent::set_switch_state(SwitchType type, bool state) {
}
}
void LD6002BComponent::press_button(ButtonType type) {
switch (type) {
case ButtonType::GET_DELAY:
this->send_control_command_(CMD_GET_DELAY);
break;
case ButtonType::GET_SENSITIVITY:
this->send_control_command_(CMD_GET_SENSITIVITY);
break;
case ButtonType::GET_TRIGGER_SPEED:
this->send_control_command_(CMD_GET_TRIGGER);
break;
case ButtonType::GET_Z_RANGE:
this->send_control_command_(CMD_GET_Z_RANGE);
break;
case ButtonType::GET_INSTALLATION:
this->send_control_command_(CMD_GET_INSTALLATION);
break;
case ButtonType::GET_LOW_POWER_MODE:
this->send_control_command_(CMD_GET_LOW_POWER);
break;
case ButtonType::GET_LOW_POWER_SLEEP_TIME:
this->send_control_command_(CMD_GET_LOW_POWER_SLEEP);
break;
case ButtonType::RESET_UNATTENDED:
this->send_control_command_(CMD_RESET_UNATTENDED);
break;
case ButtonType::WAKE:
this->wake_();
break;
}
}
} // namespace esphome::ld6002b
+42 -1
View File
@@ -3,6 +3,7 @@
#include "esphome/core/defines.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/core/gpio.h"
#include "esphome/components/uart/uart.h"
#ifdef USE_SENSOR
@@ -12,12 +13,14 @@
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif
#ifdef USE_TEXT_SENSOR
#include "esphome/core/preferences.h"
#include "esphome/components/text_sensor/text_sensor.h"
#endif
#ifdef USE_NUMBER
#include "esphome/components/number/number.h"
#endif
#ifdef USE_SELECT
#include "esphome/components/select/select.h"
#endif
#ifdef USE_SWITCH
#include "esphome/components/switch/switch.h"
#endif
@@ -40,12 +43,30 @@ enum class NumberType : uint8_t {
LOW_POWER_SLEEP,
};
enum class SelectType : uint8_t {
SENSITIVITY,
TRIGGER_SPEED,
INSTALLATION_MODE,
};
enum class SwitchType : uint8_t {
LOW_POWER,
POINT_CLOUD,
TARGET_DISPLAY,
};
enum class ButtonType : uint8_t {
GET_DELAY,
GET_SENSITIVITY,
GET_TRIGGER_SPEED,
GET_Z_RANGE,
GET_INSTALLATION,
GET_LOW_POWER_MODE,
GET_LOW_POWER_SLEEP_TIME,
RESET_UNATTENDED,
WAKE,
};
#ifdef USE_SENSOR
struct TargetSensors {
sensor::Sensor *x{nullptr};
@@ -124,6 +145,12 @@ class LD6002BComponent : public Component, public uart::UARTDevice {
void set_low_power_sleep_number(number::Number *number) { this->low_power_sleep_number_ = number; }
#endif
#ifdef USE_SELECT
void set_sensitivity_select(select::Select *select) { this->sensitivity_select_ = select; }
void set_trigger_speed_select(select::Select *select) { this->trigger_speed_select_ = select; }
void set_installation_select(select::Select *select) { this->installation_select_ = select; }
#endif
#ifdef USE_SWITCH
void set_low_power_switch(switch_::Switch *sw) { this->low_power_switch_ = sw; }
void set_point_cloud_switch(switch_::Switch *sw) { this->point_cloud_switch_ = sw; }
@@ -131,7 +158,9 @@ class LD6002BComponent : public Component, public uart::UARTDevice {
#endif
void set_number_value(NumberType type, float value);
void set_select_value(SelectType type, size_t index);
void set_switch_state(SwitchType type, bool state);
void press_button(ButtonType type);
protected:
enum class ParseState : uint8_t { SOF, HEADER, HCK, DATA, DCK, DISCARD };
@@ -148,7 +177,10 @@ class LD6002BComponent : public Component, public uart::UARTDevice {
void handle_target_report_(const uint8_t *data, uint16_t len);
void handle_point_cloud_(const uint8_t *data, uint16_t len);
void handle_delay_report_(const uint8_t *data, uint16_t len);
void handle_sensitivity_report_(const uint8_t *data, uint16_t len);
void handle_trigger_speed_report_(const uint8_t *data, uint16_t len);
void handle_z_range_report_(const uint8_t *data, uint16_t len);
void handle_installation_report_(const uint8_t *data, uint16_t len);
void handle_low_power_report_(const uint8_t *data, uint16_t len);
void handle_low_power_sleep_report_(const uint8_t *data, uint16_t len);
void handle_work_mode_report_(const uint8_t *data, uint16_t len);
@@ -173,6 +205,7 @@ class LD6002BComponent : public Component, public uart::UARTDevice {
void write_frame_(uint16_t type, const uint8_t *data, uint8_t len, bool track);
void send_control_command_(uint32_t command);
void send_z_range_();
void wake_();
static uint16_t read_u16_be(const uint8_t *data);
static uint32_t read_u32_le(const uint8_t *data);
@@ -202,6 +235,11 @@ class LD6002BComponent : public Component, public uart::UARTDevice {
number::Number *z_max_number_{nullptr};
number::Number *low_power_sleep_number_{nullptr};
#endif
#ifdef USE_SELECT
select::Select *sensitivity_select_{nullptr};
select::Select *trigger_speed_select_{nullptr};
select::Select *installation_select_{nullptr};
#endif
#ifdef USE_SWITCH
switch_::Switch *low_power_switch_{nullptr};
switch_::Switch *point_cloud_switch_{nullptr};
@@ -235,6 +273,9 @@ class LD6002BComponent : public Component, public uart::UARTDevice {
// How long the module stays awake after any frame, and so still answers the next one.
static constexpr uint32_t MODULE_AWAKE_MS = 10000;
static constexpr uint8_t CMD_MAX_RETRIES = 3;
// Named so a repeated press replaces its own pending timeout instead of stacking
// another, and so the command path can cancel it when it takes the pin over.
static constexpr const char *WAKE_BUTTON_TIMEOUT = "wake_button";
// A reply cannot trail the frame that earned it for longer than this; the field worst case is ~726ms.
static constexpr uint32_t STALE_ACK_MAX_AGE_MS = 1000;
@@ -0,0 +1,60 @@
import esphome.codegen as cg
from esphome.components import select
import esphome.config_validation as cv
from esphome.const import CONF_SENSITIVITY, ENTITY_CATEGORY_CONFIG
from .. import LD6002BComponent, ld6002b_ns
from ..const import CONF_INSTALLATION_MODE, CONF_LD6002B_ID, CONF_TRIGGER_SPEED
DEPENDENCIES = ["ld6002b"]
LD6002BSelect = ld6002b_ns.class_("LD6002BSelect", select.Select)
SelectType = ld6002b_ns.enum("SelectType", is_class=True)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_LD6002B_ID): cv.use_id(LD6002BComponent),
cv.Optional(CONF_SENSITIVITY): select.select_schema(
LD6002BSelect, entity_category=ENTITY_CATEGORY_CONFIG
),
cv.Optional(CONF_TRIGGER_SPEED): select.select_schema(
LD6002BSelect, entity_category=ENTITY_CATEGORY_CONFIG
),
cv.Optional(CONF_INSTALLATION_MODE): select.select_schema(
LD6002BSelect, entity_category=ENTITY_CATEGORY_CONFIG
),
}
)
SELECT_MAP = (
(
CONF_SENSITIVITY,
SelectType.SENSITIVITY,
"set_sensitivity_select",
["low", "medium", "high"],
),
(
CONF_TRIGGER_SPEED,
SelectType.TRIGGER_SPEED,
"set_trigger_speed_select",
["slow", "medium", "fast"],
),
(
CONF_INSTALLATION_MODE,
SelectType.INSTALLATION_MODE,
"set_installation_select",
["top", "side"],
),
)
async def to_code(config):
hub = await cg.get_variable(config[CONF_LD6002B_ID])
for key, select_type, setter, options in SELECT_MAP:
if conf := config.get(key):
s = await select.new_select(conf, select_type, options=options)
await cg.register_parented(s, config[CONF_LD6002B_ID])
cg.add(getattr(hub, setter)(s))
@@ -0,0 +1,10 @@
#include "ld6002b_select.h"
namespace esphome::ld6002b {
void LD6002BSelect::control(size_t index) {
this->publish_state(index);
this->parent_->set_select_value(this->type_, index);
}
} // namespace esphome::ld6002b
@@ -0,0 +1,18 @@
#pragma once
#include "esphome/components/select/select.h"
#include "../ld6002b.h"
namespace esphome::ld6002b {
class LD6002BSelect : public select::Select, public Parented<LD6002BComponent> {
public:
explicit LD6002BSelect(SelectType type) : type_(type) {}
protected:
void control(size_t index) override;
SelectType type_;
};
} // namespace esphome::ld6002b
@@ -0,0 +1,82 @@
"""Tests for the wake button's wakeup_pin requirement in ld6002b."""
from __future__ import annotations
import pytest
from esphome.components.ld6002b.button import CONFIG_SCHEMA, FINAL_VALIDATE_SCHEMA
from esphome.config import Config
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_WAKEUP_PIN, PlatformFramework
from esphome.core import ID
from esphome.types import ConfigType
from tests.component_tests.types import SetCoreConfigCallable
HUB_ID = "ld6002b_hub"
def _full_config(hub: ConfigType) -> Config:
"""A full config carrying one ld6002b hub, as the ID pass leaves it.
final_validate resolves the hub through get_path_for_id, so the declaring
path has to be registered the way validate_config registers it: the path of
the id value itself, whose parent is the hub's own config.
"""
full = Config()
full["ld6002b"] = [hub]
full.declare_ids.append((hub[CONF_ID], ["ld6002b", 0, CONF_ID]))
return full
def _hub(*, wakeup_pin: bool) -> ConfigType:
hub: ConfigType = {CONF_ID: ID(HUB_ID, is_declaration=True, type="ld6002b")}
if wakeup_pin:
hub[CONF_WAKEUP_PIN] = {"number": 4}
return hub
def _buttons(**buttons: str) -> ConfigType:
"""A button platform config naming the given buttons on the shared hub."""
config: ConfigType = {
"ld6002b_id": ID(HUB_ID, is_declaration=False, type="ld6002b")
}
config.update({key: {"name": name} for key, name in buttons.items()})
return config
def _validated(config: ConfigType) -> ConfigType:
"""Run the button schema, then the final validation the hub is checked in."""
config = CONFIG_SCHEMA(config)
FINAL_VALIDATE_SCHEMA(config)
return config
def test_wake_without_wakeup_pin_is_rejected(
set_core_config: SetCoreConfigCallable,
) -> None:
"""The wake button drives the pin directly, so a hub without one cannot serve it."""
set_core_config(
PlatformFramework.ESP32_IDF, full_config=_full_config(_hub(wakeup_pin=False))
)
with pytest.raises(cv.Invalid, match="wake requires wakeup_pin"):
_validated(_buttons(wake="Wake"))
def test_wake_with_wakeup_pin_passes(set_core_config: SetCoreConfigCallable) -> None:
set_core_config(
PlatformFramework.ESP32_IDF, full_config=_full_config(_hub(wakeup_pin=True))
)
_validated(_buttons(wake="Wake"))
def test_other_buttons_do_not_need_the_pin(
set_core_config: SetCoreConfigCallable,
) -> None:
"""Only wake drives the pin; the query buttons stay usable without one."""
set_core_config(
PlatformFramework.ESP32_IDF, full_config=_full_config(_hub(wakeup_pin=False))
)
_validated(_buttons(get_delay="Get Delay"))
+32
View File
@@ -71,6 +71,16 @@ number:
low_power_sleep_time:
name: Low Power Sleep
select:
- platform: ld6002b
ld6002b_id: ld6002b_radar
sensitivity:
name: Sensitivity
trigger_speed:
name: Trigger Speed
installation_mode:
name: Installation
switch:
- platform: ld6002b
ld6002b_id: ld6002b_radar
@@ -80,3 +90,25 @@ switch:
name: Point Cloud
target_display:
name: Target Display
button:
- platform: ld6002b
ld6002b_id: ld6002b_radar
get_delay:
name: Get Delay
get_sensitivity:
name: Get Sensitivity
get_trigger_speed:
name: Get Trigger Speed
get_z_range:
name: Get Z Range
get_installation:
name: Get Installation
get_low_power_mode:
name: Get Low Power Mode
get_low_power_sleep_time:
name: Get Low Power Sleep
reset_unattended:
name: Reset Unattended
wake:
name: Wake