mirror of
https://github.com/esphome/esphome.git
synced 2025-03-13 22:28:14 +00:00
Merge branch 'esphome:dev' into mcp4461
This commit is contained in:
commit
270909350a
2
.github/actions/restore-python/action.yml
vendored
2
.github/actions/restore-python/action.yml
vendored
@ -22,7 +22,7 @@ runs:
|
||||
python-version: ${{ inputs.python-version }}
|
||||
- name: Restore Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache/restore@v4.2.0
|
||||
uses: actions/cache/restore@v4.2.1
|
||||
with:
|
||||
path: venv
|
||||
# yamllint disable-line rule:line-length
|
||||
|
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@ -47,7 +47,7 @@ jobs:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v4.2.0
|
||||
uses: actions/cache@v4.2.1
|
||||
with:
|
||||
path: venv
|
||||
# yamllint disable-line rule:line-length
|
||||
@ -61,8 +61,8 @@ jobs:
|
||||
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
||||
pip install -e .
|
||||
|
||||
black:
|
||||
name: Check black
|
||||
ruff:
|
||||
name: Check ruff
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
@ -74,10 +74,10 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||
- name: Run black
|
||||
- name: Run Ruff
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
black --verbose esphome tests
|
||||
ruff format esphome tests
|
||||
- name: Suggested changes
|
||||
run: script/ci-suggest-changes
|
||||
if: always()
|
||||
@ -255,7 +255,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
- black
|
||||
- ruff
|
||||
- ci-custom
|
||||
- clang-format
|
||||
- flake8
|
||||
@ -303,14 +303,14 @@ jobs:
|
||||
|
||||
- name: Cache platformio
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
uses: actions/cache@v4.2.0
|
||||
uses: actions/cache@v4.2.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-${{ matrix.pio_cache_key }}
|
||||
|
||||
- name: Cache platformio
|
||||
if: github.ref != 'refs/heads/dev'
|
||||
uses: actions/cache/restore@v4.2.0
|
||||
uses: actions/cache/restore@v4.2.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-${{ matrix.pio_cache_key }}
|
||||
@ -482,7 +482,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
- black
|
||||
- ruff
|
||||
- ci-custom
|
||||
- clang-format
|
||||
- flake8
|
||||
|
@ -4,7 +4,7 @@
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.5.4
|
||||
rev: v0.9.2
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
@ -234,6 +234,7 @@ esphome/components/kuntze/* @ssieb
|
||||
esphome/components/lcd_menu/* @numo68
|
||||
esphome/components/ld2410/* @regevbr @sebcaps
|
||||
esphome/components/ld2420/* @descipher
|
||||
esphome/components/ld2450/* @hareeshmu
|
||||
esphome/components/ledc/* @OttoWinter
|
||||
esphome/components/libretiny/* @kuba2k2
|
||||
esphome/components/libretiny_pwm/* @kuba2k2
|
||||
|
@ -35,7 +35,7 @@ RUN \
|
||||
iputils-ping=3:20221126-1+deb12u1 \
|
||||
git=1:2.39.5-0+deb12u1 \
|
||||
curl=7.88.1-10+deb12u8 \
|
||||
openssh-client=1:9.2p1-2+deb12u3 \
|
||||
openssh-client=1:9.2p1-2+deb12u4 \
|
||||
python3-cffi=1.15.1-5 \
|
||||
libcairo2=1.16.0-7 \
|
||||
libmagic1=1:5.44-3 \
|
||||
|
@ -66,7 +66,7 @@ def choose_prompt(options, purpose: str = None):
|
||||
return options[0][1]
|
||||
|
||||
safe_print(
|
||||
f'Found multiple options{f" for {purpose}" if purpose else ""}, please choose one:'
|
||||
f"Found multiple options{f' for {purpose}' if purpose else ''}, please choose one:"
|
||||
)
|
||||
for i, (desc, _) in enumerate(options):
|
||||
safe_print(f" [{i + 1}] {desc}")
|
||||
|
@ -128,7 +128,6 @@ VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
def visual_temperature_step(value):
|
||||
|
||||
# Allow defining target/current temperature steps separately
|
||||
if isinstance(value, dict):
|
||||
return VISUAL_TEMPERATURE_STEP_SCHEMA(value)
|
||||
|
@ -66,7 +66,9 @@ FINAL_VALIDATE_SCHEMA = esp32_ble.validate_variant
|
||||
|
||||
async def to_code(config):
|
||||
uuid = config[CONF_UUID].hex
|
||||
uuid_arr = [cg.RawExpression(f"0x{uuid[i:i + 2]}") for i in range(0, len(uuid), 2)]
|
||||
uuid_arr = [
|
||||
cg.RawExpression(f"0x{uuid[i : i + 2]}") for i in range(0, len(uuid), 2)
|
||||
]
|
||||
var = cg.new_Pvariable(config[CONF_ID], uuid_arr)
|
||||
|
||||
parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
|
||||
|
@ -112,8 +112,7 @@ def validate_supports(value):
|
||||
)
|
||||
if is_pullup and num == 16:
|
||||
raise cv.Invalid(
|
||||
"GPIO Pin 16 does not support pullup pin mode. "
|
||||
"Please choose another pin.",
|
||||
"GPIO Pin 16 does not support pullup pin mode. Please choose another pin.",
|
||||
[CONF_MODE, CONF_PULLUP],
|
||||
)
|
||||
if is_pulldown and num != 16:
|
||||
|
@ -1,9 +1,15 @@
|
||||
import logging
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
import esphome.final_validate as fv
|
||||
from esphome.components import uart, climate, logger
|
||||
import logging
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import climate, logger, uart
|
||||
from esphome.components.climate import (
|
||||
CONF_CURRENT_TEMPERATURE,
|
||||
ClimateMode,
|
||||
ClimatePreset,
|
||||
ClimateSwingMode,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BEEPER,
|
||||
CONF_DISPLAY,
|
||||
@ -24,12 +30,7 @@ from esphome.const import (
|
||||
CONF_VISUAL,
|
||||
CONF_WIFI,
|
||||
)
|
||||
from esphome.components.climate import (
|
||||
ClimateMode,
|
||||
ClimatePreset,
|
||||
ClimateSwingMode,
|
||||
CONF_CURRENT_TEMPERATURE,
|
||||
)
|
||||
import esphome.final_validate as fv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
51
esphome/components/ld2450/__init__.py
Normal file
51
esphome/components/ld2450/__init__.py
Normal file
@ -0,0 +1,51 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import uart
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_THROTTLE,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
CODEOWNERS = ["@hareeshmu"]
|
||||
MULTI_CONF = True
|
||||
|
||||
ld2450_ns = cg.esphome_ns.namespace("ld2450")
|
||||
LD2450Component = ld2450_ns.class_("LD2450Component", cg.Component, uart.UARTDevice)
|
||||
|
||||
CONF_LD2450_ID = "ld2450_id"
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LD2450Component),
|
||||
cv.Optional(CONF_THROTTLE, default="1000ms"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(min=cv.TimePeriod(milliseconds=1)),
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
LD2450BaseSchema = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||
},
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||
"ld2450",
|
||||
require_tx=True,
|
||||
require_rx=True,
|
||||
parity="NONE",
|
||||
stop_bits=1,
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
cg.add(var.set_throttle(config[CONF_THROTTLE]))
|
47
esphome/components/ld2450/binary_sensor.py
Normal file
47
esphome/components/ld2450/binary_sensor.py
Normal file
@ -0,0 +1,47 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_HAS_MOVING_TARGET,
|
||||
CONF_HAS_STILL_TARGET,
|
||||
CONF_HAS_TARGET,
|
||||
DEVICE_CLASS_MOTION,
|
||||
DEVICE_CLASS_OCCUPANCY,
|
||||
)
|
||||
|
||||
from . import CONF_LD2450_ID, LD2450Component
|
||||
|
||||
DEPENDENCIES = ["ld2450"]
|
||||
|
||||
ICON_MEDITATION = "mdi:meditation"
|
||||
ICON_SHIELD_ACCOUNT = "mdi:shield-account"
|
||||
ICON_TARGET_ACCOUNT = "mdi:target-account"
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||
cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_OCCUPANCY,
|
||||
icon=ICON_SHIELD_ACCOUNT,
|
||||
),
|
||||
cv.Optional(CONF_HAS_MOVING_TARGET): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_MOTION,
|
||||
icon=ICON_TARGET_ACCOUNT,
|
||||
),
|
||||
cv.Optional(CONF_HAS_STILL_TARGET): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_OCCUPANCY,
|
||||
icon=ICON_MEDITATION,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
ld2450_component = await cg.get_variable(config[CONF_LD2450_ID])
|
||||
if has_target_config := config.get(CONF_HAS_TARGET):
|
||||
sens = await binary_sensor.new_binary_sensor(has_target_config)
|
||||
cg.add(ld2450_component.set_target_binary_sensor(sens))
|
||||
if has_moving_target_config := config.get(CONF_HAS_MOVING_TARGET):
|
||||
sens = await binary_sensor.new_binary_sensor(has_moving_target_config)
|
||||
cg.add(ld2450_component.set_moving_target_binary_sensor(sens))
|
||||
if has_still_target_config := config.get(CONF_HAS_STILL_TARGET):
|
||||
sens = await binary_sensor.new_binary_sensor(has_still_target_config)
|
||||
cg.add(ld2450_component.set_still_target_binary_sensor(sens))
|
45
esphome/components/ld2450/button/__init__.py
Normal file
45
esphome/components/ld2450/button/__init__.py
Normal file
@ -0,0 +1,45 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_FACTORY_RESET,
|
||||
CONF_RESTART,
|
||||
DEVICE_CLASS_RESTART,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_RESTART,
|
||||
ICON_RESTART_ALERT,
|
||||
)
|
||||
|
||||
from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
|
||||
|
||||
ResetButton = ld2450_ns.class_("ResetButton", button.Button)
|
||||
RestartButton = ld2450_ns.class_("RestartButton", button.Button)
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||
cv.Optional(CONF_FACTORY_RESET): button.button_schema(
|
||||
ResetButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART_ALERT,
|
||||
),
|
||||
cv.Optional(CONF_RESTART): button.button_schema(
|
||||
RestartButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_RESTART,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
ld2450_component = await cg.get_variable(config[CONF_LD2450_ID])
|
||||
if factory_reset_config := config.get(CONF_FACTORY_RESET):
|
||||
b = await button.new_button(factory_reset_config)
|
||||
await cg.register_parented(b, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_reset_button(b))
|
||||
if restart_config := config.get(CONF_RESTART):
|
||||
b = await button.new_button(restart_config)
|
||||
await cg.register_parented(b, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_restart_button(b))
|
9
esphome/components/ld2450/button/reset_button.cpp
Normal file
9
esphome/components/ld2450/button/reset_button.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "reset_button.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
void ResetButton::press_action() { this->parent_->factory_reset(); }
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
18
esphome/components/ld2450/button/reset_button.h
Normal file
18
esphome/components/ld2450/button/reset_button.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/button/button.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
class ResetButton : public button::Button, public Parented<LD2450Component> {
|
||||
public:
|
||||
ResetButton() = default;
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
9
esphome/components/ld2450/button/restart_button.cpp
Normal file
9
esphome/components/ld2450/button/restart_button.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "restart_button.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
void RestartButton::press_action() { this->parent_->restart_and_read_all_info(); }
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
18
esphome/components/ld2450/button/restart_button.h
Normal file
18
esphome/components/ld2450/button/restart_button.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/button/button.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
class RestartButton : public button::Button, public Parented<LD2450Component> {
|
||||
public:
|
||||
RestartButton() = default;
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
867
esphome/components/ld2450/ld2450.cpp
Normal file
867
esphome/components/ld2450/ld2450.cpp
Normal file
@ -0,0 +1,867 @@
|
||||
#include "ld2450.h"
|
||||
#include <utility>
|
||||
#ifdef USE_NUMBER
|
||||
#include "esphome/components/number/number.h"
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#define highbyte(val) (uint8_t)((val) >> 8)
|
||||
#define lowbyte(val) (uint8_t)((val) &0xff)
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
static const char *const TAG = "ld2450";
|
||||
static const char *const UNKNOWN_MAC("unknown");
|
||||
|
||||
// LD2450 UART Serial Commands
|
||||
static const uint8_t CMD_ENABLE_CONF = 0x00FF;
|
||||
static const uint8_t CMD_DISABLE_CONF = 0x00FE;
|
||||
static const uint8_t CMD_VERSION = 0x00A0;
|
||||
static const uint8_t CMD_MAC = 0x00A5;
|
||||
static const uint8_t CMD_RESET = 0x00A2;
|
||||
static const uint8_t CMD_RESTART = 0x00A3;
|
||||
static const uint8_t CMD_BLUETOOTH = 0x00A4;
|
||||
static const uint8_t CMD_SINGLE_TARGET_MODE = 0x0080;
|
||||
static const uint8_t CMD_MULTI_TARGET_MODE = 0x0090;
|
||||
static const uint8_t CMD_QUERY_TARGET_MODE = 0x0091;
|
||||
static const uint8_t CMD_SET_BAUD_RATE = 0x00A1;
|
||||
static const uint8_t CMD_QUERY_ZONE = 0x00C1;
|
||||
static const uint8_t CMD_SET_ZONE = 0x00C2;
|
||||
|
||||
static inline uint16_t convert_seconds_to_ms(uint16_t value) { return value * 1000; };
|
||||
|
||||
static inline std::string convert_signed_int_to_hex(int value) {
|
||||
auto value_as_str = str_snprintf("%04x", 4, value & 0xFFFF);
|
||||
return value_as_str;
|
||||
}
|
||||
|
||||
static inline void convert_int_values_to_hex(const int *values, uint8_t *bytes) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
std::string temp_hex = convert_signed_int_to_hex(values[i]);
|
||||
bytes[i * 2] = std::stoi(temp_hex.substr(2, 2), nullptr, 16); // Store high byte
|
||||
bytes[i * 2 + 1] = std::stoi(temp_hex.substr(0, 2), nullptr, 16); // Store low byte
|
||||
}
|
||||
}
|
||||
|
||||
static inline int16_t decode_coordinate(uint8_t low_byte, uint8_t high_byte) {
|
||||
int16_t coordinate = (high_byte & 0x7F) << 8 | low_byte;
|
||||
if ((high_byte & 0x80) == 0) {
|
||||
coordinate = -coordinate;
|
||||
}
|
||||
return coordinate; // mm
|
||||
}
|
||||
|
||||
static inline int16_t decode_speed(uint8_t low_byte, uint8_t high_byte) {
|
||||
int16_t speed = (high_byte & 0x7F) << 8 | low_byte;
|
||||
if ((high_byte & 0x80) == 0) {
|
||||
speed = -speed;
|
||||
}
|
||||
return speed * 10; // mm/s
|
||||
}
|
||||
|
||||
static inline int16_t hex_to_signed_int(const uint8_t *buffer, uint8_t offset) {
|
||||
uint16_t hex_val = (buffer[offset + 1] << 8) | buffer[offset];
|
||||
int16_t dec_val = static_cast<int16_t>(hex_val);
|
||||
if (dec_val & 0x8000) {
|
||||
dec_val -= 65536;
|
||||
}
|
||||
return dec_val;
|
||||
}
|
||||
|
||||
static inline float calculate_angle(float base, float hypotenuse) {
|
||||
if (base < 0.0 || hypotenuse <= 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
float angle_radians = std::acos(base / hypotenuse);
|
||||
float angle_degrees = angle_radians * (180.0 / M_PI);
|
||||
return angle_degrees;
|
||||
}
|
||||
|
||||
static inline std::string get_direction(int16_t speed) {
|
||||
static const char *const APPROACHING = "Approaching";
|
||||
static const char *const MOVING_AWAY = "Moving away";
|
||||
static const char *const STATIONARY = "Stationary";
|
||||
|
||||
if (speed > 0) {
|
||||
return MOVING_AWAY;
|
||||
}
|
||||
if (speed < 0) {
|
||||
return APPROACHING;
|
||||
}
|
||||
return STATIONARY;
|
||||
}
|
||||
|
||||
static inline std::string format_mac(uint8_t *buffer) {
|
||||
return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14],
|
||||
buffer[15]);
|
||||
}
|
||||
|
||||
static inline std::string format_version(uint8_t *buffer) {
|
||||
return str_sprintf("%u.%02X.%02X%02X%02X%02X", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15],
|
||||
buffer[14]);
|
||||
}
|
||||
|
||||
LD2450Component::LD2450Component() {}
|
||||
|
||||
void LD2450Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up HLK-LD2450...");
|
||||
#ifdef USE_NUMBER
|
||||
this->pref_ = global_preferences->make_preference<float>(this->presence_timeout_number_->get_object_id_hash());
|
||||
this->set_presence_timeout();
|
||||
#endif
|
||||
this->read_all_info();
|
||||
}
|
||||
|
||||
void LD2450Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "HLK-LD2450 Human motion tracking radar module:");
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
LOG_BINARY_SENSOR(" ", "TargetBinarySensor", this->target_binary_sensor_);
|
||||
LOG_BINARY_SENSOR(" ", "MovingTargetBinarySensor", this->moving_target_binary_sensor_);
|
||||
LOG_BINARY_SENSOR(" ", "StillTargetBinarySensor", this->still_target_binary_sensor_);
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
LOG_SWITCH(" ", "BluetoothSwitch", this->bluetooth_switch_);
|
||||
LOG_SWITCH(" ", "MultiTargetSwitch", this->multi_target_switch_);
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
LOG_BUTTON(" ", "ResetButton", this->reset_button_);
|
||||
LOG_BUTTON(" ", "RestartButton", this->restart_button_);
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR(" ", "TargetCountSensor", this->target_count_sensor_);
|
||||
LOG_SENSOR(" ", "StillTargetCountSensor", this->still_target_count_sensor_);
|
||||
LOG_SENSOR(" ", "MovingTargetCountSensor", this->moving_target_count_sensor_);
|
||||
for (sensor::Sensor *s : this->move_x_sensors_) {
|
||||
LOG_SENSOR(" ", "NthTargetXSensor", s);
|
||||
}
|
||||
for (sensor::Sensor *s : this->move_y_sensors_) {
|
||||
LOG_SENSOR(" ", "NthTargetYSensor", s);
|
||||
}
|
||||
for (sensor::Sensor *s : this->move_speed_sensors_) {
|
||||
LOG_SENSOR(" ", "NthTargetSpeedSensor", s);
|
||||
}
|
||||
for (sensor::Sensor *s : this->move_angle_sensors_) {
|
||||
LOG_SENSOR(" ", "NthTargetAngleSensor", s);
|
||||
}
|
||||
for (sensor::Sensor *s : this->move_distance_sensors_) {
|
||||
LOG_SENSOR(" ", "NthTargetDistanceSensor", s);
|
||||
}
|
||||
for (sensor::Sensor *s : this->move_resolution_sensors_) {
|
||||
LOG_SENSOR(" ", "NthTargetResolutionSensor", s);
|
||||
}
|
||||
for (sensor::Sensor *s : this->zone_target_count_sensors_) {
|
||||
LOG_SENSOR(" ", "NthZoneTargetCountSensor", s);
|
||||
}
|
||||
for (sensor::Sensor *s : this->zone_still_target_count_sensors_) {
|
||||
LOG_SENSOR(" ", "NthZoneStillTargetCountSensor", s);
|
||||
}
|
||||
for (sensor::Sensor *s : this->zone_moving_target_count_sensors_) {
|
||||
LOG_SENSOR(" ", "NthZoneMovingTargetCountSensor", s);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
LOG_TEXT_SENSOR(" ", "VersionTextSensor", this->version_text_sensor_);
|
||||
LOG_TEXT_SENSOR(" ", "MacTextSensor", this->mac_text_sensor_);
|
||||
for (text_sensor::TextSensor *s : this->direction_text_sensors_) {
|
||||
LOG_TEXT_SENSOR(" ", "NthDirectionTextSensor", s);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
for (number::Number *n : this->zone_x1_numbers_) {
|
||||
LOG_NUMBER(" ", "ZoneX1Number", n);
|
||||
}
|
||||
for (number::Number *n : this->zone_y1_numbers_) {
|
||||
LOG_NUMBER(" ", "ZoneY1Number", n);
|
||||
}
|
||||
for (number::Number *n : this->zone_x2_numbers_) {
|
||||
LOG_NUMBER(" ", "ZoneX2Number", n);
|
||||
}
|
||||
for (number::Number *n : this->zone_y2_numbers_) {
|
||||
LOG_NUMBER(" ", "ZoneY2Number", n);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
LOG_SELECT(" ", "BaudRateSelect", this->baud_rate_select_);
|
||||
LOG_SELECT(" ", "ZoneTypeSelect", this->zone_type_select_);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
LOG_NUMBER(" ", "PresenceTimeoutNumber", this->presence_timeout_number_);
|
||||
#endif
|
||||
ESP_LOGCONFIG(TAG, " Throttle : %ums", this->throttle_);
|
||||
ESP_LOGCONFIG(TAG, " MAC Address : %s", const_cast<char *>(this->mac_.c_str()));
|
||||
ESP_LOGCONFIG(TAG, " Firmware version : %s", const_cast<char *>(this->version_.c_str()));
|
||||
}
|
||||
|
||||
void LD2450Component::loop() {
|
||||
while (this->available()) {
|
||||
this->readline_(read(), this->buffer_data_, MAX_LINE_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
// Count targets in zone
|
||||
uint8_t LD2450Component::count_targets_in_zone_(const Zone &zone, bool is_moving) {
|
||||
uint8_t count = 0;
|
||||
for (auto &index : this->target_info_) {
|
||||
if (index.x > zone.x1 && index.x < zone.x2 && index.y > zone.y1 && index.y < zone.y2 &&
|
||||
index.is_moving == is_moving) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Service reset_radar_zone
|
||||
void LD2450Component::reset_radar_zone() {
|
||||
this->zone_type_ = 0;
|
||||
for (auto &i : this->zone_config_) {
|
||||
i.x1 = 0;
|
||||
i.y1 = 0;
|
||||
i.x2 = 0;
|
||||
i.y2 = 0;
|
||||
}
|
||||
this->send_set_zone_command_();
|
||||
}
|
||||
|
||||
void LD2450Component::set_radar_zone(int32_t zone_type, int32_t zone1_x1, int32_t zone1_y1, int32_t zone1_x2,
|
||||
int32_t zone1_y2, int32_t zone2_x1, int32_t zone2_y1, int32_t zone2_x2,
|
||||
int32_t zone2_y2, int32_t zone3_x1, int32_t zone3_y1, int32_t zone3_x2,
|
||||
int32_t zone3_y2) {
|
||||
this->zone_type_ = zone_type;
|
||||
int zone_parameters[12] = {zone1_x1, zone1_y1, zone1_x2, zone1_y2, zone2_x1, zone2_y1,
|
||||
zone2_x2, zone2_y2, zone3_x1, zone3_y1, zone3_x2, zone3_y2};
|
||||
for (int i = 0; i < MAX_ZONES; i++) {
|
||||
this->zone_config_[i].x1 = zone_parameters[i * 4];
|
||||
this->zone_config_[i].y1 = zone_parameters[i * 4 + 1];
|
||||
this->zone_config_[i].x2 = zone_parameters[i * 4 + 2];
|
||||
this->zone_config_[i].y2 = zone_parameters[i * 4 + 3];
|
||||
}
|
||||
this->send_set_zone_command_();
|
||||
}
|
||||
|
||||
// Set Zone on LD2450 Sensor
|
||||
void LD2450Component::send_set_zone_command_() {
|
||||
uint8_t cmd_value[26] = {};
|
||||
uint8_t zone_type_bytes[2] = {static_cast<uint8_t>(this->zone_type_), 0x00};
|
||||
uint8_t area_config[24] = {};
|
||||
for (int i = 0; i < MAX_ZONES; i++) {
|
||||
int values[4] = {this->zone_config_[i].x1, this->zone_config_[i].y1, this->zone_config_[i].x2,
|
||||
this->zone_config_[i].y2};
|
||||
ld2450::convert_int_values_to_hex(values, area_config + (i * 8));
|
||||
}
|
||||
std::memcpy(cmd_value, zone_type_bytes, 2);
|
||||
std::memcpy(cmd_value + 2, area_config, 24);
|
||||
this->set_config_mode_(true);
|
||||
this->send_command_(CMD_SET_ZONE, cmd_value, 26);
|
||||
this->set_config_mode_(false);
|
||||
}
|
||||
|
||||
// Check presense timeout to reset presence status
|
||||
bool LD2450Component::get_timeout_status_(uint32_t check_millis) {
|
||||
if (check_millis == 0) {
|
||||
return true;
|
||||
}
|
||||
if (this->timeout_ == 0) {
|
||||
this->timeout_ = ld2450::convert_seconds_to_ms(DEFAULT_PRESENCE_TIMEOUT);
|
||||
}
|
||||
auto current_millis = millis();
|
||||
return current_millis - check_millis >= this->timeout_;
|
||||
}
|
||||
|
||||
// Extract, store and publish zone details LD2450 buffer
|
||||
void LD2450Component::process_zone_(uint8_t *buffer) {
|
||||
uint8_t index, start;
|
||||
for (index = 0; index < MAX_ZONES; index++) {
|
||||
start = 12 + index * 8;
|
||||
this->zone_config_[index].x1 = ld2450::hex_to_signed_int(buffer, start);
|
||||
this->zone_config_[index].y1 = ld2450::hex_to_signed_int(buffer, start + 2);
|
||||
this->zone_config_[index].x2 = ld2450::hex_to_signed_int(buffer, start + 4);
|
||||
this->zone_config_[index].y2 = ld2450::hex_to_signed_int(buffer, start + 6);
|
||||
#ifdef USE_NUMBER
|
||||
this->zone_x1_numbers_[index]->publish_state(this->zone_config_[index].x1);
|
||||
this->zone_y1_numbers_[index]->publish_state(this->zone_config_[index].y1);
|
||||
this->zone_x2_numbers_[index]->publish_state(this->zone_config_[index].x2);
|
||||
this->zone_y2_numbers_[index]->publish_state(this->zone_config_[index].y2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Read all info from LD2450 buffer
|
||||
void LD2450Component::read_all_info() {
|
||||
this->set_config_mode_(true);
|
||||
this->get_version_();
|
||||
this->get_mac_();
|
||||
this->query_target_tracking_mode_();
|
||||
this->query_zone_();
|
||||
this->set_config_mode_(false);
|
||||
#ifdef USE_SELECT
|
||||
const auto baud_rate = std::to_string(this->parent_->get_baud_rate());
|
||||
if (this->baud_rate_select_ != nullptr && this->baud_rate_select_->state != baud_rate) {
|
||||
this->baud_rate_select_->publish_state(baud_rate);
|
||||
}
|
||||
this->publish_zone_type();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Read zone info from LD2450 buffer
|
||||
void LD2450Component::query_zone_info() {
|
||||
this->set_config_mode_(true);
|
||||
this->query_zone_();
|
||||
this->set_config_mode_(false);
|
||||
}
|
||||
|
||||
// Restart LD2450 and read all info from buffer
|
||||
void LD2450Component::restart_and_read_all_info() {
|
||||
this->set_config_mode_(true);
|
||||
this->restart_();
|
||||
this->set_timeout(1000, [this]() { this->read_all_info(); });
|
||||
}
|
||||
|
||||
// Send command with values to LD2450
|
||||
void LD2450Component::send_command_(uint8_t command, const uint8_t *command_value, uint8_t command_value_len) {
|
||||
ESP_LOGV(TAG, "Sending command %02X", command);
|
||||
// frame header
|
||||
this->write_array(CMD_FRAME_HEADER, 4);
|
||||
// length bytes
|
||||
int len = 2;
|
||||
if (command_value != nullptr) {
|
||||
len += command_value_len;
|
||||
}
|
||||
this->write_byte(lowbyte(len));
|
||||
this->write_byte(highbyte(len));
|
||||
// command
|
||||
this->write_byte(lowbyte(command));
|
||||
this->write_byte(highbyte(command));
|
||||
// command value bytes
|
||||
if (command_value != nullptr) {
|
||||
for (int i = 0; i < command_value_len; i++) {
|
||||
this->write_byte(command_value[i]);
|
||||
}
|
||||
}
|
||||
// footer
|
||||
this->write_array(CMD_FRAME_END, 4);
|
||||
// FIXME to remove
|
||||
delay(50); // NOLINT
|
||||
}
|
||||
|
||||
// LD2450 Radar data message:
|
||||
// [AA FF 03 00] [0E 03 B1 86 10 00 40 01] [00 00 00 00 00 00 00 00] [00 00 00 00 00 00 00 00] [55 CC]
|
||||
// Header Target 1 Target 2 Target 3 End
|
||||
void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
||||
if (len < 29) { // header (4 bytes) + 8 x 3 target data + footer (2 bytes)
|
||||
ESP_LOGE(TAG, "Periodic data: invalid message length");
|
||||
return;
|
||||
}
|
||||
if (buffer[0] != 0xAA || buffer[1] != 0xFF || buffer[2] != 0x03 || buffer[3] != 0x00) { // header
|
||||
ESP_LOGE(TAG, "Periodic data: invalid message header");
|
||||
return;
|
||||
}
|
||||
if (buffer[len - 2] != 0x55 || buffer[len - 1] != 0xCC) { // footer
|
||||
ESP_LOGE(TAG, "Periodic data: invalid message footer");
|
||||
return;
|
||||
}
|
||||
|
||||
auto current_millis = millis();
|
||||
if (current_millis - this->last_periodic_millis_ < this->throttle_) {
|
||||
ESP_LOGV(TAG, "Throttling: %d", this->throttle_);
|
||||
return;
|
||||
}
|
||||
|
||||
this->last_periodic_millis_ = current_millis;
|
||||
|
||||
int16_t target_count = 0;
|
||||
int16_t still_target_count = 0;
|
||||
int16_t moving_target_count = 0;
|
||||
int16_t start = 0;
|
||||
int16_t val = 0;
|
||||
uint8_t index = 0;
|
||||
int16_t tx = 0;
|
||||
int16_t ty = 0;
|
||||
int16_t td = 0;
|
||||
int16_t ts = 0;
|
||||
int16_t angle = 0;
|
||||
std::string direction{};
|
||||
bool is_moving = false;
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
// Loop thru targets
|
||||
// X
|
||||
for (index = 0; index < MAX_TARGETS; index++) {
|
||||
start = TARGET_X + index * 8;
|
||||
is_moving = false;
|
||||
sensor::Sensor *sx = this->move_x_sensors_[index];
|
||||
if (sx != nullptr) {
|
||||
val = ld2450::decode_coordinate(buffer[start], buffer[start + 1]);
|
||||
tx = val;
|
||||
sx->publish_state(val);
|
||||
}
|
||||
// Y
|
||||
start = TARGET_Y + index * 8;
|
||||
sensor::Sensor *sy = this->move_y_sensors_[index];
|
||||
if (sy != nullptr) {
|
||||
val = ld2450::decode_coordinate(buffer[start], buffer[start + 1]);
|
||||
ty = val;
|
||||
sy->publish_state(val);
|
||||
}
|
||||
// SPEED
|
||||
start = TARGET_SPEED + index * 8;
|
||||
sensor::Sensor *ss = this->move_speed_sensors_[index];
|
||||
if (ss != nullptr) {
|
||||
val = ld2450::decode_speed(buffer[start], buffer[start + 1]);
|
||||
ts = val;
|
||||
if (val) {
|
||||
is_moving = true;
|
||||
moving_target_count++;
|
||||
}
|
||||
ss->publish_state(val);
|
||||
}
|
||||
// RESOLUTION
|
||||
start = TARGET_RESOLUTION + index * 8;
|
||||
sensor::Sensor *sr = this->move_resolution_sensors_[index];
|
||||
if (sr != nullptr) {
|
||||
val = (buffer[start + 1] << 8) | buffer[start];
|
||||
sr->publish_state(val);
|
||||
}
|
||||
// DISTANCE
|
||||
sensor::Sensor *sd = this->move_distance_sensors_[index];
|
||||
if (sd != nullptr) {
|
||||
val = (uint16_t) sqrt(
|
||||
pow(ld2450::decode_coordinate(buffer[TARGET_X + index * 8], buffer[(TARGET_X + index * 8) + 1]), 2) +
|
||||
pow(ld2450::decode_coordinate(buffer[TARGET_Y + index * 8], buffer[(TARGET_Y + index * 8) + 1]), 2));
|
||||
td = val;
|
||||
if (val > 0) {
|
||||
target_count++;
|
||||
}
|
||||
|
||||
sd->publish_state(val);
|
||||
}
|
||||
// ANGLE
|
||||
angle = calculate_angle(static_cast<float>(ty), static_cast<float>(td));
|
||||
if (tx > 0) {
|
||||
angle = angle * -1;
|
||||
}
|
||||
sensor::Sensor *sa = this->move_angle_sensors_[index];
|
||||
if (sa != nullptr) {
|
||||
sa->publish_state(angle);
|
||||
}
|
||||
#endif
|
||||
// DIRECTION
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
direction = get_direction(ts);
|
||||
if (td == 0) {
|
||||
direction = "NA";
|
||||
}
|
||||
text_sensor::TextSensor *tsd = this->direction_text_sensors_[index];
|
||||
if (tsd != nullptr) {
|
||||
tsd->publish_state(direction);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Store target info for zone target count
|
||||
this->target_info_[index].x = tx;
|
||||
this->target_info_[index].y = ty;
|
||||
this->target_info_[index].is_moving = is_moving;
|
||||
|
||||
} // End loop thru targets
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
// Loop thru zones
|
||||
uint8_t zone_still_targets = 0;
|
||||
uint8_t zone_moving_targets = 0;
|
||||
uint8_t zone_all_targets = 0;
|
||||
for (index = 0; index < MAX_ZONES; index++) {
|
||||
// Publish Still Target Count in Zones
|
||||
sensor::Sensor *szstc = this->zone_still_target_count_sensors_[index];
|
||||
if (szstc != nullptr) {
|
||||
zone_still_targets = this->count_targets_in_zone_(this->zone_config_[index], false);
|
||||
szstc->publish_state(zone_still_targets);
|
||||
}
|
||||
// Publish Moving Target Count in Zones
|
||||
sensor::Sensor *szmtc = this->zone_moving_target_count_sensors_[index];
|
||||
if (szmtc != nullptr) {
|
||||
zone_moving_targets = this->count_targets_in_zone_(this->zone_config_[index], true);
|
||||
szmtc->publish_state(zone_moving_targets);
|
||||
}
|
||||
|
||||
zone_all_targets = zone_still_targets + zone_moving_targets;
|
||||
|
||||
// Publish All Target Count in Zones
|
||||
sensor::Sensor *sztc = this->zone_target_count_sensors_[index];
|
||||
if (sztc != nullptr) {
|
||||
sztc->publish_state(zone_all_targets);
|
||||
}
|
||||
|
||||
} // End loop thru zones
|
||||
|
||||
still_target_count = target_count - moving_target_count;
|
||||
// Target Count
|
||||
if (this->target_count_sensor_ != nullptr) {
|
||||
this->target_count_sensor_->publish_state(target_count);
|
||||
}
|
||||
// Still Target Count
|
||||
if (this->still_target_count_sensor_ != nullptr) {
|
||||
this->still_target_count_sensor_->publish_state(still_target_count);
|
||||
}
|
||||
// Moving Target Count
|
||||
if (this->moving_target_count_sensor_ != nullptr) {
|
||||
this->moving_target_count_sensor_->publish_state(moving_target_count);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
// Target Presence
|
||||
if (this->target_binary_sensor_ != nullptr) {
|
||||
if (target_count > 0) {
|
||||
this->target_binary_sensor_->publish_state(true);
|
||||
} else {
|
||||
if (this->get_timeout_status_(this->presence_millis_)) {
|
||||
this->target_binary_sensor_->publish_state(false);
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Clear presence waiting timeout: %d", this->timeout_);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Moving Target Presence
|
||||
if (this->moving_target_binary_sensor_ != nullptr) {
|
||||
if (moving_target_count > 0) {
|
||||
this->moving_target_binary_sensor_->publish_state(true);
|
||||
} else {
|
||||
if (this->get_timeout_status_(this->moving_presence_millis_)) {
|
||||
this->moving_target_binary_sensor_->publish_state(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Still Target Presence
|
||||
if (this->still_target_binary_sensor_ != nullptr) {
|
||||
if (still_target_count > 0) {
|
||||
this->still_target_binary_sensor_->publish_state(true);
|
||||
} else {
|
||||
if (this->get_timeout_status_(this->still_presence_millis_)) {
|
||||
this->still_target_binary_sensor_->publish_state(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
// For presence timeout check
|
||||
if (target_count > 0) {
|
||||
this->presence_millis_ = millis();
|
||||
}
|
||||
if (moving_target_count > 0) {
|
||||
this->moving_presence_millis_ = millis();
|
||||
}
|
||||
if (still_target_count > 0) {
|
||||
this->still_presence_millis_ = millis();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
|
||||
ESP_LOGV(TAG, "Handling ack data for command %02X", buffer[COMMAND]);
|
||||
if (len < 10) {
|
||||
ESP_LOGE(TAG, "Ack data: invalid length");
|
||||
return true;
|
||||
}
|
||||
if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // frame header
|
||||
ESP_LOGE(TAG, "Ack data: invalid header (command %02X)", buffer[COMMAND]);
|
||||
return true;
|
||||
}
|
||||
if (buffer[COMMAND_STATUS] != 0x01) {
|
||||
ESP_LOGE(TAG, "Ack data: invalid status");
|
||||
return true;
|
||||
}
|
||||
if (buffer[8] || buffer[9]) {
|
||||
ESP_LOGE(TAG, "Ack data: last buffer was %u, %u", buffer[8], buffer[9]);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (buffer[COMMAND]) {
|
||||
case lowbyte(CMD_ENABLE_CONF):
|
||||
ESP_LOGV(TAG, "Got enable conf command");
|
||||
break;
|
||||
case lowbyte(CMD_DISABLE_CONF):
|
||||
ESP_LOGV(TAG, "Got disable conf command");
|
||||
break;
|
||||
case lowbyte(CMD_SET_BAUD_RATE):
|
||||
ESP_LOGV(TAG, "Got baud rate change command");
|
||||
#ifdef USE_SELECT
|
||||
if (this->baud_rate_select_ != nullptr) {
|
||||
ESP_LOGV(TAG, "Change baud rate to %s", this->baud_rate_select_->state.c_str());
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case lowbyte(CMD_VERSION):
|
||||
this->version_ = ld2450::format_version(buffer);
|
||||
ESP_LOGV(TAG, "Firmware version: %s", this->version_.c_str());
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->version_text_sensor_ != nullptr) {
|
||||
this->version_text_sensor_->publish_state(this->version_);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case lowbyte(CMD_MAC):
|
||||
if (len < 20) {
|
||||
return false;
|
||||
}
|
||||
this->mac_ = ld2450::format_mac(buffer);
|
||||
ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str());
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->mac_text_sensor_ != nullptr) {
|
||||
this->mac_text_sensor_->publish_state(this->mac_);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
if (this->bluetooth_switch_ != nullptr) {
|
||||
this->bluetooth_switch_->publish_state(this->mac_ != UNKNOWN_MAC);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case lowbyte(CMD_BLUETOOTH):
|
||||
ESP_LOGV(TAG, "Got Bluetooth command");
|
||||
break;
|
||||
case lowbyte(CMD_SINGLE_TARGET_MODE):
|
||||
ESP_LOGV(TAG, "Got single target conf command");
|
||||
#ifdef USE_SWITCH
|
||||
if (this->multi_target_switch_ != nullptr) {
|
||||
this->multi_target_switch_->publish_state(false);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case lowbyte(CMD_MULTI_TARGET_MODE):
|
||||
ESP_LOGV(TAG, "Got multi target conf command");
|
||||
#ifdef USE_SWITCH
|
||||
if (this->multi_target_switch_ != nullptr) {
|
||||
this->multi_target_switch_->publish_state(true);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case lowbyte(CMD_QUERY_TARGET_MODE):
|
||||
ESP_LOGV(TAG, "Got query target tracking mode command");
|
||||
#ifdef USE_SWITCH
|
||||
if (this->multi_target_switch_ != nullptr) {
|
||||
this->multi_target_switch_->publish_state(buffer[10] == 0x02);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case lowbyte(CMD_QUERY_ZONE):
|
||||
ESP_LOGV(TAG, "Got query zone conf command");
|
||||
this->zone_type_ = std::stoi(std::to_string(buffer[10]), nullptr, 16);
|
||||
this->publish_zone_type();
|
||||
#ifdef USE_SELECT
|
||||
if (this->zone_type_select_ != nullptr) {
|
||||
ESP_LOGV(TAG, "Change zone type to: %s", this->zone_type_select_->state.c_str());
|
||||
}
|
||||
#endif
|
||||
if (buffer[10] == 0x00) {
|
||||
ESP_LOGV(TAG, "Zone: Disabled");
|
||||
}
|
||||
if (buffer[10] == 0x01) {
|
||||
ESP_LOGV(TAG, "Zone: Area detection");
|
||||
}
|
||||
if (buffer[10] == 0x02) {
|
||||
ESP_LOGV(TAG, "Zone: Area filter");
|
||||
}
|
||||
this->process_zone_(buffer);
|
||||
break;
|
||||
case lowbyte(CMD_SET_ZONE):
|
||||
ESP_LOGV(TAG, "Got set zone conf command");
|
||||
this->query_zone_info();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read LD2450 buffer data
|
||||
void LD2450Component::readline_(int readch, uint8_t *buffer, uint8_t len) {
|
||||
if (readch < 0) {
|
||||
return;
|
||||
}
|
||||
if (this->buffer_pos_ < len - 1) {
|
||||
buffer[this->buffer_pos_++] = readch;
|
||||
buffer[this->buffer_pos_] = 0;
|
||||
} else {
|
||||
this->buffer_pos_ = 0;
|
||||
}
|
||||
if (this->buffer_pos_ < 4) {
|
||||
return;
|
||||
}
|
||||
if (buffer[this->buffer_pos_ - 2] == 0x55 && buffer[this->buffer_pos_ - 1] == 0xCC) {
|
||||
ESP_LOGV(TAG, "Handle periodic radar data");
|
||||
this->handle_periodic_data_(buffer, this->buffer_pos_);
|
||||
this->buffer_pos_ = 0; // Reset position index for next frame
|
||||
} else if (buffer[this->buffer_pos_ - 4] == 0x04 && buffer[this->buffer_pos_ - 3] == 0x03 &&
|
||||
buffer[this->buffer_pos_ - 2] == 0x02 && buffer[this->buffer_pos_ - 1] == 0x01) {
|
||||
ESP_LOGV(TAG, "Handle command ack data");
|
||||
if (this->handle_ack_data_(buffer, this->buffer_pos_)) {
|
||||
this->buffer_pos_ = 0; // Reset position index for next frame
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Command ack data invalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set Config Mode - Pre-requisite sending commands
|
||||
void LD2450Component::set_config_mode_(bool enable) {
|
||||
uint8_t cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
|
||||
uint8_t cmd_value[2] = {0x01, 0x00};
|
||||
this->send_command_(cmd, enable ? cmd_value : nullptr, 2);
|
||||
}
|
||||
|
||||
// Set Bluetooth Enable/Disable
|
||||
void LD2450Component::set_bluetooth(bool enable) {
|
||||
this->set_config_mode_(true);
|
||||
uint8_t enable_cmd_value[2] = {0x01, 0x00};
|
||||
uint8_t disable_cmd_value[2] = {0x00, 0x00};
|
||||
this->send_command_(CMD_BLUETOOTH, enable ? enable_cmd_value : disable_cmd_value, 2);
|
||||
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
|
||||
}
|
||||
|
||||
// Set Baud rate
|
||||
void LD2450Component::set_baud_rate(const std::string &state) {
|
||||
this->set_config_mode_(true);
|
||||
uint8_t cmd_value[2] = {BAUD_RATE_ENUM_TO_INT.at(state), 0x00};
|
||||
this->send_command_(CMD_SET_BAUD_RATE, cmd_value, 2);
|
||||
this->set_timeout(200, [this]() { this->restart_(); });
|
||||
}
|
||||
|
||||
// Set Zone Type - one of: Disabled, Detection, Filter
|
||||
void LD2450Component::set_zone_type(const std::string &state) {
|
||||
ESP_LOGV(TAG, "Set zone type: %s", state.c_str());
|
||||
uint8_t zone_type = ZONE_TYPE_ENUM_TO_INT.at(state);
|
||||
this->zone_type_ = zone_type;
|
||||
this->send_set_zone_command_();
|
||||
}
|
||||
|
||||
// Publish Zone Type to Select component
|
||||
void LD2450Component::publish_zone_type() {
|
||||
#ifdef USE_SELECT
|
||||
std::string zone_type = ZONE_TYPE_INT_TO_ENUM.at(static_cast<ZoneTypeStructure>(this->zone_type_));
|
||||
if (this->zone_type_select_ != nullptr) {
|
||||
this->zone_type_select_->publish_state(zone_type);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Set Single/Multiplayer target detection
|
||||
void LD2450Component::set_multi_target(bool enable) {
|
||||
this->set_config_mode_(true);
|
||||
uint8_t cmd = enable ? CMD_MULTI_TARGET_MODE : CMD_SINGLE_TARGET_MODE;
|
||||
this->send_command_(cmd, nullptr, 0);
|
||||
this->set_config_mode_(false);
|
||||
}
|
||||
|
||||
// LD2450 factory reset
|
||||
void LD2450Component::factory_reset() {
|
||||
this->set_config_mode_(true);
|
||||
this->send_command_(CMD_RESET, nullptr, 0);
|
||||
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
|
||||
}
|
||||
|
||||
// Restart LD2450 module
|
||||
void LD2450Component::restart_() { this->send_command_(CMD_RESTART, nullptr, 0); }
|
||||
|
||||
// Get LD2450 firmware version
|
||||
void LD2450Component::get_version_() { this->send_command_(CMD_VERSION, nullptr, 0); }
|
||||
|
||||
// Get LD2450 mac address
|
||||
void LD2450Component::get_mac_() {
|
||||
uint8_t cmd_value[2] = {0x01, 0x00};
|
||||
this->send_command_(CMD_MAC, cmd_value, 2);
|
||||
}
|
||||
|
||||
// Query for target tracking mode
|
||||
void LD2450Component::query_target_tracking_mode_() { this->send_command_(CMD_QUERY_TARGET_MODE, nullptr, 0); }
|
||||
|
||||
// Query for zone info
|
||||
void LD2450Component::query_zone_() { this->send_command_(CMD_QUERY_ZONE, nullptr, 0); }
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void LD2450Component::set_move_x_sensor(uint8_t target, sensor::Sensor *s) { this->move_x_sensors_[target] = s; }
|
||||
void LD2450Component::set_move_y_sensor(uint8_t target, sensor::Sensor *s) { this->move_y_sensors_[target] = s; }
|
||||
void LD2450Component::set_move_speed_sensor(uint8_t target, sensor::Sensor *s) {
|
||||
this->move_speed_sensors_[target] = s;
|
||||
}
|
||||
void LD2450Component::set_move_angle_sensor(uint8_t target, sensor::Sensor *s) {
|
||||
this->move_angle_sensors_[target] = s;
|
||||
}
|
||||
void LD2450Component::set_move_distance_sensor(uint8_t target, sensor::Sensor *s) {
|
||||
this->move_distance_sensors_[target] = s;
|
||||
}
|
||||
void LD2450Component::set_move_resolution_sensor(uint8_t target, sensor::Sensor *s) {
|
||||
this->move_resolution_sensors_[target] = s;
|
||||
}
|
||||
void LD2450Component::set_zone_target_count_sensor(uint8_t zone, sensor::Sensor *s) {
|
||||
this->zone_target_count_sensors_[zone] = s;
|
||||
}
|
||||
void LD2450Component::set_zone_still_target_count_sensor(uint8_t zone, sensor::Sensor *s) {
|
||||
this->zone_still_target_count_sensors_[zone] = s;
|
||||
}
|
||||
void LD2450Component::set_zone_moving_target_count_sensor(uint8_t zone, sensor::Sensor *s) {
|
||||
this->zone_moving_target_count_sensors_[zone] = s;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void LD2450Component::set_direction_text_sensor(uint8_t target, text_sensor::TextSensor *s) {
|
||||
this->direction_text_sensors_[target] = s;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Send Zone coordinates data to LD2450
|
||||
#ifdef USE_NUMBER
|
||||
void LD2450Component::set_zone_coordinate(uint8_t zone) {
|
||||
number::Number *x1sens = this->zone_x1_numbers_[zone];
|
||||
number::Number *y1sens = this->zone_y1_numbers_[zone];
|
||||
number::Number *x2sens = this->zone_x2_numbers_[zone];
|
||||
number::Number *y2sens = this->zone_y2_numbers_[zone];
|
||||
if (!x1sens->has_state() || !y1sens->has_state() || !x2sens->has_state() || !y2sens->has_state()) {
|
||||
return;
|
||||
}
|
||||
this->zone_config_[zone].x1 = static_cast<int>(x1sens->state);
|
||||
this->zone_config_[zone].y1 = static_cast<int>(y1sens->state);
|
||||
this->zone_config_[zone].x2 = static_cast<int>(x2sens->state);
|
||||
this->zone_config_[zone].y2 = static_cast<int>(y2sens->state);
|
||||
this->send_set_zone_command_();
|
||||
}
|
||||
|
||||
void LD2450Component::set_zone_x1_number(uint8_t zone, number::Number *n) { this->zone_x1_numbers_[zone] = n; }
|
||||
void LD2450Component::set_zone_y1_number(uint8_t zone, number::Number *n) { this->zone_y1_numbers_[zone] = n; }
|
||||
void LD2450Component::set_zone_x2_number(uint8_t zone, number::Number *n) { this->zone_x2_numbers_[zone] = n; }
|
||||
void LD2450Component::set_zone_y2_number(uint8_t zone, number::Number *n) { this->zone_y2_numbers_[zone] = n; }
|
||||
#endif
|
||||
|
||||
// Set Presence Timeout load and save from flash
|
||||
#ifdef USE_NUMBER
|
||||
void LD2450Component::set_presence_timeout() {
|
||||
if (this->presence_timeout_number_ != nullptr) {
|
||||
if (this->presence_timeout_number_->state == 0) {
|
||||
float timeout = this->restore_from_flash_();
|
||||
this->presence_timeout_number_->publish_state(timeout);
|
||||
this->timeout_ = ld2450::convert_seconds_to_ms(timeout);
|
||||
}
|
||||
if (this->presence_timeout_number_->has_state()) {
|
||||
this->save_to_flash_(this->presence_timeout_number_->state);
|
||||
this->timeout_ = ld2450::convert_seconds_to_ms(this->presence_timeout_number_->state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save Presence Timeout to flash
|
||||
void LD2450Component::save_to_flash_(float value) { this->pref_.save(&value); }
|
||||
|
||||
// Load Presence Timeout from flash
|
||||
float LD2450Component::restore_from_flash_() {
|
||||
float value;
|
||||
if (!this->pref_.load(&value)) {
|
||||
value = DEFAULT_PRESENCE_TIMEOUT;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
231
esphome/components/ld2450/ld2450.h
Normal file
231
esphome/components/ld2450/ld2450.h
Normal file
@ -0,0 +1,231 @@
|
||||
#pragma once
|
||||
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
#include "esphome/components/number/number.h"
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
#include "esphome/components/button/button.h"
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
#include "esphome/components/select/select.h"
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
// Constants
|
||||
static const uint8_t DEFAULT_PRESENCE_TIMEOUT = 5; // Timeout to reset presense status 5 sec.
|
||||
static const uint8_t MAX_LINE_LENGTH = 60; // Max characters for serial buffer
|
||||
static const uint8_t MAX_TARGETS = 3; // Max 3 Targets in LD2450
|
||||
static const uint8_t MAX_ZONES = 3; // Max 3 Zones in LD2450
|
||||
|
||||
// Target coordinate struct
|
||||
struct Target {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
bool is_moving;
|
||||
};
|
||||
|
||||
// Zone coordinate struct
|
||||
struct Zone {
|
||||
int16_t x1 = 0;
|
||||
int16_t y1 = 0;
|
||||
int16_t x2 = 0;
|
||||
int16_t y2 = 0;
|
||||
};
|
||||
|
||||
enum BaudRateStructure : uint8_t {
|
||||
BAUD_RATE_9600 = 1,
|
||||
BAUD_RATE_19200 = 2,
|
||||
BAUD_RATE_38400 = 3,
|
||||
BAUD_RATE_57600 = 4,
|
||||
BAUD_RATE_115200 = 5,
|
||||
BAUD_RATE_230400 = 6,
|
||||
BAUD_RATE_256000 = 7,
|
||||
BAUD_RATE_460800 = 8
|
||||
};
|
||||
|
||||
// Convert baud rate enum to int
|
||||
static const std::map<std::string, uint8_t> BAUD_RATE_ENUM_TO_INT{
|
||||
{"9600", BAUD_RATE_9600}, {"19200", BAUD_RATE_19200}, {"38400", BAUD_RATE_38400},
|
||||
{"57600", BAUD_RATE_57600}, {"115200", BAUD_RATE_115200}, {"230400", BAUD_RATE_230400},
|
||||
{"256000", BAUD_RATE_256000}, {"460800", BAUD_RATE_460800}};
|
||||
|
||||
// Zone type struct
|
||||
enum ZoneTypeStructure : uint8_t { ZONE_DISABLED = 0, ZONE_DETECTION = 1, ZONE_FILTER = 2 };
|
||||
|
||||
// Convert zone type int to enum
|
||||
static const std::map<ZoneTypeStructure, std::string> ZONE_TYPE_INT_TO_ENUM{
|
||||
{ZONE_DISABLED, "Disabled"}, {ZONE_DETECTION, "Detection"}, {ZONE_FILTER, "Filter"}};
|
||||
|
||||
// Convert zone type enum to int
|
||||
static const std::map<std::string, uint8_t> ZONE_TYPE_ENUM_TO_INT{
|
||||
{"Disabled", ZONE_DISABLED}, {"Detection", ZONE_DETECTION}, {"Filter", ZONE_FILTER}};
|
||||
|
||||
// LD2450 serial command header & footer
|
||||
static const uint8_t CMD_FRAME_HEADER[4] = {0xFD, 0xFC, 0xFB, 0xFA};
|
||||
static const uint8_t CMD_FRAME_END[4] = {0x04, 0x03, 0x02, 0x01};
|
||||
|
||||
enum PeriodicDataStructure : uint8_t {
|
||||
TARGET_X = 4,
|
||||
TARGET_Y = 6,
|
||||
TARGET_SPEED = 8,
|
||||
TARGET_RESOLUTION = 10,
|
||||
};
|
||||
|
||||
enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 };
|
||||
|
||||
enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 };
|
||||
|
||||
class LD2450Component : public Component, public uart::UARTDevice {
|
||||
#ifdef USE_SENSOR
|
||||
SUB_SENSOR(target_count)
|
||||
SUB_SENSOR(still_target_count)
|
||||
SUB_SENSOR(moving_target_count)
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
SUB_BINARY_SENSOR(target)
|
||||
SUB_BINARY_SENSOR(moving_target)
|
||||
SUB_BINARY_SENSOR(still_target)
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
SUB_TEXT_SENSOR(version)
|
||||
SUB_TEXT_SENSOR(mac)
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
SUB_SELECT(baud_rate)
|
||||
SUB_SELECT(zone_type)
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
SUB_SWITCH(bluetooth)
|
||||
SUB_SWITCH(multi_target)
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
SUB_BUTTON(reset)
|
||||
SUB_BUTTON(restart)
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
SUB_NUMBER(presence_timeout)
|
||||
#endif
|
||||
|
||||
public:
|
||||
LD2450Component();
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
void set_presence_timeout();
|
||||
void set_throttle(uint16_t value) { this->throttle_ = value; };
|
||||
void read_all_info();
|
||||
void query_zone_info();
|
||||
void restart_and_read_all_info();
|
||||
void set_bluetooth(bool enable);
|
||||
void set_multi_target(bool enable);
|
||||
void set_baud_rate(const std::string &state);
|
||||
void set_zone_type(const std::string &state);
|
||||
void publish_zone_type();
|
||||
void factory_reset();
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void set_direction_text_sensor(uint8_t target, text_sensor::TextSensor *s);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void set_zone_coordinate(uint8_t zone);
|
||||
void set_zone_x1_number(uint8_t zone, number::Number *n);
|
||||
void set_zone_y1_number(uint8_t zone, number::Number *n);
|
||||
void set_zone_x2_number(uint8_t zone, number::Number *n);
|
||||
void set_zone_y2_number(uint8_t zone, number::Number *n);
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
void set_move_x_sensor(uint8_t target, sensor::Sensor *s);
|
||||
void set_move_y_sensor(uint8_t target, sensor::Sensor *s);
|
||||
void set_move_speed_sensor(uint8_t target, sensor::Sensor *s);
|
||||
void set_move_angle_sensor(uint8_t target, sensor::Sensor *s);
|
||||
void set_move_distance_sensor(uint8_t target, sensor::Sensor *s);
|
||||
void set_move_resolution_sensor(uint8_t target, sensor::Sensor *s);
|
||||
void set_zone_target_count_sensor(uint8_t zone, sensor::Sensor *s);
|
||||
void set_zone_still_target_count_sensor(uint8_t zone, sensor::Sensor *s);
|
||||
void set_zone_moving_target_count_sensor(uint8_t zone, sensor::Sensor *s);
|
||||
#endif
|
||||
void reset_radar_zone();
|
||||
void set_radar_zone(int32_t zone_type, int32_t zone1_x1, int32_t zone1_y1, int32_t zone1_x2, int32_t zone1_y2,
|
||||
int32_t zone2_x1, int32_t zone2_y1, int32_t zone2_x2, int32_t zone2_y2, int32_t zone3_x1,
|
||||
int32_t zone3_y1, int32_t zone3_x2, int32_t zone3_y2);
|
||||
|
||||
protected:
|
||||
void send_command_(uint8_t command_str, const uint8_t *command_value, uint8_t command_value_len);
|
||||
void set_config_mode_(bool enable);
|
||||
void handle_periodic_data_(uint8_t *buffer, uint8_t len);
|
||||
bool handle_ack_data_(uint8_t *buffer, uint8_t len);
|
||||
void process_zone_(uint8_t *buffer);
|
||||
void readline_(int readch, uint8_t *buffer, uint8_t len);
|
||||
void get_version_();
|
||||
void get_mac_();
|
||||
void query_target_tracking_mode_();
|
||||
void query_zone_();
|
||||
void restart_();
|
||||
void send_set_zone_command_();
|
||||
void save_to_flash_(float value);
|
||||
float restore_from_flash_();
|
||||
bool get_timeout_status_(uint32_t check_millis);
|
||||
uint8_t count_targets_in_zone_(const Zone &zone, bool is_moving);
|
||||
|
||||
Target target_info_[MAX_TARGETS];
|
||||
Zone zone_config_[MAX_ZONES];
|
||||
uint8_t buffer_pos_ = 0; // where to resume processing/populating buffer
|
||||
uint8_t buffer_data_[MAX_LINE_LENGTH];
|
||||
uint32_t last_periodic_millis_ = 0;
|
||||
uint32_t presence_millis_ = 0;
|
||||
uint32_t still_presence_millis_ = 0;
|
||||
uint32_t moving_presence_millis_ = 0;
|
||||
uint16_t throttle_ = 0;
|
||||
uint16_t timeout_ = 5;
|
||||
uint8_t zone_type_ = 0;
|
||||
std::string version_{};
|
||||
std::string mac_{};
|
||||
#ifdef USE_NUMBER
|
||||
ESPPreferenceObject pref_; // only used when numbers are in use
|
||||
std::vector<number::Number *> zone_x1_numbers_ = std::vector<number::Number *>(MAX_ZONES);
|
||||
std::vector<number::Number *> zone_y1_numbers_ = std::vector<number::Number *>(MAX_ZONES);
|
||||
std::vector<number::Number *> zone_x2_numbers_ = std::vector<number::Number *>(MAX_ZONES);
|
||||
std::vector<number::Number *> zone_y2_numbers_ = std::vector<number::Number *>(MAX_ZONES);
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
std::vector<sensor::Sensor *> move_x_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
|
||||
std::vector<sensor::Sensor *> move_y_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
|
||||
std::vector<sensor::Sensor *> move_speed_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
|
||||
std::vector<sensor::Sensor *> move_angle_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
|
||||
std::vector<sensor::Sensor *> move_distance_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
|
||||
std::vector<sensor::Sensor *> move_resolution_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
|
||||
std::vector<sensor::Sensor *> zone_target_count_sensors_ = std::vector<sensor::Sensor *>(MAX_ZONES);
|
||||
std::vector<sensor::Sensor *> zone_still_target_count_sensors_ = std::vector<sensor::Sensor *>(MAX_ZONES);
|
||||
std::vector<sensor::Sensor *> zone_moving_target_count_sensors_ = std::vector<sensor::Sensor *>(MAX_ZONES);
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
std::vector<text_sensor::TextSensor *> direction_text_sensors_ = std::vector<text_sensor::TextSensor *>(3);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
120
esphome/components/ld2450/number/__init__.py
Normal file
120
esphome/components/ld2450/number/__init__.py
Normal file
@ -0,0 +1,120 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import number
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ICON_TIMELAPSE,
|
||||
UNIT_MILLIMETER,
|
||||
UNIT_SECOND,
|
||||
)
|
||||
|
||||
from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
|
||||
|
||||
CONF_PRESENCE_TIMEOUT = "presence_timeout"
|
||||
CONF_X1 = "x1"
|
||||
CONF_X2 = "x2"
|
||||
CONF_Y1 = "y1"
|
||||
CONF_Y2 = "y2"
|
||||
ICON_ARROW_BOTTOM_RIGHT = "mdi:arrow-bottom-right"
|
||||
ICON_ARROW_BOTTOM_RIGHT_BOLD_BOX_OUTLINE = "mdi:arrow-bottom-right-bold-box-outline"
|
||||
ICON_ARROW_TOP_LEFT = "mdi:arrow-top-left"
|
||||
ICON_ARROW_TOP_LEFT_BOLD_BOX_OUTLINE = "mdi:arrow-top-left-bold-box-outline"
|
||||
MAX_ZONES = 3
|
||||
|
||||
PresenceTimeoutNumber = ld2450_ns.class_("PresenceTimeoutNumber", number.Number)
|
||||
ZoneCoordinateNumber = ld2450_ns.class_("ZoneCoordinateNumber", number.Number)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||
cv.Required(CONF_PRESENCE_TIMEOUT): number.number_schema(
|
||||
PresenceTimeoutNumber,
|
||||
unit_of_measurement=UNIT_SECOND,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_TIMELAPSE,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(f"zone_{n + 1}"): cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_X1): number.number_schema(
|
||||
ZoneCoordinateNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_ARROW_TOP_LEFT_BOLD_BOX_OUTLINE,
|
||||
),
|
||||
cv.Required(CONF_Y1): number.number_schema(
|
||||
ZoneCoordinateNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_ARROW_TOP_LEFT,
|
||||
),
|
||||
cv.Required(CONF_X2): number.number_schema(
|
||||
ZoneCoordinateNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_ARROW_BOTTOM_RIGHT_BOLD_BOX_OUTLINE,
|
||||
),
|
||||
cv.Required(CONF_Y2): number.number_schema(
|
||||
ZoneCoordinateNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_ARROW_BOTTOM_RIGHT,
|
||||
),
|
||||
}
|
||||
)
|
||||
for n in range(MAX_ZONES)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
ld2450_component = await cg.get_variable(config[CONF_LD2450_ID])
|
||||
if presence_timeout_config := config.get(CONF_PRESENCE_TIMEOUT):
|
||||
n = await number.new_number(
|
||||
presence_timeout_config,
|
||||
min_value=0,
|
||||
max_value=3600,
|
||||
step=1,
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_presence_timeout_number(n))
|
||||
for x in range(MAX_ZONES):
|
||||
if zone_conf := config.get(f"zone_{x + 1}"):
|
||||
if zone_x1_config := zone_conf.get(CONF_X1):
|
||||
n = cg.new_Pvariable(zone_x1_config[CONF_ID], x)
|
||||
await number.register_number(
|
||||
n, zone_x1_config, min_value=-4860, max_value=4860, step=1
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_zone_x1_number(x, n))
|
||||
if zone_y1_config := zone_conf.get(CONF_Y1):
|
||||
n = cg.new_Pvariable(zone_y1_config[CONF_ID], x)
|
||||
await number.register_number(
|
||||
n, zone_y1_config, min_value=0, max_value=7560, step=1
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_zone_y1_number(x, n))
|
||||
if zone_x2_config := zone_conf.get(CONF_X2):
|
||||
n = cg.new_Pvariable(zone_x2_config[CONF_ID], x)
|
||||
await number.register_number(
|
||||
n, zone_x2_config, min_value=-4860, max_value=4860, step=1
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_zone_x2_number(x, n))
|
||||
if zone_y2_config := zone_conf.get(CONF_Y2):
|
||||
n = cg.new_Pvariable(zone_y2_config[CONF_ID], x)
|
||||
await number.register_number(
|
||||
n, zone_y2_config, min_value=0, max_value=7560, step=1
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_zone_y2_number(x, n))
|
12
esphome/components/ld2450/number/presence_timeout_number.cpp
Normal file
12
esphome/components/ld2450/number/presence_timeout_number.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "presence_timeout_number.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
void PresenceTimeoutNumber::control(float value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_presence_timeout();
|
||||
}
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
18
esphome/components/ld2450/number/presence_timeout_number.h
Normal file
18
esphome/components/ld2450/number/presence_timeout_number.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
class PresenceTimeoutNumber : public number::Number, public Parented<LD2450Component> {
|
||||
public:
|
||||
PresenceTimeoutNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float value) override;
|
||||
};
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
14
esphome/components/ld2450/number/zone_coordinate_number.cpp
Normal file
14
esphome/components/ld2450/number/zone_coordinate_number.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "zone_coordinate_number.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
ZoneCoordinateNumber::ZoneCoordinateNumber(uint8_t zone) : zone_(zone) {}
|
||||
|
||||
void ZoneCoordinateNumber::control(float value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_zone_coordinate(this->zone_);
|
||||
}
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
19
esphome/components/ld2450/number/zone_coordinate_number.h
Normal file
19
esphome/components/ld2450/number/zone_coordinate_number.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
class ZoneCoordinateNumber : public number::Number, public Parented<LD2450Component> {
|
||||
public:
|
||||
ZoneCoordinateNumber(uint8_t zone);
|
||||
|
||||
protected:
|
||||
uint8_t zone_;
|
||||
void control(float value) override;
|
||||
};
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
56
esphome/components/ld2450/select/__init__.py
Normal file
56
esphome/components/ld2450/select/__init__.py
Normal file
@ -0,0 +1,56 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import select
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_BAUD_RATE, ENTITY_CATEGORY_CONFIG, ICON_THERMOMETER
|
||||
|
||||
from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
|
||||
|
||||
CONF_ZONE_TYPE = "zone_type"
|
||||
|
||||
BaudRateSelect = ld2450_ns.class_("BaudRateSelect", select.Select)
|
||||
ZoneTypeSelect = ld2450_ns.class_("ZoneTypeSelect", select.Select)
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||
cv.Optional(CONF_BAUD_RATE): select.select_schema(
|
||||
BaudRateSelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_THERMOMETER,
|
||||
),
|
||||
cv.Optional(CONF_ZONE_TYPE): select.select_schema(
|
||||
ZoneTypeSelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_THERMOMETER,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
ld2450_component = await cg.get_variable(config[CONF_LD2450_ID])
|
||||
if baud_rate_config := config.get(CONF_BAUD_RATE):
|
||||
s = await select.new_select(
|
||||
baud_rate_config,
|
||||
options=[
|
||||
"9600",
|
||||
"19200",
|
||||
"38400",
|
||||
"57600",
|
||||
"115200",
|
||||
"230400",
|
||||
"256000",
|
||||
"460800",
|
||||
],
|
||||
)
|
||||
await cg.register_parented(s, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_baud_rate_select(s))
|
||||
if zone_type_config := config.get(CONF_ZONE_TYPE):
|
||||
s = await select.new_select(
|
||||
zone_type_config,
|
||||
options=[
|
||||
"Disabled",
|
||||
"Detection",
|
||||
"Filter",
|
||||
],
|
||||
)
|
||||
await cg.register_parented(s, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_zone_type_select(s))
|
12
esphome/components/ld2450/select/baud_rate_select.cpp
Normal file
12
esphome/components/ld2450/select/baud_rate_select.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "baud_rate_select.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
void BaudRateSelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_baud_rate(state);
|
||||
}
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
18
esphome/components/ld2450/select/baud_rate_select.h
Normal file
18
esphome/components/ld2450/select/baud_rate_select.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
class BaudRateSelect : public select::Select, public Parented<LD2450Component> {
|
||||
public:
|
||||
BaudRateSelect() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
};
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
12
esphome/components/ld2450/select/zone_type_select.cpp
Normal file
12
esphome/components/ld2450/select/zone_type_select.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "zone_type_select.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
void ZoneTypeSelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_zone_type(state);
|
||||
}
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
18
esphome/components/ld2450/select/zone_type_select.h
Normal file
18
esphome/components/ld2450/select/zone_type_select.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
class ZoneTypeSelect : public select::Select, public Parented<LD2450Component> {
|
||||
public:
|
||||
ZoneTypeSelect() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
};
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
156
esphome/components/ld2450/sensor.py
Normal file
156
esphome/components/ld2450/sensor.py
Normal file
@ -0,0 +1,156 @@
|
||||
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,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
DEVICE_CLASS_SPEED,
|
||||
UNIT_DEGREES,
|
||||
UNIT_MILLIMETER,
|
||||
)
|
||||
|
||||
from . import CONF_LD2450_ID, LD2450Component
|
||||
|
||||
DEPENDENCIES = ["ld2450"]
|
||||
|
||||
CONF_MOVING_TARGET_COUNT = "moving_target_count"
|
||||
CONF_STILL_TARGET_COUNT = "still_target_count"
|
||||
CONF_TARGET_COUNT = "target_count"
|
||||
CONF_X = "x"
|
||||
CONF_Y = "y"
|
||||
|
||||
ICON_ACCOUNT_GROUP = "mdi:account-group"
|
||||
ICON_ACCOUNT_SWITCH = "mdi:account-switch"
|
||||
ICON_ALPHA_X_BOX_OUTLINE = "mdi:alpha-x-box-outline"
|
||||
ICON_ALPHA_Y_BOX_OUTLINE = "mdi:alpha-y-box-outline"
|
||||
ICON_FORMAT_TEXT_ROTATION_ANGLE_UP = "mdi:format-text-rotation-angle-up"
|
||||
ICON_HUMAN_GREETING_PROXIMITY = "mdi:human-greeting-proximity"
|
||||
ICON_MAP_MARKER_ACCOUNT = "mdi:map-marker-account"
|
||||
ICON_MAP_MARKER_DISTANCE = "mdi:map-marker-distance"
|
||||
ICON_RELATION_ZERO_OR_ONE_TO_ZERO_OR_ONE = "mdi:relation-zero-or-one-to-zero-or-one"
|
||||
ICON_SPEEDOMETER_SLOW = "mdi:speedometer-slow"
|
||||
|
||||
MAX_TARGETS = 3
|
||||
MAX_ZONES = 3
|
||||
|
||||
UNIT_MILLIMETER_PER_SECOND = "mm/s"
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||
cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema(
|
||||
icon=ICON_ACCOUNT_GROUP,
|
||||
),
|
||||
cv.Optional(CONF_STILL_TARGET_COUNT): sensor.sensor_schema(
|
||||
icon=ICON_HUMAN_GREETING_PROXIMITY,
|
||||
),
|
||||
cv.Optional(CONF_MOVING_TARGET_COUNT): sensor.sensor_schema(
|
||||
icon=ICON_ACCOUNT_SWITCH,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(f"target_{n + 1}"): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_X): sensor.sensor_schema(
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
icon=ICON_ALPHA_X_BOX_OUTLINE,
|
||||
),
|
||||
cv.Optional(CONF_Y): sensor.sensor_schema(
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
icon=ICON_ALPHA_Y_BOX_OUTLINE,
|
||||
),
|
||||
cv.Optional(CONF_SPEED): sensor.sensor_schema(
|
||||
device_class=DEVICE_CLASS_SPEED,
|
||||
unit_of_measurement=UNIT_MILLIMETER_PER_SECOND,
|
||||
icon=ICON_SPEEDOMETER_SLOW,
|
||||
),
|
||||
cv.Optional(CONF_ANGLE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREES,
|
||||
icon=ICON_FORMAT_TEXT_ROTATION_ANGLE_UP,
|
||||
),
|
||||
cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
icon=ICON_MAP_MARKER_DISTANCE,
|
||||
),
|
||||
cv.Optional(CONF_RESOLUTION): sensor.sensor_schema(
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
icon=ICON_RELATION_ZERO_OR_ONE_TO_ZERO_OR_ONE,
|
||||
),
|
||||
}
|
||||
)
|
||||
for n in range(MAX_TARGETS)
|
||||
},
|
||||
{
|
||||
cv.Optional(f"zone_{n + 1}"): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema(
|
||||
icon=ICON_MAP_MARKER_ACCOUNT,
|
||||
),
|
||||
cv.Optional(CONF_STILL_TARGET_COUNT): sensor.sensor_schema(
|
||||
icon=ICON_MAP_MARKER_ACCOUNT,
|
||||
),
|
||||
cv.Optional(CONF_MOVING_TARGET_COUNT): sensor.sensor_schema(
|
||||
icon=ICON_MAP_MARKER_ACCOUNT,
|
||||
),
|
||||
}
|
||||
)
|
||||
for n in range(MAX_ZONES)
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
ld2450_component = await cg.get_variable(config[CONF_LD2450_ID])
|
||||
|
||||
if target_count_config := config.get(CONF_TARGET_COUNT):
|
||||
sens = await sensor.new_sensor(target_count_config)
|
||||
cg.add(ld2450_component.set_target_count_sensor(sens))
|
||||
|
||||
if still_target_count_config := config.get(CONF_STILL_TARGET_COUNT):
|
||||
sens = await sensor.new_sensor(still_target_count_config)
|
||||
cg.add(ld2450_component.set_still_target_count_sensor(sens))
|
||||
|
||||
if moving_target_count_config := config.get(CONF_MOVING_TARGET_COUNT):
|
||||
sens = await sensor.new_sensor(moving_target_count_config)
|
||||
cg.add(ld2450_component.set_moving_target_count_sensor(sens))
|
||||
for n in range(MAX_TARGETS):
|
||||
if target_conf := config.get(f"target_{n + 1}"):
|
||||
if x_config := target_conf.get(CONF_X):
|
||||
sens = await sensor.new_sensor(x_config)
|
||||
cg.add(ld2450_component.set_move_x_sensor(n, sens))
|
||||
if y_config := target_conf.get(CONF_Y):
|
||||
sens = await sensor.new_sensor(y_config)
|
||||
cg.add(ld2450_component.set_move_y_sensor(n, sens))
|
||||
if speed_config := target_conf.get(CONF_SPEED):
|
||||
sens = await sensor.new_sensor(speed_config)
|
||||
cg.add(ld2450_component.set_move_speed_sensor(n, sens))
|
||||
if angle_config := target_conf.get(CONF_ANGLE):
|
||||
sens = await sensor.new_sensor(angle_config)
|
||||
cg.add(ld2450_component.set_move_angle_sensor(n, sens))
|
||||
if distance_config := target_conf.get(CONF_DISTANCE):
|
||||
sens = await sensor.new_sensor(distance_config)
|
||||
cg.add(ld2450_component.set_move_distance_sensor(n, sens))
|
||||
if resolution_config := target_conf.get(CONF_RESOLUTION):
|
||||
sens = await sensor.new_sensor(resolution_config)
|
||||
cg.add(ld2450_component.set_move_resolution_sensor(n, sens))
|
||||
for n in range(MAX_ZONES):
|
||||
if zone_config := config.get(f"zone_{n + 1}"):
|
||||
if target_count_config := zone_config.get(CONF_TARGET_COUNT):
|
||||
sens = await sensor.new_sensor(target_count_config)
|
||||
cg.add(ld2450_component.set_zone_target_count_sensor(n, sens))
|
||||
if still_target_count_config := zone_config.get(CONF_STILL_TARGET_COUNT):
|
||||
sens = await sensor.new_sensor(still_target_count_config)
|
||||
cg.add(ld2450_component.set_zone_still_target_count_sensor(n, sens))
|
||||
if moving_target_count_config := zone_config.get(CONF_MOVING_TARGET_COUNT):
|
||||
sens = await sensor.new_sensor(moving_target_count_config)
|
||||
cg.add(ld2450_component.set_zone_moving_target_count_sensor(n, sens))
|
45
esphome/components/ld2450/switch/__init__.py
Normal file
45
esphome/components/ld2450/switch/__init__.py
Normal file
@ -0,0 +1,45 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import switch
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
DEVICE_CLASS_SWITCH,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ICON_BLUETOOTH,
|
||||
ICON_PULSE,
|
||||
)
|
||||
|
||||
from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
|
||||
|
||||
BluetoothSwitch = ld2450_ns.class_("BluetoothSwitch", switch.Switch)
|
||||
MultiTargetSwitch = ld2450_ns.class_("MultiTargetSwitch", switch.Switch)
|
||||
|
||||
CONF_BLUETOOTH = "bluetooth"
|
||||
CONF_MULTI_TARGET = "multi_target"
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||
cv.Optional(CONF_BLUETOOTH): switch.switch_schema(
|
||||
BluetoothSwitch,
|
||||
device_class=DEVICE_CLASS_SWITCH,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_BLUETOOTH,
|
||||
),
|
||||
cv.Optional(CONF_MULTI_TARGET): switch.switch_schema(
|
||||
MultiTargetSwitch,
|
||||
device_class=DEVICE_CLASS_SWITCH,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_PULSE,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
ld2450_component = await cg.get_variable(config[CONF_LD2450_ID])
|
||||
if bluetooth_config := config.get(CONF_BLUETOOTH):
|
||||
s = await switch.new_switch(bluetooth_config)
|
||||
await cg.register_parented(s, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_bluetooth_switch(s))
|
||||
if multi_target_config := config.get(CONF_MULTI_TARGET):
|
||||
s = await switch.new_switch(multi_target_config)
|
||||
await cg.register_parented(s, config[CONF_LD2450_ID])
|
||||
cg.add(ld2450_component.set_multi_target_switch(s))
|
12
esphome/components/ld2450/switch/bluetooth_switch.cpp
Normal file
12
esphome/components/ld2450/switch/bluetooth_switch.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "bluetooth_switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
void BluetoothSwitch::write_state(bool state) {
|
||||
this->publish_state(state);
|
||||
this->parent_->set_bluetooth(state);
|
||||
}
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
18
esphome/components/ld2450/switch/bluetooth_switch.h
Normal file
18
esphome/components/ld2450/switch/bluetooth_switch.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
class BluetoothSwitch : public switch_::Switch, public Parented<LD2450Component> {
|
||||
public:
|
||||
BluetoothSwitch() = default;
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
};
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
12
esphome/components/ld2450/switch/multi_target_switch.cpp
Normal file
12
esphome/components/ld2450/switch/multi_target_switch.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "multi_target_switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
void MultiTargetSwitch::write_state(bool state) {
|
||||
this->publish_state(state);
|
||||
this->parent_->set_multi_target(state);
|
||||
}
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
18
esphome/components/ld2450/switch/multi_target_switch.h
Normal file
18
esphome/components/ld2450/switch/multi_target_switch.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2450 {
|
||||
|
||||
class MultiTargetSwitch : public switch_::Switch, public Parented<LD2450Component> {
|
||||
public:
|
||||
MultiTargetSwitch() = default;
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
};
|
||||
|
||||
} // namespace ld2450
|
||||
} // namespace esphome
|
62
esphome/components/ld2450/text_sensor.py
Normal file
62
esphome/components/ld2450/text_sensor.py
Normal file
@ -0,0 +1,62 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_DIRECTION,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_VERSION,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ENTITY_CATEGORY_NONE,
|
||||
ICON_BLUETOOTH,
|
||||
ICON_CHIP,
|
||||
ICON_SIGN_DIRECTION,
|
||||
)
|
||||
|
||||
from . import CONF_LD2450_ID, LD2450Component
|
||||
|
||||
DEPENDENCIES = ["ld2450"]
|
||||
|
||||
MAX_TARGETS = 3
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||
cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_CHIP,
|
||||
),
|
||||
cv.Optional(CONF_MAC_ADDRESS): text_sensor.text_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_BLUETOOTH,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(f"target_{n + 1}"): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_DIRECTION): text_sensor.text_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_NONE,
|
||||
icon=ICON_SIGN_DIRECTION,
|
||||
),
|
||||
}
|
||||
)
|
||||
for n in range(MAX_TARGETS)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
ld2450_component = await cg.get_variable(config[CONF_LD2450_ID])
|
||||
if version_config := config.get(CONF_VERSION):
|
||||
sens = await text_sensor.new_text_sensor(version_config)
|
||||
cg.add(ld2450_component.set_version_text_sensor(sens))
|
||||
if mac_address_config := config.get(CONF_MAC_ADDRESS):
|
||||
sens = await text_sensor.new_text_sensor(mac_address_config)
|
||||
cg.add(ld2450_component.set_mac_text_sensor(sens))
|
||||
for n in range(MAX_TARGETS):
|
||||
if direction_conf := config.get(f"target_{n + 1}"):
|
||||
if direction_config := direction_conf.get(CONF_DIRECTION):
|
||||
sens = await text_sensor.new_text_sensor(direction_config)
|
||||
cg.add(ld2450_component.set_direction_text_sensor(n, sens))
|
@ -36,6 +36,7 @@ from esphome.const import (
|
||||
CONF_PAYLOAD_AVAILABLE,
|
||||
CONF_PAYLOAD_NOT_AVAILABLE,
|
||||
CONF_PORT,
|
||||
CONF_PUBLISH_NAN_AS_NONE,
|
||||
CONF_QOS,
|
||||
CONF_REBOOT_TIMEOUT,
|
||||
CONF_RETAIN,
|
||||
@ -49,7 +50,6 @@ from esphome.const import (
|
||||
CONF_USE_ABBREVIATIONS,
|
||||
CONF_USERNAME,
|
||||
CONF_WILL_MESSAGE,
|
||||
CONF_PUBLISH_NAN_AS_NONE,
|
||||
PLATFORM_BK72XX,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
@ -406,7 +406,7 @@ async def to_code(config):
|
||||
if CONF_SSL_FINGERPRINTS in config:
|
||||
for fingerprint in config[CONF_SSL_FINGERPRINTS]:
|
||||
arr = [
|
||||
cg.RawExpression(f"0x{fingerprint[i:i + 2]}") for i in range(0, 40, 2)
|
||||
cg.RawExpression(f"0x{fingerprint[i : i + 2]}") for i in range(0, 40, 2)
|
||||
]
|
||||
cg.add(var.add_ssl_fingerprint(arr))
|
||||
cg.add_build_flag("-DASYNC_TCP_SSL_ENABLED=1")
|
||||
|
@ -1,9 +1,10 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_UID
|
||||
from esphome.core import HexInt
|
||||
from .. import nfc_ns, Nfcc, NfcTagListener
|
||||
|
||||
from .. import Nfcc, NfcTagListener, nfc_ns
|
||||
|
||||
DEPENDENCIES = ["nfc"]
|
||||
|
||||
@ -25,8 +26,7 @@ def validate_uid(value):
|
||||
for x in value.split("-"):
|
||||
if len(x) != 2:
|
||||
raise cv.Invalid(
|
||||
"Each part (separated by '-') of the UID must be two characters "
|
||||
"long."
|
||||
"Each part (separated by '-') of the UID must be two characters long."
|
||||
)
|
||||
try:
|
||||
x = int(x, 16)
|
||||
|
@ -1,8 +1,9 @@
|
||||
from typing import Any
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from .. import const, schema, validate, generate
|
||||
import esphome.config_validation as cv
|
||||
|
||||
from .. import const, generate, schema, validate
|
||||
|
||||
DEPENDENCIES = [const.OPENTHERM]
|
||||
COMPONENT_TYPE = const.BINARY_SENSOR
|
||||
@ -11,8 +12,7 @@ COMPONENT_TYPE = const.BINARY_SENSOR
|
||||
def get_entity_validation_schema(entity: schema.BinarySensorSchema) -> cv.Schema:
|
||||
return binary_sensor.binary_sensor_schema(
|
||||
device_class=(
|
||||
entity.device_class
|
||||
or binary_sensor._UNDEF # pylint: disable=protected-access
|
||||
entity.device_class or binary_sensor._UNDEF # pylint: disable=protected-access
|
||||
),
|
||||
icon=(entity.icon or binary_sensor._UNDEF), # pylint: disable=protected-access
|
||||
)
|
||||
|
@ -3,8 +3,9 @@ from typing import Any, Callable, Optional
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
from . import const
|
||||
from .schema import TSchema, SettingSchema
|
||||
from .schema import SettingSchema, TSchema
|
||||
|
||||
opentherm_ns = cg.esphome_ns.namespace("opentherm")
|
||||
OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component)
|
||||
@ -112,11 +113,10 @@ def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]):
|
||||
msg_expr = cg.RawExpression(f"esphome::opentherm::MessageId::{msg}")
|
||||
if keep_updated:
|
||||
cg.add(hub.add_repeating_message(msg_expr))
|
||||
elif order is not None:
|
||||
cg.add(hub.add_initial_message(msg_expr, order))
|
||||
else:
|
||||
if order is not None:
|
||||
cg.add(hub.add_initial_message(msg_expr, order))
|
||||
else:
|
||||
cg.add(hub.add_initial_message(msg_expr))
|
||||
cg.add(hub.add_initial_message(msg_expr))
|
||||
|
||||
|
||||
def add_property_set(var: cg.MockObj, config_key: str, config: dict[str, Any]) -> None:
|
||||
@ -128,7 +128,7 @@ Create = Callable[[dict[str, Any], str, cg.MockObj], Awaitable[cg.Pvariable]]
|
||||
|
||||
|
||||
def create_only_conf(
|
||||
create: Callable[[dict[str, Any]], Awaitable[cg.Pvariable]]
|
||||
create: Callable[[dict[str, Any]], Awaitable[cg.Pvariable]],
|
||||
) -> Create:
|
||||
return lambda conf, _key, _hub: create(conf)
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
from typing import Any
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from .. import const, schema, validate, generate
|
||||
import esphome.config_validation as cv
|
||||
|
||||
from .. import const, generate, schema, validate
|
||||
|
||||
DEPENDENCIES = [const.OPENTHERM]
|
||||
COMPONENT_TYPE = const.SENSOR
|
||||
@ -22,11 +23,9 @@ MSG_DATA_TYPES = {
|
||||
|
||||
def get_entity_validation_schema(entity: schema.SensorSchema) -> cv.Schema:
|
||||
return sensor.sensor_schema(
|
||||
unit_of_measurement=entity.unit_of_measurement
|
||||
or sensor._UNDEF, # pylint: disable=protected-access
|
||||
unit_of_measurement=entity.unit_of_measurement or sensor._UNDEF, # pylint: disable=protected-access
|
||||
accuracy_decimals=entity.accuracy_decimals,
|
||||
device_class=entity.device_class
|
||||
or sensor._UNDEF, # pylint: disable=protected-access
|
||||
device_class=entity.device_class or sensor._UNDEF, # pylint: disable=protected-access
|
||||
icon=entity.icon or sensor._UNDEF, # pylint: disable=protected-access
|
||||
state_class=entity.state_class,
|
||||
).extend(
|
||||
|
@ -1,9 +1,10 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_UID
|
||||
from esphome.core import HexInt
|
||||
from . import pn532_ns, PN532, CONF_PN532_ID
|
||||
|
||||
from . import CONF_PN532_ID, PN532, pn532_ns
|
||||
|
||||
DEPENDENCIES = ["pn532"]
|
||||
|
||||
@ -13,8 +14,7 @@ def validate_uid(value):
|
||||
for x in value.split("-"):
|
||||
if len(x) != 2:
|
||||
raise cv.Invalid(
|
||||
"Each part (separated by '-') of the UID must be two characters "
|
||||
"long."
|
||||
"Each part (separated by '-') of the UID must be two characters long."
|
||||
)
|
||||
try:
|
||||
x = int(x, 16)
|
||||
|
@ -1,9 +1,10 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_UID
|
||||
from esphome.core import HexInt
|
||||
from . import rc522_ns, RC522, CONF_RC522_ID
|
||||
|
||||
from . import CONF_RC522_ID, RC522, rc522_ns
|
||||
|
||||
DEPENDENCIES = ["rc522"]
|
||||
|
||||
@ -13,8 +14,7 @@ def validate_uid(value):
|
||||
for x in value.split("-"):
|
||||
if len(x) != 2:
|
||||
raise cv.Invalid(
|
||||
"Each part (separated by '-') of the UID must be two characters "
|
||||
"long."
|
||||
"Each part (separated by '-') of the UID must be two characters long."
|
||||
)
|
||||
try:
|
||||
x = int(x, 16)
|
||||
|
@ -36,7 +36,7 @@ template<typename... Ts> class TotoAction : public RemoteTransmitterActionBase<T
|
||||
data.rc_code_2 = this->rc_code_2_.value(x...);
|
||||
data.command = this->command_.value(x...);
|
||||
this->set_send_times(this->send_times_.value_or(x..., 3));
|
||||
this->set_send_wait(this->send_wait_.value_or(x..., 32000));
|
||||
this->set_send_wait(this->send_wait_.value_or(x..., 36000));
|
||||
TotoProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
@ -137,7 +137,7 @@ def validate_temperature_preset(preset, root_config, name, requirements):
|
||||
|
||||
|
||||
def generate_comparable_preset(config, name):
|
||||
comparable_preset = f"{CONF_PRESET}:\n" f" - {CONF_NAME}: {name}\n"
|
||||
comparable_preset = f"{CONF_PRESET}:\n - {CONF_NAME}: {name}\n"
|
||||
|
||||
if CONF_DEFAULT_TARGET_TEMPERATURE_LOW in config:
|
||||
comparable_preset += f" {CONF_DEFAULT_TARGET_TEMPERATURE_LOW}: {config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW]}\n"
|
||||
|
@ -1223,8 +1223,7 @@ def subscribe_topic(value):
|
||||
if index != len(value) - 1:
|
||||
# If there are multiple wildcards, this will also trigger
|
||||
raise Invalid(
|
||||
"Multi-level wildcard must be the last "
|
||||
"character in the topic filter."
|
||||
"Multi-level wildcard must be the last character in the topic filter."
|
||||
)
|
||||
if len(value) > 1 and value[index - 1] != "/":
|
||||
raise Invalid("Multi-level wildcard must be after a topic level separator.")
|
||||
|
@ -1,5 +1,4 @@
|
||||
import logging
|
||||
import multiprocessing
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
@ -94,10 +93,19 @@ def valid_project_name(value: str):
|
||||
return value
|
||||
|
||||
|
||||
def get_usable_cpu_count() -> int:
|
||||
"""Return the number of CPUs that can be used for processes.
|
||||
On Python 3.13+ this is the number of CPUs that can be used for processes.
|
||||
On older Python versions this is the number of CPUs.
|
||||
"""
|
||||
return (
|
||||
os.process_cpu_count() if hasattr(os, "process_cpu_count") else os.cpu_count()
|
||||
)
|
||||
|
||||
|
||||
if "ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT" in os.environ:
|
||||
_compile_process_limit_default = min(
|
||||
int(os.environ["ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT"]),
|
||||
multiprocessing.cpu_count(),
|
||||
int(os.environ["ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT"]), get_usable_cpu_count()
|
||||
)
|
||||
else:
|
||||
_compile_process_limit_default = cv.UNDEFINED
|
||||
@ -156,7 +164,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
),
|
||||
cv.Optional(
|
||||
CONF_COMPILE_PROCESS_LIMIT, default=_compile_process_limit_default
|
||||
): cv.int_range(min=1, max=multiprocessing.cpu_count()),
|
||||
): cv.int_range(min=1, max=get_usable_cpu_count()),
|
||||
}
|
||||
),
|
||||
validate_hostname,
|
||||
|
@ -506,9 +506,9 @@ def with_local_variable(id_: ID, rhs: SafeExpType, callback: Callable, *args) ->
|
||||
"""
|
||||
|
||||
# throw if the callback is async:
|
||||
assert not inspect.iscoroutinefunction(
|
||||
callback
|
||||
), "with_local_variable() callback cannot be async!"
|
||||
assert not inspect.iscoroutinefunction(callback), (
|
||||
"with_local_variable() callback cannot be async!"
|
||||
)
|
||||
|
||||
CORE.add(RawStatement("{")) # output opening curly brace
|
||||
obj = variable(id_, rhs, None, True)
|
||||
|
@ -144,17 +144,17 @@ def wizard_file(**kwargs):
|
||||
|
||||
# Configure API
|
||||
if "password" in kwargs:
|
||||
config += f" password: \"{kwargs['password']}\"\n"
|
||||
config += f' password: "{kwargs["password"]}"\n'
|
||||
if "api_encryption_key" in kwargs:
|
||||
config += f" encryption:\n key: \"{kwargs['api_encryption_key']}\"\n"
|
||||
config += f' encryption:\n key: "{kwargs["api_encryption_key"]}"\n'
|
||||
|
||||
# Configure OTA
|
||||
config += "\nota:\n"
|
||||
config += " - platform: esphome\n"
|
||||
if "ota_password" in kwargs:
|
||||
config += f" password: \"{kwargs['ota_password']}\""
|
||||
config += f' password: "{kwargs["ota_password"]}"'
|
||||
elif "password" in kwargs:
|
||||
config += f" password: \"{kwargs['password']}\""
|
||||
config += f' password: "{kwargs["password"]}"'
|
||||
|
||||
# Configuring wifi
|
||||
config += "\n\nwifi:\n"
|
||||
@ -181,18 +181,14 @@ def wizard_file(**kwargs):
|
||||
password: "{fallback_psk}"
|
||||
|
||||
captive_portal:
|
||||
""".format(
|
||||
**kwargs
|
||||
)
|
||||
""".format(**kwargs)
|
||||
else:
|
||||
config += """
|
||||
# Enable fallback hotspot in case wifi connection fails
|
||||
ap:
|
||||
ssid: "{fallback_name}"
|
||||
password: "{fallback_psk}"
|
||||
""".format(
|
||||
**kwargs
|
||||
)
|
||||
""".format(**kwargs)
|
||||
|
||||
return config
|
||||
|
||||
@ -388,19 +384,19 @@ def wizard(path):
|
||||
safe_print()
|
||||
# Don't sleep because user needs to copy link
|
||||
if platform == "ESP32":
|
||||
safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'nodemcu-32s')}\".")
|
||||
safe_print(f'For example "{color(Fore.BOLD_WHITE, "nodemcu-32s")}".')
|
||||
boards_list = esp32_boards.BOARDS.items()
|
||||
elif platform == "ESP8266":
|
||||
safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'nodemcuv2')}\".")
|
||||
safe_print(f'For example "{color(Fore.BOLD_WHITE, "nodemcuv2")}".')
|
||||
boards_list = esp8266_boards.BOARDS.items()
|
||||
elif platform == "BK72XX":
|
||||
safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'cb2s')}\".")
|
||||
safe_print(f'For example "{color(Fore.BOLD_WHITE, "cb2s")}".')
|
||||
boards_list = bk72xx_boards.BOARDS.items()
|
||||
elif platform == "RTL87XX":
|
||||
safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'wr3')}\".")
|
||||
safe_print(f'For example "{color(Fore.BOLD_WHITE, "wr3")}".')
|
||||
boards_list = rtl87xx_boards.BOARDS.items()
|
||||
elif platform == "RP2040":
|
||||
safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'rpipicow')}\".")
|
||||
safe_print(f'For example "{color(Fore.BOLD_WHITE, "rpipicow")}".')
|
||||
boards_list = rp2040_boards.BOARDS.items()
|
||||
|
||||
else:
|
||||
@ -439,7 +435,7 @@ def wizard(path):
|
||||
f"First, what's the {color(Fore.GREEN, 'SSID')} (the name) of the WiFi network {name} should connect to?"
|
||||
)
|
||||
sleep(1.5)
|
||||
safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'Abraham Linksys')}\".")
|
||||
safe_print(f'For example "{color(Fore.BOLD_WHITE, "Abraham Linksys")}".')
|
||||
while True:
|
||||
ssid = safe_input(color(Fore.BOLD_WHITE, "(ssid): "))
|
||||
try:
|
||||
@ -465,7 +461,7 @@ def wizard(path):
|
||||
f"Now please state the {color(Fore.GREEN, 'password')} of the WiFi network so that I can connect to it (Leave empty for no password)"
|
||||
)
|
||||
safe_print()
|
||||
safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'PASSWORD42')}\"")
|
||||
safe_print(f'For example "{color(Fore.BOLD_WHITE, "PASSWORD42")}"')
|
||||
sleep(0.5)
|
||||
psk = safe_input(color(Fore.BOLD_WHITE, "(PSK): "))
|
||||
safe_print(
|
||||
|
@ -212,9 +212,7 @@ def write_platformio_project():
|
||||
write_platformio_ini(content)
|
||||
|
||||
|
||||
DEFINES_H_FORMAT = (
|
||||
ESPHOME_H_FORMAT
|
||||
) = """\
|
||||
DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\
|
||||
#pragma once
|
||||
#include "esphome/core/macros.h"
|
||||
{}
|
||||
|
@ -14,7 +14,7 @@ esptool==4.7.0
|
||||
click==8.1.7
|
||||
esphome-dashboard==20250212.0
|
||||
aioesphomeapi==29.1.0
|
||||
zeroconf==0.144.3
|
||||
zeroconf==0.145.1
|
||||
puremagic==1.27
|
||||
ruamel.yaml==0.18.6 # dashboard_import
|
||||
esphome-glyphsets==0.1.0
|
||||
|
@ -1,6 +1,6 @@
|
||||
pylint==3.2.7
|
||||
flake8==7.0.0 # also change in .pre-commit-config.yaml when updating
|
||||
black==24.4.2 # also change in .pre-commit-config.yaml when updating
|
||||
ruff==0.9.2 # also change in .pre-commit-config.yaml when updating
|
||||
pyupgrade==3.15.2 # also change in .pre-commit-config.yaml when updating
|
||||
pre-commit
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import multiprocessing
|
||||
import os
|
||||
import queue
|
||||
import re
|
||||
@ -11,7 +10,13 @@ import threading
|
||||
|
||||
import click
|
||||
import colorama
|
||||
from helpers import filter_changed, get_binary, git_ls_files, print_error_for_file
|
||||
from helpers import (
|
||||
filter_changed,
|
||||
get_binary,
|
||||
get_usable_cpu_count,
|
||||
git_ls_files,
|
||||
print_error_for_file,
|
||||
)
|
||||
|
||||
|
||||
def run_format(executable, args, queue, lock, failed_files):
|
||||
@ -25,7 +30,9 @@ def run_format(executable, args, queue, lock, failed_files):
|
||||
invocation.extend(["--dry-run", "-Werror"])
|
||||
invocation.append(path)
|
||||
|
||||
proc = subprocess.run(invocation, capture_output=True, encoding="utf-8")
|
||||
proc = subprocess.run(
|
||||
invocation, capture_output=True, encoding="utf-8", check=False
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
with lock:
|
||||
print_error_for_file(path, proc.stderr)
|
||||
@ -45,7 +52,7 @@ def main():
|
||||
"-j",
|
||||
"--jobs",
|
||||
type=int,
|
||||
default=multiprocessing.cpu_count(),
|
||||
default=get_usable_cpu_count(),
|
||||
help="number of format instances to be run in parallel.",
|
||||
)
|
||||
parser.add_argument(
|
||||
@ -80,7 +87,8 @@ def main():
|
||||
lock = threading.Lock()
|
||||
for _ in range(args.jobs):
|
||||
t = threading.Thread(
|
||||
target=run_format, args=(executable, args, task_queue, lock, failed_files)
|
||||
target=run_format,
|
||||
args=(executable, args, task_queue, lock, failed_files),
|
||||
)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
@ -95,7 +103,7 @@ def main():
|
||||
# Wait for all threads to be done.
|
||||
task_queue.join()
|
||||
|
||||
except FileNotFoundError as ex:
|
||||
except FileNotFoundError:
|
||||
return 1
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
@ -103,7 +111,7 @@ def main():
|
||||
# Kill subprocesses (and ourselves!)
|
||||
# No simple, clean alternative appears to be available.
|
||||
os.kill(0, 9)
|
||||
return 2 # Will not execute.
|
||||
return 2 # Will not execute.
|
||||
|
||||
return len(failed_files)
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import multiprocessing
|
||||
import os
|
||||
import queue
|
||||
import re
|
||||
@ -19,6 +18,7 @@ from helpers import (
|
||||
filter_changed,
|
||||
filter_grep,
|
||||
get_binary,
|
||||
get_usable_cpu_count,
|
||||
git_ls_files,
|
||||
load_idedata,
|
||||
print_error_for_file,
|
||||
@ -170,7 +170,7 @@ def main():
|
||||
"-j",
|
||||
"--jobs",
|
||||
type=int,
|
||||
default=multiprocessing.cpu_count(),
|
||||
default=get_usable_cpu_count(),
|
||||
help="number of tidy instances to be run in parallel.",
|
||||
)
|
||||
parser.add_argument(
|
||||
|
@ -188,3 +188,14 @@ def get_binary(name: str, version: str) -> str:
|
||||
"""
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
def get_usable_cpu_count() -> int:
|
||||
"""Return the number of CPUs that can be used for processes.
|
||||
|
||||
On Python 3.13+ this is the number of CPUs that can be used for processes.
|
||||
On older Python versions this is the number of CPUs.
|
||||
"""
|
||||
return (
|
||||
os.process_cpu_count() if hasattr(os, "process_cpu_count") else os.cpu_count()
|
||||
)
|
||||
|
168
tests/components/ld2450/common.yaml
Normal file
168
tests/components/ld2450/common.yaml
Normal file
@ -0,0 +1,168 @@
|
||||
uart:
|
||||
- id: ld2450_uart
|
||||
tx_pin: ${tx_pin}
|
||||
rx_pin: ${rx_pin}
|
||||
baud_rate: 256000
|
||||
parity: NONE
|
||||
stop_bits: 1
|
||||
|
||||
ld2450:
|
||||
- id: ld2450_radar
|
||||
uart_id: ld2450_uart
|
||||
throttle: 1000ms
|
||||
|
||||
button:
|
||||
- platform: ld2450
|
||||
ld2450_id: ld2450_radar
|
||||
factory_reset:
|
||||
name: LD2450 Factory Reset
|
||||
entity_category: config
|
||||
restart:
|
||||
name: LD2450 Restart
|
||||
entity_category: config
|
||||
|
||||
sensor:
|
||||
- platform: ld2450
|
||||
ld2450_id: ld2450_radar
|
||||
target_count:
|
||||
name: Presence Target Count
|
||||
still_target_count:
|
||||
name: Still Target Count
|
||||
moving_target_count:
|
||||
name: Moving 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
|
||||
zone_1:
|
||||
target_count:
|
||||
name: Zone-1 All Target Count
|
||||
still_target_count:
|
||||
name: Zone-1 Still Target Count
|
||||
moving_target_count:
|
||||
name: Zone-1 Moving Target Count
|
||||
zone_2:
|
||||
target_count:
|
||||
name: Zone-2 All Target Count
|
||||
still_target_count:
|
||||
name: Zone-2 Still Target Count
|
||||
moving_target_count:
|
||||
name: Zone-2 Moving Target Count
|
||||
zone_3:
|
||||
target_count:
|
||||
name: Zone-3 All Target Count
|
||||
still_target_count:
|
||||
name: Zone-3 Still Target Count
|
||||
moving_target_count:
|
||||
name: Zone-3 Moving Target Count
|
||||
|
||||
binary_sensor:
|
||||
- platform: ld2450
|
||||
ld2450_id: ld2450_radar
|
||||
has_target:
|
||||
name: Presence
|
||||
has_moving_target:
|
||||
name: Moving Target
|
||||
has_still_target:
|
||||
name: Still Target
|
||||
|
||||
switch:
|
||||
- platform: ld2450
|
||||
ld2450_id: ld2450_radar
|
||||
bluetooth:
|
||||
name: Bluetooth
|
||||
multi_target:
|
||||
name: Multi Target Tracking
|
||||
|
||||
text_sensor:
|
||||
- platform: ld2450
|
||||
ld2450_id: ld2450_radar
|
||||
version:
|
||||
name: LD2450 Firmware
|
||||
mac_address:
|
||||
name: LD2450 BT MAC
|
||||
target_1:
|
||||
direction:
|
||||
name: Target-1 Direction
|
||||
target_2:
|
||||
direction:
|
||||
name: Target-2 Direction
|
||||
target_3:
|
||||
direction:
|
||||
name: Target-3 Direction
|
||||
|
||||
number:
|
||||
- platform: ld2450
|
||||
ld2450_id: ld2450_radar
|
||||
presence_timeout:
|
||||
name: Timeout
|
||||
zone_1:
|
||||
x1:
|
||||
name: Zone-1 X1
|
||||
y1:
|
||||
name: Zone-1 Y1
|
||||
x2:
|
||||
name: Zone-1 X2
|
||||
y2:
|
||||
name: Zone-1 Y2
|
||||
zone_2:
|
||||
x1:
|
||||
name: Zone-2 X1
|
||||
y1:
|
||||
name: Zone-2 Y1
|
||||
x2:
|
||||
name: Zone-2 X2
|
||||
y2:
|
||||
name: Zone-2 Y2
|
||||
zone_3:
|
||||
x1:
|
||||
name: Zone-3 X1
|
||||
y1:
|
||||
name: Zone-3 Y1
|
||||
x2:
|
||||
name: Zone-3 X2
|
||||
y2:
|
||||
name: Zone-3 Y2
|
||||
|
||||
select:
|
||||
- platform: ld2450
|
||||
ld2450_id: ld2450_radar
|
||||
baud_rate:
|
||||
name: Baud Rate
|
||||
zone_type:
|
||||
name: Zone Type
|
5
tests/components/ld2450/test.esp32-ard.yaml
Normal file
5
tests/components/ld2450/test.esp32-ard.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO17
|
||||
rx_pin: GPIO16
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/ld2450/test.esp32-c3-ard.yaml
Normal file
5
tests/components/ld2450/test.esp32-c3-ard.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/ld2450/test.esp32-c3-idf.yaml
Normal file
5
tests/components/ld2450/test.esp32-c3-idf.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/ld2450/test.esp32-idf.yaml
Normal file
5
tests/components/ld2450/test.esp32-idf.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO17
|
||||
rx_pin: GPIO16
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/ld2450/test.esp8266-ard.yaml
Normal file
5
tests/components/ld2450/test.esp8266-ard.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/ld2450/test.rp2040-ard.yaml
Normal file
5
tests/components/ld2450/test.rp2040-ard.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
@ -1,11 +1,9 @@
|
||||
from collections.abc import Iterator
|
||||
|
||||
import math
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome import cpp_generator as cg
|
||||
from esphome import cpp_types as ct
|
||||
from esphome import cpp_generator as cg, cpp_types as ct
|
||||
|
||||
|
||||
class TestExpressions:
|
||||
@ -156,10 +154,7 @@ class TestLambdaExpression:
|
||||
actual = str(target)
|
||||
|
||||
assert actual == (
|
||||
"[=](int32_t foo, float bar) {\n"
|
||||
" if ((foo == 5) && (bar < 10))) {\n"
|
||||
" }\n"
|
||||
"}"
|
||||
"[=](int32_t foo, float bar) {\n if ((foo == 5) && (bar < 10))) {\n }\n}"
|
||||
)
|
||||
|
||||
def test_str__with_return(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user