mirror of
https://github.com/esphome/esphome.git
synced 2026-01-08 03:00:48 -07:00
[rd03d] Add Ai-Thinker RD-03D mmWave radar component (#12764)
Co-authored-by: jas <jas@asspa.in> Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
@@ -395,6 +395,7 @@ esphome/components/radon_eye_rd200/* @jeffeb3
|
||||
esphome/components/rc522/* @glmnet
|
||||
esphome/components/rc522_i2c/* @glmnet
|
||||
esphome/components/rc522_spi/* @glmnet
|
||||
esphome/components/rd03d/* @jasstrong
|
||||
esphome/components/resampler/speaker/* @kahrendt
|
||||
esphome/components/restart/* @esphome/core
|
||||
esphome/components/rf_bridge/* @jesserockz
|
||||
|
||||
50
esphome/components/rd03d/__init__.py
Normal file
50
esphome/components/rd03d/__init__.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_THROTTLE
|
||||
|
||||
CODEOWNERS = ["@jasstrong"]
|
||||
DEPENDENCIES = ["uart"]
|
||||
MULTI_CONF = True
|
||||
|
||||
CONF_RD03D_ID = "rd03d_id"
|
||||
CONF_TRACKING_MODE = "tracking_mode"
|
||||
|
||||
rd03d_ns = cg.esphome_ns.namespace("rd03d")
|
||||
RD03DComponent = rd03d_ns.class_("RD03DComponent", cg.Component, uart.UARTDevice)
|
||||
TrackingMode = rd03d_ns.enum("TrackingMode", is_class=True)
|
||||
|
||||
TRACKING_MODES = {
|
||||
"single": TrackingMode.SINGLE_TARGET,
|
||||
"multi": TrackingMode.MULTI_TARGET,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(RD03DComponent),
|
||||
cv.Optional(CONF_TRACKING_MODE): cv.enum(TRACKING_MODES, lower=True),
|
||||
cv.Optional(CONF_THROTTLE): cv.positive_time_period_milliseconds,
|
||||
}
|
||||
)
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||
"rd03d",
|
||||
require_tx=False,
|
||||
require_rx=True,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
if CONF_TRACKING_MODE in config:
|
||||
cg.add(var.set_tracking_mode(config[CONF_TRACKING_MODE]))
|
||||
|
||||
if CONF_THROTTLE in config:
|
||||
cg.add(var.set_throttle(config[CONF_THROTTLE]))
|
||||
39
esphome/components/rd03d/binary_sensor.py
Normal file
39
esphome/components/rd03d/binary_sensor.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_TARGET, DEVICE_CLASS_OCCUPANCY
|
||||
|
||||
from . import CONF_RD03D_ID, RD03DComponent
|
||||
|
||||
DEPENDENCIES = ["rd03d"]
|
||||
|
||||
MAX_TARGETS = 3
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_RD03D_ID): cv.use_id(RD03DComponent),
|
||||
cv.Optional(CONF_TARGET): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_OCCUPANCY,
|
||||
),
|
||||
}
|
||||
).extend(
|
||||
{
|
||||
cv.Optional(f"target_{i + 1}"): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_OCCUPANCY,
|
||||
)
|
||||
for i in range(MAX_TARGETS)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_RD03D_ID])
|
||||
|
||||
if target_config := config.get(CONF_TARGET):
|
||||
sens = await binary_sensor.new_binary_sensor(target_config)
|
||||
cg.add(hub.set_target_binary_sensor(sens))
|
||||
|
||||
for i in range(MAX_TARGETS):
|
||||
if target_config := config.get(f"target_{i + 1}"):
|
||||
sens = await binary_sensor.new_binary_sensor(target_config)
|
||||
cg.add(hub.set_target_binary_sensor(i, sens))
|
||||
243
esphome/components/rd03d/rd03d.cpp
Normal file
243
esphome/components/rd03d/rd03d.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
#include "rd03d.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace esphome::rd03d {
|
||||
|
||||
static const char *const TAG = "rd03d";
|
||||
|
||||
// Delay before sending configuration commands to allow radar to initialize
|
||||
static constexpr uint32_t SETUP_TIMEOUT_MS = 100;
|
||||
|
||||
// Data frame format (radar -> host)
|
||||
static constexpr uint8_t FRAME_HEADER[] = {0xAA, 0xFF, 0x03, 0x00};
|
||||
static constexpr uint8_t FRAME_FOOTER[] = {0x55, 0xCC};
|
||||
|
||||
// Command frame format (host -> radar)
|
||||
static constexpr uint8_t CMD_FRAME_HEADER[] = {0xFD, 0xFC, 0xFB, 0xFA};
|
||||
static constexpr uint8_t CMD_FRAME_FOOTER[] = {0x04, 0x03, 0x02, 0x01};
|
||||
|
||||
// RD-03D tracking mode commands
|
||||
static constexpr uint16_t CMD_SINGLE_TARGET = 0x0080;
|
||||
static constexpr uint16_t CMD_MULTI_TARGET = 0x0090;
|
||||
|
||||
// Decode coordinate/speed value from RD-03D format
|
||||
// Per datasheet: MSB=1 means positive, MSB=0 means negative
|
||||
static constexpr int16_t decode_value(uint8_t low_byte, uint8_t high_byte) {
|
||||
int16_t value = ((high_byte & 0x7F) << 8) | low_byte;
|
||||
if ((high_byte & 0x80) == 0) {
|
||||
value = -value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void RD03DComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up RD-03D...");
|
||||
this->set_timeout(SETUP_TIMEOUT_MS, [this]() { this->apply_config_(); });
|
||||
}
|
||||
|
||||
void RD03DComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "RD-03D:");
|
||||
if (this->tracking_mode_.has_value()) {
|
||||
ESP_LOGCONFIG(TAG, " Tracking Mode: %s",
|
||||
*this->tracking_mode_ == TrackingMode::SINGLE_TARGET ? "single" : "multi");
|
||||
}
|
||||
if (this->throttle_ > 0) {
|
||||
ESP_LOGCONFIG(TAG, " Throttle: %ums", this->throttle_);
|
||||
}
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR(" ", "Target Count", this->target_count_sensor_);
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
LOG_BINARY_SENSOR(" ", "Target", this->target_binary_sensor_);
|
||||
#endif
|
||||
for (uint8_t i = 0; i < MAX_TARGETS; i++) {
|
||||
ESP_LOGCONFIG(TAG, " Target %d:", i + 1);
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR(" ", "X", this->targets_[i].x);
|
||||
LOG_SENSOR(" ", "Y", this->targets_[i].y);
|
||||
LOG_SENSOR(" ", "Speed", this->targets_[i].speed);
|
||||
LOG_SENSOR(" ", "Distance", this->targets_[i].distance);
|
||||
LOG_SENSOR(" ", "Resolution", this->targets_[i].resolution);
|
||||
LOG_SENSOR(" ", "Angle", this->targets_[i].angle);
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
LOG_BINARY_SENSOR(" ", "Presence", this->target_presence_[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void RD03DComponent::loop() {
|
||||
while (this->available()) {
|
||||
uint8_t byte = this->read();
|
||||
ESP_LOGVV(TAG, "Received byte: 0x%02X, buffer_pos: %d", byte, this->buffer_pos_);
|
||||
|
||||
// Check if we're looking for frame header
|
||||
if (this->buffer_pos_ < FRAME_HEADER_SIZE) {
|
||||
if (byte == FRAME_HEADER[this->buffer_pos_]) {
|
||||
this->buffer_[this->buffer_pos_++] = byte;
|
||||
} else if (byte == FRAME_HEADER[0]) {
|
||||
// Start over if we see a potential new header
|
||||
this->buffer_[0] = byte;
|
||||
this->buffer_pos_ = 1;
|
||||
} else {
|
||||
this->buffer_pos_ = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Accumulate data bytes
|
||||
this->buffer_[this->buffer_pos_++] = byte;
|
||||
|
||||
// Check if we have a complete frame
|
||||
if (this->buffer_pos_ == FRAME_SIZE) {
|
||||
// Validate footer
|
||||
if (this->buffer_[FRAME_SIZE - 2] == FRAME_FOOTER[0] && this->buffer_[FRAME_SIZE - 1] == FRAME_FOOTER[1]) {
|
||||
this->process_frame_();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Invalid frame footer: 0x%02X 0x%02X (expected 0x55 0xCC)", this->buffer_[FRAME_SIZE - 2],
|
||||
this->buffer_[FRAME_SIZE - 1]);
|
||||
}
|
||||
this->buffer_pos_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RD03DComponent::process_frame_() {
|
||||
// Apply throttle if configured
|
||||
if (this->throttle_ > 0) {
|
||||
uint32_t now = millis();
|
||||
if (now - this->last_publish_time_ < this->throttle_) {
|
||||
return;
|
||||
}
|
||||
this->last_publish_time_ = now;
|
||||
}
|
||||
|
||||
uint8_t target_count = 0;
|
||||
|
||||
for (uint8_t i = 0; i < MAX_TARGETS; i++) {
|
||||
// Calculate offset for this target's data
|
||||
// Header is 4 bytes, each target is 8 bytes
|
||||
uint8_t offset = FRAME_HEADER_SIZE + (i * TARGET_DATA_SIZE);
|
||||
|
||||
// Extract raw bytes for this target
|
||||
uint8_t x_low = this->buffer_[offset + 0];
|
||||
uint8_t x_high = this->buffer_[offset + 1];
|
||||
uint8_t y_low = this->buffer_[offset + 2];
|
||||
uint8_t y_high = this->buffer_[offset + 3];
|
||||
uint8_t speed_low = this->buffer_[offset + 4];
|
||||
uint8_t speed_high = this->buffer_[offset + 5];
|
||||
uint8_t res_low = this->buffer_[offset + 6];
|
||||
uint8_t res_high = this->buffer_[offset + 7];
|
||||
|
||||
// Decode values per RD-03D format
|
||||
int16_t x = decode_value(x_low, x_high);
|
||||
int16_t y = decode_value(y_low, y_high);
|
||||
int16_t speed = decode_value(speed_low, speed_high);
|
||||
uint16_t resolution = (res_high << 8) | res_low;
|
||||
|
||||
// Check if target is present (non-zero coordinates)
|
||||
bool target_present = (x != 0 || y != 0);
|
||||
if (target_present) {
|
||||
target_count++;
|
||||
}
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
this->publish_target_(i, x, y, speed, resolution);
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
if (this->target_presence_[i] != nullptr) {
|
||||
this->target_presence_[i]->publish_state(target_present);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
if (this->target_count_sensor_ != nullptr) {
|
||||
this->target_count_sensor_->publish_state(target_count);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
if (this->target_binary_sensor_ != nullptr) {
|
||||
this->target_binary_sensor_->publish_state(target_count > 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void RD03DComponent::publish_target_(uint8_t target_num, int16_t x, int16_t y, int16_t speed, uint16_t resolution) {
|
||||
TargetSensor &target = this->targets_[target_num];
|
||||
|
||||
// Publish X coordinate (mm)
|
||||
if (target.x != nullptr) {
|
||||
target.x->publish_state(x);
|
||||
}
|
||||
|
||||
// Publish Y coordinate (mm)
|
||||
if (target.y != nullptr) {
|
||||
target.y->publish_state(y);
|
||||
}
|
||||
|
||||
// Publish speed (convert from cm/s to mm/s)
|
||||
if (target.speed != nullptr) {
|
||||
target.speed->publish_state(static_cast<float>(speed) * 10.0f);
|
||||
}
|
||||
|
||||
// Publish resolution (mm)
|
||||
if (target.resolution != nullptr) {
|
||||
target.resolution->publish_state(resolution);
|
||||
}
|
||||
|
||||
// Calculate and publish distance (mm)
|
||||
if (target.distance != nullptr) {
|
||||
float distance = std::hypot(static_cast<float>(x), static_cast<float>(y));
|
||||
target.distance->publish_state(distance);
|
||||
}
|
||||
|
||||
// Calculate and publish angle (degrees)
|
||||
// Angle is measured from the Y axis (radar forward direction)
|
||||
if (target.angle != nullptr) {
|
||||
if (x == 0 && y == 0) {
|
||||
target.angle->publish_state(0);
|
||||
} else {
|
||||
float angle = std::atan2(static_cast<float>(x), static_cast<float>(y)) * 180.0f / M_PI;
|
||||
target.angle->publish_state(angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void RD03DComponent::send_command_(uint16_t command, const uint8_t *data, uint8_t data_len) {
|
||||
// Send header
|
||||
this->write_array(CMD_FRAME_HEADER, sizeof(CMD_FRAME_HEADER));
|
||||
|
||||
// Send length (command word + data)
|
||||
uint16_t len = 2 + data_len;
|
||||
this->write_byte(len & 0xFF);
|
||||
this->write_byte((len >> 8) & 0xFF);
|
||||
|
||||
// Send command word (little-endian)
|
||||
this->write_byte(command & 0xFF);
|
||||
this->write_byte((command >> 8) & 0xFF);
|
||||
|
||||
// Send data if any
|
||||
if (data != nullptr && data_len > 0) {
|
||||
this->write_array(data, data_len);
|
||||
}
|
||||
|
||||
// Send footer
|
||||
this->write_array(CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER));
|
||||
|
||||
ESP_LOGD(TAG, "Sent command 0x%04X with %d bytes of data", command, data_len);
|
||||
}
|
||||
|
||||
void RD03DComponent::apply_config_() {
|
||||
if (this->tracking_mode_.has_value()) {
|
||||
uint16_t mode_cmd = (*this->tracking_mode_ == TrackingMode::SINGLE_TARGET) ? CMD_SINGLE_TARGET : CMD_MULTI_TARGET;
|
||||
this->send_command_(mode_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::rd03d
|
||||
93
esphome/components/rd03d/rd03d.h
Normal file
93
esphome/components/rd03d/rd03d.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace esphome::rd03d {
|
||||
|
||||
static constexpr uint8_t MAX_TARGETS = 3;
|
||||
static constexpr uint8_t FRAME_HEADER_SIZE = 4;
|
||||
static constexpr uint8_t FRAME_FOOTER_SIZE = 2;
|
||||
static constexpr uint8_t TARGET_DATA_SIZE = 8;
|
||||
static constexpr uint8_t FRAME_SIZE =
|
||||
FRAME_HEADER_SIZE + (MAX_TARGETS * TARGET_DATA_SIZE) + FRAME_FOOTER_SIZE; // 30 bytes
|
||||
|
||||
enum class TrackingMode : uint8_t {
|
||||
SINGLE_TARGET = 0,
|
||||
MULTI_TARGET = 1,
|
||||
};
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
struct TargetSensor {
|
||||
sensor::Sensor *x{nullptr};
|
||||
sensor::Sensor *y{nullptr};
|
||||
sensor::Sensor *speed{nullptr};
|
||||
sensor::Sensor *distance{nullptr};
|
||||
sensor::Sensor *resolution{nullptr};
|
||||
sensor::Sensor *angle{nullptr};
|
||||
};
|
||||
#endif
|
||||
|
||||
class RD03DComponent : public Component, public uart::UARTDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void set_target_count_sensor(sensor::Sensor *sensor) { this->target_count_sensor_ = sensor; }
|
||||
void set_x_sensor(uint8_t target, sensor::Sensor *sensor) { this->targets_[target].x = sensor; }
|
||||
void set_y_sensor(uint8_t target, sensor::Sensor *sensor) { this->targets_[target].y = sensor; }
|
||||
void set_speed_sensor(uint8_t target, sensor::Sensor *sensor) { this->targets_[target].speed = sensor; }
|
||||
void set_distance_sensor(uint8_t target, sensor::Sensor *sensor) { this->targets_[target].distance = sensor; }
|
||||
void set_resolution_sensor(uint8_t target, sensor::Sensor *sensor) { this->targets_[target].resolution = sensor; }
|
||||
void set_angle_sensor(uint8_t target, sensor::Sensor *sensor) { this->targets_[target].angle = sensor; }
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
void set_target_binary_sensor(binary_sensor::BinarySensor *sensor) { this->target_binary_sensor_ = sensor; }
|
||||
void set_target_binary_sensor(uint8_t target, binary_sensor::BinarySensor *sensor) {
|
||||
this->target_presence_[target] = sensor;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Configuration setters (called from code generation)
|
||||
void set_tracking_mode(TrackingMode mode) { this->tracking_mode_ = mode; }
|
||||
void set_throttle(uint32_t throttle) { this->throttle_ = throttle; }
|
||||
|
||||
protected:
|
||||
void apply_config_();
|
||||
void send_command_(uint16_t command, const uint8_t *data = nullptr, uint8_t data_len = 0);
|
||||
void process_frame_();
|
||||
#ifdef USE_SENSOR
|
||||
void publish_target_(uint8_t target_num, int16_t x, int16_t y, int16_t speed, uint16_t resolution);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
std::array<TargetSensor, MAX_TARGETS> targets_{};
|
||||
sensor::Sensor *target_count_sensor_{nullptr};
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
std::array<binary_sensor::BinarySensor *, MAX_TARGETS> target_presence_{};
|
||||
binary_sensor::BinarySensor *target_binary_sensor_{nullptr};
|
||||
#endif
|
||||
|
||||
// Configuration (only sent if explicitly set)
|
||||
optional<TrackingMode> tracking_mode_{};
|
||||
uint32_t throttle_{0};
|
||||
uint32_t last_publish_time_{0};
|
||||
|
||||
std::array<uint8_t, FRAME_SIZE> buffer_{};
|
||||
uint8_t buffer_pos_{0};
|
||||
};
|
||||
|
||||
} // namespace esphome::rd03d
|
||||
105
esphome/components/rd03d/sensor.py
Normal file
105
esphome/components/rd03d/sensor.py
Normal file
@@ -0,0 +1,105 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ANGLE,
|
||||
CONF_DISTANCE,
|
||||
CONF_RESOLUTION,
|
||||
CONF_SPEED,
|
||||
CONF_X,
|
||||
CONF_Y,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
DEVICE_CLASS_SPEED,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_DEGREES,
|
||||
UNIT_MILLIMETER,
|
||||
)
|
||||
|
||||
from . import CONF_RD03D_ID, RD03DComponent
|
||||
|
||||
DEPENDENCIES = ["rd03d"]
|
||||
|
||||
CONF_TARGET_COUNT = "target_count"
|
||||
|
||||
MAX_TARGETS = 3
|
||||
|
||||
UNIT_MILLIMETER_PER_SECOND = "mm/s"
|
||||
|
||||
TARGET_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_X): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_Y): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_SPEED): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MILLIMETER_PER_SECOND,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_SPEED,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_RESOLUTION): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ANGLE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREES,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_RD03D_ID): cv.use_id(RD03DComponent),
|
||||
cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
).extend({cv.Optional(f"target_{i + 1}"): TARGET_SCHEMA for i in range(MAX_TARGETS)})
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_RD03D_ID])
|
||||
|
||||
if target_count_config := config.get(CONF_TARGET_COUNT):
|
||||
sens = await sensor.new_sensor(target_count_config)
|
||||
cg.add(hub.set_target_count_sensor(sens))
|
||||
|
||||
for i in range(MAX_TARGETS):
|
||||
if target_config := config.get(f"target_{i + 1}"):
|
||||
if x_config := target_config.get(CONF_X):
|
||||
sens = await sensor.new_sensor(x_config)
|
||||
cg.add(hub.set_x_sensor(i, sens))
|
||||
if y_config := target_config.get(CONF_Y):
|
||||
sens = await sensor.new_sensor(y_config)
|
||||
cg.add(hub.set_y_sensor(i, sens))
|
||||
if speed_config := target_config.get(CONF_SPEED):
|
||||
sens = await sensor.new_sensor(speed_config)
|
||||
cg.add(hub.set_speed_sensor(i, sens))
|
||||
if distance_config := target_config.get(CONF_DISTANCE):
|
||||
sens = await sensor.new_sensor(distance_config)
|
||||
cg.add(hub.set_distance_sensor(i, sens))
|
||||
if resolution_config := target_config.get(CONF_RESOLUTION):
|
||||
sens = await sensor.new_sensor(resolution_config)
|
||||
cg.add(hub.set_resolution_sensor(i, sens))
|
||||
if angle_config := target_config.get(CONF_ANGLE):
|
||||
sens = await sensor.new_sensor(angle_config)
|
||||
cg.add(hub.set_angle_sensor(i, sens))
|
||||
59
tests/components/rd03d/common.yaml
Normal file
59
tests/components/rd03d/common.yaml
Normal file
@@ -0,0 +1,59 @@
|
||||
rd03d:
|
||||
id: rd03d_radar
|
||||
|
||||
sensor:
|
||||
- platform: rd03d
|
||||
rd03d_id: rd03d_radar
|
||||
target_count:
|
||||
name: Target Count
|
||||
target_1:
|
||||
x:
|
||||
name: Target-1 X
|
||||
y:
|
||||
name: Target-1 Y
|
||||
speed:
|
||||
name: Target-1 Speed
|
||||
angle:
|
||||
name: Target-1 Angle
|
||||
distance:
|
||||
name: Target-1 Distance
|
||||
resolution:
|
||||
name: Target-1 Resolution
|
||||
target_2:
|
||||
x:
|
||||
name: Target-2 X
|
||||
y:
|
||||
name: Target-2 Y
|
||||
speed:
|
||||
name: Target-2 Speed
|
||||
angle:
|
||||
name: Target-2 Angle
|
||||
distance:
|
||||
name: Target-2 Distance
|
||||
resolution:
|
||||
name: Target-2 Resolution
|
||||
target_3:
|
||||
x:
|
||||
name: Target-3 X
|
||||
y:
|
||||
name: Target-3 Y
|
||||
speed:
|
||||
name: Target-3 Speed
|
||||
angle:
|
||||
name: Target-3 Angle
|
||||
distance:
|
||||
name: Target-3 Distance
|
||||
resolution:
|
||||
name: Target-3 Resolution
|
||||
|
||||
binary_sensor:
|
||||
- platform: rd03d
|
||||
rd03d_id: rd03d_radar
|
||||
target:
|
||||
name: Presence
|
||||
target_1:
|
||||
name: Target-1 Presence
|
||||
target_2:
|
||||
name: Target-2 Presence
|
||||
target_3:
|
||||
name: Target-3 Presence
|
||||
4
tests/components/rd03d/test.esp32-idf.yaml
Normal file
4
tests/components/rd03d/test.esp32-idf.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
uart: !include ../../test_build_components/common/uart/esp32-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
4
tests/components/rd03d/test.esp8266-ard.yaml
Normal file
4
tests/components/rd03d/test.esp8266-ard.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
4
tests/components/rd03d/test.rp2040-ard.yaml
Normal file
4
tests/components/rd03d/test.rp2040-ard.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
Reference in New Issue
Block a user