mirror of
https://github.com/esphome/esphome.git
synced 2026-10-23 22:48:48 +00:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fd9e52f57 | ||
|
|
6db1f8fd67 | ||
|
|
fc79bf1ad2 | ||
|
|
5c77327e3d | ||
|
|
77aa518265 | ||
|
|
de9a7c05c0 | ||
|
|
02b4548ad8 | ||
|
|
3ce75e3fb8 | ||
|
|
7f3fa434ee | ||
|
|
64822c4f3a | ||
|
|
4605a0c4a2 | ||
|
|
f6aeca28e0 | ||
|
|
f09312dcad | ||
|
|
c8404a6493 | ||
|
|
27a4ebd606 |
No files matched your search
@@ -442,8 +442,6 @@ file does, and it is the authority when they disagree. The most useful starting
|
||||
uses `button.ApplyButton` (`switch.ApplySwitch`, `number.ApplyNumber`, `select.ApplySelect`) in its schema
|
||||
and `apply=` on the `sub_*` call. The body is a call on the hub with `entity` and `state`, `value` or
|
||||
`index`/`option` in scope; on a switch, number or select `publish="before"` or `"after"` publishes the state.
|
||||
`publish="momentary"` is only for legacy action switches (restart, factory reset): it publishes off and runs
|
||||
the body only when turned on. Never use it for a new entity; an action is a button.
|
||||
```python
|
||||
switches = switch.sub_switches(config, parent=hub)
|
||||
await switches(CONF_BLUETOOTH, None, apply="set_bluetooth(state)", publish="after")
|
||||
|
||||
@@ -436,7 +436,6 @@ esphome/components/modbus_controller/sensor/* @martgras
|
||||
esphome/components/modbus_controller/switch/* @martgras
|
||||
esphome/components/modbus_controller/text_sensor/* @martgras
|
||||
esphome/components/modbus_server/* @exciton
|
||||
esphome/components/modem/* @oarcher
|
||||
esphome/components/mopeka_ble/* @Fabian-Schmidt @spbrogan
|
||||
esphome/components/mopeka_pro_check/* @spbrogan
|
||||
esphome/components/mopeka_std_check/* @Fabian-Schmidt
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#if defined(USE_ESP32) && defined(USE_FAN)
|
||||
#ifdef USE_ESP32
|
||||
#include "ecocomfort2_fan.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
@@ -138,4 +138,4 @@ void Ecocomfort2Fan::on_status() {
|
||||
|
||||
} // namespace esphome::ecocomfort2
|
||||
|
||||
#endif // USE_ESP32 && USE_FAN
|
||||
#endif // USE_ESP32
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#if defined(USE_ESP32) && defined(USE_FAN)
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/fan/fan.h"
|
||||
@@ -23,4 +23,4 @@ class Ecocomfort2Fan : public fan::Fan, public Ecocomfort2Client, public Compone
|
||||
|
||||
} // namespace esphome::ecocomfort2
|
||||
|
||||
#endif // USE_ESP32 && USE_FAN
|
||||
#endif // USE_ESP32
|
||||
@@ -48,16 +48,12 @@ void EzoPMP::update() {
|
||||
}
|
||||
|
||||
if (this->is_first_read_) {
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_CALIBRATION_STATUS, 0, 0, (bool) this->calibration_status_);
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_MAX_FLOW_RATE, 0, 0, (bool) this->max_flow_rate_);
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_SINGLE_REPORT, 0, 0, (bool) this->current_volume_dosed_);
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_TOTAL_VOLUME_DOSED, 0, 0, (bool) this->total_volume_dosed_);
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_ABSOLUTE_TOTAL_VOLUME_DOSED, 0, 0,
|
||||
(bool) this->absolute_total_volume_dosed_);
|
||||
#endif
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_PAUSE_STATUS, 0, 0, true);
|
||||
this->is_first_read_ = false;
|
||||
}
|
||||
@@ -65,7 +61,6 @@ void EzoPMP::update() {
|
||||
if (!this->is_waiting_ && this->peek_next_command_() == EZO_PMP_COMMAND_NONE) {
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_DOSING, 0, 0, true);
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
if (this->is_dosing_flag_) {
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_SINGLE_REPORT, 0, 0, (bool) this->current_volume_dosed_);
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_TOTAL_VOLUME_DOSED, 0, 0, (bool) this->total_volume_dosed_);
|
||||
@@ -74,7 +69,6 @@ void EzoPMP::update() {
|
||||
}
|
||||
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_PUMP_VOLTAGE, 0, 0, (bool) this->pump_voltage_);
|
||||
#endif
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Not Scheduling new Command during update()");
|
||||
}
|
||||
@@ -198,68 +192,51 @@ void EzoPMP::read_command_result_() {
|
||||
if (parsed_third_parameter.has_value())
|
||||
this->is_dosing_flag_ = parsed_third_parameter.value_or(0) == 1;
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
if (this->is_dosing_)
|
||||
this->is_dosing_->publish_state(this->is_dosing_flag_);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
if (parsed_second_parameter.has_value() && this->last_volume_requested_) {
|
||||
this->last_volume_requested_->publish_state(parsed_second_parameter.value_or(0));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (!this->is_dosing_flag_ && !this->is_paused_flag_) {
|
||||
// If pump is not paused and not dispensing
|
||||
if (this->dosing_mode_ && this->dosing_mode_->state != DOSING_MODE_NONE)
|
||||
this->dosing_mode_->publish_state(DOSING_MODE_NONE);
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_READ_SINGLE_REPORT: // Single Report (page 53)
|
||||
#ifdef USE_SENSOR
|
||||
if (parsed_first_parameter.has_value() && (bool) this->current_volume_dosed_) {
|
||||
this->current_volume_dosed_->publish_state(parsed_first_parameter.value_or(0));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_READ_MAX_FLOW_RATE: // Constant Flow Rate (page 57)
|
||||
#ifdef USE_SENSOR
|
||||
if (parsed_second_parameter.has_value() && this->max_flow_rate_)
|
||||
this->max_flow_rate_->publish_state(parsed_second_parameter.value_or(0));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_READ_PAUSE_STATUS: // Pause (page 61)
|
||||
if (parsed_second_parameter.has_value())
|
||||
this->is_paused_flag_ = parsed_second_parameter.value_or(0) == 1;
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
if (this->is_paused_)
|
||||
this->is_paused_->publish_state(this->is_paused_flag_);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_READ_TOTAL_VOLUME_DOSED: // Total Volume Dispensed (page 64)
|
||||
#ifdef USE_SENSOR
|
||||
if (parsed_second_parameter.has_value() && this->total_volume_dosed_)
|
||||
this->total_volume_dosed_->publish_state(parsed_second_parameter.value_or(0));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_READ_ABSOLUTE_TOTAL_VOLUME_DOSED: // Total Volume Dispensed (page 64)
|
||||
#ifdef USE_SENSOR
|
||||
if (parsed_second_parameter.has_value() && this->absolute_total_volume_dosed_)
|
||||
this->absolute_total_volume_dosed_->publish_state(parsed_second_parameter.value_or(0));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_READ_CALIBRATION_STATUS: // Calibration (page 65)
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (parsed_second_parameter.has_value() && this->calibration_status_) {
|
||||
if (parsed_second_parameter.value_or(0) == 1) {
|
||||
this->calibration_status_->publish_state("Fixed Volume");
|
||||
@@ -271,56 +248,41 @@ void EzoPMP::read_command_result_() {
|
||||
this->calibration_status_->publish_state("Uncalibrated");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_READ_PUMP_VOLTAGE: // Pump Voltage (page 67)
|
||||
#ifdef USE_SENSOR
|
||||
if (parsed_second_parameter.has_value() && this->pump_voltage_)
|
||||
this->pump_voltage_->publish_state(parsed_second_parameter.value_or(0));
|
||||
#endif
|
||||
break;
|
||||
|
||||
// Non-Read Commands
|
||||
|
||||
case EZO_PMP_COMMAND_DOSE_VOLUME: // Volume Dispensing (page 55)
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->dosing_mode_ && this->dosing_mode_->state != DOSING_MODE_VOLUME)
|
||||
this->dosing_mode_->publish_state(DOSING_MODE_VOLUME);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_DOSE_VOLUME_OVER_TIME: // Dose over time (page 56)
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->dosing_mode_ && this->dosing_mode_->state != DOSING_MODE_VOLUME_OVER_TIME)
|
||||
this->dosing_mode_->publish_state(DOSING_MODE_VOLUME_OVER_TIME);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_DOSE_WITH_CONSTANT_FLOW_RATE: // Constant Flow Rate (page 57)
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->dosing_mode_ && this->dosing_mode_->state != DOSING_MODE_CONSTANT_FLOW_RATE)
|
||||
this->dosing_mode_->publish_state(DOSING_MODE_CONSTANT_FLOW_RATE);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_DOSE_CONTINUOUSLY: // Continuous Dispensing (page 54)
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->dosing_mode_ && this->dosing_mode_->state != DOSING_MODE_CONTINUOUS)
|
||||
this->dosing_mode_->publish_state(DOSING_MODE_CONTINUOUS);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_STOP_DOSING: // Stop (page 62)
|
||||
this->is_paused_flag_ = false;
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
if (this->is_paused_)
|
||||
this->is_paused_->publish_state(this->is_paused_flag_);
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->dosing_mode_ && this->dosing_mode_->state != DOSING_MODE_NONE)
|
||||
this->dosing_mode_->publish_state(DOSING_MODE_NONE);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case EZO_PMP_COMMAND_EXEC_ARBITRARY_COMMAND_ADDRESS:
|
||||
@@ -527,33 +489,25 @@ void EzoPMP::find() { this->queue_command_(EZO_PMP_COMMAND_FIND, 0, 0, true); }
|
||||
void EzoPMP::dose_continuously() {
|
||||
this->queue_command_(EZO_PMP_COMMAND_DOSE_CONTINUOUSLY, 0, 0, true);
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_DOSING, 0, 0, true);
|
||||
#ifdef USE_SENSOR
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_SINGLE_REPORT, 0, 0, (bool) this->current_volume_dosed_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EzoPMP::dose_volume(double volume) {
|
||||
this->queue_command_(EZO_PMP_COMMAND_DOSE_VOLUME, volume, 0, true);
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_DOSING, 0, 0, true);
|
||||
#ifdef USE_SENSOR
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_SINGLE_REPORT, 0, 0, (bool) this->current_volume_dosed_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EzoPMP::dose_volume_over_time(double volume, int duration) {
|
||||
this->queue_command_(EZO_PMP_COMMAND_DOSE_VOLUME_OVER_TIME, volume, duration, true);
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_DOSING, 0, 0, true);
|
||||
#ifdef USE_SENSOR
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_SINGLE_REPORT, 0, 0, (bool) this->current_volume_dosed_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EzoPMP::dose_with_constant_flow_rate(double volume, int duration) {
|
||||
this->queue_command_(EZO_PMP_COMMAND_DOSE_WITH_CONSTANT_FLOW_RATE, volume, duration, true);
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_DOSING, 0, 0, true);
|
||||
#ifdef USE_SENSOR
|
||||
this->queue_command_(EZO_PMP_COMMAND_READ_SINGLE_REPORT, 0, 0, (bool) this->current_volume_dosed_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EzoPMP::set_calibration_volume(double volume) {
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphome import automation, pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.const import CONF_RTS_PIN
|
||||
from esphome.components.esp32 import add_idf_component, add_idf_sdkconfig_option
|
||||
from esphome.components.network import add_use_address, require_ipv4
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_COMMAND,
|
||||
CONF_ENABLE_ON_BOOT,
|
||||
CONF_ID,
|
||||
CONF_MODEL,
|
||||
CONF_ON_CONNECT,
|
||||
CONF_ON_DISCONNECT,
|
||||
CONF_REBOOT_TIMEOUT,
|
||||
CONF_RX_BUFFER_SIZE,
|
||||
CONF_RX_PIN,
|
||||
CONF_TX_BUFFER_SIZE,
|
||||
CONF_TX_PIN,
|
||||
CONF_USE_ADDRESS,
|
||||
)
|
||||
from esphome.core import CORE, ID, coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.types import ConfigType, TemplateArgsType
|
||||
|
||||
# Static so `api:` finds network before its dependency check; a config-dependent
|
||||
# AUTO_LOAD resolves after that check and fails a cellular-only node
|
||||
AUTO_LOAD = ["network"]
|
||||
CODEOWNERS = ["@oarcher"]
|
||||
CONFLICTS_WITH = ["captive_portal", "ethernet"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
DOMAIN = "modem"
|
||||
|
||||
CONF_PIN_CODE = "pin_code"
|
||||
CONF_APN = "apn"
|
||||
CONF_CTS_PIN = "cts_pin"
|
||||
CONF_INIT_AT = "init_at"
|
||||
CONF_ON_SYNC = "on_sync"
|
||||
CONF_DTE_BUFFER_SIZE = "dte_buffer_size"
|
||||
CONF_MODEM_ID = "modem_id"
|
||||
|
||||
esp_modem_ns = cg.global_ns.namespace("esp_modem")
|
||||
|
||||
# Model name to the esp_modem factory that drives it
|
||||
MODEM_MODELS = {
|
||||
"BG96": esp_modem_ns.create_BG96_dce,
|
||||
"SIM800": esp_modem_ns.create_SIM800_dce,
|
||||
"SIM7000": esp_modem_ns.create_SIM7000_dce,
|
||||
"SIM7070": esp_modem_ns.create_SIM7070_dce,
|
||||
"SIM7080": esp_modem_ns.create_SIM7000_dce,
|
||||
"SIM7600": esp_modem_ns.create_SIM7600_dce,
|
||||
"SIM7670": esp_modem_ns.create_SIM7600_dce,
|
||||
"GENERIC": esp_modem_ns.create_generic_dce,
|
||||
}
|
||||
modem_ns = cg.esphome_ns.namespace("modem")
|
||||
ModemComponent = modem_ns.class_("ModemComponent", cg.Component)
|
||||
|
||||
ModemState = modem_ns.enum("ModemState", is_class=True)
|
||||
|
||||
|
||||
def _on_state(conf_key: str, state: str) -> automation.CallbackAutomation:
|
||||
return automation.CallbackAutomation(
|
||||
conf_key,
|
||||
"add_on_state_callback",
|
||||
params=[(ModemState, "state")],
|
||||
forward=[],
|
||||
when=f"state == modem::ModemState::{state}",
|
||||
)
|
||||
|
||||
|
||||
def _default_use_address(config: ConfigType) -> ConfigType:
|
||||
# The host tools resolve the device through this, like the other network components
|
||||
config.setdefault(CONF_USE_ADDRESS, f"{CORE.name}.local")
|
||||
return config
|
||||
|
||||
|
||||
_CALLBACK_AUTOMATIONS = (
|
||||
_on_state(CONF_ON_CONNECT, "MODEM_STATE_CONNECTED"),
|
||||
_on_state(CONF_ON_DISCONNECT, "MODEM_STATE_DISCONNECTING"),
|
||||
_on_state(CONF_ON_SYNC, "MODEM_STATE_SYNCED"),
|
||||
)
|
||||
|
||||
# AT_COMMAND_SIZE - 1 in modem_component.h; the buffer keeps one byte for the terminator
|
||||
MAX_AT_COMMAND_LENGTH = 127
|
||||
|
||||
|
||||
# A SIM PIN is 4 to 8 digits; anything else would spend one of the SIM's attempts. \Z, not $, so a
|
||||
# trailing newline is refused too
|
||||
_validate_pin_code = cv.All(
|
||||
cv.string_strict,
|
||||
vol.Match(r"^[0-9]{4,8}\Z", msg="pin_code must be 4 to 8 digits"),
|
||||
)
|
||||
# The IDF UART driver needs more than its 128 byte FIFO; the setters take uint16_t
|
||||
_UART_BUFFER_SIZE = cv.int_range(min=129, max=65535)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ModemComponent),
|
||||
cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Required(CONF_RX_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_RTS_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(CONF_CTS_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_BAUD_RATE): cv.positive_int,
|
||||
cv.Required(CONF_MODEL): cv.one_of(*MODEM_MODELS, upper=True),
|
||||
cv.Required(CONF_APN): cv.string,
|
||||
cv.Optional(CONF_PIN_CODE): cv.sensitive(_validate_pin_code),
|
||||
cv.Optional(CONF_USE_ADDRESS): cv.string_strict,
|
||||
# The command count is stored in a byte
|
||||
cv.Optional(CONF_INIT_AT): cv.All(
|
||||
cv.ensure_list(
|
||||
cv.All(cv.string, cv.ByteLength(max=MAX_AT_COMMAND_LENGTH))
|
||||
),
|
||||
cv.Length(max=255),
|
||||
),
|
||||
cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
|
||||
cv.Optional(CONF_TX_BUFFER_SIZE, default=1024): _UART_BUFFER_SIZE,
|
||||
cv.Optional(CONF_RX_BUFFER_SIZE, default=1024): _UART_BUFFER_SIZE,
|
||||
# CMUX keeps 128 bytes of the DTE buffer back, so a smaller buffer could not be read
|
||||
cv.Optional(CONF_DTE_BUFFER_SIZE, default=1024): cv.int_range(
|
||||
min=256, max=65535
|
||||
),
|
||||
cv.Optional(
|
||||
CONF_REBOOT_TIMEOUT, default="10min"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
**{
|
||||
cv.Optional(a.conf_key): automation.validate_automation({})
|
||||
for a in _CALLBACK_AUTOMATIONS
|
||||
},
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.has_none_or_all_keys(CONF_RTS_PIN, CONF_CTS_PIN),
|
||||
_default_use_address,
|
||||
require_ipv4(DOMAIN),
|
||||
)
|
||||
|
||||
|
||||
MODEM_ID_SCHEMA = automation.maybe_simple_id(
|
||||
{cv.GenerateID(): cv.use_id(ModemComponent)}
|
||||
)
|
||||
SEND_AT_SCHEMA = cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(ModemComponent),
|
||||
cv.Required(CONF_COMMAND): cv.templatable(
|
||||
cv.All(cv.string, cv.ByteLength(max=MAX_AT_COMMAND_LENGTH))
|
||||
),
|
||||
},
|
||||
key=CONF_COMMAND,
|
||||
)
|
||||
|
||||
for _name in ("enable", "disable"):
|
||||
automation.register_apply_action(
|
||||
f"modem.{_name}", MODEM_ID_SCHEMA, automation.ApplyCall(f"{_name}()")
|
||||
)
|
||||
_send_at_builder = automation.apply_action_builder(
|
||||
"modem.send_at",
|
||||
SEND_AT_SCHEMA,
|
||||
# A constant renders as a literal and takes the const char * overload; a lambda returns std::string
|
||||
automation.ApplyField(CONF_COMMAND, "send_at", cg.std_string),
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"modem.send_at", automation.ApplyAction, SEND_AT_SCHEMA, synchronous=True
|
||||
)
|
||||
async def send_at_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
cg.add_define("USE_MODEM_SEND_AT")
|
||||
return await _send_at_builder(config, action_id, template_arg, args)
|
||||
|
||||
|
||||
automation.register_apply_condition(
|
||||
"modem.connected", MODEM_ID_SCHEMA, "is_connected()"
|
||||
)
|
||||
automation.register_apply_condition("modem.enabled", MODEM_ID_SCHEMA, "is_enabled()")
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.COMMUNICATION)
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
add_idf_component(name="espressif/esp_modem", ref="2.1.0")
|
||||
|
||||
add_idf_sdkconfig_option("CONFIG_LWIP_PPP_SUPPORT", True)
|
||||
# LCP echo probes notice a modem that went away without a word, which ends the PPP session
|
||||
add_idf_sdkconfig_option("CONFIG_LWIP_ENABLE_LCP_ECHO", True)
|
||||
add_idf_sdkconfig_option("CONFIG_ESP_MODEM_CMUX_DELAY_AFTER_DLCI_SETUP", 500)
|
||||
add_idf_sdkconfig_option(
|
||||
"CONFIG_ESP_MODEM_CMUX_USE_SHORT_PAYLOADS_ONLY", True
|
||||
) # Slower but more reliable
|
||||
|
||||
cg.add_define("USE_MODEM")
|
||||
|
||||
model = config[CONF_MODEL]
|
||||
factory = MODEM_MODELS[model]
|
||||
tx = await cg.gpio_pin_expression(config[CONF_TX_PIN])
|
||||
rx = await cg.gpio_pin_expression(config[CONF_RX_PIN])
|
||||
var = cg.new_Pvariable(config[CONF_ID], tx, rx, model, factory, config[CONF_APN])
|
||||
|
||||
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
||||
add_use_address(var, config[CONF_USE_ADDRESS])
|
||||
if (pin_code := config.get(CONF_PIN_CODE)) is not None:
|
||||
cg.add_define("USE_MODEM_PIN_CODE")
|
||||
cg.add(var.set_pin_code(pin_code))
|
||||
|
||||
if config[CONF_ENABLE_ON_BOOT]:
|
||||
cg.add(var.enable())
|
||||
|
||||
if init_at := config.get(CONF_INIT_AT):
|
||||
# The commands are literals in flash; the component keeps a pointer to the array
|
||||
arr = cg.shared_progmem_array(
|
||||
"modem_init_at", cg.const_char_ptr, cg.ArrayInitializer(*init_at)
|
||||
)
|
||||
cg.add(var.set_init_at_commands(arr, len(init_at)))
|
||||
|
||||
await cg.add_pin(var.set_rts_pin, config.get(CONF_RTS_PIN))
|
||||
await cg.add_pin(var.set_cts_pin, config.get(CONF_CTS_PIN))
|
||||
|
||||
cg.add_optional(var.set_baud_rate, config.get(CONF_BAUD_RATE))
|
||||
cg.add(var.set_rx_buffer_size(config[CONF_RX_BUFFER_SIZE]))
|
||||
cg.add(var.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE]))
|
||||
cg.add(var.set_dte_buffer_size(config[CONF_DTE_BUFFER_SIZE]))
|
||||
|
||||
await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,282 +0,0 @@
|
||||
#pragma once
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/gpio.h"
|
||||
#ifdef USE_MODEM_URC
|
||||
#include "esphome/core/line_reader.h"
|
||||
#endif
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/static_task.h"
|
||||
#include "esphome/components/network/ip_address.h"
|
||||
#ifdef USE_MODEM_URC
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#endif
|
||||
|
||||
#include <cxx_include/esp_modem_api.hpp>
|
||||
#include <esp_modem_config.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace esphome::modem {
|
||||
|
||||
enum class ModemState : uint8_t {
|
||||
MODEM_STATE_DISABLED,
|
||||
MODEM_STATE_ENABLING,
|
||||
MODEM_STATE_SYNCED,
|
||||
MODEM_STATE_CONNECTING,
|
||||
MODEM_STATE_WAIT_IP,
|
||||
MODEM_STATE_CONNECTED,
|
||||
MODEM_STATE_DISCONNECTING,
|
||||
};
|
||||
|
||||
// CONNECTING runs its long commands once; the later passes only poll the attachment
|
||||
enum class ConnectStep : uint8_t {
|
||||
CONNECT_STEP_START,
|
||||
CONNECT_STEP_RADIO_ON,
|
||||
CONNECT_STEP_ATTACH_REQUESTED,
|
||||
};
|
||||
|
||||
enum class SimResult : uint8_t {
|
||||
SIM_RESULT_READY,
|
||||
SIM_RESULT_RETRY, // status not readable yet, or a PIN step is in progress
|
||||
SIM_RESULT_LOCKED, // nothing more to try: PUK, another lock, no pin_code or the PIN is not sent again
|
||||
SIM_RESULT_HALT, // stop this attempt; a reboot may still open the SIM, so the reboot timer stays
|
||||
};
|
||||
|
||||
// The step that failed most recently; CONNECTED clears it
|
||||
enum class ModemFault : uint8_t {
|
||||
MODEM_FAULT_NONE,
|
||||
MODEM_FAULT_NO_RESPONSE,
|
||||
MODEM_FAULT_BAUD,
|
||||
MODEM_FAULT_INIT_AT,
|
||||
MODEM_FAULT_SIM_NOT_READY,
|
||||
MODEM_FAULT_SIM_LOCKED,
|
||||
MODEM_FAULT_PIN_NOT_SENT,
|
||||
MODEM_FAULT_PIN_RECORD,
|
||||
MODEM_FAULT_SETUP,
|
||||
MODEM_FAULT_NOT_ATTACHED,
|
||||
MODEM_FAULT_PPP,
|
||||
MODEM_FAULT_TEARDOWN,
|
||||
};
|
||||
|
||||
#ifdef USE_MODEM_PIN_CODE
|
||||
enum class PinStep : uint8_t {
|
||||
PIN_STEP_IDLE,
|
||||
PIN_STEP_SAVING, // the send is counted and waits for the record to reach flash
|
||||
PIN_STEP_SENT,
|
||||
};
|
||||
|
||||
// Written by loop() once it tried to save pin_record_; the task leaves the record alone while PENDING
|
||||
enum class PinSave : uint8_t {
|
||||
PIN_SAVE_DONE,
|
||||
PIN_SAVE_PENDING,
|
||||
PIN_SAVE_FAILED,
|
||||
};
|
||||
#endif
|
||||
|
||||
// Every esp_modem factory shares this signature, so codegen picks the one for the model
|
||||
using DceFactory = std::unique_ptr<esp_modem::DCE> (*)(const esp_modem::dce_config *, std::shared_ptr<esp_modem::DTE>,
|
||||
esp_netif_t *);
|
||||
|
||||
class ModemComponent : public Component {
|
||||
public:
|
||||
ModemComponent(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, const char *model, DceFactory factory,
|
||||
const char *apn);
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::WIFI + 1; } // Just before Wi-Fi
|
||||
|
||||
void set_reboot_timeout(uint32_t timeout) { this->timeout_ = timeout; }
|
||||
void set_use_address(const char *use_address) { this->use_address_ = use_address; }
|
||||
void set_rts_pin(InternalGPIOPin *rts_pin) { this->rts_pin_ = rts_pin; }
|
||||
void set_cts_pin(InternalGPIOPin *cts_pin) { this->cts_pin_ = cts_pin; }
|
||||
void set_baud_rate(int baud_rate) { this->baud_rate_ = baud_rate; }
|
||||
#ifdef USE_MODEM_PIN_CODE
|
||||
void set_pin_code(const char *pin_code) { this->pin_code_ = pin_code; }
|
||||
#endif
|
||||
void set_tx_buffer_size(uint16_t tx_buffer_size) { this->tx_buffer_size_ = tx_buffer_size; }
|
||||
void set_rx_buffer_size(uint16_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; }
|
||||
void set_dte_buffer_size(uint16_t dte_buffer_size) { this->dte_buffer_size_ = dte_buffer_size; }
|
||||
void set_init_at_commands(const char *const *cmds, uint8_t count) {
|
||||
this->init_at_commands_ = cmds;
|
||||
this->init_at_count_ = count;
|
||||
}
|
||||
#ifdef USE_MODEM_URC
|
||||
void set_urc_text_sensor(text_sensor::TextSensor *urc_text_sensor) { this->urc_text_sensor_ = urc_text_sensor; }
|
||||
#endif
|
||||
template<typename F> void add_on_state_callback(F &&callback) {
|
||||
this->on_state_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
bool is_connected() const { return this->component_state_.load() == ModemState::MODEM_STATE_CONNECTED; }
|
||||
bool is_disabled() const {
|
||||
return this->component_state_.load() == ModemState::MODEM_STATE_DISABLED &&
|
||||
this->target_state_.load() == ModemState::MODEM_STATE_DISABLED;
|
||||
}
|
||||
bool is_enabled() const { return !this->is_disabled(); }
|
||||
void enable();
|
||||
void disable();
|
||||
#ifdef USE_MODEM_SEND_AT
|
||||
/// Queue an AT command for the modem task, which sends it at its next pass and logs the result and response.
|
||||
/// Returns false when a command is still queued or the command is too long.
|
||||
bool send_at(const char *cmd, uint32_t timeout = COMMAND_TIMEOUT_MS);
|
||||
bool send_at(const std::string &cmd, uint32_t timeout = COMMAND_TIMEOUT_MS);
|
||||
#endif
|
||||
|
||||
network::IPAddresses get_ip_addresses();
|
||||
const char *get_use_address() const { return this->use_address_; }
|
||||
|
||||
protected:
|
||||
static constexpr uint32_t COMMAND_TIMEOUT_MS = 1000;
|
||||
static constexpr uint32_t RETRY_DELAY_MS = 1000;
|
||||
static constexpr size_t URC_LINE_SIZE = 128;
|
||||
static constexpr size_t AT_COMMAND_SIZE = 128;
|
||||
|
||||
// esp_modem's calls block until the modem answers or their own timeouts run out, up to a minute for a
|
||||
// mode change, so the state machine runs on its own task and leaves the main loop alone
|
||||
static void task_entry(void *arg);
|
||||
void run_task_();
|
||||
void wake_task_();
|
||||
ModemState compute_next_state_();
|
||||
ModemState handle_state_enabling_();
|
||||
ModemState handle_state_synced_();
|
||||
ModemState handle_state_connecting_();
|
||||
ModemState handle_state_wait_ip_();
|
||||
ModemState handle_state_disconnecting_();
|
||||
/// Pause the task before the next pass, then go to state
|
||||
uint32_t next_probe_delay_();
|
||||
ModemState retry_(ModemState state, uint32_t delay_ms = RETRY_DELAY_MS);
|
||||
void transition_to_(ModemState next_state);
|
||||
// What the task left for loop() to do
|
||||
enum Pending : uint32_t {
|
||||
PENDING_STATUS = 1 << 0,
|
||||
PENDING_SAVE_BAUD = 1 << 1,
|
||||
#ifdef USE_MODEM_PIN_CODE
|
||||
PENDING_SAVE_PIN = 1 << 2,
|
||||
#endif
|
||||
PENDING_CONNECT_TIMEOUT = 1 << 3, // arm or cancel the reboot timer for the current state
|
||||
PENDING_ON_SYNC = 1 << 4, // loop() wakes the task once on_sync ran
|
||||
PENDING_ON_CONNECT = 1 << 5,
|
||||
PENDING_ON_DISCONNECT = 1 << 6,
|
||||
};
|
||||
|
||||
// Main loop only, so arming and cancelling cannot interleave
|
||||
void arm_connect_timeout_();
|
||||
void cancel_connect_timeout_();
|
||||
void fail_setup_(const LogString *reason);
|
||||
void set_fault_(ModemFault fault);
|
||||
void set_teardown_fault_();
|
||||
void clear_config_fault_(ModemFault fault);
|
||||
void hand_over_(uint32_t pending);
|
||||
/// Send an AT command from the task and log the result; blocks for up to the timeout
|
||||
esp_modem::command_result at_(const char *cmd, uint32_t timeout = COMMAND_TIMEOUT_MS);
|
||||
void publish_status_();
|
||||
void not_responding_(int baud);
|
||||
void dump_connect_params_();
|
||||
SimResult prepare_sim_();
|
||||
#ifdef USE_MODEM_PIN_CODE
|
||||
SimResult send_pin_();
|
||||
void save_pin_record_();
|
||||
#endif
|
||||
|
||||
InternalGPIOPin *tx_pin_;
|
||||
InternalGPIOPin *rx_pin_;
|
||||
InternalGPIOPin *rts_pin_{nullptr};
|
||||
InternalGPIOPin *cts_pin_{nullptr};
|
||||
const char *model_;
|
||||
DceFactory dce_factory_;
|
||||
const char *apn_;
|
||||
const char *const *init_at_commands_{nullptr}; // static array from code generation
|
||||
#ifdef USE_MODEM_URC
|
||||
text_sensor::TextSensor *urc_text_sensor_{nullptr};
|
||||
const char *urc_line_{nullptr}; // the line urc_pending_ hands to loop(), inside urc_reader_
|
||||
#endif
|
||||
std::unique_ptr<esp_modem::DCE> dce_;
|
||||
std::shared_ptr<esp_modem::DTE> dte_;
|
||||
esp_netif_t *ppp_netif_{nullptr};
|
||||
ESPPreferenceObject pref_;
|
||||
LazyCallbackManager<void(ModemState)> on_state_callback_;
|
||||
int baud_rate_{0}; // 0 = use the esp_modem default
|
||||
int current_baud_rate_{0};
|
||||
int restored_baud_{0};
|
||||
StaticTask task_;
|
||||
uint32_t timeout_{0};
|
||||
uint32_t step_delay_ms_{0};
|
||||
uint32_t state_started_ms_{0}; // when the IP wait or the PIN unlock wait began
|
||||
std::atomic<uint32_t> pending_{0};
|
||||
#ifdef USE_MODEM_PIN_CODE
|
||||
const char *pin_code_{nullptr};
|
||||
ESPPreferenceObject pin_pref_;
|
||||
#endif
|
||||
// Kept in flash so reboots cannot keep spending the SIM's attempts on a PIN it never confirmed
|
||||
struct PinRecord {
|
||||
uint32_t hash; // pin_code the record is about
|
||||
uint8_t sends; // AT+CPIN= sent without the SIM reporting READY since
|
||||
};
|
||||
#ifdef USE_MODEM_PIN_CODE
|
||||
PinRecord pin_record_{}; // the task writes it, loop() saves it while pin_save_state_ is PENDING
|
||||
#endif
|
||||
#ifdef USE_MODEM_SEND_AT
|
||||
uint32_t at_timeout_{COMMAND_TIMEOUT_MS};
|
||||
#endif
|
||||
uint16_t tx_buffer_size_{1024};
|
||||
uint16_t rx_buffer_size_{1024};
|
||||
uint16_t dte_buffer_size_{1024};
|
||||
// The task owns component_state_; the main loop sets target_state_ and reads both
|
||||
std::atomic<ModemState> component_state_{ModemState::MODEM_STATE_DISABLED};
|
||||
std::atomic<ModemState> target_state_{ModemState::MODEM_STATE_DISABLED};
|
||||
// Written by the task only, read by loop()
|
||||
std::atomic<ModemFault> fault_{ModemFault::MODEM_FAULT_NONE};
|
||||
// A failed baud_rate or init_at step, shown once fault_ is clear until the step passes again
|
||||
std::atomic<ModemFault> config_fault_{ModemFault::MODEM_FAULT_NONE};
|
||||
// std::atomic<uint8_t> flags: GCC on Xtensa calls out of line for std::atomic<bool>
|
||||
// Written by the IP event task, read by the modem task
|
||||
std::atomic<uint8_t> got_ip_{0};
|
||||
std::atomic<uint8_t> ppp_error_{0}; // last NETIF_PPP_STATUS error code, 0 = none
|
||||
// Doublings of the silent-modem probe delay; enable() and an answer reset it
|
||||
std::atomic<uint8_t> probe_backoff_{0};
|
||||
// Set by loop() once on_sync ran; the task waits for it, not for any notification
|
||||
std::atomic<uint8_t> sync_acked_{0};
|
||||
#ifdef USE_MODEM_SEND_AT
|
||||
// Set by send_at() on the main loop once at_command_ is written, cleared by the task after sending it
|
||||
std::atomic<uint8_t> at_pending_{0};
|
||||
#endif
|
||||
#ifdef USE_MODEM_URC
|
||||
// Set by the UART task with urc_line_; until loop() has published it, new lines are dropped
|
||||
std::atomic<uint8_t> urc_pending_{0};
|
||||
#endif
|
||||
#ifdef USE_MODEM_PIN_CODE
|
||||
std::atomic<PinSave> pin_save_state_{PinSave::PIN_SAVE_DONE};
|
||||
PinStep pin_step_{PinStep::PIN_STEP_IDLE};
|
||||
bool pin_record_unsaved_{false}; // loop() only; the last PIN record save failed
|
||||
#endif
|
||||
ConnectStep connect_step_{ConnectStep::CONNECT_STEP_START};
|
||||
uint8_t init_at_count_{0};
|
||||
uint8_t uart_port_{UART_NUM_MAX};
|
||||
uint8_t attach_polls_{0};
|
||||
bool connect_timeout_armed_{false};
|
||||
bool modem_synced_{false}; // a modem that never answered has nothing to tear down
|
||||
bool resync_{false}; // the attachment read failed, so CONNECTING syncs again first
|
||||
#ifdef USE_MODEM_SEND_AT
|
||||
char at_command_[AT_COMMAND_SIZE];
|
||||
#endif
|
||||
#ifdef USE_MODEM_URC
|
||||
// Fed on the UART task
|
||||
LineReader<URC_LINE_SIZE> urc_reader_;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Points at a string literal from code generation, never at dynamic storage
|
||||
const char *use_address_{""};
|
||||
};
|
||||
|
||||
extern ModemComponent *global_modem_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace esphome::modem
|
||||
|
||||
#endif
|
||||
@@ -1,27 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
import esphome.config_validation as cv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import CONF_MODEM_ID, ModemComponent
|
||||
|
||||
DEPENDENCIES = ["modem"]
|
||||
|
||||
CONF_URC = "urc"
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_MODEM_ID): cv.use_id(ModemComponent),
|
||||
cv.Optional(CONF_URC): text_sensor.text_sensor_schema(),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_MODEM_ID])
|
||||
texts = text_sensor.sub_text_sensors(config)
|
||||
if await texts(CONF_URC, hub.set_urc_text_sensor) is not None:
|
||||
cg.add_define("USE_MODEM_URC")
|
||||
cg.add_define("USE_LINE_READER")
|
||||
add_idf_sdkconfig_option("CONFIG_ESP_MODEM_URC_HANDLER", True)
|
||||
@@ -303,7 +303,6 @@ APPLY_ENTITY = ApplyEntity(
|
||||
ApplySwitch,
|
||||
((SwitchPtr, "entity"), (cg.bool_, "state")),
|
||||
"entity->publish_state(state);",
|
||||
momentary=("entity->publish_state(false);", "state"),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -108,35 +108,6 @@ std::vector<CdcEps> USBUartTypeCdcAcm::parse_descriptors(usb_device_handle_t dev
|
||||
return cdc_devs;
|
||||
}
|
||||
|
||||
void RingBuffer::push(uint8_t item) {
|
||||
if (this->get_free_space() == 0)
|
||||
return;
|
||||
this->buffer_[this->insert_pos_] = item;
|
||||
this->insert_pos_ = (this->insert_pos_ + 1) % this->buffer_size_;
|
||||
}
|
||||
void RingBuffer::push(const uint8_t *data, size_t len) {
|
||||
size_t free = this->get_free_space();
|
||||
if (len > free)
|
||||
len = free;
|
||||
for (size_t i = 0; i != len; i++) {
|
||||
this->buffer_[this->insert_pos_] = *data++;
|
||||
this->insert_pos_ = (this->insert_pos_ + 1) % this->buffer_size_;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t RingBuffer::pop() {
|
||||
uint8_t item = this->buffer_[this->read_pos_];
|
||||
this->read_pos_ = (this->read_pos_ + 1) % this->buffer_size_;
|
||||
return item;
|
||||
}
|
||||
size_t RingBuffer::pop(uint8_t *data, size_t len) {
|
||||
len = std::min(len, this->get_available());
|
||||
for (size_t i = 0; i != len; i++) {
|
||||
*data++ = this->buffer_[this->read_pos_];
|
||||
this->read_pos_ = (this->read_pos_ + 1) % this->buffer_size_;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
void USBUartChannelBase::write_array(const uint8_t *data, size_t len) {
|
||||
if (!this->initialised_.load()) {
|
||||
ESP_LOGD(TAG, "Channel not initialised - write ignored");
|
||||
@@ -188,10 +159,10 @@ uart::UARTFlushResult USBUartChannelBase::flush() {
|
||||
}
|
||||
|
||||
bool USBUartChannelBase::peek_byte(uint8_t *data) {
|
||||
if (this->input_buffer_.is_empty()) {
|
||||
if (this->input_buffer_.empty()) {
|
||||
return false;
|
||||
}
|
||||
*data = this->input_buffer_.peek();
|
||||
*data = this->input_buffer_.front();
|
||||
return true;
|
||||
}
|
||||
bool USBUartChannelBase::read_array(uint8_t *data, size_t len) {
|
||||
@@ -199,18 +170,12 @@ bool USBUartChannelBase::read_array(uint8_t *data, size_t len) {
|
||||
ESP_LOGV(TAG, "Channel not initialised - read ignored");
|
||||
return false;
|
||||
}
|
||||
auto available = this->available();
|
||||
bool status = true;
|
||||
if (len > available) {
|
||||
ESP_LOGV(TAG, "underflow: requested %zu but returned %d, bytes", len, available);
|
||||
len = available;
|
||||
status = false;
|
||||
}
|
||||
for (size_t i = 0; i != len; i++) {
|
||||
*data++ = this->input_buffer_.pop();
|
||||
const size_t got = this->input_buffer_.pop(data, len);
|
||||
if (got < len) {
|
||||
ESP_LOGV(TAG, "underflow: requested %zu but returned %zu bytes", len, got);
|
||||
}
|
||||
this->parent_->start_input(this);
|
||||
return status;
|
||||
return got == len;
|
||||
}
|
||||
void USBUartComponent::setup() { USBClient::setup(); }
|
||||
void USBUartComponent::loop() {
|
||||
@@ -233,7 +198,7 @@ void USBUartComponent::loop() {
|
||||
|
||||
// If there is not enough space for the full chunk, let the device subclass
|
||||
// handle it (e.g. FTDI clears the buffer to prevent mid-telegram corruption).
|
||||
if (channel->input_buffer_.get_free_space() < chunk->length) {
|
||||
if (channel->input_buffer_.free_space() < chunk->length) {
|
||||
this->on_rx_overflow(channel);
|
||||
}
|
||||
// Push data to ring buffer (now safe in main loop)
|
||||
|
||||
@@ -112,28 +112,6 @@ enum class ConfigMode : uint8_t {
|
||||
CONFIG_MODE_MODEM, ///< Apply the DTR/RTS state of one already-open channel
|
||||
};
|
||||
|
||||
class RingBuffer {
|
||||
public:
|
||||
RingBuffer(uint16_t buffer_size) : buffer_size_(buffer_size), buffer_(new uint8_t[buffer_size]) {}
|
||||
bool is_empty() const { return this->read_pos_ == this->insert_pos_; }
|
||||
size_t get_available() const {
|
||||
return (this->insert_pos_ + this->buffer_size_ - this->read_pos_) % this->buffer_size_;
|
||||
};
|
||||
size_t get_free_space() const { return this->buffer_size_ - 1 - this->get_available(); }
|
||||
uint8_t peek() const { return this->buffer_[this->read_pos_]; }
|
||||
void push(uint8_t item);
|
||||
void push(const uint8_t *data, size_t len);
|
||||
uint8_t pop();
|
||||
size_t pop(uint8_t *data, size_t len);
|
||||
void clear() { this->read_pos_ = this->insert_pos_ = 0; }
|
||||
|
||||
protected:
|
||||
uint16_t insert_pos_ = 0;
|
||||
uint16_t read_pos_ = 0;
|
||||
uint16_t buffer_size_;
|
||||
uint8_t *buffer_;
|
||||
};
|
||||
|
||||
// Structure for queuing received USB data chunks
|
||||
struct UsbDataChunk {
|
||||
uint8_t data[usb_host::USB_MAX_PACKET_SIZE];
|
||||
@@ -174,7 +152,7 @@ class USBUartChannelBase : public uart::UARTComponent, public Parented<USBUartCo
|
||||
void write_array(const uint8_t *data, size_t len) override;
|
||||
bool peek_byte(uint8_t *data) override;
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
size_t available() override { return this->input_buffer_.get_available(); }
|
||||
size_t available() override { return this->input_buffer_.size(); }
|
||||
bool is_connected() override { return this->initialised_.load(); }
|
||||
uart::UARTFlushResult flush() override;
|
||||
// Re-apply the current line settings (baud, parity, etc) to this already-open channel.
|
||||
@@ -215,12 +193,13 @@ class USBUartChannelBase : public uart::UARTComponent, public Parented<USBUartCo
|
||||
|
||||
protected:
|
||||
// Not directly instantiable; construct a concrete channel type instead.
|
||||
USBUartChannelBase(uint8_t index, uint16_t buffer_size) : input_buffer_(RingBuffer(buffer_size)), index_(index) {}
|
||||
USBUartChannelBase(uint8_t index, uint16_t buffer_size) : index_(index) { this->input_buffer_.init(buffer_size); }
|
||||
void check_logger_conflict() override {}
|
||||
LineStopBits line_stop_bits_() const;
|
||||
LineParity line_parity_() const;
|
||||
// Larger structures first (8+ bytes)
|
||||
RingBuffer input_buffer_;
|
||||
// Only the main loop touches it; the USB task hands data over through usb_data_queue_
|
||||
FixedRingBuffer<uint8_t> input_buffer_;
|
||||
LockFreeQueue<UsbOutputChunk, USB_OUTPUT_CHUNK_COUNT> output_queue_;
|
||||
// Pool sized to queue capacity (SIZE-1) because LockFreeQueue<T,N> is a ring
|
||||
// buffer that holds N-1 elements. This guarantees allocate() returns nullptr
|
||||
|
||||
@@ -15,7 +15,6 @@ from esphome.const import (
|
||||
CONF_ETHERNET,
|
||||
CONF_OPENTHREAD,
|
||||
CONF_PORT,
|
||||
CONF_PRIORITY,
|
||||
CONF_USE_ADDRESS,
|
||||
CONF_WEB_SERVER,
|
||||
CONF_WIFI,
|
||||
@@ -768,13 +767,7 @@ class EsphomeCore:
|
||||
if self.config is None:
|
||||
raise ValueError("Config has not been loaded yet")
|
||||
|
||||
# Matches network::get_use_address_to(), with modem and ethernet never both present
|
||||
# (CONFLICTS_WITH); a wifi-first priority list lifts wifi, the rule that emits
|
||||
# USE_NETWORK_PRIMARY_INTERFACE_WIFI in network/__init__.py
|
||||
priority = self.config.get("network", {}).get(CONF_PRIORITY, [])
|
||||
wifi_first = bool(priority) and priority[0]["interface"] == CONF_WIFI
|
||||
first = (CONF_WIFI, "modem") if wifi_first else ("modem", CONF_WIFI)
|
||||
for network_type in (*first, CONF_ETHERNET, CONF_OPENTHREAD):
|
||||
for network_type in (CONF_WIFI, CONF_ETHERNET, CONF_OPENTHREAD):
|
||||
if network_type in self.config:
|
||||
return self.config[network_type][CONF_USE_ADDRESS]
|
||||
|
||||
|
||||
@@ -610,13 +610,6 @@
|
||||
#define ESPHOME_ETHERNET_IP_STATE_LISTENERS 2
|
||||
#define ESPHOME_ETHERNET_PHY_REGISTER_COUNT 2
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#define USE_MODEM
|
||||
#define USE_MODEM_PIN_CODE
|
||||
#define USE_MODEM_SEND_AT
|
||||
#define USE_MODEM_URC
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#define MICRO_WAKE_WORD_TASK_CORE 0
|
||||
#define USE_MICRO_WAKE_WORD
|
||||
|
||||
@@ -494,7 +494,7 @@ async def _setup_entity_impl(var: MockObj, config: ConfigType, platform: str) ->
|
||||
config[_KEY_ICON_IDX] = icon_idx
|
||||
|
||||
|
||||
ApplyPublish = Literal["before", "after", "momentary"] | None
|
||||
ApplyPublish = Literal["before", "after"] | None
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
@@ -502,14 +502,12 @@ class ApplyEntity:
|
||||
"""A domain's ``Apply*`` class, its generated function's parameters and its publish statement.
|
||||
|
||||
``locals`` are (name, declaration) pairs, declared only when the body uses the name.
|
||||
``momentary`` is (acknowledge statement, condition) for ``publish="momentary"``.
|
||||
"""
|
||||
|
||||
cls: MockObjClass
|
||||
params: tuple[tuple[SafeExpType, str], ...]
|
||||
publish: str | None = None
|
||||
locals: tuple[tuple[str, str], ...] = ()
|
||||
momentary: tuple[str, str] | None = None
|
||||
|
||||
|
||||
_APPLY_DOMAIN = "entity_apply"
|
||||
@@ -551,9 +549,7 @@ class SubEntities:
|
||||
|
||||
With ``apply`` the entity is the domain's ``Apply*`` class and its control runs
|
||||
``parent->apply``, or ``apply`` as a namespaced free statement when no parent is bound, with
|
||||
``publish`` ("before"/"after") placing the domain's publish. ``publish="momentary"`` instead
|
||||
acknowledges first (a switch publishes off) and runs ``apply`` only when turned on. It is only for
|
||||
legacy action switches; a new action entity is a button.
|
||||
``publish`` ("before"/"after") placing the domain's publish.
|
||||
"""
|
||||
entity_id: ID = conf[CONF_ID]
|
||||
is_apply_class = self.apply_entity is not None and str(entity_id.type) == str(
|
||||
@@ -574,12 +570,8 @@ class SubEntities:
|
||||
)
|
||||
if publish is not None and (apply is None or self.apply_entity.publish is None):
|
||||
raise ValueError(f"{entity_id}: publish= needs apply= on a stateful domain")
|
||||
if publish not in (None, "before", "after", "momentary"):
|
||||
raise ValueError(
|
||||
f'{entity_id}: publish= must be "before", "after" or "momentary"'
|
||||
)
|
||||
if publish == "momentary" and self.apply_entity.momentary is None:
|
||||
raise ValueError(f'{entity_id}: publish="momentary" is not supported here')
|
||||
if publish not in (None, "before", "after"):
|
||||
raise ValueError(f'{entity_id}: publish= must be "before" or "after"')
|
||||
|
||||
if apply is None:
|
||||
var = await self.new_entity(conf, *args, **kwargs)
|
||||
@@ -600,16 +592,11 @@ class SubEntities:
|
||||
if isinstance(parent, ID):
|
||||
parent = await get_variable(parent)
|
||||
apply_entity = self.apply_entity
|
||||
call = f"{apply};" if parent is None else f"{parent_ref(parent)}->{apply};"
|
||||
body = [f"{apply};" if parent is None else f"{parent_ref(parent)}->{apply};"]
|
||||
if publish == "before":
|
||||
body = [apply_entity.publish, call]
|
||||
body.insert(0, apply_entity.publish)
|
||||
elif publish == "after":
|
||||
body = [call, apply_entity.publish]
|
||||
elif publish == "momentary":
|
||||
acknowledge, condition = apply_entity.momentary
|
||||
body = [acknowledge, f"if ({condition}) {{ {call} }}"]
|
||||
else:
|
||||
body = [call]
|
||||
body.append(apply_entity.publish)
|
||||
for name, decl in apply_entity.locals:
|
||||
# Declared right before its first use, so calls keep their order.
|
||||
uses = (i for i, line in enumerate(body) if re.search(rf"\b{name}\b", line))
|
||||
|
||||
+38
-11
@@ -475,7 +475,7 @@ template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()>
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(FixedRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
|
||||
T &operator*() { return buf_->data_[(buf_->head_ + pos_) % buf_->capacity_]; }
|
||||
T &operator*() { return buf_->data_[buf_->wrap_(static_cast<size_t>(buf_->head_) + pos_)]; }
|
||||
Iterator &operator++() {
|
||||
++pos_;
|
||||
return *this;
|
||||
@@ -490,7 +490,7 @@ template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()>
|
||||
class ConstIterator {
|
||||
public:
|
||||
ConstIterator(const FixedRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
|
||||
const T &operator*() const { return buf_->data_[(buf_->head_ + pos_) % buf_->capacity_]; }
|
||||
const T &operator*() const { return buf_->data_[buf_->wrap_(static_cast<size_t>(buf_->head_) + pos_)]; }
|
||||
ConstIterator &operator++() {
|
||||
++pos_;
|
||||
return *this;
|
||||
@@ -532,7 +532,7 @@ template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()>
|
||||
if (this->count_ >= this->capacity_)
|
||||
return false;
|
||||
this->data_[this->tail_] = value;
|
||||
this->tail_ = (this->tail_ + 1) % this->capacity_;
|
||||
this->tail_ = this->wrap_(static_cast<size_t>(this->tail_) + 1);
|
||||
++this->count_;
|
||||
return true;
|
||||
}
|
||||
@@ -540,7 +540,7 @@ template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()>
|
||||
/// Push a value, overwriting the oldest if full.
|
||||
void push_overwrite(const T &value) {
|
||||
this->data_[this->tail_] = value;
|
||||
this->tail_ = (this->tail_ + 1) % this->capacity_;
|
||||
this->tail_ = this->wrap_(static_cast<size_t>(this->tail_) + 1);
|
||||
if (this->count_ >= this->capacity_) {
|
||||
// Buffer full - advance head to drop oldest, count stays at capacity
|
||||
this->head_ = this->tail_;
|
||||
@@ -549,19 +549,48 @@ template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()>
|
||||
}
|
||||
}
|
||||
|
||||
/// Push up to `len` values without overwriting. Returns how many fit.
|
||||
size_t push(const T *data, size_t len) {
|
||||
static_assert(std::is_trivially_copyable<T>::value, "bulk push copies with memcpy");
|
||||
len = std::min<size_t>(len, this->free_space());
|
||||
if (len == 0)
|
||||
return 0; // data_ may be null, and memcpy with a null pointer is undefined even for 0 bytes
|
||||
const size_t first = std::min<size_t>(len, this->capacity_ - this->tail_);
|
||||
std::memcpy(this->data_ + this->tail_, data, first * sizeof(T));
|
||||
std::memcpy(this->data_, data + first, (len - first) * sizeof(T));
|
||||
this->tail_ = this->wrap_(static_cast<size_t>(this->tail_) + len);
|
||||
this->count_ += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
/// Remove the oldest element.
|
||||
void pop() {
|
||||
if (this->count_ > 0) {
|
||||
this->head_ = (this->head_ + 1) % this->capacity_;
|
||||
this->head_ = this->wrap_(static_cast<size_t>(this->head_) + 1);
|
||||
--this->count_;
|
||||
}
|
||||
}
|
||||
|
||||
/// Move up to `len` of the oldest values into `out`. Returns how many were copied.
|
||||
size_t pop(T *out, size_t len) {
|
||||
static_assert(std::is_trivially_copyable<T>::value, "bulk pop copies with memcpy");
|
||||
len = std::min<size_t>(len, this->count_);
|
||||
if (len == 0)
|
||||
return 0;
|
||||
const size_t first = std::min<size_t>(len, this->capacity_ - this->head_);
|
||||
std::memcpy(out, this->data_ + this->head_, first * sizeof(T));
|
||||
std::memcpy(out + first, this->data_, (len - first) * sizeof(T));
|
||||
this->head_ = this->wrap_(static_cast<size_t>(this->head_) + len);
|
||||
this->count_ -= len;
|
||||
return len;
|
||||
}
|
||||
|
||||
T &front() { return this->data_[this->head_]; }
|
||||
const T &front() const { return this->data_[this->head_]; }
|
||||
index_type size() const { return this->count_; }
|
||||
bool empty() const { return this->count_ == 0; }
|
||||
index_type capacity() const { return this->capacity_; }
|
||||
index_type free_space() const { return this->capacity_ - this->count_; }
|
||||
bool full() const { return this->count_ == this->capacity_; }
|
||||
|
||||
/// Clear all elements (reset to empty, keep capacity)
|
||||
@@ -577,14 +606,12 @@ template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()>
|
||||
ConstIterator end() const { return ConstIterator(this, this->count_); }
|
||||
|
||||
/// The element `pos` places after the oldest one; the sum is widened so a small index type cannot truncate it
|
||||
const T &operator[](index_type pos) const {
|
||||
size_t index = static_cast<size_t>(this->head_) + pos;
|
||||
if (index >= this->capacity_)
|
||||
index -= this->capacity_;
|
||||
return this->data_[index];
|
||||
}
|
||||
const T &operator[](index_type pos) const { return this->data_[this->wrap_(static_cast<size_t>(this->head_) + pos)]; }
|
||||
|
||||
protected:
|
||||
/// Fold an index below 2 * capacity back into the buffer
|
||||
index_type wrap_(size_t index) const { return index >= this->capacity_ ? index - this->capacity_ : index; }
|
||||
|
||||
T *data_{nullptr};
|
||||
index_type head_{0};
|
||||
index_type tail_{0};
|
||||
|
||||
@@ -25,8 +25,6 @@ dependencies:
|
||||
version: 1.1.2
|
||||
espressif/esp32-camera:
|
||||
version: 2.1.7
|
||||
espressif/esp_modem:
|
||||
version: 2.1.0
|
||||
espressif/mdns:
|
||||
version: 1.14.0
|
||||
espressif/esp_wifi_remote:
|
||||
|
||||
@@ -98,7 +98,6 @@ ISOLATED_COMPONENTS = {
|
||||
"microphone": "Defines PDM microphone requiring I2S port 0 - conflicts with micro_wake_word PDM mic when merged",
|
||||
"mipi_rgb": "RGB display occupies many GPIOs (including ones used by the shared i2c bus) that conflict when merged with other bus components",
|
||||
"modbus_controller": "Defines multiple modbus buses for testing client/server functionality - conflicts with package modbus bus",
|
||||
"modem": "Cellular-only node with api - grouping with wifi-bearing components hides that case",
|
||||
"neopixelbus": "RMT type conflict with ESP32 Arduino/ESP-IDF headers (enum vs struct rmt_channel_t)",
|
||||
"packages": "cannot merge packages",
|
||||
"tinyusb": "Conflicts with usb_host component - cannot be used together",
|
||||
|
||||
@@ -26,7 +26,3 @@ CONFIG_BT_ENABLED=y
|
||||
|
||||
# esp32_camera
|
||||
CONFIG_SPIRAM=y
|
||||
|
||||
# modem
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_ESP_MODEM_URC_HANDLER=y
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: addressable_light_matrix
|
||||
id: led_matrix_32x8
|
||||
default_transition_length: 500ms
|
||||
chipset: ws2812
|
||||
channel_colors: GRB
|
||||
@@ -9,8 +9,8 @@ light:
|
||||
|
||||
display:
|
||||
- platform: addressable_light
|
||||
id: addressable_light_display
|
||||
addressable_light_id: addressable_light_matrix
|
||||
id: led_matrix_32x8_display
|
||||
addressable_light_id: led_matrix_32x8
|
||||
width: 32
|
||||
height: 8
|
||||
pixel_mapper: |-
|
||||
@@ -0,0 +1,32 @@
|
||||
light:
|
||||
- platform: fastled_clockless
|
||||
id: led_matrix_32x8
|
||||
name: led_matrix_32x8
|
||||
chipset: WS2812B
|
||||
pin: ${pin}
|
||||
num_leds: 256
|
||||
rgb_order: GRB
|
||||
default_transition_length: 0s
|
||||
color_correct: [50%, 50%, 50%]
|
||||
|
||||
display:
|
||||
- platform: addressable_light
|
||||
id: led_matrix_32x8_display
|
||||
addressable_light_id: led_matrix_32x8
|
||||
width: 32
|
||||
height: 8
|
||||
pixel_mapper: |-
|
||||
if (x % 2 == 0) {
|
||||
return (x * 8) + y;
|
||||
}
|
||||
return (x * 8) + (7 - y);
|
||||
lambda: |-
|
||||
Color red = Color(0xFF0000);
|
||||
Color green = Color(0x00FF00);
|
||||
Color blue = Color(0x0000FF);
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height(), red);
|
||||
it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green);
|
||||
it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue);
|
||||
it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red);
|
||||
rotation: 0°
|
||||
update_interval: 16ms
|
||||
@@ -0,0 +1,30 @@
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: led_matrix_32x8
|
||||
default_transition_length: 500ms
|
||||
chipset: ws2812
|
||||
channel_colors: GRB
|
||||
num_leds: 256
|
||||
pin: ${pin}
|
||||
|
||||
display:
|
||||
- platform: addressable_light
|
||||
id: led_matrix_32x8_display
|
||||
addressable_light_id: led_matrix_32x8
|
||||
width: 32
|
||||
height: 8
|
||||
pixel_mapper: |-
|
||||
if (x % 2 == 0) {
|
||||
return (x * 8) + y;
|
||||
}
|
||||
return (x * 8) + (7 - y);
|
||||
lambda: |-
|
||||
Color red = Color(0xFF0000);
|
||||
Color green = Color(0x00FF00);
|
||||
Color blue = Color(0x0000FF);
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height(), red);
|
||||
it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green);
|
||||
it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue);
|
||||
it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red);
|
||||
rotation: 0°
|
||||
update_interval: 16ms
|
||||
@@ -0,0 +1,4 @@
|
||||
substitutions:
|
||||
pin: GPIO2
|
||||
|
||||
<<: !include common-idf-esp32_rmt_led_strip.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
substitutions:
|
||||
pin: GPIO2
|
||||
|
||||
<<: !include common-idf-esp32_rmt_led_strip.yaml
|
||||
@@ -1,5 +0,0 @@
|
||||
substitutions:
|
||||
pin: GPIO2
|
||||
|
||||
packages:
|
||||
addressable_light: !include common.yaml
|
||||
@@ -32,10 +32,6 @@ esphome:
|
||||
action: notify.html5
|
||||
data:
|
||||
message: Button was pressed
|
||||
- homeassistant.service:
|
||||
service: notify.html5
|
||||
data:
|
||||
message: Button was pressed
|
||||
- homeassistant.tag_scanned: pulse
|
||||
- homeassistant.action:
|
||||
action: weather.get_forecasts
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
packages:
|
||||
api: !include common.yaml
|
||||
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
@@ -7,7 +7,3 @@ climate:
|
||||
min_temperature: 18
|
||||
max_temperature: 30
|
||||
transmitter_id: xmitr
|
||||
|
||||
- platform: ballu
|
||||
name: Ballu Climate
|
||||
transmitter_id: xmitr
|
||||
@@ -1,17 +0,0 @@
|
||||
output:
|
||||
- platform: gpio
|
||||
id: binary_fan_output
|
||||
pin: ${fan_pin}
|
||||
- platform: gpio
|
||||
id: binary_light_output
|
||||
pin: ${light_pin}
|
||||
|
||||
fan:
|
||||
- platform: binary
|
||||
name: Binary Fan
|
||||
output: binary_fan_output
|
||||
|
||||
light:
|
||||
- platform: binary
|
||||
name: Binary Light
|
||||
output: binary_light_output
|
||||
@@ -1,6 +0,0 @@
|
||||
substitutions:
|
||||
fan_pin: GPIO12
|
||||
light_pin: GPIO13
|
||||
|
||||
packages:
|
||||
binary: !include common.yaml
|
||||
@@ -1,6 +0,0 @@
|
||||
substitutions:
|
||||
fan_pin: GPIO12
|
||||
light_pin: GPIO13
|
||||
|
||||
packages:
|
||||
binary: !include common.yaml
|
||||
@@ -1,6 +0,0 @@
|
||||
substitutions:
|
||||
fan_pin: GPIO12
|
||||
light_pin: GPIO13
|
||||
|
||||
packages:
|
||||
binary: !include common.yaml
|
||||
@@ -31,10 +31,6 @@ ble_client:
|
||||
- ble_client.numeric_comparison_reply:
|
||||
id: test_blec
|
||||
accept: false
|
||||
on_passkey_notification:
|
||||
- logger.log:
|
||||
format: "Passkey %u"
|
||||
args: [passkey]
|
||||
|
||||
sensor:
|
||||
- platform: ble_client
|
||||
@@ -56,11 +52,6 @@ sensor:
|
||||
name: "BLE Sensor without Lambda"
|
||||
service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678"
|
||||
characteristic_uuid: "abcd1237-abcd-1234-abcd-abcd12345678"
|
||||
notify: true
|
||||
on_notify:
|
||||
- logger.log:
|
||||
format: "Notify %.2f"
|
||||
args: [x]
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
@@ -83,28 +74,3 @@ button:
|
||||
value: !lambda |-
|
||||
uint8_t val = (uint8_t)id(ble_client_test_number).state;
|
||||
return std::vector<uint8_t>{0xAA, val, 0xBB};
|
||||
- ble_client.disconnect: test_blec
|
||||
- ble_client.connect: test_blec
|
||||
- ble_client.remove_bond:
|
||||
id: test_blec
|
||||
|
||||
switch:
|
||||
- platform: ble_client
|
||||
ble_client_id: test_blec
|
||||
name: BLE Client Enabled
|
||||
|
||||
output:
|
||||
- platform: ble_client
|
||||
id: ble_client_output
|
||||
ble_client_id: test_blec
|
||||
service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678"
|
||||
characteristic_uuid: "abcd1238-abcd-1234-abcd-abcd12345678"
|
||||
require_response: true
|
||||
|
||||
text_sensor:
|
||||
- platform: ble_client
|
||||
ble_client_id: test_blec
|
||||
name: BLE Text Sensor
|
||||
service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678"
|
||||
characteristic_uuid: "abcd1239-abcd-1234-abcd-abcd12345678"
|
||||
notify: true
|
||||
@@ -95,17 +95,3 @@ motion:
|
||||
x: y
|
||||
y: x
|
||||
z: -z
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: Calibrate Motion
|
||||
on_press:
|
||||
- motion.calibrate_level:
|
||||
id: bmi270_motion
|
||||
save: true
|
||||
on_success:
|
||||
- logger.log: Level calibrated
|
||||
on_error:
|
||||
- logger.log: Level calibration failed
|
||||
- motion.calibrate_heading:
|
||||
id: bmi270_motion
|
||||
@@ -5,9 +5,3 @@ cap1188:
|
||||
reset_pin: ${reset_pin}
|
||||
touch_threshold: 0x20
|
||||
allow_multiple_touches: true
|
||||
|
||||
binary_sensor:
|
||||
- platform: cap1188
|
||||
cap1188_id: cap1188_component
|
||||
name: CAP1188 Touch Key 0
|
||||
channel: 0
|
||||
@@ -32,64 +32,3 @@ button:
|
||||
- platform: copy
|
||||
source_id: copy_source_button
|
||||
name: Button Copy
|
||||
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
id: copy_source_binary_sensor
|
||||
lambda: return true;
|
||||
- platform: copy
|
||||
source_id: copy_source_binary_sensor
|
||||
name: Binary Sensor Copy
|
||||
|
||||
cover:
|
||||
- platform: template
|
||||
id: copy_source_cover
|
||||
optimistic: true
|
||||
- platform: copy
|
||||
source_id: copy_source_cover
|
||||
name: Cover Copy
|
||||
|
||||
lock:
|
||||
- platform: template
|
||||
id: copy_source_lock
|
||||
optimistic: true
|
||||
- platform: copy
|
||||
source_id: copy_source_lock
|
||||
name: Lock Copy
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
id: copy_source_number
|
||||
optimistic: true
|
||||
min_value: 0
|
||||
max_value: 100
|
||||
step: 1
|
||||
- platform: copy
|
||||
source_id: copy_source_number
|
||||
name: Number Copy
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
id: copy_source_switch
|
||||
optimistic: true
|
||||
- platform: copy
|
||||
source_id: copy_source_switch
|
||||
name: Switch Copy
|
||||
|
||||
text:
|
||||
- platform: template
|
||||
id: copy_source_text
|
||||
optimistic: true
|
||||
mode: text
|
||||
- platform: copy
|
||||
source_id: copy_source_text
|
||||
name: Text Copy
|
||||
mode: text
|
||||
|
||||
text_sensor:
|
||||
- platform: template
|
||||
id: copy_source_text_sensor
|
||||
lambda: return {"hello"};
|
||||
- platform: copy
|
||||
source_id: copy_source_text_sensor
|
||||
name: Text Sensor Copy
|
||||
@@ -0,0 +1,137 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::core::testing {
|
||||
|
||||
TEST(FixedRingBufferBulk, PushAndPopInOrder) {
|
||||
FixedRingBuffer<uint8_t, 255> buf;
|
||||
buf.init(8);
|
||||
const uint8_t in[] = {1, 2, 3, 4, 5};
|
||||
EXPECT_EQ(buf.push(in, sizeof(in)), 5u);
|
||||
EXPECT_EQ(buf.size(), 5);
|
||||
EXPECT_EQ(buf.front(), 1);
|
||||
|
||||
std::array<uint8_t, 5> out{};
|
||||
EXPECT_EQ(buf.pop(out.data(), out.size()), 5u);
|
||||
EXPECT_EQ(out, (std::array<uint8_t, 5>{1, 2, 3, 4, 5}));
|
||||
EXPECT_TRUE(buf.empty());
|
||||
}
|
||||
|
||||
TEST(FixedRingBufferBulk, PushStopsWhenFullWithoutOverwriting) {
|
||||
FixedRingBuffer<uint8_t, 255> buf;
|
||||
buf.init(4);
|
||||
const uint8_t first[] = {1, 2, 3};
|
||||
const uint8_t second[] = {4, 5, 6};
|
||||
EXPECT_EQ(buf.push(first, sizeof(first)), 3u);
|
||||
EXPECT_EQ(buf.free_space(), 1);
|
||||
EXPECT_EQ(buf.push(second, sizeof(second)), 1u);
|
||||
EXPECT_TRUE(buf.full());
|
||||
EXPECT_EQ(buf.free_space(), 0);
|
||||
EXPECT_EQ(buf.push(second, sizeof(second)), 0u);
|
||||
|
||||
std::array<uint8_t, 4> out{};
|
||||
EXPECT_EQ(buf.pop(out.data(), out.size()), 4u);
|
||||
EXPECT_EQ(out, (std::array<uint8_t, 4>{1, 2, 3, 4}));
|
||||
}
|
||||
|
||||
TEST(FixedRingBufferBulk, PopFromEmptyReturnsZero) {
|
||||
FixedRingBuffer<uint8_t, 255> buf;
|
||||
buf.init(4);
|
||||
uint8_t out[4] = {9, 9, 9, 9};
|
||||
EXPECT_EQ(buf.pop(out, sizeof(out)), 0u);
|
||||
EXPECT_EQ(out[0], 9);
|
||||
}
|
||||
|
||||
TEST(FixedRingBufferBulk, PartialPopLeavesTheRest) {
|
||||
FixedRingBuffer<uint8_t, 255> buf;
|
||||
buf.init(6);
|
||||
const uint8_t in[] = {1, 2, 3, 4, 5};
|
||||
buf.push(in, sizeof(in));
|
||||
uint8_t out[2];
|
||||
EXPECT_EQ(buf.pop(out, sizeof(out)), 2u);
|
||||
EXPECT_EQ(out[0], 1);
|
||||
EXPECT_EQ(out[1], 2);
|
||||
EXPECT_EQ(buf.size(), 3);
|
||||
EXPECT_EQ(buf.front(), 3);
|
||||
}
|
||||
|
||||
TEST(FixedRingBufferBulk, WrapsAroundTheEnd) {
|
||||
FixedRingBuffer<uint8_t, 255> buf;
|
||||
buf.init(5);
|
||||
const uint8_t lead[] = {10, 11, 12, 13};
|
||||
buf.push(lead, sizeof(lead));
|
||||
uint8_t drop[3];
|
||||
buf.pop(drop, sizeof(drop)); // head 3, tail 4, one element
|
||||
|
||||
// Fills the last slot, then wraps to the start
|
||||
const uint8_t in[] = {20, 21, 22, 23};
|
||||
EXPECT_EQ(buf.push(in, sizeof(in)), 4u);
|
||||
EXPECT_TRUE(buf.full());
|
||||
EXPECT_EQ(buf[0], 13);
|
||||
EXPECT_EQ(buf[4], 23);
|
||||
|
||||
// Pop crosses the end in two runs
|
||||
std::array<uint8_t, 5> out{};
|
||||
EXPECT_EQ(buf.pop(out.data(), out.size()), 5u);
|
||||
EXPECT_EQ(out, (std::array<uint8_t, 5>{13, 20, 21, 22, 23}));
|
||||
EXPECT_TRUE(buf.empty());
|
||||
|
||||
// Indices stay consistent after wrapping, including an exact landing on the end
|
||||
EXPECT_EQ(buf.push(in, 3), 3u);
|
||||
EXPECT_EQ(buf.pop(out.data(), 3), 3u);
|
||||
EXPECT_EQ(out[0], 20);
|
||||
EXPECT_EQ(out[2], 22);
|
||||
EXPECT_EQ(buf.push(lead, sizeof(lead)), 4u);
|
||||
buf.push(lead[0]);
|
||||
EXPECT_TRUE(buf.full());
|
||||
EXPECT_EQ(buf.pop(out.data(), out.size()), 5u);
|
||||
EXPECT_EQ(out, (std::array<uint8_t, 5>{10, 11, 12, 13, 10}));
|
||||
}
|
||||
|
||||
TEST(FixedRingBufferBulk, MixesWithSingleElementCalls) {
|
||||
FixedRingBuffer<uint16_t, 1000> buf;
|
||||
buf.init(3);
|
||||
buf.push(uint16_t{1});
|
||||
const uint16_t in[] = {2, 3};
|
||||
EXPECT_EQ(buf.push(in, 2), 2u);
|
||||
buf.pop();
|
||||
EXPECT_TRUE(buf.push(uint16_t{4}));
|
||||
uint16_t out[3];
|
||||
EXPECT_EQ(buf.pop(out, 3), 3u);
|
||||
EXPECT_EQ(out[0], 2);
|
||||
EXPECT_EQ(out[1], 3);
|
||||
EXPECT_EQ(out[2], 4);
|
||||
}
|
||||
|
||||
TEST(FixedRingBufferBulk, UninitialisedHoldsNothing) {
|
||||
FixedRingBuffer<uint8_t, 255> buf;
|
||||
const uint8_t in[] = {1};
|
||||
uint8_t out[1];
|
||||
EXPECT_EQ(buf.push(in, 1), 0u);
|
||||
EXPECT_EQ(buf.pop(out, 1), 0u);
|
||||
}
|
||||
|
||||
// A uint8_t index at its limit: head and tail plus a full-buffer length go past 255
|
||||
TEST(FixedRingBufferBulk, WrapsAtTheIndexTypeLimit) {
|
||||
FixedRingBuffer<uint8_t, 255> buf;
|
||||
buf.init(255);
|
||||
std::array<uint8_t, 255> data{};
|
||||
ASSERT_EQ(buf.push(data.data(), 250), 250u);
|
||||
ASSERT_EQ(buf.pop(data.data(), 250), 250u); // head and tail now at 250
|
||||
for (size_t i = 0; i < data.size(); i++)
|
||||
data[i] = static_cast<uint8_t>(i);
|
||||
EXPECT_EQ(buf.push(data.data(), data.size()), 255u);
|
||||
EXPECT_TRUE(buf.full());
|
||||
EXPECT_EQ(buf[0], 0);
|
||||
EXPECT_EQ(buf[254], 254);
|
||||
std::array<uint8_t, 255> out{};
|
||||
EXPECT_EQ(buf.pop(out.data(), out.size()), 255u);
|
||||
EXPECT_EQ(out, data);
|
||||
EXPECT_TRUE(buf.empty());
|
||||
}
|
||||
|
||||
} // namespace esphome::core::testing
|
||||
@@ -24,11 +24,3 @@ display:
|
||||
to: display_page_change_page2
|
||||
then:
|
||||
lambda: 'ESP_LOGD("display", "1 -> 2, %p %p", from, to);'
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: Show Page
|
||||
on_press:
|
||||
- display.page.show: display_page_change_page1
|
||||
- display.page.show:
|
||||
id: !lambda return id(display_page_change_page2);
|
||||
@@ -4,7 +4,6 @@ esp32:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
enable_ota_rollback: true
|
||||
ignore_efuse_custom_mac: true
|
||||
enable_lwip_mdns_queries: true
|
||||
enable_lwip_bridge_interface: true
|
||||
disable_libc_locks_in_iram: false # Test explicit opt-out of RAM optimization
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
esp32_ble:
|
||||
io_capability: keyboard_only
|
||||
advertising: true
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO20
|
||||
rx_pin: GPIO21
|
||||
|
||||
packages:
|
||||
esp32_can: !include common.yaml
|
||||
@@ -1,6 +0,0 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
packages:
|
||||
esp32_can: !include common.yaml
|
||||
@@ -1,4 +1,4 @@
|
||||
output:
|
||||
- platform: esp32_dac
|
||||
id: dac_output
|
||||
pin: ${dac_pin}
|
||||
pin: 25
|
||||
@@ -1,5 +1 @@
|
||||
substitutions:
|
||||
dac_pin: 25
|
||||
|
||||
packages:
|
||||
esp32_dac: !include common.yaml
|
||||
<<: !include common.yaml
|
||||
@@ -1,5 +0,0 @@
|
||||
substitutions:
|
||||
dac_pin: 17
|
||||
|
||||
packages:
|
||||
esp32_dac: !include common.yaml
|
||||
@@ -4,15 +4,3 @@ sensor:
|
||||
id: ph_ezo
|
||||
address: 99
|
||||
unit_of_measurement: pH
|
||||
on_custom:
|
||||
- logger.log: custom
|
||||
on_calibration:
|
||||
- logger.log: calibration
|
||||
on_slope:
|
||||
- logger.log: slope
|
||||
on_t:
|
||||
- logger.log: temperature compensation
|
||||
on_device_information:
|
||||
- logger.log: device information
|
||||
on_led:
|
||||
- logger.log: led
|
||||
@@ -5,9 +5,3 @@ button:
|
||||
factory_reset:
|
||||
resets_required: 5
|
||||
max_delay: 10s
|
||||
on_increment:
|
||||
- logger.log: Reset counter incremented
|
||||
|
||||
switch:
|
||||
- platform: factory_reset
|
||||
name: Factory Reset Switch
|
||||
@@ -33,12 +33,3 @@ fan:
|
||||
- logger.log:
|
||||
format: "Preset %s"
|
||||
args: ["x.c_str()"]
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: Cycle Fan Speed
|
||||
on_press:
|
||||
- fan.cycle_speed: fan_triggers
|
||||
- fan.cycle_speed:
|
||||
id: fan_triggers
|
||||
off_speed_cycle: false
|
||||
@@ -1,4 +0,0 @@
|
||||
text_sensor:
|
||||
- platform: libretiny
|
||||
version:
|
||||
name: LibreTiny Version
|
||||
@@ -1,6 +1,3 @@
|
||||
packages:
|
||||
libretiny: !include common.yaml
|
||||
|
||||
logger:
|
||||
level: VERBOSE
|
||||
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
packages:
|
||||
libretiny: !include common.yaml
|
||||
|
||||
logger:
|
||||
level: VERBOSE
|
||||
@@ -1,5 +1,2 @@
|
||||
packages:
|
||||
libretiny: !include common.yaml
|
||||
|
||||
logger:
|
||||
level: VERBOSE
|
||||
@@ -7,7 +7,3 @@ sensor:
|
||||
integration_time: 100ms
|
||||
ambient_light: "Ambient light"
|
||||
ps_counts: "Proximity counts"
|
||||
on_ps_high_threshold:
|
||||
- logger.log: PS high
|
||||
on_ps_low_threshold:
|
||||
- logger.log: PS low
|
||||
@@ -9,7 +9,3 @@ sensor:
|
||||
full_spectrum_counts: "Full spectrum counts"
|
||||
infrared_counts: "Infrared counts"
|
||||
actual_gain: "Actual gain"
|
||||
on_ps_high_threshold:
|
||||
- logger.log: PS high
|
||||
on_ps_low_threshold:
|
||||
- logger.log: PS low
|
||||
@@ -90,25 +90,6 @@ binary_sensor:
|
||||
- lvgl.page.previous:
|
||||
animation: move_right
|
||||
time: 600ms
|
||||
- lvgl.page.show: page1
|
||||
- lvgl.page.show:
|
||||
id: page1
|
||||
animation: fade_in
|
||||
time: 300ms
|
||||
- lvgl.animimg.start: anim_img
|
||||
- lvgl.tileview.select:
|
||||
id: tileview_id
|
||||
tile_id: tile_1
|
||||
animated: true
|
||||
- lvgl.tileview.select:
|
||||
id: tileview_id
|
||||
row: 0
|
||||
column: 0
|
||||
- if:
|
||||
condition:
|
||||
lvgl.is_paused:
|
||||
then:
|
||||
- logger.log: LVGL is paused
|
||||
- platform: lvgl
|
||||
id: common_button_checker
|
||||
name: Common button
|
||||
@@ -144,9 +125,6 @@ text:
|
||||
mode: text
|
||||
|
||||
text_sensor:
|
||||
- platform: lvgl
|
||||
widget: hello_label
|
||||
name: LVGL Label Text
|
||||
- platform: template
|
||||
id: test_text_sensor
|
||||
name: "Test Text Sensor"
|
||||
@@ -163,8 +141,3 @@ text_sensor:
|
||||
return "WILDCARD";
|
||||
}
|
||||
return x.c_str();
|
||||
|
||||
switch:
|
||||
- platform: lvgl
|
||||
widget: checkbox_id
|
||||
name: LVGL Checkbox Switch
|
||||
@@ -13,9 +13,3 @@ binary_sensor:
|
||||
input: true
|
||||
pullup: true
|
||||
inverted: false
|
||||
|
||||
output:
|
||||
- platform: max6956
|
||||
id: max6956_output
|
||||
max6956: max6956_1
|
||||
pin: 5
|
||||
@@ -25,9 +25,3 @@ sensor:
|
||||
max_ppm: 500.0
|
||||
hydrogen:
|
||||
name: MiCS-5524 H2
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: MiCS-5524 Calibrate
|
||||
on_press:
|
||||
- mics_5524.request_calibration: mics_5524_module
|
||||
@@ -6,9 +6,6 @@ modbus_controller:
|
||||
on_online:
|
||||
then:
|
||||
logger.log: "Module Online"
|
||||
on_command_sent:
|
||||
then:
|
||||
logger.log: "Command sent"
|
||||
binary_sensor:
|
||||
- platform: modbus_controller
|
||||
modbus_controller_id: modbus_controller1
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
esphome:
|
||||
on_boot:
|
||||
priority: -100 # the modem is set up by then, so send_at reaches the DCE
|
||||
then:
|
||||
- modem.disable
|
||||
- modem.enable
|
||||
- if:
|
||||
condition:
|
||||
modem.enabled:
|
||||
then:
|
||||
modem.send_at: "AT+CGSN"
|
||||
else:
|
||||
modem.enable:
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
modem.connected:
|
||||
then:
|
||||
logger.log: modem not connected
|
||||
@@ -1,32 +0,0 @@
|
||||
logger:
|
||||
|
||||
# No wifi: a cellular-only node must satisfy the api network dependency
|
||||
api:
|
||||
|
||||
modem:
|
||||
id: atmodem
|
||||
rx_pin: GPIO26
|
||||
tx_pin: GPIO27
|
||||
rts_pin: GPIO25
|
||||
cts_pin: GPIO33
|
||||
baud_rate: 115200
|
||||
model: SIM7600
|
||||
apn: orange
|
||||
pin_code: "0000"
|
||||
init_at:
|
||||
- ATI
|
||||
reboot_timeout: 5min
|
||||
on_connect:
|
||||
logger.log: "modem connected"
|
||||
on_disconnect:
|
||||
logger.log: "modem disconnected"
|
||||
on_sync:
|
||||
logger.log: "modem synced - ready for AT commands"
|
||||
|
||||
text_sensor:
|
||||
- platform: modem
|
||||
urc:
|
||||
name: Modem URC
|
||||
|
||||
packages:
|
||||
modem: !include common.yaml
|
||||
@@ -345,4 +345,3 @@ display:
|
||||
touch_sleep_timeout: 3
|
||||
update_interval: 5s
|
||||
wake_up_page: 2
|
||||
skip_connection_handshake: true
|
||||
@@ -1,3 +0,0 @@
|
||||
packages:
|
||||
uart: !include ../../test_build_components/common/uart/bk72xx-ard.yaml
|
||||
base: !include common.yaml
|
||||
@@ -12,9 +12,6 @@ ota:
|
||||
on_end:
|
||||
then:
|
||||
- logger.log: "OTA end"
|
||||
on_abort:
|
||||
then:
|
||||
- logger.log: "OTA abort"
|
||||
on_error:
|
||||
then:
|
||||
- logger.log:
|
||||
|
||||
@@ -20,14 +20,3 @@ output:
|
||||
- platform: ${output_platform}
|
||||
id: output_light_output_1
|
||||
pin: ${pin}
|
||||
|
||||
switch:
|
||||
- platform: output
|
||||
name: Output Switch
|
||||
output: output_light_output_1
|
||||
|
||||
button:
|
||||
- platform: output
|
||||
name: Output Button
|
||||
output: output_light_output_1
|
||||
duration: 500ms
|
||||
@@ -15,12 +15,6 @@ esphome:
|
||||
- tag.emulation_on: nfcc_pn7150
|
||||
- tag.polling_off: nfcc_pn7150
|
||||
- tag.polling_on: nfcc_pn7150
|
||||
- if:
|
||||
condition:
|
||||
pn7150.is_writing:
|
||||
id: nfcc_pn7150
|
||||
then:
|
||||
- logger.log: Writing
|
||||
|
||||
pn7150_i2c:
|
||||
id: nfcc_pn7150
|
||||
|
||||
@@ -8,12 +8,6 @@ esphome:
|
||||
message: https://www.home-assistant.io/tag/pulse
|
||||
include_android_app_record: false
|
||||
- tag.set_write_mode: nfcc_pn7160_i2c
|
||||
- if:
|
||||
condition:
|
||||
pn7160.is_writing:
|
||||
id: nfcc_pn7160_i2c
|
||||
then:
|
||||
- logger.log: Writing
|
||||
- tag.set_emulation_message:
|
||||
message: https://www.home-assistant.io/tag/pulse
|
||||
include_android_app_record: false
|
||||
|
||||
@@ -7,8 +7,3 @@ binary_sensor:
|
||||
rc522_id: rc522_nfcc_spi
|
||||
name: RC522 NFC Tag
|
||||
uid: 74-10-37-94
|
||||
|
||||
- platform: rc522_spi
|
||||
rc522_id: rc522_nfcc_spi
|
||||
name: RC522 SPI NFC Tag
|
||||
uid: 74-10-37-95
|
||||
@@ -1,29 +0,0 @@
|
||||
zephyr_ble_server:
|
||||
|
||||
ota:
|
||||
- platform: zephyr_mcumgr
|
||||
transport:
|
||||
ble: true
|
||||
hardware_uart: CDC
|
||||
|
||||
safe_mode:
|
||||
id: safe_mode_1
|
||||
boot_is_good_after: 2min
|
||||
num_attempts: 3
|
||||
reboot_timeout: 2min
|
||||
on_safe_mode:
|
||||
- logger.log: Time for safe mode
|
||||
|
||||
button:
|
||||
- platform: safe_mode
|
||||
name: Safe Mode Button
|
||||
|
||||
switch:
|
||||
- platform: safe_mode
|
||||
name: Safe Mode Switch
|
||||
|
||||
esphome:
|
||||
on_boot:
|
||||
- safe_mode.mark_successful
|
||||
- safe_mode.mark_successful:
|
||||
id: safe_mode_1
|
||||
@@ -73,9 +73,3 @@ interval:
|
||||
- interval: 10s
|
||||
then:
|
||||
- snapshot.take: sdl_sdl_display
|
||||
|
||||
touchscreen:
|
||||
- platform: sdl
|
||||
id: sdl_touch
|
||||
sdl_id: sdl_sdl_display
|
||||
display: sdl_sdl_display
|
||||
@@ -29,17 +29,3 @@ sim800l:
|
||||
- sim800l.disconnect
|
||||
on_ussd_received:
|
||||
- logger.log: "ussd_received"
|
||||
on_call_connected:
|
||||
- logger.log: "call connected"
|
||||
on_call_disconnected:
|
||||
- logger.log: "call disconnected"
|
||||
|
||||
binary_sensor:
|
||||
- platform: sim800l
|
||||
registered:
|
||||
name: SIM800L Registered
|
||||
|
||||
sensor:
|
||||
- platform: sim800l
|
||||
rssi:
|
||||
name: SIM800L RSSI
|
||||
@@ -57,10 +57,3 @@ button:
|
||||
# Same bytes as above on purpose: equal payloads share one flash table
|
||||
- sx126x.send_packet: [0xC5, 0x51, 0x78, 0x82, 0xB7, 0xF9, 0x9C, 0x5C]
|
||||
- sx126x.send_packet: "hello"
|
||||
|
||||
packet_transport:
|
||||
- platform: sx126x
|
||||
id: sx126x_transport
|
||||
update_interval: 5s
|
||||
providers:
|
||||
- name: other-device
|
||||
@@ -57,10 +57,3 @@ button:
|
||||
return {0x01, 0x02, (uint8_t)id(sx127x_my_number).state};
|
||||
- sx127x.send_packet: [0xC5, 0x51, 0x78, 0x82, 0xB7, 0xF9, 0x9C, 0x5C]
|
||||
- sx127x.send_packet: "hello"
|
||||
|
||||
packet_transport:
|
||||
- platform: sx127x
|
||||
id: sx127x_transport
|
||||
update_interval: 5s
|
||||
providers:
|
||||
- name: other-device
|
||||
@@ -38,9 +38,3 @@ switch:
|
||||
number: 1
|
||||
mode:
|
||||
output: true
|
||||
|
||||
output:
|
||||
- platform: sx1509
|
||||
id: sx1509_output
|
||||
sx1509_id: sx1509_hub
|
||||
pin: 5
|
||||
@@ -1,2 +0,0 @@
|
||||
packages:
|
||||
tinyusb_keyboard: !include common.yaml
|
||||
@@ -1,6 +1,5 @@
|
||||
display:
|
||||
- platform: tm1637
|
||||
id: tm1637_display
|
||||
clk_pin: ${clk_pin}
|
||||
dio_pin: ${dio_pin}
|
||||
intensity: 3
|
||||
@@ -8,9 +7,3 @@ display:
|
||||
it.print("1234");
|
||||
static const uint8_t buf[] = {0x3f, 0x06, 0x5b, 0x4f | 0x80};
|
||||
it.set_buffer(buf, sizeof(buf));
|
||||
|
||||
binary_sensor:
|
||||
- platform: tm1637
|
||||
tm1637_id: tm1637_display
|
||||
name: TM1637 Key 0
|
||||
key: 0
|
||||
@@ -144,17 +144,3 @@ water_heater:
|
||||
min_temperature: 30
|
||||
max_temperature: 75
|
||||
target_temperature_step: 1
|
||||
|
||||
fan:
|
||||
- platform: tuya
|
||||
id: tuya_fan
|
||||
switch_datapoint: 10
|
||||
speed_datapoint: 11
|
||||
oscillation_datapoint: 12
|
||||
direction_datapoint: 13
|
||||
speed_count: 4
|
||||
|
||||
text_sensor:
|
||||
- platform: tuya
|
||||
id: tuya_text_sensor
|
||||
sensor_datapoint: 7
|
||||
@@ -1,11 +0,0 @@
|
||||
usb_host:
|
||||
max_transfer_requests: 32 # Test uint32_t bitmask path (17-32 requests)
|
||||
devices:
|
||||
- id: device_1
|
||||
vid: 0x1234
|
||||
pid: 0x1234
|
||||
- id: device_2
|
||||
vid: 0x1234
|
||||
pid: 0xABCD
|
||||
manufacturer: Example Corp
|
||||
product: Example Widget
|
||||
@@ -1,2 +0,0 @@
|
||||
packages:
|
||||
usb_uart: !include common.yaml
|
||||
@@ -74,20 +74,6 @@ voice_assistant:
|
||||
args: [x.c_str()]
|
||||
on_end:
|
||||
- logger.log: "Voice assistant ended"
|
||||
on_intent_start:
|
||||
- logger.log: "Intent start"
|
||||
on_intent_progress:
|
||||
- logger.log: "Intent progress"
|
||||
on_intent_end:
|
||||
- logger.log: "Intent end"
|
||||
on_stt_vad_start:
|
||||
- logger.log: "VAD start"
|
||||
on_stt_vad_end:
|
||||
- logger.log: "VAD end"
|
||||
on_tts_stream_start:
|
||||
- logger.log: "TTS stream start"
|
||||
on_tts_stream_end:
|
||||
- logger.log: "TTS stream end"
|
||||
on_error:
|
||||
- logger.log:
|
||||
format: "Voice assistant error - code %s, message: %s"
|
||||
|
||||
@@ -17,8 +17,6 @@ esphome:
|
||||
|
||||
wifi:
|
||||
use_psram: true
|
||||
enable_btm: true
|
||||
enable_rrm: true
|
||||
min_auth_mode: WPA
|
||||
post_connect_roaming: false
|
||||
manual_ip:
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
|
||||
wled:
|
||||
|
||||
light:
|
||||
- platform: beken_spi_led_strip
|
||||
id: addr
|
||||
name: Beken LED Strip
|
||||
channel_colors: GRB
|
||||
pin: P16
|
||||
num_leds: 30
|
||||
chipset: ws2812
|
||||
effects:
|
||||
- wled:
|
||||
@@ -35,7 +35,3 @@ esp32_camera:
|
||||
then:
|
||||
- lambda: |-
|
||||
ESP_LOGD("main", "image len=%d, data=%c", image.length, image.data[0]);
|
||||
on_stream_start:
|
||||
- logger.log: Stream started
|
||||
on_stream_stop:
|
||||
- logger.log: Stream stopped
|
||||
@@ -1479,7 +1479,6 @@ _SWITCH_APPLY = entity_helpers.ApplyEntity(
|
||||
_APPLY_SWITCH_CLS,
|
||||
((RawExpression("switch_::Switch *"), "entity"), (RawExpression("bool"), "state")),
|
||||
"entity->publish_state(state);",
|
||||
momentary=("entity->publish_state(false);", "state"),
|
||||
)
|
||||
_BUTTON_APPLY = entity_helpers.ApplyEntity(
|
||||
cg.esphome_ns.namespace("button").class_("ApplyButton"),
|
||||
@@ -1671,12 +1670,6 @@ async def test_sub_entities_apply_parent_id(
|
||||
{"apply": "x()", "publish": "Before"},
|
||||
"must be",
|
||||
),
|
||||
(
|
||||
_SELECT_APPLY,
|
||||
_apply_conf("k_id", _SELECT_APPLY.cls),
|
||||
{"apply": "x()", "publish": "momentary"},
|
||||
"not supported",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_sub_entities_apply_errors(
|
||||
@@ -1696,35 +1689,6 @@ async def test_sub_entities_apply_errors(
|
||||
assert apply_functions == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
("parent", "apply", "body"),
|
||||
[
|
||||
(
|
||||
_HUB,
|
||||
"restart()",
|
||||
"entity->publish_state(false);\nif (state) { ::hub->restart(); }",
|
||||
),
|
||||
(
|
||||
None,
|
||||
"restart::restart_device()",
|
||||
"entity->publish_state(false);\nif (state) { restart::restart_device(); }",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_sub_entities_apply_momentary(
|
||||
setup_test_environment: list[str],
|
||||
apply_functions: list[tuple[str, str, str]],
|
||||
parent: MockObj | None,
|
||||
apply: str,
|
||||
body: str,
|
||||
) -> None:
|
||||
"""A momentary switch publishes off first and acts only when turned on."""
|
||||
sub = _apply_sub([], {"r": _apply_conf("r_sw")}, parent=parent)
|
||||
await sub("r", None, apply=apply, publish="momentary")
|
||||
assert apply_functions[0][2] == body
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sub_entities_apply_without_parent_needs_namespaced_call(
|
||||
setup_test_environment: list[str],
|
||||
|
||||
@@ -593,26 +593,6 @@ class TestEsphomeCore:
|
||||
|
||||
assert target.address == "1.2.3.4"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("priority", "expected"),
|
||||
[
|
||||
(None, "10.0.0.1"),
|
||||
("ethernet", "10.0.0.1"),
|
||||
("wifi", "1.2.3.4"),
|
||||
],
|
||||
)
|
||||
def test_address__modem(
|
||||
self, target: core.EsphomeCore, priority: str | None, expected: str
|
||||
) -> None:
|
||||
target.config = {
|
||||
const.CONF_WIFI: {const.CONF_USE_ADDRESS: "1.2.3.4"},
|
||||
"modem": {const.CONF_USE_ADDRESS: "10.0.0.1"},
|
||||
}
|
||||
if priority is not None:
|
||||
target.config["network"] = {const.CONF_PRIORITY: [{"interface": priority}]}
|
||||
|
||||
assert target.address == expected
|
||||
|
||||
def test_address__ethernet(self, target):
|
||||
target.config = {}
|
||||
target.config[const.CONF_ETHERNET] = {const.CONF_USE_ADDRESS: "4.3.2.1"}
|
||||
|
||||
Reference in new issue
Block a user