mirror of
https://github.com/esphome/esphome.git
synced 2026-02-28 18:04:19 -07:00
Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
@@ -6,12 +6,12 @@
|
||||
namespace esphome {
|
||||
namespace gp8403 {
|
||||
|
||||
enum GP8403Voltage {
|
||||
enum GP8403Voltage : uint8_t {
|
||||
GP8403_VOLTAGE_5V = 0x00,
|
||||
GP8403_VOLTAGE_10V = 0x11,
|
||||
};
|
||||
|
||||
enum GP8403Model {
|
||||
enum GP8403Model : uint8_t {
|
||||
GP8403,
|
||||
GP8413,
|
||||
};
|
||||
|
||||
@@ -560,8 +560,6 @@ void LD2420Component::read_batch_(std::span<uint8_t, MAX_LINE_LENGTH> buffer) {
|
||||
void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
|
||||
this->cmd_reply_.command = buffer[CMD_FRAME_COMMAND];
|
||||
this->cmd_reply_.length = buffer[CMD_FRAME_DATA_LENGTH];
|
||||
uint8_t reg_element = 0;
|
||||
uint8_t data_element = 0;
|
||||
uint16_t data_pos = 0;
|
||||
if (this->cmd_reply_.length > CMD_MAX_BYTES) {
|
||||
ESP_LOGW(TAG, "Reply frame too long");
|
||||
@@ -583,43 +581,44 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
|
||||
case (CMD_DISABLE_CONF):
|
||||
ESP_LOGV(TAG, "Set config disable: CMD = %2X %s", CMD_DISABLE_CONF, result);
|
||||
break;
|
||||
case (CMD_READ_REGISTER):
|
||||
case (CMD_READ_REGISTER): {
|
||||
ESP_LOGV(TAG, "Read register: CMD = %2X %s", CMD_READ_REGISTER, result);
|
||||
// TODO Read/Write register is not implemented yet, this will get flushed out to a proper header file
|
||||
data_pos = 0x0A;
|
||||
for (uint16_t index = 0; index < (CMD_REG_DATA_REPLY_SIZE * // NOLINT
|
||||
((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_REG_DATA_REPLY_SIZE));
|
||||
index += CMD_REG_DATA_REPLY_SIZE) {
|
||||
memcpy(&this->cmd_reply_.data[reg_element], &buffer[data_pos + index], CMD_REG_DATA_REPLY_SIZE);
|
||||
byteswap(this->cmd_reply_.data[reg_element]);
|
||||
reg_element++;
|
||||
uint16_t reg_count = std::min<uint16_t>((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_REG_DATA_REPLY_SIZE,
|
||||
sizeof(this->cmd_reply_.data) / sizeof(this->cmd_reply_.data[0]));
|
||||
for (uint16_t i = 0; i < reg_count; i++) {
|
||||
memcpy(&this->cmd_reply_.data[i], &buffer[data_pos + i * CMD_REG_DATA_REPLY_SIZE], CMD_REG_DATA_REPLY_SIZE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (CMD_WRITE_REGISTER):
|
||||
ESP_LOGV(TAG, "Write register: CMD = %2X %s", CMD_WRITE_REGISTER, result);
|
||||
break;
|
||||
case (CMD_WRITE_ABD_PARAM):
|
||||
ESP_LOGV(TAG, "Write gate parameter(s): %2X %s", CMD_WRITE_ABD_PARAM, result);
|
||||
break;
|
||||
case (CMD_READ_ABD_PARAM):
|
||||
case (CMD_READ_ABD_PARAM): {
|
||||
ESP_LOGV(TAG, "Read gate parameter(s): %2X %s", CMD_READ_ABD_PARAM, result);
|
||||
data_pos = CMD_ABD_DATA_REPLY_START;
|
||||
for (uint16_t index = 0; index < (CMD_ABD_DATA_REPLY_SIZE * // NOLINT
|
||||
((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_ABD_DATA_REPLY_SIZE));
|
||||
index += CMD_ABD_DATA_REPLY_SIZE) {
|
||||
memcpy(&this->cmd_reply_.data[data_element], &buffer[data_pos + index],
|
||||
sizeof(this->cmd_reply_.data[data_element]));
|
||||
byteswap(this->cmd_reply_.data[data_element]);
|
||||
data_element++;
|
||||
uint16_t abd_count = std::min<uint16_t>((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_ABD_DATA_REPLY_SIZE,
|
||||
sizeof(this->cmd_reply_.data) / sizeof(this->cmd_reply_.data[0]));
|
||||
for (uint16_t i = 0; i < abd_count; i++) {
|
||||
memcpy(&this->cmd_reply_.data[i], &buffer[data_pos + i * CMD_ABD_DATA_REPLY_SIZE],
|
||||
sizeof(this->cmd_reply_.data[i]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (CMD_WRITE_SYS_PARAM):
|
||||
ESP_LOGV(TAG, "Set system parameter(s): %2X %s", CMD_WRITE_SYS_PARAM, result);
|
||||
break;
|
||||
case (CMD_READ_VERSION):
|
||||
memcpy(this->firmware_ver_, &buffer[12], buffer[10]);
|
||||
ESP_LOGV(TAG, "Firmware version: %7s %s", this->firmware_ver_, result);
|
||||
case (CMD_READ_VERSION): {
|
||||
uint8_t ver_len = std::min<uint8_t>(buffer[10], sizeof(this->firmware_ver_) - 1);
|
||||
memcpy(this->firmware_ver_, &buffer[12], ver_len);
|
||||
this->firmware_ver_[ver_len] = '\0';
|
||||
ESP_LOGV(TAG, "Firmware version: %s %s", this->firmware_ver_, result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -31,13 +31,12 @@ void LightWaveRF::read_tx() {
|
||||
void LightWaveRF::send_rx(const std::vector<uint8_t> &msg, uint8_t repeats, bool inverted, int u_sec) {
|
||||
this->lwtx_.lwtx_setup(pin_tx_, repeats, inverted, u_sec);
|
||||
|
||||
uint32_t timeout = 0;
|
||||
uint32_t timeout = millis();
|
||||
if (this->lwtx_.lwtx_free()) {
|
||||
this->lwtx_.lwtx_send(msg);
|
||||
timeout = millis();
|
||||
ESP_LOGD(TAG, "[%i] msg start", timeout);
|
||||
}
|
||||
while (!this->lwtx_.lwtx_free() && millis() < (timeout + 1000)) {
|
||||
while (!this->lwtx_.lwtx_free() && millis() - timeout < 1000) {
|
||||
delay(10);
|
||||
}
|
||||
timeout = millis() - timeout;
|
||||
|
||||
@@ -133,8 +133,8 @@ uint8_t MCP2515::get_status_() {
|
||||
canbus::Error MCP2515::set_mode_(const CanctrlReqopMode mode) {
|
||||
modify_register_(MCP_CANCTRL, CANCTRL_REQOP, mode);
|
||||
|
||||
uint32_t end_time = millis() + 10;
|
||||
while (millis() < end_time) {
|
||||
uint32_t start_time = millis();
|
||||
while (millis() - start_time < 10) {
|
||||
if ((read_register_(MCP_CANSTAT) & CANSTAT_OPMOD) == mode)
|
||||
return canbus::ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -308,13 +308,13 @@ void PN532::send_nack_() {
|
||||
enum PN532ReadReady PN532::read_ready_(bool block) {
|
||||
if (this->rd_ready_ == READY) {
|
||||
if (block) {
|
||||
this->rd_start_time_ = 0;
|
||||
this->rd_start_time_.reset();
|
||||
this->rd_ready_ = WOULDBLOCK;
|
||||
}
|
||||
return READY;
|
||||
}
|
||||
|
||||
if (!this->rd_start_time_) {
|
||||
if (!this->rd_start_time_.has_value()) {
|
||||
this->rd_start_time_ = millis();
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ enum PN532ReadReady PN532::read_ready_(bool block) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (millis() - this->rd_start_time_ > 100) {
|
||||
if (millis() - *this->rd_start_time_ > 100) {
|
||||
ESP_LOGV(TAG, "Timed out waiting for readiness from PN532!");
|
||||
this->rd_ready_ = TIMEOUT;
|
||||
break;
|
||||
@@ -340,7 +340,7 @@ enum PN532ReadReady PN532::read_ready_(bool block) {
|
||||
|
||||
auto rdy = this->rd_ready_;
|
||||
if (block || rdy == TIMEOUT) {
|
||||
this->rd_start_time_ = 0;
|
||||
this->rd_start_time_.reset();
|
||||
this->rd_ready_ = WOULDBLOCK;
|
||||
}
|
||||
return rdy;
|
||||
|
||||
@@ -99,7 +99,7 @@ class PN532 : public PollingComponent {
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontagremoved_;
|
||||
nfc::NfcTagUid current_uid_;
|
||||
nfc::NdefMessage *next_task_message_to_write_;
|
||||
uint32_t rd_start_time_{0};
|
||||
optional<uint32_t> rd_start_time_{};
|
||||
enum PN532ReadReady rd_ready_ { WOULDBLOCK };
|
||||
enum NfcTask {
|
||||
READ = 0,
|
||||
|
||||
@@ -608,7 +608,7 @@ DELTA_SCHEMA = cv.Any(
|
||||
def _get_delta(value):
|
||||
if isinstance(value, str):
|
||||
assert value.endswith("%")
|
||||
return 0.0, float(value[:-1])
|
||||
return 0.0, float(value[:-1]) / 100.0
|
||||
return value, 0.0
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace wled {
|
||||
// https://github.com/Aircoookie/WLED/wiki/UDP-Realtime-Control
|
||||
enum Protocol { WLED_NOTIFIER = 0, WARLS = 1, DRGB = 2, DRGBW = 3, DNRGB = 4 };
|
||||
|
||||
const int DEFAULT_BLANK_TIME = 1000;
|
||||
constexpr uint32_t DEFAULT_BLANK_TIME = 1000;
|
||||
|
||||
static const char *const TAG = "wled_light_effect";
|
||||
|
||||
@@ -34,9 +34,10 @@ void WLEDLightEffect::start() {
|
||||
AddressableLightEffect::start();
|
||||
|
||||
if (this->blank_on_start_) {
|
||||
this->blank_at_ = 0;
|
||||
this->blank_start_ = millis();
|
||||
this->blank_timeout_ = 0;
|
||||
} else {
|
||||
this->blank_at_ = UINT32_MAX;
|
||||
this->blank_start_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,10 +82,10 @@ void WLEDLightEffect::apply(light::AddressableLight &it, const Color ¤t_co
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Use roll-over safe arithmetic
|
||||
if (blank_at_ < millis()) {
|
||||
if (this->blank_start_.has_value() && millis() - *this->blank_start_ >= this->blank_timeout_) {
|
||||
blank_all_leds_(it);
|
||||
blank_at_ = millis() + DEFAULT_BLANK_TIME;
|
||||
this->blank_start_ = millis();
|
||||
this->blank_timeout_ = DEFAULT_BLANK_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,11 +143,13 @@ bool WLEDLightEffect::parse_frame_(light::AddressableLight &it, const uint8_t *p
|
||||
}
|
||||
|
||||
if (timeout == UINT8_MAX) {
|
||||
blank_at_ = UINT32_MAX;
|
||||
this->blank_start_.reset();
|
||||
} else if (timeout > 0) {
|
||||
blank_at_ = millis() + timeout * 1000;
|
||||
this->blank_start_ = millis();
|
||||
this->blank_timeout_ = timeout * 1000;
|
||||
} else {
|
||||
blank_at_ = millis() + DEFAULT_BLANK_TIME;
|
||||
this->blank_start_ = millis();
|
||||
this->blank_timeout_ = DEFAULT_BLANK_TIME;
|
||||
}
|
||||
|
||||
it.schedule_show();
|
||||
|
||||
@@ -35,7 +35,8 @@ class WLEDLightEffect : public light::AddressableLightEffect {
|
||||
|
||||
uint16_t port_{0};
|
||||
std::unique_ptr<UDP> udp_;
|
||||
uint32_t blank_at_{0};
|
||||
optional<uint32_t> blank_start_{};
|
||||
uint32_t blank_timeout_{0};
|
||||
uint32_t dropped_{0};
|
||||
uint8_t sync_group_mask_{0};
|
||||
bool blank_on_start_{true};
|
||||
|
||||
@@ -28,6 +28,11 @@ sensor:
|
||||
id: source_sensor_4
|
||||
accuracy_decimals: 1
|
||||
|
||||
- platform: template
|
||||
name: "Source Sensor 5"
|
||||
id: source_sensor_5
|
||||
accuracy_decimals: 1
|
||||
|
||||
- platform: copy
|
||||
source_id: source_sensor_1
|
||||
name: "Filter Min"
|
||||
@@ -69,6 +74,13 @@ sensor:
|
||||
filters:
|
||||
- delta: 0
|
||||
|
||||
- platform: copy
|
||||
source_id: source_sensor_5
|
||||
name: "Filter Percentage"
|
||||
id: filter_percentage
|
||||
filters:
|
||||
- delta: 50%
|
||||
|
||||
script:
|
||||
- id: test_filter_min
|
||||
then:
|
||||
@@ -154,6 +166,28 @@ script:
|
||||
id: source_sensor_4
|
||||
state: 2.0
|
||||
|
||||
- id: test_filter_percentage
|
||||
then:
|
||||
- sensor.template.publish:
|
||||
id: source_sensor_5
|
||||
state: 100.0
|
||||
- delay: 20ms
|
||||
- sensor.template.publish:
|
||||
id: source_sensor_5
|
||||
state: 120.0 # Filtered out (delta=20, need >50)
|
||||
- delay: 20ms
|
||||
- sensor.template.publish:
|
||||
id: source_sensor_5
|
||||
state: 160.0 # Passes (delta=60 > 50% of 100=50)
|
||||
- delay: 20ms
|
||||
- sensor.template.publish:
|
||||
id: source_sensor_5
|
||||
state: 200.0 # Filtered out (delta=40, need >50% of 160=80)
|
||||
- delay: 20ms
|
||||
- sensor.template.publish:
|
||||
id: source_sensor_5
|
||||
state: 250.0 # Passes (delta=90 > 80)
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: "Test Filter Min"
|
||||
@@ -178,3 +212,9 @@ button:
|
||||
id: btn_filter_zero_delta
|
||||
on_press:
|
||||
- script.execute: test_filter_zero_delta
|
||||
|
||||
- platform: template
|
||||
name: "Test Filter Percentage"
|
||||
id: btn_filter_percentage
|
||||
on_press:
|
||||
- script.execute: test_filter_percentage
|
||||
|
||||
@@ -24,12 +24,14 @@ async def test_sensor_filters_delta(
|
||||
"filter_max": [],
|
||||
"filter_baseline_max": [],
|
||||
"filter_zero_delta": [],
|
||||
"filter_percentage": [],
|
||||
}
|
||||
|
||||
filter_min_done = loop.create_future()
|
||||
filter_max_done = loop.create_future()
|
||||
filter_baseline_max_done = loop.create_future()
|
||||
filter_zero_delta_done = loop.create_future()
|
||||
filter_percentage_done = loop.create_future()
|
||||
|
||||
def on_state(state: EntityState) -> None:
|
||||
if not isinstance(state, SensorState) or state.missing_state:
|
||||
@@ -66,6 +68,12 @@ async def test_sensor_filters_delta(
|
||||
and not filter_zero_delta_done.done()
|
||||
):
|
||||
filter_zero_delta_done.set_result(True)
|
||||
elif (
|
||||
sensor_name == "filter_percentage"
|
||||
and len(sensor_values[sensor_name]) == 3
|
||||
and not filter_percentage_done.done()
|
||||
):
|
||||
filter_percentage_done.set_result(True)
|
||||
|
||||
async with (
|
||||
run_compiled(yaml_config),
|
||||
@@ -80,6 +88,7 @@ async def test_sensor_filters_delta(
|
||||
"filter_max": "Filter Max",
|
||||
"filter_baseline_max": "Filter Baseline Max",
|
||||
"filter_zero_delta": "Filter Zero Delta",
|
||||
"filter_percentage": "Filter Percentage",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -98,13 +107,14 @@ async def test_sensor_filters_delta(
|
||||
"Test Filter Max": "filter_max",
|
||||
"Test Filter Baseline Max": "filter_baseline_max",
|
||||
"Test Filter Zero Delta": "filter_zero_delta",
|
||||
"Test Filter Percentage": "filter_percentage",
|
||||
}
|
||||
buttons = {}
|
||||
for entity in entities:
|
||||
if isinstance(entity, ButtonInfo) and entity.name in button_name_map:
|
||||
buttons[button_name_map[entity.name]] = entity.key
|
||||
|
||||
assert len(buttons) == 4, f"Expected 3 buttons, found {len(buttons)}"
|
||||
assert len(buttons) == 5, f"Expected 5 buttons, found {len(buttons)}"
|
||||
|
||||
# Test 1: Min
|
||||
sensor_values["filter_min"].clear()
|
||||
@@ -161,3 +171,18 @@ async def test_sensor_filters_delta(
|
||||
assert sensor_values["filter_zero_delta"] == pytest.approx(expected), (
|
||||
f"Test 4 failed: expected {expected}, got {sensor_values['filter_zero_delta']}"
|
||||
)
|
||||
|
||||
# Test 5: Percentage (delta: 50%)
|
||||
sensor_values["filter_percentage"].clear()
|
||||
client.button_command(buttons["filter_percentage"])
|
||||
try:
|
||||
await asyncio.wait_for(filter_percentage_done, timeout=2.0)
|
||||
except TimeoutError:
|
||||
pytest.fail(
|
||||
f"Test 5 timed out. Values: {sensor_values['filter_percentage']}"
|
||||
)
|
||||
|
||||
expected = [100.0, 160.0, 250.0]
|
||||
assert sensor_values["filter_percentage"] == pytest.approx(expected), (
|
||||
f"Test 5 failed: expected {expected}, got {sensor_values['filter_percentage']}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user