mirror of
https://github.com/esphome/esphome.git
synced 2026-10-27 16:32:11 +00:00
Compare commits
41
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbecd1cbcb | ||
|
|
a4e7f4c680 | ||
|
|
557ed04f7f | ||
|
|
e08cd5a4f0 | ||
|
|
c04cdea1a0 | ||
|
|
2c11cfd1fa | ||
|
|
46d8c3ec02 | ||
|
|
3259e72d9d | ||
|
|
6cb622417a | ||
|
|
960316abd6 | ||
|
|
29adc6f7e7 | ||
|
|
87341dc136 | ||
|
|
cfe5809654 | ||
|
|
9c54f96520 | ||
|
|
0cc5b372fb | ||
|
|
f7c8cba8b7 | ||
|
|
79a22e9a9a | ||
|
|
103023737d | ||
|
|
6b6b70a8b6 | ||
|
|
d312bf1b73 | ||
|
|
0d2f8c0e88 | ||
|
|
2f8b50c5af | ||
|
|
c4b598be3e | ||
|
|
0cd16df16f | ||
|
|
a680974da2 | ||
|
|
78f0b1ceba | ||
|
|
c5a841e11e | ||
|
|
365965f9bb | ||
|
|
16011d7fce | ||
|
|
4486dc53d8 | ||
|
|
8d5e3ce534 | ||
|
|
9ea4d35a50 | ||
|
|
5cceddfbf4 | ||
|
|
ff6a37a052 | ||
|
|
6990092be0 | ||
|
|
9220c79a51 | ||
|
|
215fcd4f5d | ||
|
|
918e3b3a58 | ||
|
|
347eb3902e | ||
|
|
177820083f | ||
|
|
b0a0524091 |
No files matched your search
@@ -443,7 +443,8 @@ file does, and it is the authority when they disagree. The most useful starting
|
||||
- **Hub forwarding entities need no C++ class:** a button, switch, number or select that only calls its hub
|
||||
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.
|
||||
`index`/`option` in scope; on a switch, number or select `publish="before"` or `"after"` publishes the state,
|
||||
and `publish="on_success"` publishes only when the hub call returns true (a write the device can refuse).
|
||||
`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
|
||||
|
||||
+17
-10
@@ -1305,10 +1305,7 @@ def upload_program(
|
||||
f"{option_string} is only supported with the esphome OTA platform; "
|
||||
"the web_server OTA path can only update the firmware image."
|
||||
)
|
||||
binary = CORE.firmware_bin
|
||||
if getattr(args, "file", None) is not None:
|
||||
binary = Path(args.file)
|
||||
return _upload_via_web_server(config, network_devices, binary)
|
||||
return _upload_via_web_server(config, network_devices, _app_ota_image(args))
|
||||
|
||||
return _upload_via_native_api(config, network_devices, args)
|
||||
|
||||
@@ -1364,6 +1361,16 @@ def _esphome_ota_conf(config: ConfigType) -> ConfigType | None:
|
||||
return next(iter(esphome_ota_items(config)), None)
|
||||
|
||||
|
||||
def _app_ota_image(args: ArgsProtocol) -> Path:
|
||||
"""The file an app update sends: ``--file`` or the build output, as the
|
||||
target platform's ota_image hook picks it."""
|
||||
binary = CORE.firmware_bin
|
||||
if (file := getattr(args, "file", None)) is not None:
|
||||
binary = Path(file)
|
||||
ota_image = platform_hooks.get_platform_hook(CORE.target_platform, "ota_image")
|
||||
return ota_image(binary) if ota_image is not None else binary
|
||||
|
||||
|
||||
def _upload_via_native_api(
|
||||
config: ConfigType, network_devices: list[str], args: ArgsProtocol
|
||||
) -> tuple[int, str | None]:
|
||||
@@ -1413,27 +1420,27 @@ def _upload_via_native_api(
|
||||
f"flash a build with the option enabled, and then retry {option_string}."
|
||||
)
|
||||
|
||||
binary = CORE.firmware_bin
|
||||
ota_type = espota2.OTA_TYPE_UPDATE_APP
|
||||
file = getattr(args, "file", None)
|
||||
if getattr(args, "partition_table", False):
|
||||
# Fail fast if the resolved ESPHome OTA config does not enable allow_partition_access.
|
||||
# The device-side handshake also rejects this with "Device only supports app updates",
|
||||
# but checking here surfaces the misconfiguration before opening a network connection.
|
||||
check_partition_access("--partition-table")
|
||||
binary = CORE.partition_table_bin
|
||||
binary = Path(file) if file is not None else CORE.partition_table_bin
|
||||
ota_type = espota2.OTA_TYPE_UPDATE_PARTITION_TABLE
|
||||
elif getattr(args, "bootloader", False):
|
||||
check_partition_access("--bootloader")
|
||||
if (
|
||||
getattr(args, "file", None) is None
|
||||
file is None
|
||||
and (native := native_backend())
|
||||
and (hint := getattr(native, "missing_image_hint", lambda: None)())
|
||||
):
|
||||
raise EsphomeError(hint)
|
||||
binary = CORE.bootloader_bin
|
||||
binary = Path(file) if file is not None else CORE.bootloader_bin
|
||||
ota_type = espota2.OTA_TYPE_UPDATE_BOOTLOADER
|
||||
if getattr(args, "file", None) is not None:
|
||||
binary = Path(args.file)
|
||||
else:
|
||||
binary = _app_ota_image(args)
|
||||
|
||||
if ota_type == espota2.OTA_TYPE_UPDATE_PARTITION_TABLE:
|
||||
_validate_partition_table_binary(binary)
|
||||
|
||||
@@ -33,9 +33,35 @@ _CACHE_VERSION = 1
|
||||
_LAMBDA_KEY = "__esphome_lambda__"
|
||||
|
||||
|
||||
_CACHE_SUFFIX = ".validated.json"
|
||||
|
||||
|
||||
def compiled_config_path(config_filename: str) -> Path:
|
||||
"""Path to the cached validated config alongside the storage sidecar."""
|
||||
return CORE.data_dir / "storage" / f"{config_filename}.validated.json"
|
||||
return CORE.data_dir / "storage" / f"{config_filename}{_CACHE_SUFFIX}"
|
||||
|
||||
|
||||
def invalidate_compiled_config() -> None:
|
||||
"""Drop every cache in the storage dir after a change to a file the
|
||||
mtime check cannot see, such as secrets.yaml or an include; other
|
||||
configurations may share that file."""
|
||||
storage = compiled_config_path(CORE.config_filename).parent
|
||||
try:
|
||||
caches = [p for p in storage.iterdir() if p.name.endswith(_CACHE_SUFFIX)]
|
||||
except FileNotFoundError:
|
||||
return
|
||||
except OSError as err:
|
||||
raise EsphomeError(
|
||||
f"Could not list the validated config caches in {storage}: {err}"
|
||||
) from err
|
||||
for path in caches:
|
||||
try:
|
||||
path.unlink(missing_ok=True)
|
||||
except OSError as err:
|
||||
# A stale cache would present the old key on the next upload
|
||||
raise EsphomeError(
|
||||
f"Could not remove the validated config cache {path}: {err}"
|
||||
) from err
|
||||
|
||||
|
||||
def save_compiled_config(config: ConfigType) -> None:
|
||||
|
||||
@@ -297,9 +297,7 @@ void ADE7880::init_device_() {
|
||||
void ADE7880::reset_device_() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
ESP_LOGD(TAG, "Reset device using RESET pin");
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(1);
|
||||
this->reset_pin_->digital_write(true);
|
||||
pulse_reset(this->reset_pin_, {.low_ms = 1});
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Reset device using SWRST command");
|
||||
this->write_u16_register16_(CONFIG, CONFIG_SWRST);
|
||||
|
||||
@@ -14,80 +14,80 @@ ESPHOME_LOG_TAG(TAG, "aic3204");
|
||||
return; \
|
||||
}
|
||||
|
||||
static constexpr i2c::I2CRegValue AIC3204_INIT[] PROGMEM = {
|
||||
// Set register page to 0
|
||||
{AIC3204_PAGE_CTRL, 0x00},
|
||||
// Initiate SW reset (PLL is powered off as part of reset)
|
||||
{AIC3204_SW_RST, 0x01},
|
||||
// *** Program clock settings ***
|
||||
// Default is CODEC_CLKIN is from MCLK pin. Don't need to change this.
|
||||
// MDAC*NDAC*FOSR*48Khz = mClk (24.576 MHz when the XMOS is expecting 48kHz audio)
|
||||
// (See page 51 of https://www.ti.com/lit/ml/slaa557/slaa557.pdf)
|
||||
// We do need MDAC*DOSR/32 >= the resource compute level for the processing block
|
||||
// So here 2*128/32 = 8, which is equal to processing block 1 's resource compute
|
||||
// See page 5 of https://www.ti.com/lit/an/slaa404c/slaa404c.pdf for the workflow
|
||||
// for determining these settings.
|
||||
// Power up NDAC and set to 2
|
||||
{AIC3204_NDAC, 0x82},
|
||||
// Power up MDAC and set to 2
|
||||
{AIC3204_MDAC, 0x82},
|
||||
// Program DOSR = 128
|
||||
{AIC3204_DOSR, 0x80},
|
||||
// Set Audio Interface Config: I2S, 32 bits, DOUT always driving
|
||||
{AIC3204_CODEC_IF, 0x30},
|
||||
// For I2S Firmware only, set SCLK/MFP3 pin as Audio Data In
|
||||
{AIC3204_SCLK_MFP3, 0x02},
|
||||
{AIC3204_AUDIO_IF_4, 0x01},
|
||||
{AIC3204_AUDIO_IF_5, 0x01},
|
||||
// Program the DAC processing block to be used - PRB_P1
|
||||
{AIC3204_DAC_SIG_PROC, 0x01},
|
||||
// *** Select Page 1 ***
|
||||
{AIC3204_PAGE_CTRL, 0x01},
|
||||
// Enable the internal AVDD_LDO:
|
||||
{AIC3204_LDO_CTRL, 0x09},
|
||||
// *** Program Analog Blocks ***
|
||||
// Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO
|
||||
{AIC3204_PWR_CFG, 0x08},
|
||||
// Enable Master Analog Power Control
|
||||
{AIC3204_LDO_CTRL, 0x01},
|
||||
// Page 125: Common mode control register, set d6 to 1 to make the full chip common mode = 0.75 v
|
||||
// We are using the internal AVdd regulator with a nominal output of 1.72 V (see LDO_CTRL_REGISTER on page 123)
|
||||
// Page 86 says to only set the common mode voltage to 0.9 v if AVdd >= 1.8... but it isn't on our hardware
|
||||
// We also adjust the HPL and HPR gains to -2dB gian later in this config flow compensate (see page 47)
|
||||
// (All pages refer to the TLV320AIC3204 Application Reference Guide)
|
||||
{AIC3204_CM_CTRL, 0x40},
|
||||
// *** Set PowerTune Modes ***
|
||||
// Set the Left & Right DAC PowerTune mode to PTM_P3/4. Use Class-AB driver.
|
||||
{AIC3204_PLAY_CFG1, 0x00},
|
||||
{AIC3204_PLAY_CFG2, 0x00},
|
||||
// Set the REF charging time to 40ms
|
||||
{AIC3204_REF_STARTUP, 0x01},
|
||||
// HP soft stepping settings for optimal pop performance at power up
|
||||
// Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling
|
||||
// capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound.
|
||||
{AIC3204_HP_START, 0x25},
|
||||
// Route Left DAC to HPL
|
||||
{AIC3204_HPL_ROUTE, 0x08},
|
||||
// Route Right DAC to HPR
|
||||
{AIC3204_HPR_ROUTE, 0x08},
|
||||
// Route Left DAC to LOL
|
||||
{AIC3204_LOL_ROUTE, 0x08},
|
||||
// Route Right DAC to LOR
|
||||
{AIC3204_LOR_ROUTE, 0x08},
|
||||
// Unmute HPL and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
|
||||
{AIC3204_HPL_GAIN, 0x3e},
|
||||
// Unmute HPR and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
|
||||
{AIC3204_HPR_GAIN, 0x3e},
|
||||
// Unmute LOL and set gain to 0dB
|
||||
{AIC3204_LOL_DRV_GAIN, 0x00},
|
||||
// Unmute LOR and set gain to 0dB
|
||||
{AIC3204_LOR_DRV_GAIN, 0x00},
|
||||
// Power up HPL and HPR, LOL and LOR drivers
|
||||
{AIC3204_OP_PWR_CTRL, 0x3C},
|
||||
};
|
||||
|
||||
void AIC3204::setup() {
|
||||
// Set register page to 0
|
||||
ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set page 0 failed");
|
||||
// Initiate SW reset (PLL is powered off as part of reset)
|
||||
ERROR_CHECK(this->write_byte(AIC3204_SW_RST, 0x01), "Software reset failed");
|
||||
// *** Program clock settings ***
|
||||
// Default is CODEC_CLKIN is from MCLK pin. Don't need to change this.
|
||||
// MDAC*NDAC*FOSR*48Khz = mClk (24.576 MHz when the XMOS is expecting 48kHz audio)
|
||||
// (See page 51 of https://www.ti.com/lit/ml/slaa557/slaa557.pdf)
|
||||
// We do need MDAC*DOSR/32 >= the resource compute level for the processing block
|
||||
// So here 2*128/32 = 8, which is equal to processing block 1 's resource compute
|
||||
// See page 5 of https://www.ti.com/lit/an/slaa404c/slaa404c.pdf for the workflow
|
||||
// for determining these settings.
|
||||
|
||||
// Power up NDAC and set to 2
|
||||
ERROR_CHECK(this->write_byte(AIC3204_NDAC, 0x82), "Set NDAC failed");
|
||||
// Power up MDAC and set to 2
|
||||
ERROR_CHECK(this->write_byte(AIC3204_MDAC, 0x82), "Set MDAC failed");
|
||||
// Program DOSR = 128
|
||||
ERROR_CHECK(this->write_byte(AIC3204_DOSR, 0x80), "Set DOSR failed");
|
||||
// Set Audio Interface Config: I2S, 32 bits, DOUT always driving
|
||||
ERROR_CHECK(this->write_byte(AIC3204_CODEC_IF, 0x30), "Set CODEC_IF failed");
|
||||
// For I2S Firmware only, set SCLK/MFP3 pin as Audio Data In
|
||||
ERROR_CHECK(this->write_byte(AIC3204_SCLK_MFP3, 0x02), "Set SCLK/MFP3 failed");
|
||||
ERROR_CHECK(this->write_byte(AIC3204_AUDIO_IF_4, 0x01), "Set AUDIO_IF_4 failed");
|
||||
ERROR_CHECK(this->write_byte(AIC3204_AUDIO_IF_5, 0x01), "Set AUDIO_IF_5 failed");
|
||||
// Program the DAC processing block to be used - PRB_P1
|
||||
ERROR_CHECK(this->write_byte(AIC3204_DAC_SIG_PROC, 0x01), "Set DAC_SIG_PROC failed");
|
||||
|
||||
// *** Select Page 1 ***
|
||||
ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x01), "Set page 1 failed");
|
||||
// Enable the internal AVDD_LDO:
|
||||
ERROR_CHECK(this->write_byte(AIC3204_LDO_CTRL, 0x09), "Set LDO_CTRL failed");
|
||||
// *** Program Analog Blocks ***
|
||||
// Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO
|
||||
ERROR_CHECK(this->write_byte(AIC3204_PWR_CFG, 0x08), "Set PWR_CFG failed");
|
||||
// Enable Master Analog Power Control
|
||||
ERROR_CHECK(this->write_byte(AIC3204_LDO_CTRL, 0x01), "Set LDO_CTRL failed");
|
||||
// Page 125: Common mode control register, set d6 to 1 to make the full chip common mode = 0.75 v
|
||||
// We are using the internal AVdd regulator with a nominal output of 1.72 V (see LDO_CTRL_REGISTER on page 123)
|
||||
// Page 86 says to only set the common mode voltage to 0.9 v if AVdd >= 1.8... but it isn't on our hardware
|
||||
// We also adjust the HPL and HPR gains to -2dB gian later in this config flow compensate (see page 47)
|
||||
// (All pages refer to the TLV320AIC3204 Application Reference Guide)
|
||||
ERROR_CHECK(this->write_byte(AIC3204_CM_CTRL, 0x40), "Set CM_CTRL failed");
|
||||
// *** Set PowerTune Modes ***
|
||||
// Set the Left & Right DAC PowerTune mode to PTM_P3/4. Use Class-AB driver.
|
||||
ERROR_CHECK(this->write_byte(AIC3204_PLAY_CFG1, 0x00), "Set PLAY_CFG1 failed");
|
||||
ERROR_CHECK(this->write_byte(AIC3204_PLAY_CFG2, 0x00), "Set PLAY_CFG2 failed");
|
||||
// Set the REF charging time to 40ms
|
||||
ERROR_CHECK(this->write_byte(AIC3204_REF_STARTUP, 0x01), "Set REF_STARTUP failed");
|
||||
// HP soft stepping settings for optimal pop performance at power up
|
||||
// Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling
|
||||
// capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound.
|
||||
ERROR_CHECK(this->write_byte(AIC3204_HP_START, 0x25), "Set HP_START failed");
|
||||
// Route Left DAC to HPL
|
||||
ERROR_CHECK(this->write_byte(AIC3204_HPL_ROUTE, 0x08), "Set HPL_ROUTE failed");
|
||||
// Route Right DAC to HPR
|
||||
ERROR_CHECK(this->write_byte(AIC3204_HPR_ROUTE, 0x08), "Set HPR_ROUTE failed");
|
||||
// Route Left DAC to LOL
|
||||
ERROR_CHECK(this->write_byte(AIC3204_LOL_ROUTE, 0x08), "Set LOL_ROUTE failed");
|
||||
// Route Right DAC to LOR
|
||||
ERROR_CHECK(this->write_byte(AIC3204_LOR_ROUTE, 0x08), "Set LOR_ROUTE failed");
|
||||
|
||||
// Unmute HPL and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
|
||||
ERROR_CHECK(this->write_byte(AIC3204_HPL_GAIN, 0x3e), "Set HPL_GAIN failed");
|
||||
// Unmute HPR and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
|
||||
ERROR_CHECK(this->write_byte(AIC3204_HPR_GAIN, 0x3e), "Set HPR_GAIN failed");
|
||||
// Unmute LOL and set gain to 0dB
|
||||
ERROR_CHECK(this->write_byte(AIC3204_LOL_DRV_GAIN, 0x00), "Set LOL_DRV_GAIN failed");
|
||||
// Unmute LOR and set gain to 0dB
|
||||
ERROR_CHECK(this->write_byte(AIC3204_LOR_DRV_GAIN, 0x00), "Set LOR_DRV_GAIN failed");
|
||||
|
||||
// Power up HPL and HPR, LOL and LOR drivers
|
||||
ERROR_CHECK(this->write_byte(AIC3204_OP_PWR_CTRL, 0x3C), "Set OP_PWR_CTRL failed");
|
||||
ERROR_CHECK(this->write_registers(AIC3204_INIT), "Failed to initialize");
|
||||
|
||||
// Wait for 2.5 sec for soft stepping to take effect before attempting power-up
|
||||
this->set_timeout(2500, [this]() {
|
||||
|
||||
@@ -174,14 +174,10 @@ def _register_provisioning_source(config: ConfigType) -> ConfigType:
|
||||
|
||||
CONF_SUPPORTS_RESPONSE = "supports_response"
|
||||
|
||||
# Enum values in api::enums namespace
|
||||
enums_ns = api_ns.namespace("enums")
|
||||
SUPPORTS_RESPONSE_OPTIONS = {
|
||||
"none": enums_ns.SUPPORTS_RESPONSE_NONE,
|
||||
"optional": enums_ns.SUPPORTS_RESPONSE_OPTIONAL,
|
||||
"only": enums_ns.SUPPORTS_RESPONSE_ONLY,
|
||||
"status": enums_ns.SUPPORTS_RESPONSE_STATUS,
|
||||
}
|
||||
SupportsResponseType = api_ns.namespace("enums").enum("SupportsResponseType")
|
||||
SUPPORTS_RESPONSE_OPTIONS = SupportsResponseType.options(
|
||||
"none", "optional", "only", "status", prefix="SUPPORTS_RESPONSE_"
|
||||
)
|
||||
|
||||
|
||||
def _auto_detect_supports_response(config: ConfigType) -> ConfigType:
|
||||
|
||||
@@ -54,7 +54,8 @@ static const char AS7341_NAME[] PROGMEM = "AS7341";
|
||||
static constexpr uint8_t AS7341_ADC_SLOTS = 6;
|
||||
static constexpr uint8_t AS7341_SMUX_LEN = 20;
|
||||
static constexpr uint8_t AS7341_SMUX_STEPS = 2;
|
||||
static const RegValue AS7341_INIT[] PROGMEM = {{0x70, 0x00}}; // CONFIG: SPM mode, INT_SEL and LED_SEL cleared
|
||||
static constexpr i2c::I2CRegValue AS7341_INIT[] PROGMEM = {
|
||||
{0x70, 0x00}}; // CONFIG: SPM mode, INT_SEL and LED_SEL cleared
|
||||
static const uint8_t AS7341_SMUX[] PROGMEM = {
|
||||
0x30, 0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x50, 0x00, // step 0: F1 to F4, NIR, Clear
|
||||
0x00, 0x00, 0x20, 0x04, 0x00, 0x30, 0x01, 0x50, 0x00, 0x06, // step 0, continued
|
||||
@@ -150,8 +151,8 @@ static constexpr uint8_t AS7343_ADC_SLOTS = 18;
|
||||
static constexpr uint8_t AS7343_SMUX_LEN = 10;
|
||||
static const char TCS3448_NAME[] PROGMEM = "TCS3448";
|
||||
// CFG6, FD_CFG0, CFG10, CFG8, CFG20 (auto_smux, 18 channels in 3 cycles), AGC_GAIN_MAX, FD_TIME_1, FD_TIME_2
|
||||
static const RegValue AS7343_INIT[] PROGMEM = {{0xF5, 0x00}, {0xDF, 0xA1}, {0x65, 0xF2}, {0xC9, 0xC8},
|
||||
{0xD6, 0x62}, {0xD7, 0x99}, {0xE0, 0x64}, {0xE2, 0x21}};
|
||||
static constexpr i2c::I2CRegValue AS7343_INIT[] PROGMEM = {{0xF5, 0x00}, {0xDF, 0xA1}, {0x65, 0xF2}, {0xC9, 0xC8},
|
||||
{0xD6, 0x62}, {0xD7, 0x99}, {0xE0, 0x64}, {0xE2, 0x21}};
|
||||
static const uint8_t AS7343_SMUX[] PROGMEM = {0x00, 0x04, 0x65, 0x02, 0x00, 0x05, 0x00, 0x01, 0x00, 0x30,
|
||||
0x00, 0x00, 0x60, 0x20, 0x04, 0x50, 0x03, 0x00, 0x01, 0x05,
|
||||
0x05, 0x00, 0x60, 0x00, 0x30, 0x00, 0x40, 0x10, 0x20, 0x00};
|
||||
@@ -230,7 +231,7 @@ static constexpr ChipInfo as7343_chip(const char *name) {
|
||||
.gain_correction = AS7343_GAIN_CORRECTION,
|
||||
.contributions = AS7343_CONTRIBUTIONS,
|
||||
.tristimulus = AS7343_TRISTIMULUS,
|
||||
.init_count = sizeof(AS7343_INIT) / sizeof(RegValue),
|
||||
.init_count = sizeof(AS7343_INIT) / sizeof(i2c::I2CRegValue),
|
||||
.astep_reg = 0xD4,
|
||||
.cfg0_reg = 0xBF,
|
||||
.cfg1_reg = 0xC6,
|
||||
@@ -298,9 +299,9 @@ void AS734XComponent::setup() {
|
||||
this->mark_failed(LOG_STR("Wrong chip ID"));
|
||||
return;
|
||||
}
|
||||
const RegValue *init = pgm(chip->init);
|
||||
const i2c::I2CRegValue *init = pgm(chip->init);
|
||||
for (uint8_t i = 0, n = pgm(chip->init_count); ok && i < n; i++) {
|
||||
RegValue rv;
|
||||
i2c::I2CRegValue rv;
|
||||
progmem_memcpy(&rv, &init[i], sizeof(rv));
|
||||
ok = this->select_bank_(rv.reg) && this->write_byte(rv.reg, rv.value);
|
||||
}
|
||||
|
||||
@@ -68,15 +68,10 @@ struct ChannelTristimulus {
|
||||
float z;
|
||||
};
|
||||
|
||||
struct RegValue {
|
||||
uint8_t reg;
|
||||
uint8_t value;
|
||||
};
|
||||
|
||||
/// Per-chip constants, stored in flash. The pointed-to tables are in flash too.
|
||||
struct ChipInfo {
|
||||
const char *name;
|
||||
const RegValue *init; // written in order; the bank is selected per register address
|
||||
const i2c::I2CRegValue *init; // written in order; the bank is selected per register address
|
||||
const uint8_t *smux; // AS7341: SMUX RAM image per step; AS7343: the three chain images
|
||||
const uint8_t *readout; // per step, the ADC slot of each band (0xFF: not read in this step)
|
||||
const uint16_t *wavelengths; // per band, WIDEBAND_NM for clear and ABSENT_NM for a missing band
|
||||
|
||||
@@ -16,13 +16,7 @@ constexpr static const uint8_t AXS_READ_TOUCHPAD[11] = {0xb5, 0xab, 0xa5, 0x5a,
|
||||
}
|
||||
|
||||
void AXS15231Touchscreen::setup() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup();
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(10);
|
||||
}
|
||||
pulse_reset(this->reset_pin_, {.low_ms = 5, .high_after_ms = 10});
|
||||
if (this->interrupt_pin_ != nullptr) {
|
||||
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT);
|
||||
this->interrupt_pin_->setup();
|
||||
|
||||
@@ -78,3 +78,7 @@ async def pin_to_code(config: ConfigType) -> MockObj:
|
||||
# Called by writer.py; delegates to the shared libretiny implementation.
|
||||
def copy_files() -> None:
|
||||
libretiny.copy_files()
|
||||
|
||||
|
||||
# Called by the OTA upload; the shared libretiny implementation.
|
||||
ota_image = libretiny.device_ota_image
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "bme680_bsec.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <string>
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
namespace esphome::bme680_bsec {
|
||||
#ifdef USE_BSEC
|
||||
@@ -9,7 +9,7 @@ ESPHOME_LOG_TAG(TAG, "bme680_bsec.sensor");
|
||||
|
||||
static constexpr uint32_t READ_TIMEOUT_ID = 0;
|
||||
|
||||
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
|
||||
PROGMEM_STRING_TABLE(IaqAccuracyStrings, "Stabilizing", "Uncertain", "Calibrating", "Calibrated");
|
||||
|
||||
std::vector<BME680BSECComponent *>
|
||||
BME680BSECComponent::instances; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -386,10 +386,11 @@ void BME680BSECComponent::publish_(const bsec_output_t *outputs, uint8_t num_out
|
||||
switch (outputs[i].sensor_id) {
|
||||
case BSEC_OUTPUT_IAQ:
|
||||
case BSEC_OUTPUT_STATIC_IAQ: {
|
||||
uint8_t accuracy = std::min<uint8_t>(outputs[i].accuracy, std::size(IAQ_ACCURACY_STATES) - 1);
|
||||
uint8_t accuracy = std::min<uint8_t>(outputs[i].accuracy, IaqAccuracyStrings::LAST_INDEX);
|
||||
this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_sensor_, signal); });
|
||||
this->queue_push_([this, accuracy]() {
|
||||
this->publish_sensor_(this->iaq_accuracy_text_sensor_, IAQ_ACCURACY_STATES[accuracy]);
|
||||
this->publish_sensor_(this->iaq_accuracy_text_sensor_,
|
||||
IaqAccuracyStrings::get_progmem_str(accuracy, IaqAccuracyStrings::LAST_INDEX));
|
||||
});
|
||||
this->queue_push_([this, accuracy]() { this->publish_sensor_(this->iaq_accuracy_sensor_, accuracy, true); });
|
||||
|
||||
@@ -435,8 +436,9 @@ void BME680BSECComponent::publish_sensor_(sensor::Sensor *sensor, float value, b
|
||||
sensor->publish_state(value);
|
||||
}
|
||||
|
||||
void BME680BSECComponent::publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value) {
|
||||
if (!sensor || (sensor->has_state() && sensor->state == value)) {
|
||||
void BME680BSECComponent::publish_sensor_(text_sensor::TextSensor *sensor, ProgmemStr value) {
|
||||
if (!sensor ||
|
||||
(sensor->has_state() && ESPHOME_strcmp_P(sensor->state.c_str(), reinterpret_cast<ESPHOME_PGM_P>(value)) == 0)) {
|
||||
return;
|
||||
}
|
||||
sensor->publish_state(value);
|
||||
|
||||
@@ -77,7 +77,7 @@ class BME680BSECComponent final : public Component, public i2c::I2CDevice {
|
||||
int64_t get_time_ns_();
|
||||
|
||||
void publish_sensor_(sensor::Sensor *sensor, float value, bool change_only = false);
|
||||
void publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value);
|
||||
void publish_sensor_(text_sensor::TextSensor *sensor, ProgmemStr value);
|
||||
|
||||
void snapshot_state_(); // Fetch the current BSEC library state and save it in the bsec_state_data_ member (volatile
|
||||
// memory)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "cc1101pa.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
@@ -14,6 +15,9 @@ namespace esphome::cc1101 {
|
||||
ESPHOME_LOG_TAG(TAG, "cc1101");
|
||||
static constexpr size_t TX_FIFO_SIZE = 64;
|
||||
|
||||
// MDMCFG2 MOD_FORMAT values
|
||||
PROGMEM_STRING_TABLE(ModulationStrings, "2-FSK", "GFSK", "UNUSED", "ASK/OOK", "4-FSK", "UNUSED", "UNUSED", "MSK");
|
||||
|
||||
static void split_float(float value, int mbits, uint8_t &e, uint32_t &m) {
|
||||
int e_tmp;
|
||||
float m_tmp = std::frexp(value, &e_tmp);
|
||||
@@ -238,8 +242,6 @@ void CC1101Component::loop() {
|
||||
}
|
||||
|
||||
void CC1101Component::dump_config() {
|
||||
static const char *const MODULATION_NAMES[] = {"2-FSK", "GFSK", "UNUSED", "ASK/OOK",
|
||||
"4-FSK", "UNUSED", "UNUSED", "MSK"};
|
||||
int32_t freq = static_cast<int32_t>(this->state_.FREQ2 << 16 | this->state_.FREQ1 << 8 | this->state_.FREQ0) *
|
||||
XTAL_FREQUENCY / (1 << 16);
|
||||
float symbol_rate = (((256.0f + this->state_.DRATE_M) * (1 << this->state_.DRATE_E)) / (1 << 28)) * XTAL_FREQUENCY;
|
||||
@@ -253,8 +255,9 @@ void CC1101Component::dump_config() {
|
||||
" Symbol Rate: %.0f baud\n"
|
||||
" Filter Bandwidth: %.1f Hz\n"
|
||||
" Output Power: %.1f dBm",
|
||||
this->chip_id_, freq, this->state_.CHANNR, MODULATION_NAMES[this->state_.MOD_FORMAT & 0x07],
|
||||
symbol_rate, bw, this->output_power_effective_);
|
||||
this->chip_id_, freq, this->state_.CHANNR,
|
||||
LOG_STR_ARG(ModulationStrings::get_log_str(this->state_.MOD_FORMAT & 0x07, 0)), symbol_rate, bw,
|
||||
this->output_power_effective_);
|
||||
LOG_PIN(" CS Pin: ", this->cs_);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,7 @@ ESPHOME_LOG_TAG(TAG, "cst226.touchscreen");
|
||||
|
||||
void CST226Touchscreen::setup() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup();
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(true);
|
||||
pulse_reset(this->reset_pin_, {.high_before_ms = 5, .low_ms = 5});
|
||||
this->set_timeout(30, [this] { this->continue_setup_(); });
|
||||
} else {
|
||||
this->continue_setup_();
|
||||
|
||||
@@ -43,9 +43,7 @@ void CST328Touchscreen::setup() {
|
||||
}
|
||||
|
||||
void CST328Touchscreen::reset_device_() {
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(true);
|
||||
pulse_reset(this->reset_pin_, {.low_ms = 5});
|
||||
this->set_timeout(CST328_TRANSITION_TIMEOUT, [this] { this->continue_setup_(); });
|
||||
}
|
||||
|
||||
|
||||
@@ -52,12 +52,7 @@ void CST816Touchscreen::continue_setup_() {
|
||||
|
||||
void CST816Touchscreen::setup() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup();
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(true);
|
||||
pulse_reset(this->reset_pin_, {.high_before_ms = 5, .low_ms = 5});
|
||||
this->set_timeout(30, [this] { this->continue_setup_(); });
|
||||
} else {
|
||||
this->continue_setup_();
|
||||
|
||||
@@ -10,14 +10,7 @@ static constexpr uint32_t REREAD_TIMEOUT_ID = 1;
|
||||
static constexpr uint32_t REREAD_DELAY_MS = 20;
|
||||
|
||||
void CST9220Touchscreen::setup() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup();
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(10);
|
||||
this->reset_pin_->digital_write(true);
|
||||
}
|
||||
pulse_reset(this->reset_pin_, {.high_before_ms = 5, .low_ms = 10});
|
||||
// Wait for the controller to leave its bootloader before talking to it.
|
||||
this->set_timeout(30, [this] { this->continue_setup_(); });
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
cg.add_define("USE_LINE_READER")
|
||||
await uart.new_uart_component(config)
|
||||
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
namespace esphome::dfrobot_sen0395 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "dfrobot_sen0395");
|
||||
static constexpr char ASCII_CR = 0x0D;
|
||||
static constexpr char ASCII_LF = 0x0A;
|
||||
static constexpr float DET_RANGE_STEP_M = 0.15f;
|
||||
static constexpr float LATENCY_STEP_S = 0.025f;
|
||||
// Sent after a reset with no line end
|
||||
static constexpr char PROMPT[] PROGMEM = "leapMMW:/>";
|
||||
|
||||
// What each CommandType cannot do while the sensor runs; empty for commands that do not need it stopped
|
||||
PROGMEM_STRING_TABLE(BusyActions, "", "configure range config", "configure output latency",
|
||||
@@ -121,9 +121,9 @@ bool DfrobotSen0395Component::run_command_(const Command &cmd, uint32_t now) {
|
||||
return false; // Command not done yet
|
||||
}
|
||||
|
||||
if (this->read_message_()) {
|
||||
if (ESPHOME_strstr_P(this->read_buffer_, ESPHOME_PSTR("is not recognized as a CLI command")) == nullptr) {
|
||||
if (!this->handle_reply_(cmd))
|
||||
if (const char *message = this->read_message_()) {
|
||||
if (ESPHOME_strstr_P(message, ESPHOME_PSTR("is not recognized as a CLI command")) == nullptr) {
|
||||
if (!this->handle_reply_(cmd, message))
|
||||
return false; // Command not done yet
|
||||
this->read_message_(); // Skip the prompt
|
||||
return true;
|
||||
@@ -146,12 +146,13 @@ bool DfrobotSen0395Component::run_command_(const Command &cmd, uint32_t now) {
|
||||
}
|
||||
|
||||
void DfrobotSen0395Component::read_state_() {
|
||||
if (!this->read_message_())
|
||||
const char *message = this->read_message_();
|
||||
if (message == nullptr)
|
||||
return;
|
||||
if (ESPHOME_strstr_P(this->read_buffer_, ESPHOME_PSTR("$JYBSS,0, , , *")) != nullptr) {
|
||||
if (ESPHOME_strstr_P(message, ESPHOME_PSTR("$JYBSS,0, , , *")) != nullptr) {
|
||||
this->set_detected_(false);
|
||||
this->set_active(true);
|
||||
} else if (ESPHOME_strstr_P(this->read_buffer_, ESPHOME_PSTR("$JYBSS,1, , , *")) != nullptr) {
|
||||
} else if (ESPHOME_strstr_P(message, ESPHOME_PSTR("$JYBSS,1, , , *")) != nullptr) {
|
||||
this->set_detected_(true);
|
||||
this->set_active(true);
|
||||
}
|
||||
@@ -206,8 +207,7 @@ void DfrobotSen0395Component::format_command(const Command &cmd, char *buf, size
|
||||
}
|
||||
}
|
||||
|
||||
bool DfrobotSen0395Component::handle_reply_(const Command &cmd) {
|
||||
const char *message = this->read_buffer_;
|
||||
bool DfrobotSen0395Component::handle_reply_(const Command &cmd, const char *message) {
|
||||
auto is = [message](ESPHOME_PGM_P text) { return ESPHOME_strcmp_P(message, text) == 0; };
|
||||
|
||||
// Commands with their own replies
|
||||
@@ -230,7 +230,7 @@ bool DfrobotSen0395Component::handle_reply_(const Command &cmd) {
|
||||
}
|
||||
return true;
|
||||
case CommandType::COMMAND_TYPE_RESET_SYSTEM:
|
||||
if (!is(ESPHOME_PSTR("leapMMW:/>")))
|
||||
if (!is(PROMPT))
|
||||
return false;
|
||||
ESP_LOGI(TAG, "Restarted sensor.");
|
||||
return true;
|
||||
@@ -300,34 +300,24 @@ bool DfrobotSen0395Component::handle_reply_(const Command &cmd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DfrobotSen0395Component::read_message_() {
|
||||
const char *DfrobotSen0395Component::read_message_() {
|
||||
while (this->available()) {
|
||||
uint8_t byte;
|
||||
this->read_byte(&byte);
|
||||
|
||||
if (this->read_pos_ == MMWAVE_READ_BUFFER_LENGTH)
|
||||
this->read_pos_ = 0;
|
||||
|
||||
ESP_LOGVV(TAG, "Buffer pos: %u %d", this->read_pos_, byte);
|
||||
|
||||
if (byte == ASCII_CR)
|
||||
continue;
|
||||
if (byte >= 0x7F)
|
||||
byte = '?'; // needs to be valid utf8 string for log functions.
|
||||
this->read_buffer_[this->read_pos_] = byte;
|
||||
|
||||
if (this->read_pos_ == 9 && byte == '>')
|
||||
this->read_buffer_[++this->read_pos_] = ASCII_LF;
|
||||
|
||||
if (this->read_buffer_[this->read_pos_] == ASCII_LF) {
|
||||
this->read_buffer_[this->read_pos_] = 0;
|
||||
this->read_pos_ = 0;
|
||||
ESP_LOGV(TAG, "Message: %s", this->read_buffer_);
|
||||
return true; // Full message in buffer
|
||||
const char *line = this->line_reader_.feed(byte);
|
||||
if (line == nullptr && byte == '>' && this->line_reader_.length() == sizeof(PROMPT) - 1) {
|
||||
line = this->line_reader_.partial();
|
||||
this->line_reader_.reset();
|
||||
}
|
||||
this->read_pos_++;
|
||||
if (line != nullptr) {
|
||||
ESP_LOGV(TAG, "Message: %s", line);
|
||||
return line;
|
||||
}
|
||||
this->line_reader_.log_if_overflowed(TAG);
|
||||
}
|
||||
return false; // No full message yet
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DfrobotSen0395Component::set_detected_(bool detected) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/line_reader.h"
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
@@ -14,7 +15,7 @@
|
||||
|
||||
namespace esphome::dfrobot_sen0395 {
|
||||
|
||||
static constexpr uint8_t MMWAVE_READ_BUFFER_LENGTH = 255;
|
||||
static constexpr uint8_t MMWAVE_READ_BUFFER_LENGTH = 253; // largest LineReader size with a one byte position
|
||||
static constexpr uint8_t COMMAND_QUEUE_SIZE = 20;
|
||||
|
||||
// Keep in sync with the BusyActions table in dfrobot_sen0395.cpp
|
||||
@@ -121,8 +122,7 @@ class DfrobotSen0395Component final : public uart::UARTDevice, public Component
|
||||
bool led_active_{false};
|
||||
bool uart_presence_active_{false};
|
||||
bool start_after_boot_{false};
|
||||
char read_buffer_[MMWAVE_READ_BUFFER_LENGTH];
|
||||
size_t read_pos_{0};
|
||||
LineReader<MMWAVE_READ_BUFFER_LENGTH> line_reader_;
|
||||
bool cmd_sent_{false};
|
||||
uint8_t retries_left_{2};
|
||||
StaticRingBuffer<Command, COMMAND_QUEUE_SIZE> cmd_queue_;
|
||||
@@ -131,12 +131,13 @@ class DfrobotSen0395Component final : public uart::UARTDevice, public Component
|
||||
int16_t det_range_steps_[8]{};
|
||||
uint16_t latency_steps_[2]{};
|
||||
|
||||
bool read_message_();
|
||||
/// The next complete line, valid until the next call, or nullptr
|
||||
const char *read_message_();
|
||||
void read_state_();
|
||||
/// Run the command at the front of the queue; true when it is done
|
||||
bool run_command_(const Command &cmd, uint32_t now);
|
||||
/// Handle a reply to a sent command; true when the command is done
|
||||
bool handle_reply_(const Command &cmd);
|
||||
bool handle_reply_(const Command &cmd, const char *message);
|
||||
|
||||
void set_detected_(bool detected);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "ds248x_one_wire_bus.h"
|
||||
#include "ds248x.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::ds248x {
|
||||
@@ -151,11 +152,7 @@ uint64_t DS248xOneWireBus::search_int() {
|
||||
}
|
||||
|
||||
// Update address based on direction taken
|
||||
if (dir_taken) {
|
||||
address |= bit_mask;
|
||||
} else {
|
||||
address &= ~bit_mask;
|
||||
}
|
||||
assign_bits(address, bit_mask, dir_taken);
|
||||
}
|
||||
|
||||
// Search successful
|
||||
|
||||
@@ -18,13 +18,11 @@ E131AddressableLightEffect = e131_ns.class_(
|
||||
)
|
||||
E131Component = e131_ns.class_("E131Component", cg.Component)
|
||||
|
||||
METHODS = {"UNICAST": e131_ns.E131_UNICAST, "MULTICAST": e131_ns.E131_MULTICAST}
|
||||
E131ListenMethod = e131_ns.enum("E131ListenMethod")
|
||||
METHODS = E131ListenMethod.options("UNICAST", "MULTICAST", prefix="E131_")
|
||||
|
||||
CHANNELS = {
|
||||
"MONO": e131_ns.E131_MONO,
|
||||
"RGB": e131_ns.E131_RGB,
|
||||
"RGBW": e131_ns.E131_RGBW,
|
||||
}
|
||||
E131LightChannels = e131_ns.enum("E131LightChannels")
|
||||
CHANNELS = E131LightChannels.options("MONO", "RGB", "RGBW", prefix="E131_")
|
||||
|
||||
CONF_UNIVERSE = "universe"
|
||||
CONF_E131_ID = "e131_id"
|
||||
|
||||
@@ -20,9 +20,7 @@ void EKTF2232Touchscreen::setup() {
|
||||
|
||||
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
|
||||
|
||||
this->reset_pin_->setup();
|
||||
|
||||
this->hard_reset_();
|
||||
pulse_reset(this->reset_pin_, {.low_ms = 15, .high_after_ms = 15});
|
||||
if (!this->soft_reset_()) {
|
||||
ESP_LOGE(TAG, "Failed to soft reset EKT2232!");
|
||||
this->interrupt_pin_->detach_interrupt();
|
||||
@@ -96,13 +94,6 @@ bool EKTF2232Touchscreen::get_power_state() {
|
||||
return (received[1] >> 3) & 1;
|
||||
}
|
||||
|
||||
void EKTF2232Touchscreen::hard_reset_() {
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(15);
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(15);
|
||||
}
|
||||
|
||||
bool EKTF2232Touchscreen::soft_reset_() {
|
||||
auto err = this->write(SOFT_RESET_CMD, 4);
|
||||
if (err != i2c::ERROR_OK)
|
||||
|
||||
@@ -22,7 +22,6 @@ class EKTF2232Touchscreen final : public Touchscreen, public i2c::I2CDevice {
|
||||
bool get_power_state();
|
||||
|
||||
protected:
|
||||
void hard_reset_();
|
||||
bool soft_reset_();
|
||||
void update_touches() override;
|
||||
|
||||
|
||||
@@ -4,21 +4,18 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
namespace esphome::epaper_spi {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "epaper_spi");
|
||||
static constexpr size_t EPAPER_MAX_CMD_LOG_BYTES = 128;
|
||||
|
||||
static constexpr const char *const EPAPER_STATE_STRINGS[] = {
|
||||
"IDLE", "UPDATE", "RESET", "RESET_END", "SHOULD_WAIT", "INITIALISE",
|
||||
"TRANSFER_DATA", "POWER_ON", "REFRESH_SCREEN", "POWER_OFF", "DEEP_SLEEP",
|
||||
};
|
||||
PROGMEM_STRING_TABLE(EPaperStateStrings, "IDLE", "UPDATE", "RESET", "RESET_END", "SHOULD_WAIT", "INITIALISE",
|
||||
"TRANSFER_DATA", "POWER_ON", "REFRESH_SCREEN", "POWER_OFF", "DEEP_SLEEP", "Unknown");
|
||||
|
||||
const char *EPaperBase::epaper_state_to_string_() {
|
||||
if (auto idx = static_cast<unsigned>(this->state_); idx < std::size(EPAPER_STATE_STRINGS))
|
||||
return EPAPER_STATE_STRINGS[idx];
|
||||
return "Unknown";
|
||||
const LogString *EPaperBase::epaper_state_to_string_() {
|
||||
return EPaperStateStrings::get_log_str(static_cast<uint8_t>(this->state_), EPaperStateStrings::LAST_INDEX);
|
||||
}
|
||||
|
||||
void EPaperBase::setup() {
|
||||
@@ -159,7 +156,7 @@ void EPaperBase::update_effective_transform_() {
|
||||
|
||||
void EPaperBase::update() {
|
||||
if (this->state_ != EPaperState::IDLE) {
|
||||
ESP_LOGE(TAG, "Display already in state %s", epaper_state_to_string_());
|
||||
ESP_LOGE(TAG, "Display already in state %s", LOG_STR_ARG(epaper_state_to_string_()));
|
||||
return;
|
||||
}
|
||||
this->set_state_(EPaperState::UPDATE);
|
||||
@@ -212,7 +209,7 @@ void EPaperBase::loop() {
|
||||
this->high_frequency_loop_.stop();
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
if (now - this->waiting_for_idle_last_print_ >= 1000) {
|
||||
ESP_LOGV(TAG, "Waiting for idle in state %s", this->epaper_state_to_string_());
|
||||
ESP_LOGV(TAG, "Waiting for idle in state %s", LOG_STR_ARG(this->epaper_state_to_string_()));
|
||||
this->waiting_for_idle_last_print_ = millis();
|
||||
}
|
||||
#endif
|
||||
@@ -227,7 +224,7 @@ void EPaperBase::loop() {
|
||||
// The busy pin never cleared: give up on this update so later ones are not refused, and make the next one full
|
||||
void EPaperBase::abandon_update_() {
|
||||
ESP_LOGE(TAG, "Display stayed busy for %u s in state %s; check the busy pin", (unsigned) this->busy_timeout_seconds_,
|
||||
this->epaper_state_to_string_());
|
||||
LOG_STR_ARG(this->epaper_state_to_string_()));
|
||||
this->status_set_warning(LOG_STR("Busy pin timeout"));
|
||||
if (this->state_ == EPaperState::TRANSFER_DATA)
|
||||
this->abort_transfer();
|
||||
@@ -247,10 +244,10 @@ void EPaperBase::abandon_update_() {
|
||||
* Should a subclassed class need to override this, the method will need to be made virtual.
|
||||
*/
|
||||
void EPaperBase::process_state_() {
|
||||
ESP_LOGV(TAG, "Process state entered in state %s", epaper_state_to_string_());
|
||||
ESP_LOGV(TAG, "Process state entered in state %s", LOG_STR_ARG(epaper_state_to_string_()));
|
||||
switch (this->state_) {
|
||||
default:
|
||||
ESP_LOGE(TAG, "Display is in unhandled state %s", epaper_state_to_string_());
|
||||
ESP_LOGE(TAG, "Display is in unhandled state %s", LOG_STR_ARG(epaper_state_to_string_()));
|
||||
this->set_state_(EPaperState::IDLE);
|
||||
break;
|
||||
case EPaperState::IDLE:
|
||||
@@ -342,7 +339,7 @@ void EPaperBase::process_state_() {
|
||||
}
|
||||
|
||||
void EPaperBase::set_state_(EPaperState state, uint16_t delay) {
|
||||
ESP_LOGV(TAG, "Exit state %s", this->epaper_state_to_string_());
|
||||
ESP_LOGV(TAG, "Exit state %s", LOG_STR_ARG(this->epaper_state_to_string_()));
|
||||
this->state_ = state;
|
||||
// allow subclasses to nominate delays
|
||||
if (delay == 0)
|
||||
@@ -350,7 +347,7 @@ void EPaperBase::set_state_(EPaperState state, uint16_t delay) {
|
||||
this->next_delay_ = 0;
|
||||
this->delay_until_ = millis() + delay;
|
||||
this->wait_for_idle_(state > EPaperState::SHOULD_WAIT);
|
||||
ESP_LOGV(TAG, "Enter state %s, delay %u, wait_for_idle=%s", this->epaper_state_to_string_(), delay,
|
||||
ESP_LOGV(TAG, "Enter state %s, delay %u, wait_for_idle=%s", LOG_STR_ARG(this->epaper_state_to_string_()), delay,
|
||||
TRUEFALSE(this->waiting_for_idle_));
|
||||
// Each step of an update otherwise waits for the next loop iteration; loop() slows down again for long waits
|
||||
if (state == EPaperState::IDLE) {
|
||||
|
||||
@@ -125,7 +125,7 @@ class EPaperBase : public Display,
|
||||
bool is_using_partial_update_() const { return this->full_update_every_ > 1; }
|
||||
void process_state_();
|
||||
|
||||
const char *epaper_state_to_string_();
|
||||
const LogString *epaper_state_to_string_();
|
||||
bool is_idle_() const;
|
||||
void setup_pins_() const;
|
||||
virtual bool reset();
|
||||
|
||||
@@ -72,9 +72,7 @@ bool EPaperJD79660::reset() {
|
||||
|
||||
// As commented on SLEEP_MS_RESET1: Reset pulse must happen within time window.
|
||||
// So do not use FSM loop, and avoid other calls/logs during pulse below.
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(SLEEP_MS_RESET1);
|
||||
this->reset_pin_->digital_write(true);
|
||||
pulse_reset(this->reset_pin_, {.low_ms = SLEEP_MS_RESET1});
|
||||
|
||||
this->reset_duration_ = SLEEP_MS_RESET2;
|
||||
this->step_ = FSMState::RESET_STEP2_IDLECHECK;
|
||||
|
||||
@@ -33,53 +33,59 @@ void ES7210::dump_config() {
|
||||
this->channel_gains_[2], this->channel_gains_[3]);
|
||||
}
|
||||
|
||||
static constexpr i2c::I2CRegValue ES7210_RESET[] PROGMEM = {
|
||||
// Software reset
|
||||
{ES7210_RESET_REG00, 0xff},
|
||||
{ES7210_RESET_REG00, 0x32},
|
||||
{ES7210_CLOCK_OFF_REG01, 0x3f},
|
||||
// Set initialization time when device powers up
|
||||
{ES7210_TIME_CONTROL0_REG09, 0x30},
|
||||
{ES7210_TIME_CONTROL1_REG0A, 0x30},
|
||||
// Configure HFP for all ADC channels
|
||||
{ES7210_ADC12_HPF2_REG23, 0x2a},
|
||||
{ES7210_ADC12_HPF1_REG22, 0x0a},
|
||||
{ES7210_ADC34_HPF2_REG20, 0x0a},
|
||||
{ES7210_ADC34_HPF1_REG21, 0x2a},
|
||||
};
|
||||
|
||||
static constexpr i2c::I2CRegValue ES7210_ANALOG[] PROGMEM = {
|
||||
// Configure analog power
|
||||
{ES7210_ANALOG_REG40, 0xC3},
|
||||
// Set mic bias
|
||||
{ES7210_MIC12_BIAS_REG41, 0x70},
|
||||
{ES7210_MIC34_BIAS_REG42, 0x70},
|
||||
};
|
||||
|
||||
static constexpr i2c::I2CRegValue ES7210_POWER_ON[] PROGMEM = {
|
||||
// Power on mics 1 through 4
|
||||
{ES7210_MIC1_POWER_REG47, 0x08},
|
||||
{ES7210_MIC2_POWER_REG48, 0x08},
|
||||
{ES7210_MIC3_POWER_REG49, 0x08},
|
||||
{ES7210_MIC4_POWER_REG4A, 0x08},
|
||||
// Power down DLL
|
||||
{ES7210_POWER_DOWN_REG06, 0x04},
|
||||
// Power on MIC1-4 bias & ADC1-4 & PGA1-4 Power
|
||||
{ES7210_MIC12_POWER_REG4B, 0x0F},
|
||||
{ES7210_MIC34_POWER_REG4C, 0x0F},
|
||||
// Enable device
|
||||
{ES7210_RESET_REG00, 0x71},
|
||||
{ES7210_RESET_REG00, 0x41},
|
||||
};
|
||||
|
||||
void ES7210::setup() {
|
||||
// Software reset
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0xff));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x32));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_CLOCK_OFF_REG01, 0x3f));
|
||||
|
||||
// Set initialization time when device powers up
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_TIME_CONTROL0_REG09, 0x30));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_TIME_CONTROL1_REG0A, 0x30));
|
||||
|
||||
// Configure HFP for all ADC channels
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC12_HPF2_REG23, 0x2a));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC12_HPF1_REG22, 0x0a));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC34_HPF2_REG20, 0x0a));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC34_HPF1_REG21, 0x2a));
|
||||
ES7210_ERROR_FAILED(this->write_registers(ES7210_RESET));
|
||||
|
||||
// Secondary I2S mode settings
|
||||
ES7210_ERROR_FAILED(this->update_register(ES7210_MODE_CONFIG_REG08, 0x01, 0x00));
|
||||
|
||||
// Configure analog power
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ANALOG_REG40, 0xC3));
|
||||
|
||||
// Set mic bias
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC12_BIAS_REG41, 0x70));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC34_BIAS_REG42, 0x70));
|
||||
ES7210_ERROR_FAILED(this->write_registers(ES7210_ANALOG));
|
||||
|
||||
// Configure I2S settings, sample rate, and microphone gains
|
||||
ES7210_ERROR_FAILED(this->configure_i2s_format_());
|
||||
ES7210_ERROR_FAILED(this->configure_sample_rate_());
|
||||
ES7210_ERROR_FAILED(this->configure_mic_gain_());
|
||||
|
||||
// Power on mics 1 through 4
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC1_POWER_REG47, 0x08));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC2_POWER_REG48, 0x08));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC3_POWER_REG49, 0x08));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC4_POWER_REG4A, 0x08));
|
||||
|
||||
// Power down DLL
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_POWER_DOWN_REG06, 0x04));
|
||||
|
||||
// Power on MIC1-4 bias & ADC1-4 & PGA1-4 Power
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x0F));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x0F));
|
||||
|
||||
// Enable device
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x71));
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x41));
|
||||
ES7210_ERROR_FAILED(this->write_registers(ES7210_POWER_ON));
|
||||
|
||||
this->setup_complete_ = true;
|
||||
}
|
||||
|
||||
@@ -25,56 +25,62 @@ ESPHOME_LOG_TAG(TAG, "es7243e");
|
||||
|
||||
void ES7243E::dump_config() { ESP_LOGCONFIG(TAG, "ES7243E audio ADC:"); }
|
||||
|
||||
// clang-format off
|
||||
static constexpr i2c::I2CRegValue ES7243E_INIT[] PROGMEM = {
|
||||
{ES7243E_CLOCK_MGR_REG01, 0x3A},
|
||||
{ES7243E_RESET_REG00, 0x80},
|
||||
{ES7243E_TEST_MODE_REGF9, 0x00},
|
||||
{ES7243E_CLOCK_MGR_REG04, 0x02},
|
||||
{ES7243E_CLOCK_MGR_REG04, 0x01},
|
||||
{ES7243E_TEST_MODE_REGF9, 0x01},
|
||||
{ES7243E_RESET_REG00, 0x1E},
|
||||
{ES7243E_CLOCK_MGR_REG01, 0x00},
|
||||
{ES7243E_CLOCK_MGR_REG02, 0x00},
|
||||
{ES7243E_CLOCK_MGR_REG03, 0x20},
|
||||
{ES7243E_CLOCK_MGR_REG04, 0x01},
|
||||
{ES7243E_ADC_CTRL_REG0D, 0x00},
|
||||
{ES7243E_CLOCK_MGR_REG05, 0x00},
|
||||
{ES7243E_CLOCK_MGR_REG06, 0x03}, // SCLK=MCLK/4
|
||||
{ES7243E_CLOCK_MGR_REG07, 0x00}, // LRCK=MCLK/256
|
||||
{ES7243E_CLOCK_MGR_REG08, 0xFF}, // LRCK=MCLK/256
|
||||
{ES7243E_CLOCK_MGR_REG09, 0xCA},
|
||||
{ES7243E_SDP_REG0A, 0x85},
|
||||
{ES7243E_SDP_REG0B, 0x00},
|
||||
{ES7243E_ADC_CTRL_REG0E, 0xBF},
|
||||
{ES7243E_ADC_CTRL_REG0F, 0x80},
|
||||
{ES7243E_ADC_CTRL_REG14, 0x0C},
|
||||
{ES7243E_ADC_CTRL_REG15, 0x0C},
|
||||
{ES7243E_ANALOG_REG17, 0x02},
|
||||
{ES7243E_ANALOG_REG18, 0x26},
|
||||
{ES7243E_ANALOG_REG19, 0x77},
|
||||
{ES7243E_ANALOG_REG1A, 0xF4},
|
||||
{ES7243E_ANALOG_REG1B, 0x66},
|
||||
{ES7243E_ANALOG_REG1C, 0x44},
|
||||
{ES7243E_ANALOG_REG1E, 0x00},
|
||||
{ES7243E_ANALOG_REG1F, 0x0C},
|
||||
{ES7243E_ANALOG_REG20, 0x1A}, // PGA gain +30dB
|
||||
{ES7243E_ANALOG_REG21, 0x1A},
|
||||
{ES7243E_RESET_REG00, 0x80},
|
||||
{ES7243E_CLOCK_MGR_REG01, 0x3A},
|
||||
{ES7243E_ANALOG_REG16, 0x3F},
|
||||
{ES7243E_ANALOG_REG16, 0x00},
|
||||
{ES7243E_TEST_MODE_REGF9, 0x00},
|
||||
{ES7243E_CLOCK_MGR_REG04, 0x01},
|
||||
{ES7243E_ANALOG_REG17, 0x01},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static constexpr i2c::I2CRegValue ES7243E_START[] PROGMEM = {
|
||||
{ES7243E_RESET_REG00, 0x80},
|
||||
{ES7243E_CLOCK_MGR_REG01, 0x3A},
|
||||
{ES7243E_ANALOG_REG16, 0x3F},
|
||||
{ES7243E_ANALOG_REG16, 0x00},
|
||||
};
|
||||
|
||||
void ES7243E::setup() {
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x00));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x02));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x01));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x1E));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x00));
|
||||
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG02, 0x00));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG03, 0x20));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0D, 0x00));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG05, 0x00));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG06, 0x03)); // SCLK=MCLK/4
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG07, 0x00)); // LRCK=MCLK/256
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG08, 0xFF)); // LRCK=MCLK/256
|
||||
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG09, 0xCA));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_SDP_REG0A, 0x85));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_SDP_REG0B, 0x00));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0E, 0xBF));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0F, 0x80));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG14, 0x0C));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG15, 0x0C));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG17, 0x02));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG18, 0x26));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG19, 0x77));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1A, 0xF4));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1B, 0x66));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1C, 0x44));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1E, 0x00));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1F, 0x0C));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG20, 0x1A)); // PGA gain +30dB
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG21, 0x1A));
|
||||
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x3F));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x00));
|
||||
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x00));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG17, 0x01));
|
||||
ES7243E_ERROR_FAILED(this->write_registers(ES7243E_INIT));
|
||||
ES7243E_ERROR_FAILED(this->configure_mic_gain_());
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x3F));
|
||||
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x00));
|
||||
ES7243E_ERROR_FAILED(this->write_registers(ES7243E_START));
|
||||
|
||||
this->setup_complete_ = true;
|
||||
}
|
||||
|
||||
@@ -9,70 +9,54 @@ namespace esphome::es8156 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "es8156");
|
||||
|
||||
// Mark the component as failed; use only in setup
|
||||
#define ES8156_ERROR_FAILED(func) \
|
||||
if (!(func)) { \
|
||||
this->mark_failed(LOG_STR("Failed to initialize")); \
|
||||
return; \
|
||||
}
|
||||
static constexpr i2c::I2CRegValue ES8156_INIT[] PROGMEM = {
|
||||
// REG02 MODE CONFIG 1: Enable software mode for I2C control of volume/mute
|
||||
// Bit 2: SOFT_MODE_SEL=1 (software mode enabled)
|
||||
{ES8156_REG02_SCLK_MODE, 0x04},
|
||||
// Analog system configuration (active-low power down bits, active-high enables)
|
||||
// REG20 ANALOG SYSTEM: Configure analog signal path
|
||||
{ES8156_REG20_ANALOG_SYS1, 0x2A},
|
||||
// REG21 ANALOG SYSTEM: VSEL=0x1C (bias level ~120%), normal VREF ramp speed
|
||||
{ES8156_REG21_ANALOG_SYS2, 0x3C},
|
||||
// REG22 ANALOG SYSTEM: Line out mode (HPSW=0), OUT_MUTE=0 (not muted)
|
||||
{ES8156_REG22_ANALOG_SYS3, 0x00},
|
||||
// REG24 ANALOG SYSTEM: Low power mode for VREFBUF, HPCOM, DACVRP; DAC normal power
|
||||
// Bits 2:0 = 0x07: LPVREFBUF=1, LPHPCOM=1, LPDACVRP=1, LPDAC=0
|
||||
{ES8156_REG24_ANALOG_LP, 0x07},
|
||||
// REG23 ANALOG SYSTEM: Lowest bias (IBIAS_SW=0), VMIDLVL=VDDA/2, normal impedance
|
||||
{ES8156_REG23_ANALOG_SYS4, 0x00},
|
||||
// Timing and interface configuration
|
||||
// REG0A/0B TIME CONTROL: Fast state machine transitions
|
||||
{ES8156_REG0A_TIME_CONTROL1, 0x01},
|
||||
{ES8156_REG0B_TIME_CONTROL2, 0x01},
|
||||
// REG11 SDP INTERFACE CONFIG: Default I2S format (24-bit, I2S mode)
|
||||
{ES8156_REG11_DAC_SDP, 0x00},
|
||||
// REG19 EQ CONTROL 1: EQ disabled (EQ_ON=0), EQ_BAND_NUM=2
|
||||
{ES8156_REG19_EQ_CONTROL1, 0x20},
|
||||
// REG0D P2S CONTROL: Parallel-to-serial converter settings
|
||||
{ES8156_REG0D_P2S_CONTROL, 0x14},
|
||||
// REG09 MISC CONTROL 2: Default settings
|
||||
{ES8156_REG09_MISC_CONTROL2, 0x00},
|
||||
// REG18 MISC CONTROL 3: Stereo channel routing, no inversion
|
||||
// Bits 5:4 CHN_CROSS: 0=L→L/R→R, 1=L to both, 2=R to both, 3=swap L/R
|
||||
// Bits 3:2: LCH_INV/RCH_INV channel inversion
|
||||
{ES8156_REG18_MISC_CONTROL3, 0x00},
|
||||
// REG08 CLOCK OFF: Enable all internal clocks (0x3F = all clock gates open)
|
||||
{ES8156_REG08_CLOCK_ON_OFF, 0x3F},
|
||||
// REG00 RESET CONTROL: Reset sequence
|
||||
// First: RST_DIG=1 (assert digital reset)
|
||||
{ES8156_REG00_RESET, 0x02},
|
||||
// Then: CSM_ON=1 (enable chip state machine), RST_DIG=1
|
||||
{ES8156_REG00_RESET, 0x03},
|
||||
// REG25 ANALOG SYSTEM: Power up analog blocks
|
||||
// VMIDSEL=2 (normal VMID operation), PDN_ANA=0, ENREFR=0, ENHPCOM=0
|
||||
// PDN_DACVREFGEN=0, PDN_VREFBUF=0, PDN_DAC=0 (all enabled)
|
||||
{ES8156_REG25_ANALOG_SYS5, 0x20},
|
||||
};
|
||||
|
||||
void ES8156::setup() {
|
||||
// REG02 MODE CONFIG 1: Enable software mode for I2C control of volume/mute
|
||||
// Bit 2: SOFT_MODE_SEL=1 (software mode enabled)
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG02_SCLK_MODE, 0x04));
|
||||
|
||||
// Analog system configuration (active-low power down bits, active-high enables)
|
||||
// REG20 ANALOG SYSTEM: Configure analog signal path
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG20_ANALOG_SYS1, 0x2A));
|
||||
|
||||
// REG21 ANALOG SYSTEM: VSEL=0x1C (bias level ~120%), normal VREF ramp speed
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG21_ANALOG_SYS2, 0x3C));
|
||||
|
||||
// REG22 ANALOG SYSTEM: Line out mode (HPSW=0), OUT_MUTE=0 (not muted)
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG22_ANALOG_SYS3, 0x00));
|
||||
|
||||
// REG24 ANALOG SYSTEM: Low power mode for VREFBUF, HPCOM, DACVRP; DAC normal power
|
||||
// Bits 2:0 = 0x07: LPVREFBUF=1, LPHPCOM=1, LPDACVRP=1, LPDAC=0
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG24_ANALOG_LP, 0x07));
|
||||
|
||||
// REG23 ANALOG SYSTEM: Lowest bias (IBIAS_SW=0), VMIDLVL=VDDA/2, normal impedance
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG23_ANALOG_SYS4, 0x00));
|
||||
|
||||
// Timing and interface configuration
|
||||
// REG0A/0B TIME CONTROL: Fast state machine transitions
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0A_TIME_CONTROL1, 0x01));
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0B_TIME_CONTROL2, 0x01));
|
||||
|
||||
// REG11 SDP INTERFACE CONFIG: Default I2S format (24-bit, I2S mode)
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG11_DAC_SDP, 0x00));
|
||||
|
||||
// REG19 EQ CONTROL 1: EQ disabled (EQ_ON=0), EQ_BAND_NUM=2
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG19_EQ_CONTROL1, 0x20));
|
||||
|
||||
// REG0D P2S CONTROL: Parallel-to-serial converter settings
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0D_P2S_CONTROL, 0x14));
|
||||
|
||||
// REG09 MISC CONTROL 2: Default settings
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG09_MISC_CONTROL2, 0x00));
|
||||
|
||||
// REG18 MISC CONTROL 3: Stereo channel routing, no inversion
|
||||
// Bits 5:4 CHN_CROSS: 0=L→L/R→R, 1=L to both, 2=R to both, 3=swap L/R
|
||||
// Bits 3:2: LCH_INV/RCH_INV channel inversion
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG18_MISC_CONTROL3, 0x00));
|
||||
|
||||
// REG08 CLOCK OFF: Enable all internal clocks (0x3F = all clock gates open)
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG08_CLOCK_ON_OFF, 0x3F));
|
||||
|
||||
// REG00 RESET CONTROL: Reset sequence
|
||||
// First: RST_DIG=1 (assert digital reset)
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG00_RESET, 0x02));
|
||||
// Then: CSM_ON=1 (enable chip state machine), RST_DIG=1
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG00_RESET, 0x03));
|
||||
|
||||
// REG25 ANALOG SYSTEM: Power up analog blocks
|
||||
// VMIDSEL=2 (normal VMID operation), PDN_ANA=0, ENREFR=0, ENHPCOM=0
|
||||
// PDN_DACVREFGEN=0, PDN_VREFBUF=0, PDN_DAC=0 (all enabled)
|
||||
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG25_ANALOG_SYS5, 0x20));
|
||||
if (!this->write_registers(ES8156_INIT))
|
||||
this->mark_failed(LOG_STR("Failed to initialize"));
|
||||
}
|
||||
|
||||
void ES8156::dump_config() { ESP_LOGCONFIG(TAG, "ES8156 Audio Codec:"); }
|
||||
|
||||
@@ -22,79 +22,76 @@ ESPHOME_LOG_TAG(TAG, "es8388");
|
||||
return false; \
|
||||
}
|
||||
|
||||
static constexpr i2c::I2CRegValue ES8388_INIT[] PROGMEM = {
|
||||
// I2S worker mode
|
||||
{ES8388_MASTERMODE, 0x00},
|
||||
/* Chip Control and Power Management */
|
||||
{ES8388_CONTROL2, 0x50},
|
||||
// normal all and power up all
|
||||
{ES8388_CHIPPOWER, 0x00},
|
||||
// vmidsel/500k
|
||||
// EnRef=0,Play&Record Mode,(0x17-both of mic&play)
|
||||
{ES8388_CONTROL1, 0x12},
|
||||
// i2s 16 bits
|
||||
{ES8388_DACCONTROL1, 0x18},
|
||||
// sample freq 256
|
||||
// DACFsMode,SINGLE SPEED; DACFsRatio,256
|
||||
{ES8388_DACCONTROL2, 0x02},
|
||||
// 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
|
||||
{ES8388_DACCONTROL16, 0x00},
|
||||
// only left DAC to left mixer enable 0db
|
||||
{ES8388_DACCONTROL17, 0x90},
|
||||
// only right DAC to right mixer enable 0db
|
||||
{ES8388_DACCONTROL20, 0x90},
|
||||
// set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
|
||||
{ES8388_DACCONTROL21, 0x80},
|
||||
// vroi=0 - 1.5k VREF to analog output resistance (default)
|
||||
{ES8388_DACCONTROL23, 0x00},
|
||||
// power down adc and line in
|
||||
{ES8388_ADCPOWER, 0xFF},
|
||||
//@nightdav
|
||||
{ES8388_ADCCONTROL1, 0x00}, // +21dB : recommended value for ALC & voice recording
|
||||
// set to Mono Right
|
||||
{ES8388_ADCCONTROL3, 0x02},
|
||||
// I2S 16 Bits length and I2S serial audio data format
|
||||
{ES8388_ADCCONTROL4, 0x0d},
|
||||
// ADCFsMode,singel SPEED,RATIO=256
|
||||
{ES8388_ADCCONTROL5, 0x02},
|
||||
// ADC Volume
|
||||
{ES8388_ADCCONTROL8, 0x00},
|
||||
{ES8388_ADCCONTROL9, 0x00},
|
||||
//@nightDav
|
||||
// ALC Config (as recommended by ES8388 user guide for voice recording)
|
||||
// Reg 0x12 = 0xe2 (ALC enable, PGA Max. Gain=23.5dB, Min. Gain=0dB)
|
||||
{ES8388_ADCCONTROL10, 0xe2},
|
||||
// Reg 0x13 = 0xa0 (ALC Target=-1.5dB, ALC Hold time =0 mS)
|
||||
{ES8388_ADCCONTROL11, 0xa0},
|
||||
// Reg 0x14 = 0x12(Decay time =820uS , Attack time = 416 uS)
|
||||
{ES8388_ADCCONTROL12, 0x12},
|
||||
// Reg 0x15 = 0x06(ALC mode)
|
||||
{ES8388_ADCCONTROL13, 0x06},
|
||||
// Reg 0x16 = 0xc3(nose gate = -40.5dB, NGG = 0x01(mute ADC))
|
||||
{ES8388_ADCCONTROL14, 0xc3},
|
||||
// Power on ADC
|
||||
{ES8388_DACCONTROL21, 0x80},
|
||||
// Start state machine
|
||||
{ES8388_CHIPPOWER, 0xF0},
|
||||
};
|
||||
|
||||
static constexpr i2c::I2CRegValue ES8388_UNMUTE[] PROGMEM = {
|
||||
// unmute ADC with fade in
|
||||
{ES8388_ADCCONTROL7, 0x60},
|
||||
// unmute DAC with fade in
|
||||
{ES8388_DACCONTROL3, 0x20},
|
||||
// Power on ADC, Enable LIN&RIN, Power off MICBIAS, set int1lp to low power mode
|
||||
{ES8388_ADCPOWER, 0x09},
|
||||
};
|
||||
|
||||
void ES8388::setup() {
|
||||
// mute DAC
|
||||
this->set_mute_state_(true);
|
||||
|
||||
// I2S worker mode
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_MASTERMODE, 0x00));
|
||||
|
||||
/* Chip Control and Power Management */
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_CONTROL2, 0x50));
|
||||
// normal all and power up all
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0x00));
|
||||
|
||||
// vmidsel/500k
|
||||
// EnRef=0,Play&Record Mode,(0x17-both of mic&play)
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_CONTROL1, 0x12));
|
||||
|
||||
// i2s 16 bits
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL1, 0x18));
|
||||
// sample freq 256
|
||||
// DACFsMode,SINGLE SPEED; DACFsRatio,256
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL2, 0x02));
|
||||
// 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL16, 0x00));
|
||||
// only left DAC to left mixer enable 0db
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL17, 0x90));
|
||||
// only right DAC to right mixer enable 0db
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL20, 0x90));
|
||||
// set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL21, 0x80));
|
||||
// vroi=0 - 1.5k VREF to analog output resistance (default)
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL23, 0x00));
|
||||
|
||||
// power down adc and line in
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCPOWER, 0xFF));
|
||||
|
||||
//@nightdav
|
||||
ES8388_ERROR_FAILED(
|
||||
this->write_byte(ES8388_ADCCONTROL1, 0x00)); // +21dB : recommended value for ALC & voice recording
|
||||
|
||||
// set to Mono Right
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL3, 0x02));
|
||||
|
||||
// I2S 16 Bits length and I2S serial audio data format
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL4, 0x0d));
|
||||
// ADCFsMode,singel SPEED,RATIO=256
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL5, 0x02));
|
||||
|
||||
// ADC Volume
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL8, 0x00));
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL9, 0x00));
|
||||
|
||||
//@nightDav
|
||||
// ALC Config (as recommended by ES8388 user guide for voice recording)
|
||||
|
||||
// Reg 0x12 = 0xe2 (ALC enable, PGA Max. Gain=23.5dB, Min. Gain=0dB)
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL10, 0xe2));
|
||||
|
||||
// Reg 0x13 = 0xa0 (ALC Target=-1.5dB, ALC Hold time =0 mS)
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL11, 0xa0));
|
||||
// Reg 0x14 = 0x12(Decay time =820uS , Attack time = 416 uS)
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL12, 0x12));
|
||||
|
||||
// Reg 0x15 = 0x06(ALC mode)
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL13, 0x06));
|
||||
|
||||
// Reg 0x16 = 0xc3(nose gate = -40.5dB, NGG = 0x01(mute ADC))
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL14, 0xc3));
|
||||
|
||||
// Power on ADC
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL21, 0x80));
|
||||
|
||||
// Start state machine
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0xF0));
|
||||
ES8388_ERROR_FAILED(this->write_registers(ES8388_INIT));
|
||||
delay(1);
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0x00));
|
||||
|
||||
@@ -104,13 +101,7 @@ void ES8388::setup() {
|
||||
|
||||
this->set_mute_state_(false);
|
||||
|
||||
// unmute ADC with fade in
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL7, 0x60));
|
||||
// unmute DAC with fade in
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL3, 0x20));
|
||||
|
||||
// Power on ADC, Enable LIN&RIN, Power off MICBIAS, set int1lp to low power mode
|
||||
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCPOWER, 0x09));
|
||||
ES8388_ERROR_FAILED(this->write_registers(ES8388_UNMUTE));
|
||||
|
||||
#ifdef USE_ES8388_SELECT
|
||||
if (this->dac_output_select_ != nullptr) {
|
||||
@@ -152,8 +143,8 @@ void ES8388::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "ES8388 Audio Codec:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
#ifdef USE_ES8388_SELECT
|
||||
LOG_SELECT(" ", "DacOutputSelect", this->dac_output_select_);
|
||||
LOG_SELECT(" ", "ADCInputMicSelect", this->adc_input_mic_select_);
|
||||
LOG_SELECT(" ", "DAC Output", this->dac_output_select_);
|
||||
LOG_SELECT(" ", "ADC Input Mic", this->adc_input_mic_select_);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -24,14 +24,12 @@ ADC_INPUT_MIC_OPTIONS = AdcInputMicLine.options(
|
||||
)
|
||||
ADC_INPUT_MIC_OPTION_NAMES = list(ADC_INPUT_MIC_OPTIONS)
|
||||
|
||||
DacOutputSelect = es8388_ns.class_("DacOutputSelect", select.Select)
|
||||
ADCInputMicSelect = es8388_ns.class_("ADCInputMicSelect", select.Select)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
{
|
||||
cv.GenerateID(CONF_ES8388_ID): cv.use_id(ES8388),
|
||||
cv.Optional(CONF_DAC_OUTPUT): select.select_schema(
|
||||
DacOutputSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_CHIP,
|
||||
).extend(
|
||||
@@ -42,7 +40,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ADC_INPUT_MIC): select.select_schema(
|
||||
ADCInputMicSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_CHIP,
|
||||
).extend(
|
||||
@@ -61,12 +59,18 @@ async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_ES8388_ID])
|
||||
selects = select.sub_selects(config, parent=hub)
|
||||
await selects(
|
||||
CONF_DAC_OUTPUT, hub.set_dac_output_select, options=DAC_OUTPUT_OPTION_NAMES
|
||||
CONF_DAC_OUTPUT,
|
||||
hub.set_dac_output_select,
|
||||
options=DAC_OUTPUT_OPTION_NAMES,
|
||||
apply="set_dac_output(static_cast<es8388::DacOutputLine>(index))",
|
||||
publish="before",
|
||||
)
|
||||
await selects(
|
||||
CONF_ADC_INPUT_MIC,
|
||||
hub.set_adc_input_mic_select,
|
||||
options=ADC_INPUT_MIC_OPTION_NAMES,
|
||||
apply="set_adc_input_mic(static_cast<es8388::AdcInputMicLine>(index))",
|
||||
publish="before",
|
||||
)
|
||||
if (
|
||||
initial := config.get(CONF_DAC_OUTPUT, {}).get(CONF_INITIAL_OPTION)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "adc_input_mic_select.h"
|
||||
|
||||
namespace esphome::es8388 {
|
||||
|
||||
void ADCInputMicSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_adc_input_mic(static_cast<AdcInputMicLine>(index));
|
||||
}
|
||||
|
||||
} // namespace esphome::es8388
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/es8388/es8388.h"
|
||||
#include "esphome/components/select/select.h"
|
||||
|
||||
namespace esphome::es8388 {
|
||||
|
||||
class ADCInputMicSelect final : public select::Select, public Parented<ES8388> {
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::es8388
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "dac_output_select.h"
|
||||
|
||||
namespace esphome::es8388 {
|
||||
|
||||
void DacOutputSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_dac_output(static_cast<DacOutputLine>(index));
|
||||
}
|
||||
|
||||
} // namespace esphome::es8388
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/es8388/es8388.h"
|
||||
#include "esphome/components/select/select.h"
|
||||
|
||||
namespace esphome::es8388 {
|
||||
|
||||
class DacOutputSelect final : public select::Select, public Parented<ES8388> {
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::es8388
|
||||
@@ -114,7 +114,8 @@ BLECharacteristicNotifyAction = esp32_ble_server_automations_ns.class_(
|
||||
"BLECharacteristicNotifyAction", automation.Action
|
||||
)
|
||||
bytebuffer_ns = cg.esphome_ns.namespace("bytebuffer")
|
||||
Endianness_ns = bytebuffer_ns.namespace("Endian")
|
||||
# Unscoped in C++; is_class keeps the generated Endian::LITTLE spelling
|
||||
Endian = bytebuffer_ns.enum("Endian", is_class=True)
|
||||
|
||||
|
||||
PROPERTY_MAP = {
|
||||
@@ -410,10 +411,7 @@ def value_schema(default_type=UNDEFINED, templatable=True):
|
||||
),
|
||||
cv.Optional(CONF_STRING_ENCODING, default="utf_8"): validate_encoding,
|
||||
cv.Optional(CONF_ENDIANNESS, default="LITTLE"): cv.enum(
|
||||
{
|
||||
"LITTLE": Endianness_ns.LITTLE,
|
||||
"BIG": Endianness_ns.BIG,
|
||||
}
|
||||
Endian.options("LITTLE", "BIG")
|
||||
),
|
||||
},
|
||||
validate_value_type,
|
||||
|
||||
@@ -54,25 +54,21 @@ CAN_MODES = {
|
||||
# The supported bit rates differ between ESP32 variants.
|
||||
# See ESP-IDF Programming Guide --> API Reference --> Two-Wire Automotive Interface (TWAI)
|
||||
|
||||
CAN_SPEEDS_ESP32 = {
|
||||
"20KBPS": CanSpeed.CAN_20KBPS,
|
||||
"25KBPS": CanSpeed.CAN_25KBPS,
|
||||
"50KBPS": CanSpeed.CAN_50KBPS,
|
||||
"100KBPS": CanSpeed.CAN_100KBPS,
|
||||
"125KBPS": CanSpeed.CAN_125KBPS,
|
||||
"250KBPS": CanSpeed.CAN_250KBPS,
|
||||
"500KBPS": CanSpeed.CAN_500KBPS,
|
||||
"800KBPS": CanSpeed.CAN_800KBPS,
|
||||
"1000KBPS": CanSpeed.CAN_1000KBPS,
|
||||
}
|
||||
CAN_SPEEDS_ESP32 = CanSpeed.options(
|
||||
"20KBPS",
|
||||
"25KBPS",
|
||||
"50KBPS",
|
||||
"100KBPS",
|
||||
"125KBPS",
|
||||
"250KBPS",
|
||||
"500KBPS",
|
||||
"800KBPS",
|
||||
"1000KBPS",
|
||||
prefix="CAN_",
|
||||
)
|
||||
|
||||
CAN_SPEEDS_ESP32_S2 = {
|
||||
"1KBPS": CanSpeed.CAN_1KBPS,
|
||||
"5KBPS": CanSpeed.CAN_5KBPS,
|
||||
"10KBPS": CanSpeed.CAN_10KBPS,
|
||||
"12K5BPS": CanSpeed.CAN_12K5BPS,
|
||||
"16KBPS": CanSpeed.CAN_16KBPS,
|
||||
"20KBPS": CanSpeed.CAN_20KBPS,
|
||||
**CanSpeed.options("1KBPS", "5KBPS", "10KBPS", "12K5BPS", "16KBPS", prefix="CAN_"),
|
||||
**CAN_SPEEDS_ESP32,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "led_strip.h"
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
||||
#ifdef USE_ESP32
|
||||
@@ -14,6 +15,8 @@ namespace esphome::esp32_rmt_led_strip {
|
||||
ESPHOME_LOG_TAG(TAG, "esp32_rmt_led_strip");
|
||||
|
||||
static const size_t RMT_SYMBOLS_PER_BYTE = 8;
|
||||
// Idle time the line must stay low between frames so the strip latches
|
||||
static constexpr uint32_t RESET_GAP_US = 50;
|
||||
|
||||
// Query the RMT default clock source frequency. This varies by variant:
|
||||
// APB (80MHz) on ESP32/S2/S3/C3, PLL_F80M (80MHz) on C6/P4, XTAL (32MHz) on H2.
|
||||
@@ -91,10 +94,20 @@ void ESP32RMTLEDStripLightOutput::setup() {
|
||||
this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1);
|
||||
#endif
|
||||
|
||||
const uint32_t resolution_hz = rmt_resolution_hz();
|
||||
// Longest wire time of one frame; write_state() defers instead of blocking until it has elapsed
|
||||
uint32_t bit_ticks = std::max(this->params_.bit0.duration0 + this->params_.bit0.duration1,
|
||||
this->params_.bit1.duration0 + this->params_.bit1.duration1);
|
||||
uint64_t frame_ticks = (uint64_t) bit_ticks * buffer_size * RMT_SYMBOLS_PER_BYTE + this->params_.reset.duration0 +
|
||||
this->params_.reset.duration1;
|
||||
this->frame_time_us_ = frame_ticks * 1000000 / resolution_hz + RESET_GAP_US;
|
||||
// Seed with the interval write_state() gates on, so the first frame is not deferred
|
||||
this->last_refresh_ = micros() - std::max(this->frame_time_us_, this->max_refresh_rate_);
|
||||
|
||||
rmt_tx_channel_config_t channel;
|
||||
memset(&channel, 0, sizeof(channel));
|
||||
channel.clk_src = RMT_CLK_SRC_DEFAULT;
|
||||
channel.resolution_hz = rmt_resolution_hz();
|
||||
channel.resolution_hz = resolution_hz;
|
||||
channel.gpio_num = gpio_num_t(this->pin_);
|
||||
channel.mem_block_symbols = this->rmt_symbols_;
|
||||
channel.trans_queue_depth = 1;
|
||||
@@ -157,22 +170,21 @@ void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bi
|
||||
}
|
||||
|
||||
void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
|
||||
uint32_t now = micros();
|
||||
if (this->throttle_show_(now, this->last_refresh_, this->max_refresh_rate_))
|
||||
// Previous frame still on the wire, inside its reset gap, or refreshing too often: try again next loop
|
||||
if (this->throttle_show_(micros(), this->last_refresh_, std::max(this->frame_time_us_, this->max_refresh_rate_)))
|
||||
return;
|
||||
this->last_refresh_ = now;
|
||||
|
||||
// The gate above should always cover the previous frame; never overwrite rmt_buf_ while the RMT still reads it
|
||||
if (rmt_tx_wait_all_done(this->channel_, 0) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "RMT TX still busy");
|
||||
this->status_set_warning();
|
||||
this->schedule_show();
|
||||
return;
|
||||
}
|
||||
this->mark_shown_();
|
||||
|
||||
ESP_LOGVV(TAG, "Writing RGB values to bus");
|
||||
|
||||
esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "RMT TX timeout");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
delayMicroseconds(50);
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
memcpy(this->rmt_buf_, this->buf_, this->get_buffer_size_());
|
||||
#else
|
||||
@@ -203,13 +215,19 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
|
||||
rmt_transmit_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, this->get_buffer_size_(), &config);
|
||||
esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, this->get_buffer_size_(), &config);
|
||||
#else
|
||||
error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config);
|
||||
esp_err_t error =
|
||||
rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config);
|
||||
#endif
|
||||
if (!this->status_comm_result(error == ESP_OK, nullptr)) {
|
||||
ESP_LOGE(TAG, "RMT TX error");
|
||||
// retry next loop iteration so the frame is not lost
|
||||
this->schedule_show();
|
||||
return;
|
||||
}
|
||||
// Stamped after the transfer started so the gate above stays conservative
|
||||
this->last_refresh_ = micros();
|
||||
}
|
||||
|
||||
void ESP32RMTLEDStripLightOutput::dump_config() {
|
||||
|
||||
@@ -81,6 +81,8 @@ class ESP32RMTLEDStripLightOutput final : public light::AddressableLight {
|
||||
|
||||
uint32_t last_refresh_{0};
|
||||
uint32_t max_refresh_rate_{0};
|
||||
// Worst-case wire time of one frame plus the reset gap, computed in setup()
|
||||
uint32_t frame_time_us_{0};
|
||||
};
|
||||
|
||||
} // namespace esphome::esp32_rmt_led_strip
|
||||
|
||||
@@ -9,24 +9,23 @@ from .. import (
|
||||
CONF_ESPECTRE_ID,
|
||||
DOMAIN,
|
||||
ESPectreComponent,
|
||||
espectre_ns,
|
||||
supported_traffic_generator_modes,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["espectre"]
|
||||
|
||||
TrafficModeSelect = espectre_ns.class_(
|
||||
"TrafficModeSelect", select.Select, cg.Parented.template(ESPectreComponent)
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = select.select_schema(
|
||||
TrafficModeSelect, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
select.ApplySelect, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
).extend({cv.GenerateID(CONF_ESPECTRE_ID): cv.use_id(ESPectreComponent)})
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
options = supported_traffic_generator_modes(CORE.config[DOMAIN])
|
||||
var = await select.new_select(config, options=options)
|
||||
await cg.register_parented(var, config[CONF_ESPECTRE_ID])
|
||||
parent = await cg.get_variable(config[CONF_ESPECTRE_ID])
|
||||
cg.add(parent.set_traffic_mode_select(var))
|
||||
hub = await cg.get_variable(config[CONF_ESPECTRE_ID])
|
||||
await select.sub_selects(config, parent=hub).create(
|
||||
config,
|
||||
hub.set_traffic_mode_select,
|
||||
options=options,
|
||||
apply="request_traffic_generator_mode(option)",
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
#include "espectre_select.h"
|
||||
|
||||
#ifdef USE_ESPECTRE
|
||||
|
||||
namespace esphome::espectre {
|
||||
|
||||
void TrafficModeSelect::control(size_t index) { this->parent_->request_traffic_generator_mode(this->option_at(index)); }
|
||||
|
||||
} // namespace esphome::espectre
|
||||
|
||||
#endif // USE_ESPECTRE
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_ESPECTRE
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../espectre.h"
|
||||
|
||||
namespace esphome::espectre {
|
||||
|
||||
class TrafficModeSelect final : public select::Select, public Parented<ESPectreComponent> {
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::espectre
|
||||
|
||||
#endif // USE_ESPECTRE
|
||||
@@ -1,14 +1,14 @@
|
||||
#include "ezo.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
namespace esphome::ezo {
|
||||
|
||||
static const char *const EZO_COMMAND_TYPE_STRINGS[] = {"EZO_READ", "EZO_LED", "EZO_DEVICE_INFORMATION",
|
||||
"EZO_SLOPE", "EZO_CALIBRATION", "EZO_SLEEP",
|
||||
"EZO_I2C", "EZO_T", "EZO_CUSTOM"};
|
||||
PROGMEM_STRING_TABLE(EzoCommandTypeStrings, "EZO_READ", "EZO_LED", "EZO_DEVICE_INFORMATION", "EZO_SLOPE",
|
||||
"EZO_CALIBRATION", "EZO_SLEEP", "EZO_I2C", "EZO_T", "EZO_CUSTOM");
|
||||
|
||||
static const char *const EZO_CALIBRATION_TYPE_STRINGS[] = {"LOW", "MID", "HIGH"};
|
||||
PROGMEM_STRING_TABLE(EzoCalibrationTypeStrings, "LOW", "MID", "HIGH");
|
||||
|
||||
void EZOSensor::dump_config() {
|
||||
LOG_SENSOR("", "EZO", this);
|
||||
@@ -103,7 +103,8 @@ void EZOSensor::loop() {
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Received buffer \"%s\" for command type %s", &buf[1], EZO_COMMAND_TYPE_STRINGS[to_run->command_type]);
|
||||
ESP_LOGV(TAG, "Received buffer \"%s\" for command type %s", &buf[1],
|
||||
LOG_STR_ARG(EzoCommandTypeStrings::get_log_str(to_run->command_type, EzoCommandTypeStrings::LAST_INDEX)));
|
||||
|
||||
if (buf[0] == 1) {
|
||||
std::string payload = reinterpret_cast<char *>(&buf[1]);
|
||||
@@ -168,7 +169,9 @@ void EZOSensor::add_command_(const char *command, EzoCommandType command_type, u
|
||||
void EZOSensor::set_calibration_point_(EzoCalibrationType type, float value) {
|
||||
// max 21: "Cal,"(4) + type(4) + ","(1) + float(11) + null; use 24 for safety
|
||||
char payload[24];
|
||||
snprintf(payload, sizeof(payload), "Cal,%s,%0.2f", EZO_CALIBRATION_TYPE_STRINGS[type], value);
|
||||
ESPHOME_snprintf_P(payload, sizeof(payload), ESPHOME_PSTR("Cal,%s,%0.2f"),
|
||||
LOG_STR_ARG(EzoCalibrationTypeStrings::get_log_str(type, EzoCalibrationTypeStrings::LAST_INDEX)),
|
||||
value);
|
||||
this->add_command_(payload, EzoCommandType::EZO_CALIBRATION, 900);
|
||||
}
|
||||
|
||||
@@ -176,7 +179,7 @@ void EZOSensor::set_address(uint8_t address) {
|
||||
if (address > 0 && address < 128) {
|
||||
// max 8: "I2C,"(4) + uint8(3) + null
|
||||
char payload[8];
|
||||
snprintf(payload, sizeof(payload), "I2C,%u", address);
|
||||
ESPHOME_snprintf_P(payload, sizeof(payload), ESPHOME_PSTR("I2C,%u"), address);
|
||||
this->new_address_ = address;
|
||||
this->add_command_(payload, EzoCommandType::EZO_I2C);
|
||||
} else {
|
||||
@@ -197,7 +200,7 @@ void EZOSensor::get_t() { this->add_command_("T,?", EzoCommandType::EZO_T); }
|
||||
void EZOSensor::set_t(float value) {
|
||||
// max 14 bytes: "T,"(2) + float with "%0.2f" (up to 11 chars) + null(1); use 16 for alignment
|
||||
char payload[16];
|
||||
snprintf(payload, sizeof(payload), "T,%0.2f", value);
|
||||
ESPHOME_snprintf_P(payload, sizeof(payload), ESPHOME_PSTR("T,%0.2f"), value);
|
||||
this->add_command_(payload, EzoCommandType::EZO_T);
|
||||
}
|
||||
|
||||
@@ -220,7 +223,7 @@ void EZOSensor::set_calibration_point_high(float value) {
|
||||
void EZOSensor::set_calibration_generic(float value) {
|
||||
// exact 16 bytes: "Cal," (4) + float with "%0.2f" (up to 11 chars, e.g. "-9999999.99") + null (1) = 16
|
||||
char payload[16];
|
||||
snprintf(payload, sizeof(payload), "Cal,%0.2f", value);
|
||||
ESPHOME_snprintf_P(payload, sizeof(payload), ESPHOME_PSTR("Cal,%0.2f"), value);
|
||||
this->add_command_(payload, EzoCommandType::EZO_CALIBRATION, 900);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,10 +34,7 @@ void FT63X6Touchscreen::setup() {
|
||||
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_ANY_EDGE);
|
||||
}
|
||||
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup();
|
||||
this->hard_reset_();
|
||||
}
|
||||
pulse_reset(this->reset_pin_, {.low_ms = 10});
|
||||
|
||||
// Get touch resolution
|
||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
||||
@@ -57,14 +54,6 @@ void FT63X6Touchscreen::setup() {
|
||||
this->write_byte(FT6X36_ADDR_TOUCHRATE_ACTIVE, 0x0E);
|
||||
}
|
||||
|
||||
void FT63X6Touchscreen::hard_reset_() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(10);
|
||||
this->reset_pin_->digital_write(true);
|
||||
}
|
||||
}
|
||||
|
||||
void FT63X6Touchscreen::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "FT63X6 Touchscreen:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
|
||||
@@ -27,7 +27,6 @@ class FT63X6Touchscreen final : public Touchscreen, public i2c::I2CDevice {
|
||||
void set_threshold(uint8_t threshold) { this->threshold_ = threshold; }
|
||||
|
||||
protected:
|
||||
void hard_reset_();
|
||||
void update_touches() override;
|
||||
|
||||
InternalGPIOPin *interrupt_pin_{nullptr};
|
||||
|
||||
@@ -83,13 +83,7 @@ void GSL3670Touchscreen::update_touches() {
|
||||
void GSL3670Touchscreen::clear_reg_() {
|
||||
ESP_LOGD(TAG, "clear_reg");
|
||||
|
||||
// GPIO reset pulse
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(1);
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(5);
|
||||
}
|
||||
pulse_reset(this->reset_pin_, {.low_ms = 1, .high_after_ms = 5});
|
||||
|
||||
this->write_reg8_(0x88, 0x01);
|
||||
// delay(5);
|
||||
@@ -106,12 +100,7 @@ void GSL3670Touchscreen::clear_reg_() {
|
||||
void GSL3670Touchscreen::reset_() {
|
||||
ESP_LOGD(TAG, "reset");
|
||||
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(1);
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(5);
|
||||
}
|
||||
pulse_reset(this->reset_pin_, {.low_ms = 1, .high_after_ms = 5});
|
||||
|
||||
this->write_reg8_(0xe4, 0x04);
|
||||
|
||||
|
||||
@@ -3,7 +3,11 @@ import logging
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import climate, logger, uart
|
||||
from esphome.components.climate import ClimateMode, ClimatePreset, ClimateSwingMode
|
||||
from esphome.components.climate import (
|
||||
CLIMATE_MODES,
|
||||
CLIMATE_PRESETS,
|
||||
CLIMATE_SWING_MODES,
|
||||
)
|
||||
from esphome.components.logger import DOMAIN as LOGGER_DOMAIN
|
||||
from esphome.components.wifi import DOMAIN as WIFI_DOMAIN
|
||||
import esphome.config_validation as cv
|
||||
@@ -77,31 +81,19 @@ AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS = AirflowHorizontalDirection.options(
|
||||
)
|
||||
|
||||
SUPPORTED_SWING_MODES_OPTIONS = {
|
||||
"OFF": ClimateSwingMode.CLIMATE_SWING_OFF,
|
||||
"VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL,
|
||||
"HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL,
|
||||
"BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH,
|
||||
k: CLIMATE_SWING_MODES[k] for k in ("OFF", "VERTICAL", "HORIZONTAL", "BOTH")
|
||||
}
|
||||
|
||||
SUPPORTED_CLIMATE_MODES_OPTIONS = {
|
||||
"OFF": ClimateMode.CLIMATE_MODE_OFF, # always available
|
||||
"HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL, # always available
|
||||
"COOL": ClimateMode.CLIMATE_MODE_COOL,
|
||||
"HEAT": ClimateMode.CLIMATE_MODE_HEAT,
|
||||
"DRY": ClimateMode.CLIMATE_MODE_DRY,
|
||||
"FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY,
|
||||
k: CLIMATE_MODES[k] for k in ("OFF", "HEAT_COOL", "COOL", "HEAT", "DRY", "FAN_ONLY")
|
||||
}
|
||||
|
||||
SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = {
|
||||
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
|
||||
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
|
||||
"COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT,
|
||||
k: CLIMATE_PRESETS[k] for k in ("AWAY", "BOOST", "COMFORT")
|
||||
}
|
||||
|
||||
SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = {
|
||||
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
|
||||
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
|
||||
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
|
||||
k: CLIMATE_PRESETS[k] for k in ("AWAY", "BOOST", "SLEEP")
|
||||
}
|
||||
|
||||
HonControlMethod = haier_ns.enum("HonControlMethod", True)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "esphome/components/wifi/wifi_component.h"
|
||||
#endif
|
||||
#include "haier_base.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
using namespace esphome::climate;
|
||||
using namespace esphome::uart;
|
||||
@@ -19,28 +20,16 @@ constexpr size_t PROTOCOL_INITIALIZATION_INTERVAL = 10000;
|
||||
constexpr size_t DEFAULT_MESSAGES_INTERVAL_MS = 2000;
|
||||
constexpr size_t CONTROL_MESSAGES_INTERVAL_MS = 400;
|
||||
|
||||
const char *HaierClimateBase::phase_to_string_(ProtocolPhases phase) {
|
||||
static const char *phase_names[] = {
|
||||
"SENDING_INIT_1",
|
||||
"SENDING_INIT_2",
|
||||
"SENDING_FIRST_STATUS_REQUEST",
|
||||
"SENDING_FIRST_ALARM_STATUS_REQUEST",
|
||||
"IDLE",
|
||||
"SENDING_STATUS_REQUEST",
|
||||
"SENDING_UPDATE_SIGNAL_REQUEST",
|
||||
"SENDING_SIGNAL_LEVEL",
|
||||
"SENDING_CONTROL",
|
||||
"SENDING_ACTION_COMMAND",
|
||||
"SENDING_ALARM_STATUS_REQUEST",
|
||||
"UNKNOWN" // Should be the last!
|
||||
};
|
||||
static_assert(
|
||||
(sizeof(phase_names) / sizeof(char *)) == (((int) ProtocolPhases::NUM_PROTOCOL_PHASES) + 1),
|
||||
"Wrong phase_names array size. Please, make sure that this array is aligned with the enum ProtocolPhases");
|
||||
int phase_index = (int) phase;
|
||||
if ((phase_index > (int) ProtocolPhases::NUM_PROTOCOL_PHASES) || (phase_index < 0))
|
||||
phase_index = (int) ProtocolPhases::NUM_PROTOCOL_PHASES;
|
||||
return phase_names[phase_index];
|
||||
PROGMEM_STRING_TABLE(ProtocolPhaseStrings, "SENDING_INIT_1", "SENDING_INIT_2", "SENDING_FIRST_STATUS_REQUEST",
|
||||
"SENDING_FIRST_ALARM_STATUS_REQUEST", "IDLE", "SENDING_STATUS_REQUEST",
|
||||
"SENDING_UPDATE_SIGNAL_REQUEST", "SENDING_SIGNAL_LEVEL", "SENDING_CONTROL",
|
||||
"SENDING_ACTION_COMMAND", "SENDING_ALARM_STATUS_REQUEST", "UNKNOWN");
|
||||
|
||||
const LogString *HaierClimateBase::phase_to_string_(ProtocolPhases phase) {
|
||||
static_assert(ProtocolPhaseStrings::COUNT == ((int) ProtocolPhases::NUM_PROTOCOL_PHASES) + 1,
|
||||
"ProtocolPhaseStrings must match the enum ProtocolPhases, with UNKNOWN last");
|
||||
// UNKNOWN (-1) wraps past the table and reads as the last entry, "UNKNOWN"
|
||||
return ProtocolPhaseStrings::get_log_str(static_cast<uint8_t>(phase), ProtocolPhaseStrings::LAST_INDEX);
|
||||
}
|
||||
|
||||
bool check_timeout(std::chrono::steady_clock::time_point now, std::chrono::steady_clock::time_point tpoint,
|
||||
@@ -71,7 +60,8 @@ HaierClimateBase::~HaierClimateBase() {}
|
||||
|
||||
void HaierClimateBase::set_phase(ProtocolPhases phase) {
|
||||
if (this->protocol_phase_ != phase) {
|
||||
ESP_LOGV(TAG, "Phase transition: %s => %s", phase_to_string_(this->protocol_phase_), phase_to_string_(phase));
|
||||
ESP_LOGV(TAG, "Phase transition: %s => %s", LOG_STR_ARG(phase_to_string_(this->protocol_phase_)),
|
||||
LOG_STR_ARG(phase_to_string_(phase)));
|
||||
this->protocol_phase_ = phase;
|
||||
}
|
||||
}
|
||||
@@ -225,7 +215,7 @@ haier_protocol::HandlerError HaierClimateBase::report_network_status_answer_hand
|
||||
|
||||
haier_protocol::HandlerError HaierClimateBase::timeout_default_handler_(haier_protocol::FrameType request_type) {
|
||||
ESP_LOGW(TAG, "Answer timeout for command %02X, phase %s", (uint8_t) request_type,
|
||||
phase_to_string_(this->protocol_phase_));
|
||||
LOG_STR_ARG(phase_to_string_(this->protocol_phase_)));
|
||||
if (this->protocol_phase_ > ProtocolPhases::IDLE) {
|
||||
this->set_phase(ProtocolPhases::IDLE);
|
||||
} else {
|
||||
|
||||
@@ -95,7 +95,7 @@ class HaierClimateBase : public esphome::Component,
|
||||
SENDING_ALARM_STATUS_REQUEST,
|
||||
NUM_PROTOCOL_PHASES
|
||||
};
|
||||
const char *phase_to_string_(ProtocolPhases phase);
|
||||
const LogString *phase_to_string_(ProtocolPhases phase);
|
||||
virtual void set_handlers() = 0;
|
||||
virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
|
||||
virtual haier_protocol::HaierMessage get_control_message() = 0; // NOLINT(readability-identifier-naming)
|
||||
|
||||
@@ -564,7 +564,7 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) {
|
||||
default:
|
||||
// Shouldn't get here
|
||||
ESP_LOGE(TAG, "Wrong protocol handler state: %s (%d), resetting communication",
|
||||
phase_to_string_(this->protocol_phase_), (int) this->protocol_phase_);
|
||||
LOG_STR_ARG(phase_to_string_(this->protocol_phase_)), (int) this->protocol_phase_);
|
||||
this->set_phase(ProtocolPhases::SENDING_INIT_1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -9,12 +9,9 @@ from ..climate import (
|
||||
CONF_HORIZONTAL_AIRFLOW,
|
||||
CONF_VERTICAL_AIRFLOW,
|
||||
HonClimate,
|
||||
haier_ns,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@paveldn"]
|
||||
VerticalAirflowSelect = haier_ns.class_("VerticalAirflowSelect", select.Select)
|
||||
HorizontalAirflowSelect = haier_ns.class_("HorizontalAirflowSelect", select.Select)
|
||||
|
||||
# Same order as VERTICAL_SWING_MODES_ORDER and HORIZONTAL_SWING_MODES_ORDER in hon_climate.h
|
||||
AIRFLOW_VERTICAL_DIRECTION_OPTIONS = [
|
||||
@@ -39,12 +36,12 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_HAIER_ID): cv.use_id(HonClimate),
|
||||
cv.Optional(CONF_VERTICAL_AIRFLOW): select.select_schema(
|
||||
VerticalAirflowSelect,
|
||||
select.ApplySelect,
|
||||
icon="mdi:arrow-expand-vertical",
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(CONF_HORIZONTAL_AIRFLOW): select.select_schema(
|
||||
HorizontalAirflowSelect,
|
||||
select.ApplySelect,
|
||||
icon="mdi:arrow-expand-horizontal",
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
@@ -59,9 +56,13 @@ async def to_code(config: ConfigType) -> None:
|
||||
CONF_VERTICAL_AIRFLOW,
|
||||
hub.set_vertical_airflow_select,
|
||||
options=AIRFLOW_VERTICAL_DIRECTION_OPTIONS,
|
||||
apply="set_vertical_airflow("
|
||||
"haier::airflow_order_at(haier::VERTICAL_SWING_MODES_ORDER, index))",
|
||||
)
|
||||
await selects(
|
||||
CONF_HORIZONTAL_AIRFLOW,
|
||||
hub.set_horizontal_airflow_select,
|
||||
options=AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS,
|
||||
apply="set_horizontal_airflow("
|
||||
"haier::airflow_order_at(haier::HORIZONTAL_SWING_MODES_ORDER, index))",
|
||||
)
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../hon_climate.h"
|
||||
|
||||
namespace esphome::haier {
|
||||
|
||||
class HorizontalAirflowSelect : public select::Select, public Parented<HonClimate> {
|
||||
protected:
|
||||
void control(size_t index) override {
|
||||
this->parent_->set_horizontal_airflow(airflow_order_at(HORIZONTAL_SWING_MODES_ORDER, index));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::haier
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../hon_climate.h"
|
||||
|
||||
namespace esphome::haier {
|
||||
|
||||
class VerticalAirflowSelect : public select::Select, public Parented<HonClimate> {
|
||||
protected:
|
||||
void control(size_t index) override {
|
||||
this->parent_->set_vertical_airflow(airflow_order_at(VERTICAL_SWING_MODES_ORDER, index));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::haier
|
||||
@@ -93,7 +93,7 @@ haier_protocol::HandlerError Smartair2Climate::messages_timeout_handler_with_cyc
|
||||
if (this->protocol_phase_ >= ProtocolPhases::IDLE)
|
||||
return HaierClimateBase::timeout_default_handler_(message_type);
|
||||
ESP_LOGI(TAG, "Answer timeout for command %02X, phase %s", (uint8_t) message_type,
|
||||
phase_to_string_(this->protocol_phase_));
|
||||
LOG_STR_ARG(phase_to_string_(this->protocol_phase_)));
|
||||
ProtocolPhases new_phase = (ProtocolPhases) ((int) this->protocol_phase_ + 1);
|
||||
if (new_phase >= ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST)
|
||||
new_phase = ProtocolPhases::SENDING_INIT_1;
|
||||
@@ -217,7 +217,7 @@ void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now)
|
||||
default:
|
||||
// Shouldn't get here
|
||||
ESP_LOGE(TAG, "Wrong protocol handler state: %s (%d), resetting communication",
|
||||
phase_to_string_(this->protocol_phase_), (int) this->protocol_phase_);
|
||||
LOG_STR_ARG(phase_to_string_(this->protocol_phase_)), (int) this->protocol_phase_);
|
||||
this->set_phase(ProtocolPhases::SENDING_INIT_1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "htu21d.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::htu21d {
|
||||
|
||||
@@ -112,11 +113,7 @@ void HTU21DComponent::set_heater(bool status) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
if (status) {
|
||||
raw_heater |= (1 << HTU21D_REG_HTRE_BIT);
|
||||
} else {
|
||||
raw_heater &= ~(1 << HTU21D_REG_HTRE_BIT);
|
||||
}
|
||||
assign_bits(raw_heater, 1 << HTU21D_REG_HTRE_BIT, status);
|
||||
if (this->write_register(HTU21D_WRITERHT_REG_CMD, &raw_heater, 1) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
|
||||
@@ -40,10 +40,7 @@ RG_MODELS = {
|
||||
# 1.200 - https://rainsensors.com/wp-content/uploads/sites/3/2022/03/2022.02.17-rev-1.200-rg-9_instructions.pdf
|
||||
}
|
||||
|
||||
RG15_RESOLUTION = {
|
||||
"low": RG15Resolution.FORCE_LOW,
|
||||
"high": RG15Resolution.FORCE_HIGH,
|
||||
}
|
||||
RG15_RESOLUTION = RG15Resolution.options("low", "high", prefix="FORCE_")
|
||||
|
||||
SUPPORTED_OPTIONS = {
|
||||
CONF_ACC: ["RG_15"],
|
||||
|
||||
@@ -270,21 +270,23 @@ void I2SAudioMicrophone::mic_task(void *params) {
|
||||
|
||||
{ // Ensures the samples vector is freed when the task stops
|
||||
|
||||
const size_t bytes_to_read = this_microphone->audio_stream_info_.ms_to_bytes(READ_DURATION_MS);
|
||||
// Copied so the format cannot change while the task runs
|
||||
const audio::AudioStreamInfo stream_info = this_microphone->audio_stream_info_;
|
||||
const size_t bytes_to_read = stream_info.ms_to_bytes(READ_DURATION_MS);
|
||||
std::vector<uint8_t> samples;
|
||||
samples.reserve(bytes_to_read);
|
||||
|
||||
xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
|
||||
|
||||
while (!(xEventGroupGetBits(this_microphone->event_group_) & MicrophoneEventGroupBits::COMMAND_STOP)) {
|
||||
if (this_microphone->data_callbacks_.size() > 0) {
|
||||
if (this_microphone->has_data_callbacks_()) {
|
||||
samples.resize(bytes_to_read);
|
||||
size_t bytes_read = this_microphone->read_(samples.data(), bytes_to_read, 2 * READ_DURATION_MS);
|
||||
samples.resize(bytes_read);
|
||||
if (this_microphone->correct_dc_offset_) {
|
||||
this_microphone->fix_dc_offset_(samples);
|
||||
}
|
||||
this_microphone->data_callbacks_.call(samples);
|
||||
this_microphone->publish_audio_(samples, stream_info);
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(READ_DURATION_MS));
|
||||
}
|
||||
|
||||
@@ -19,12 +19,7 @@ void ICNT86Touchscreen::setup() {
|
||||
}
|
||||
|
||||
// Perform reset if necessary
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup();
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(10);
|
||||
this->reset_pin_->digital_write(true);
|
||||
}
|
||||
pulse_reset(this->reset_pin_, {.low_ms = 10});
|
||||
|
||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
||||
this->x_raw_max_ = this->display_->get_native_width();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
"""Shared base for the infrared and radio_frequency entity components."""
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import remote_base
|
||||
from esphome.components.api import DOMAIN as API_DOMAIN
|
||||
from esphome.const import CONF_API
|
||||
from esphome.core import CORE
|
||||
from esphome.core.entity_helpers import queue_entity_register
|
||||
from esphome.types import ConfigType
|
||||
@@ -24,8 +23,10 @@ async def attach_transmitter(var: cg.MockObj, config: ConfigType, key: str) -> N
|
||||
does. Without this call anywhere in the build a request is answered as
|
||||
soon as the frame is handed over.
|
||||
"""
|
||||
from esphome.components import remote_base
|
||||
|
||||
await remote_base.register_transmittable(var, config, key)
|
||||
if API_DOMAIN in CORE.config:
|
||||
if CONF_API in CORE.config:
|
||||
cg.add_define("USE_IR_RF_TRANSMIT_COMPLETE")
|
||||
|
||||
|
||||
|
||||
@@ -2,11 +2,20 @@
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
namespace esphome::kamstrup_kmp {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "kamstrup_kmp");
|
||||
|
||||
// KMP units indexed by unit code; the empty first entry doubles as the fallback for unknown codes
|
||||
PROGMEM_STRING_TABLE(KmpUnits, "", "Wh", "kWh", "MWh", "GWh", "J", "kJ", "MJ", "GJ", "Cal", "kCal", "Mcal", "Gcal",
|
||||
"varh", "kvarh", "Mvarh", "Gvarh", "VAh", "kVAh", "MVAh", "GVAh", "kW", "kW", "MW", "GW", "kvar",
|
||||
"kvar", "Mvar", "Gvar", "VA", "kVA", "MVA", "GVA", "V", "A", "kV", "kA", "C", "K", "l", "m3",
|
||||
"l/h", "m3/h", "m3xC", "ton", "ton/h", "h", "hh:mm:ss", "yy:mm:dd", "yyyy:mm:dd", "mm:dd", "",
|
||||
"bar", "RTC", "ASCII", "m3 x 10", "ton x 10", "GJ x 10", "minutes", "Bitfield", "s", "ms", "days",
|
||||
"RTC-Q", "Datetime");
|
||||
|
||||
void KamstrupKMPComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "kamstrup_kmp:");
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
@@ -237,8 +246,6 @@ void KamstrupKMPComponent::parse_command_message_(uint16_t command, const uint8_
|
||||
}
|
||||
|
||||
void KamstrupKMPComponent::set_sensor_value_(uint16_t command, float value, uint8_t unit_idx) {
|
||||
const char *unit = unit_idx < sizeof(UNITS) / sizeof(UNITS[0]) ? UNITS[unit_idx] : "";
|
||||
|
||||
// Standard sensors
|
||||
if (command == CMD_HEAT_ENERGY && this->heat_energy_sensor_ != nullptr) {
|
||||
this->heat_energy_sensor_->publish_state(value);
|
||||
@@ -263,7 +270,8 @@ void KamstrupKMPComponent::set_sensor_value_(uint16_t command, float value, uint
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Received value for command 0x%04X: %.3f [%s]", command, value, unit);
|
||||
ESP_LOGD(TAG, "Received value for command 0x%04X: %.3f [%s]", command, value,
|
||||
LOG_STR_ARG(KmpUnits::get_log_str(unit_idx, 0)));
|
||||
}
|
||||
|
||||
} // namespace esphome::kamstrup_kmp
|
||||
@@ -63,16 +63,6 @@ static const uint16_t CMD_TEMP_DIFF = 0x0059;
|
||||
static const uint16_t CMD_FLOW = 0x004A;
|
||||
static const uint16_t CMD_VOLUME = 0x0044;
|
||||
|
||||
// KMP units
|
||||
static const char *const UNITS[] = {
|
||||
"", "Wh", "kWh", "MWh", "GWh", "J", "kJ", "MJ", "GJ", "Cal",
|
||||
"kCal", "Mcal", "Gcal", "varh", "kvarh", "Mvarh", "Gvarh", "VAh", "kVAh", "MVAh",
|
||||
"GVAh", "kW", "kW", "MW", "GW", "kvar", "kvar", "Mvar", "Gvar", "VA",
|
||||
"kVA", "MVA", "GVA", "V", "A", "kV", "kA", "C", "K", "l",
|
||||
"m3", "l/h", "m3/h", "m3xC", "ton", "ton/h", "h", "hh:mm:ss", "yy:mm:dd", "yyyy:mm:dd",
|
||||
"mm:dd", "", "bar", "RTC", "ASCII", "m3 x 10", "ton x 10", "GJ x 10", "minutes", "Bitfield",
|
||||
"s", "ms", "days", "RTC-Q", "Datetime"};
|
||||
|
||||
class KamstrupKMPComponent final : public PollingComponent, public uart::UARTDevice {
|
||||
public:
|
||||
void set_heat_energy_sensor(sensor::Sensor *sensor) { this->heat_energy_sensor_ = sensor; }
|
||||
|
||||
@@ -13,11 +13,7 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_LD2410_ID, LD2410Component, ld2410_ns
|
||||
|
||||
BaudRateSelect = ld2410_ns.class_("BaudRateSelect", select.Select)
|
||||
DistanceResolutionSelect = ld2410_ns.class_("DistanceResolutionSelect", select.Select)
|
||||
LightOutControlSelect = ld2410_ns.class_("LightOutControlSelect", select.Select)
|
||||
from .. import CONF_LD2410_ID, LD2410Component
|
||||
|
||||
CONF_DISTANCE_RESOLUTION = "distance_resolution"
|
||||
CONF_LIGHT_FUNCTION = "light_function"
|
||||
@@ -27,22 +23,22 @@ CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
|
||||
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
|
||||
cv.Optional(CONF_DISTANCE_RESOLUTION): select.select_schema(
|
||||
DistanceResolutionSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RULER,
|
||||
),
|
||||
cv.Optional(CONF_LIGHT_FUNCTION): select.select_schema(
|
||||
LightOutControlSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_LIGHTBULB,
|
||||
),
|
||||
cv.Optional(CONF_OUT_PIN_LEVEL): select.select_schema(
|
||||
LightOutControlSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_SCALE,
|
||||
),
|
||||
cv.Optional(CONF_BAUD_RATE): select.select_schema(
|
||||
BaudRateSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_THERMOMETER,
|
||||
),
|
||||
@@ -57,18 +53,30 @@ async def to_code(config: ConfigType) -> None:
|
||||
CONF_DISTANCE_RESOLUTION,
|
||||
hub.set_distance_resolution_select,
|
||||
options=["0.2m", "0.75m"],
|
||||
apply=(
|
||||
"set_distance_resolution(static_cast<ld2410::DistanceResolution>("
|
||||
"progmem_read_byte(&ld2410::DISTANCE_RESOLUTIONS[index])))"
|
||||
),
|
||||
publish="before",
|
||||
)
|
||||
light = {"apply": "set_light_out_control()", "publish": "before"}
|
||||
await selects(
|
||||
CONF_OUT_PIN_LEVEL, hub.set_out_pin_level_select, options=["low", "high"]
|
||||
CONF_OUT_PIN_LEVEL,
|
||||
hub.set_out_pin_level_select,
|
||||
options=["low", "high"],
|
||||
**light,
|
||||
)
|
||||
await selects(
|
||||
CONF_LIGHT_FUNCTION,
|
||||
hub.set_light_function_select,
|
||||
options=["off", "below", "above"],
|
||||
**light,
|
||||
)
|
||||
await selects(
|
||||
CONF_BAUD_RATE,
|
||||
hub.set_baud_rate_select,
|
||||
apply="set_baud_rate(static_cast<ld2410::BaudRate>(index + 1))",
|
||||
publish="before",
|
||||
options=[
|
||||
"9600",
|
||||
"19200",
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "baud_rate_select.h"
|
||||
|
||||
namespace esphome::ld2410 {
|
||||
|
||||
void BaudRateSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_baud_rate(static_cast<BaudRate>(index + 1));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2410
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld2410.h"
|
||||
|
||||
namespace esphome::ld2410 {
|
||||
|
||||
class BaudRateSelect final : public select::Select, public Parented<LD2410Component> {
|
||||
public:
|
||||
BaudRateSelect() = default;
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2410
|
||||
@@ -1,11 +0,0 @@
|
||||
#include "distance_resolution_select.h"
|
||||
|
||||
namespace esphome::ld2410 {
|
||||
|
||||
void DistanceResolutionSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_distance_resolution(
|
||||
static_cast<DistanceResolution>(progmem_read_byte(&DISTANCE_RESOLUTIONS[index])));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2410
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld2410.h"
|
||||
|
||||
namespace esphome::ld2410 {
|
||||
|
||||
class DistanceResolutionSelect final : public select::Select, public Parented<LD2410Component> {
|
||||
public:
|
||||
DistanceResolutionSelect() = default;
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2410
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "light_out_control_select.h"
|
||||
|
||||
namespace esphome::ld2410 {
|
||||
|
||||
void LightOutControlSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_light_out_control();
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2410
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld2410.h"
|
||||
|
||||
namespace esphome::ld2410 {
|
||||
|
||||
class LightOutControlSelect final : public select::Select, public Parented<LD2410Component> {
|
||||
public:
|
||||
LightOutControlSelect() = default;
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2410
|
||||
@@ -13,11 +13,7 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_LD2412_ID, LD2412_ns, LD2412Component
|
||||
|
||||
BaudRateSelect = LD2412_ns.class_("BaudRateSelect", select.Select)
|
||||
DistanceResolutionSelect = LD2412_ns.class_("DistanceResolutionSelect", select.Select)
|
||||
LightOutControlSelect = LD2412_ns.class_("LightOutControlSelect", select.Select)
|
||||
from .. import CONF_LD2412_ID, LD2412Component
|
||||
|
||||
CONF_DISTANCE_RESOLUTION = "distance_resolution"
|
||||
CONF_LIGHT_FUNCTION = "light_function"
|
||||
@@ -27,17 +23,17 @@ CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
|
||||
cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component),
|
||||
cv.Optional(CONF_BAUD_RATE): select.select_schema(
|
||||
BaudRateSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_THERMOMETER,
|
||||
),
|
||||
cv.Optional(CONF_DISTANCE_RESOLUTION): select.select_schema(
|
||||
DistanceResolutionSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RULER,
|
||||
),
|
||||
cv.Optional(CONF_LIGHT_FUNCTION): select.select_schema(
|
||||
LightOutControlSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_LIGHTBULB,
|
||||
),
|
||||
@@ -66,16 +62,25 @@ async def to_code(config: ConfigType) -> None:
|
||||
"256000",
|
||||
"460800",
|
||||
],
|
||||
apply="set_baud_rate(static_cast<ld2412::BaudRate>(index + 1))",
|
||||
publish="before",
|
||||
)
|
||||
await selects(
|
||||
CONF_DISTANCE_RESOLUTION,
|
||||
hub.set_distance_resolution_select,
|
||||
options=["0.2m", "0.5m", "0.75m"],
|
||||
apply=(
|
||||
"set_distance_resolution(static_cast<ld2412::DistanceResolution>("
|
||||
"progmem_read_byte(&ld2412::DISTANCE_RESOLUTIONS[index])))"
|
||||
),
|
||||
publish="before",
|
||||
)
|
||||
await selects(
|
||||
CONF_LIGHT_FUNCTION,
|
||||
hub.set_light_function_select,
|
||||
options=["off", "below", "above"],
|
||||
apply="set_light_out_control()",
|
||||
publish="before",
|
||||
)
|
||||
# The pin level travels in the basic configuration command, not the light control one
|
||||
await selects(
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "baud_rate_select.h"
|
||||
|
||||
namespace esphome::ld2412 {
|
||||
|
||||
void BaudRateSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_baud_rate(static_cast<BaudRate>(index + 1));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2412
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld2412.h"
|
||||
|
||||
namespace esphome::ld2412 {
|
||||
|
||||
class BaudRateSelect final : public select::Select, public Parented<LD2412Component> {
|
||||
public:
|
||||
// User provided, not "= default": `new(p) BaudRateSelect()` would zero-fill .bss that is already zero.
|
||||
BaudRateSelect() {}
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2412
|
||||
@@ -1,11 +0,0 @@
|
||||
#include "distance_resolution_select.h"
|
||||
|
||||
namespace esphome::ld2412 {
|
||||
|
||||
void DistanceResolutionSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_distance_resolution(
|
||||
static_cast<DistanceResolution>(progmem_read_byte(&DISTANCE_RESOLUTIONS[index])));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2412
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld2412.h"
|
||||
|
||||
namespace esphome::ld2412 {
|
||||
|
||||
class DistanceResolutionSelect final : public select::Select, public Parented<LD2412Component> {
|
||||
public:
|
||||
// User provided, not "= default": `new(p) DistanceResolutionSelect()` would zero-fill .bss that is already zero.
|
||||
DistanceResolutionSelect() {}
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2412
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "light_out_control_select.h"
|
||||
|
||||
namespace esphome::ld2412 {
|
||||
|
||||
void LightOutControlSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_light_out_control();
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2412
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld2412.h"
|
||||
|
||||
namespace esphome::ld2412 {
|
||||
|
||||
class LightOutControlSelect final : public select::Select, public Parented<LD2412Component> {
|
||||
public:
|
||||
// User provided, not "= default": `new(p) LightOutControlSelect()` would zero-fill .bss that is already zero.
|
||||
LightOutControlSelect() {}
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2412
|
||||
@@ -4,7 +4,7 @@ import esphome.config_validation as cv
|
||||
from esphome.const import ENTITY_CATEGORY_CONFIG
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns
|
||||
from .. import CONF_LD2420_ID, LD2420Component
|
||||
|
||||
CONF_OPERATING_MODE = "operating_mode"
|
||||
CONF_SELECTS = [
|
||||
@@ -13,12 +13,11 @@ CONF_SELECTS = [
|
||||
"Simple",
|
||||
]
|
||||
|
||||
LD2420Select = ld2420_ns.class_("LD2420Select", select.Select, cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_LD2420_ID): cv.use_id(LD2420Component),
|
||||
cv.Required(CONF_OPERATING_MODE): select.select_schema(
|
||||
LD2420Select,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
}
|
||||
@@ -28,5 +27,9 @@ async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_LD2420_ID])
|
||||
selects = select.sub_selects(config, parent=hub)
|
||||
await selects(
|
||||
CONF_OPERATING_MODE, hub.set_operating_mode_select, options=CONF_SELECTS
|
||||
CONF_OPERATING_MODE,
|
||||
hub.set_operating_mode_select,
|
||||
options=CONF_SELECTS,
|
||||
apply="set_operating_mode(static_cast<ld2420::OpMode>(index + 1))",
|
||||
publish="before",
|
||||
)
|
||||
@@ -1,14 +0,0 @@
|
||||
#include "operating_mode_select.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::ld2420 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "ld2420.select");
|
||||
|
||||
void LD2420Select::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_operating_mode(static_cast<OpMode>(index + 1));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2420
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ld2420.h"
|
||||
#include "esphome/components/select/select.h"
|
||||
|
||||
namespace esphome::ld2420 {
|
||||
|
||||
class LD2420Select final : public Component, public select::Select, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420Select() = default;
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2420
|
||||
@@ -9,23 +9,21 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
|
||||
from .. import CONF_LD2450_ID, LD2450Component
|
||||
|
||||
CONF_ZONE_TYPE = "zone_type"
|
||||
|
||||
BaudRateSelect = ld2450_ns.class_("BaudRateSelect", select.Select)
|
||||
ZoneTypeSelect = ld2450_ns.class_("ZoneTypeSelect", select.Select)
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
|
||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||
cv.Optional(CONF_BAUD_RATE): select.select_schema(
|
||||
BaudRateSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_THERMOMETER,
|
||||
),
|
||||
cv.Optional(CONF_ZONE_TYPE): select.select_schema(
|
||||
ZoneTypeSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_THERMOMETER,
|
||||
),
|
||||
@@ -49,6 +47,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
"256000",
|
||||
"460800",
|
||||
],
|
||||
apply="set_baud_rate(static_cast<ld2450::BaudRate>(index + 1))",
|
||||
publish="before",
|
||||
)
|
||||
await selects(
|
||||
CONF_ZONE_TYPE,
|
||||
@@ -58,4 +58,6 @@ async def to_code(config: ConfigType) -> None:
|
||||
"Detection",
|
||||
"Filter",
|
||||
],
|
||||
apply="set_zone_type(static_cast<ld2450::ZoneType>(index))",
|
||||
publish="before",
|
||||
)
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "baud_rate_select.h"
|
||||
|
||||
namespace esphome::ld2450 {
|
||||
|
||||
void BaudRateSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_baud_rate(static_cast<BaudRate>(index + 1));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2450
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome::ld2450 {
|
||||
|
||||
class BaudRateSelect : public select::Select, public Parented<LD2450Component> {
|
||||
public:
|
||||
// User provided, not "= default": `new(p) BaudRateSelect()` would zero-fill .bss that is already zero.
|
||||
BaudRateSelect() {}
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2450
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "zone_type_select.h"
|
||||
|
||||
namespace esphome::ld2450 {
|
||||
|
||||
void ZoneTypeSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_zone_type(static_cast<ZoneType>(index));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2450
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome::ld2450 {
|
||||
|
||||
class ZoneTypeSelect : public select::Select, public Parented<LD2450Component> {
|
||||
public:
|
||||
// User provided, not "= default": `new(p) ZoneTypeSelect()` would zero-fill .bss that is already zero.
|
||||
ZoneTypeSelect() {}
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2450
|
||||
@@ -2,10 +2,11 @@ from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_WAKEUP_PIN
|
||||
from esphome.const import CONF_PLATFORM, CONF_WAKEUP_PIN
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .const import CONF_AUTO_WAKE, CONF_WAKEUP_PULSE
|
||||
from .const import CONF_AREA_CONFIG, CONF_AUTO_WAKE, CONF_LD6002B_ID, CONF_WAKEUP_PULSE
|
||||
|
||||
CODEOWNERS = ["@hepter"]
|
||||
DEPENDENCIES = ["uart"]
|
||||
@@ -69,3 +70,33 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_wakeup_pulse_ms(config[CONF_WAKEUP_PULSE].total_milliseconds))
|
||||
|
||||
cg.add(var.set_auto_wake(config[CONF_AUTO_WAKE]))
|
||||
|
||||
|
||||
def validate_unique_settings(domain: str, config: ConfigType) -> None:
|
||||
"""Reject a setting set in two platform blocks of one hub, which keeps one entity per setting."""
|
||||
hub_id = config[CONF_LD6002B_ID]
|
||||
entries = [
|
||||
entry
|
||||
for entry in fv.full_config.get().get(domain, [])
|
||||
if entry.get(CONF_PLATFORM) == DOMAIN and entry.get(CONF_LD6002B_ID) == hub_id
|
||||
]
|
||||
for key, value in config.items():
|
||||
if key in (CONF_PLATFORM, CONF_LD6002B_ID):
|
||||
continue
|
||||
# area_config groups six settings, so each one is checked on its own
|
||||
for path in (
|
||||
[[key, sub] for sub in value] if key == CONF_AREA_CONFIG else [[key]]
|
||||
):
|
||||
if sum(_has_path(entry, path) for entry in entries) > 1:
|
||||
raise cv.Invalid(
|
||||
f"{'.'.join(path)} is already set for this ld6002b in another {domain} block",
|
||||
path=path,
|
||||
)
|
||||
|
||||
|
||||
def _has_path(entry: ConfigType, path: list[str]) -> bool:
|
||||
for key in path:
|
||||
if key not in entry:
|
||||
return False
|
||||
entry = entry[key]
|
||||
return True
|
||||
@@ -1,3 +1,4 @@
|
||||
from esphome.automation import parent_ref
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
from esphome.components.select import DOMAIN as SELECT_DOMAIN
|
||||
@@ -9,6 +10,7 @@ from esphome.const import (
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
)
|
||||
from esphome.cpp_generator import ParameterListExpression
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -33,53 +35,52 @@ from ..const import (
|
||||
|
||||
DEPENDENCIES = ["ld6002b"]
|
||||
|
||||
LD6002BButton = ld6002b_ns.class_("LD6002BButton", button.Button)
|
||||
ButtonType = ld6002b_ns.enum("ButtonType", is_class=True)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LD6002B_ID): cv.use_id(LD6002BComponent),
|
||||
cv.Optional(CONF_APPLY_AREA): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
),
|
||||
cv.Optional(CONF_AUTO_INTERFERENCE): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
),
|
||||
cv.Optional(CONF_GET_AREAS): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Optional(CONF_CLEAR_INTERFERENCE): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
),
|
||||
cv.Optional(CONF_RESET_DETECTION_AREA): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
),
|
||||
cv.Optional(CONF_GET_DELAY): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Optional(CONF_GET_SENSITIVITY): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Optional(CONF_GET_TRIGGER_SPEED): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Optional(CONF_GET_Z_RANGE): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Optional(CONF_GET_INSTALLATION): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Optional(CONF_GET_LOW_POWER_MODE): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Optional(CONF_GET_LOW_POWER_SLEEP_TIME): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Optional(CONF_RESET_UNATTENDED): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
),
|
||||
cv.Optional(CONF_WAKE): button.button_schema(
|
||||
LD6002BButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
button.ApplyButton, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -130,8 +131,34 @@ BUTTON_MAP = {
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
for key, button_type in BUTTON_MAP.items():
|
||||
if button_config := config.get(key):
|
||||
b = cg.new_Pvariable(button_config[CONF_ID], button_type)
|
||||
await button.register_button(b, button_config)
|
||||
await cg.register_parented(b, config[CONF_LD6002B_ID])
|
||||
confs = [
|
||||
(button_type, conf)
|
||||
for key, button_type in BUTTON_MAP.items()
|
||||
if (conf := config.get(key)) is not None
|
||||
]
|
||||
if not confs:
|
||||
return
|
||||
hub = await cg.get_variable(config[CONF_LD6002B_ID])
|
||||
params = button.APPLY_ENTITY.params
|
||||
# One function for all buttons, picking the type by pointer: one class instead of one per button.
|
||||
# Named after the first button so two button blocks on one hub do not clash.
|
||||
fn = f"esphome__{confs[0][1][CONF_ID].id}__button_fn"
|
||||
cg.add_global(
|
||||
cg.RawStatement(f"static void {fn}({ParameterListExpression(*params)});")
|
||||
)
|
||||
template = cg.TemplateArguments(cg.RawExpression(fn))
|
||||
created = [
|
||||
(button_type, await button.new_button(conf, template))
|
||||
for button_type, conf in confs
|
||||
]
|
||||
*head, (last_type, _) = created
|
||||
chain = "".join(f"entity == {parent_ref(var)} ? {t} : " for t, var in head)
|
||||
cg.static_function(
|
||||
fn,
|
||||
cg.void,
|
||||
list(params),
|
||||
[
|
||||
f"const auto type = {chain}{last_type};",
|
||||
f"{parent_ref(hub)}->press_button(type);",
|
||||
],
|
||||
)
|
||||
@@ -1,7 +0,0 @@
|
||||
#include "ld6002b_button.h"
|
||||
|
||||
namespace esphome::ld6002b {
|
||||
|
||||
void LD6002BButton::press_action() { this->parent_->press_button(this->type_); }
|
||||
|
||||
} // namespace esphome::ld6002b
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/button/button.h"
|
||||
#include "../ld6002b.h"
|
||||
|
||||
namespace esphome::ld6002b {
|
||||
|
||||
class LD6002BButton : public button::Button, public Parented<LD6002BComponent> {
|
||||
public:
|
||||
explicit LD6002BButton(ButtonType type) : type_(type) {}
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
|
||||
ButtonType type_;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld6002b
|
||||
@@ -1336,92 +1336,74 @@ void LD6002BComponent::wake_() {
|
||||
[this]() { this->wakeup_pin_->digital_write(true); });
|
||||
}
|
||||
|
||||
void LD6002BComponent::set_number_value(NumberType type, float value) {
|
||||
switch (type) {
|
||||
case NumberType::HOLD_DELAY: {
|
||||
uint32_t delay = static_cast<uint32_t>(value);
|
||||
uint8_t data[4];
|
||||
write_u32_le(data, delay);
|
||||
this->queue_command_(TYPE_SET_HOLD_DELAY, data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case NumberType::Z_MIN:
|
||||
this->z_min_ = value;
|
||||
this->send_z_range_();
|
||||
break;
|
||||
case NumberType::Z_MAX:
|
||||
this->z_max_ = value;
|
||||
this->send_z_range_();
|
||||
break;
|
||||
case NumberType::LOW_POWER_SLEEP: {
|
||||
uint32_t sleep_ms = static_cast<uint32_t>(value);
|
||||
uint8_t data[4];
|
||||
write_u32_le(data, sleep_ms);
|
||||
this->queue_command_(TYPE_SET_LOW_POWER_SLEEP, data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case NumberType::AREA_X_MIN:
|
||||
this->area_x_min_ = value;
|
||||
this->area_edits_.x_min = value;
|
||||
break;
|
||||
case NumberType::AREA_X_MAX:
|
||||
this->area_x_max_ = value;
|
||||
this->area_edits_.x_max = value;
|
||||
break;
|
||||
case NumberType::AREA_Y_MIN:
|
||||
this->area_y_min_ = value;
|
||||
this->area_edits_.y_min = value;
|
||||
break;
|
||||
case NumberType::AREA_Y_MAX:
|
||||
this->area_y_max_ = value;
|
||||
this->area_edits_.y_max = value;
|
||||
break;
|
||||
case NumberType::AREA_Z_MIN:
|
||||
this->area_z_min_ = value;
|
||||
this->area_edits_.z_min = value;
|
||||
break;
|
||||
case NumberType::AREA_Z_MAX:
|
||||
this->area_z_max_ = value;
|
||||
this->area_edits_.z_max = value;
|
||||
break;
|
||||
#ifdef USE_NUMBER
|
||||
// Each setter picks its setting by entity pointer, so the entities share one generated class.
|
||||
void LD6002BComponent::set_number_value(number::Number *entity, float value) {
|
||||
if (entity == this->hold_delay_number_ || entity == this->low_power_sleep_number_) {
|
||||
uint8_t data[4];
|
||||
write_u32_le(data, static_cast<uint32_t>(value));
|
||||
this->queue_command_(entity == this->hold_delay_number_ ? TYPE_SET_HOLD_DELAY : TYPE_SET_LOW_POWER_SLEEP, data,
|
||||
sizeof(data));
|
||||
} else if (entity == this->z_min_number_) {
|
||||
this->z_min_ = value;
|
||||
this->send_z_range_();
|
||||
} else if (entity == this->z_max_number_) {
|
||||
this->z_max_ = value;
|
||||
this->send_z_range_();
|
||||
} else if (entity == this->area_x_min_number_) {
|
||||
this->area_x_min_ = value;
|
||||
this->area_edits_.x_min = value;
|
||||
} else if (entity == this->area_x_max_number_) {
|
||||
this->area_x_max_ = value;
|
||||
this->area_edits_.x_max = value;
|
||||
} else if (entity == this->area_y_min_number_) {
|
||||
this->area_y_min_ = value;
|
||||
this->area_edits_.y_min = value;
|
||||
} else if (entity == this->area_y_max_number_) {
|
||||
this->area_y_max_ = value;
|
||||
this->area_edits_.y_max = value;
|
||||
} else if (entity == this->area_z_min_number_) {
|
||||
this->area_z_min_ = value;
|
||||
this->area_edits_.z_min = value;
|
||||
} else { // Only registered entities call this, so the last one needs no compare
|
||||
this->area_z_max_ = value;
|
||||
this->area_edits_.z_max = value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void LD6002BComponent::set_select_value(SelectType type, size_t index) {
|
||||
switch (type) {
|
||||
case SelectType::SENSITIVITY:
|
||||
if (index == 0) {
|
||||
this->send_control_command_(CMD_SENSITIVITY_LOW);
|
||||
} else if (index == 1) {
|
||||
this->send_control_command_(CMD_SENSITIVITY_MEDIUM);
|
||||
} else if (index == 2) {
|
||||
this->send_control_command_(CMD_SENSITIVITY_HIGH);
|
||||
}
|
||||
break;
|
||||
case SelectType::TRIGGER_SPEED:
|
||||
if (index == 0) {
|
||||
this->send_control_command_(CMD_TRIGGER_SLOW);
|
||||
} else if (index == 1) {
|
||||
this->send_control_command_(CMD_TRIGGER_MEDIUM);
|
||||
} else if (index == 2) {
|
||||
this->send_control_command_(CMD_TRIGGER_FAST);
|
||||
}
|
||||
break;
|
||||
case SelectType::INSTALLATION_MODE:
|
||||
if (index == 0) {
|
||||
this->send_control_command_(CMD_INSTALL_TOP);
|
||||
} else if (index == 1) {
|
||||
this->send_control_command_(CMD_INSTALL_SIDE);
|
||||
}
|
||||
break;
|
||||
case SelectType::AREA_ID:
|
||||
this->area_id_ = static_cast<uint8_t>(index);
|
||||
this->area_id_set_ = true;
|
||||
this->update_area_numbers_for_id_(this->area_id_);
|
||||
this->save_area_id_pref_(this->area_id_);
|
||||
break;
|
||||
#ifdef USE_SELECT
|
||||
void LD6002BComponent::set_select_value(select::Select *entity, size_t index) {
|
||||
if (entity == this->sensitivity_select_) {
|
||||
if (index == 0) {
|
||||
this->send_control_command_(CMD_SENSITIVITY_LOW);
|
||||
} else if (index == 1) {
|
||||
this->send_control_command_(CMD_SENSITIVITY_MEDIUM);
|
||||
} else if (index == 2) {
|
||||
this->send_control_command_(CMD_SENSITIVITY_HIGH);
|
||||
}
|
||||
} else if (entity == this->trigger_speed_select_) {
|
||||
if (index == 0) {
|
||||
this->send_control_command_(CMD_TRIGGER_SLOW);
|
||||
} else if (index == 1) {
|
||||
this->send_control_command_(CMD_TRIGGER_MEDIUM);
|
||||
} else if (index == 2) {
|
||||
this->send_control_command_(CMD_TRIGGER_FAST);
|
||||
}
|
||||
} else if (entity == this->installation_select_) {
|
||||
if (index == 0) {
|
||||
this->send_control_command_(CMD_INSTALL_TOP);
|
||||
} else if (index == 1) {
|
||||
this->send_control_command_(CMD_INSTALL_SIDE);
|
||||
}
|
||||
} else { // Only registered entities call this, so the last one needs no compare
|
||||
this->area_id_ = static_cast<uint8_t>(index);
|
||||
this->area_id_set_ = true;
|
||||
this->update_area_numbers_for_id_(this->area_id_);
|
||||
this->save_area_id_pref_(this->area_id_);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void LD6002BComponent::update_area_numbers_(const AreaConfig &area) {
|
||||
// A report refreshes every axis the user is not in the middle of changing. An
|
||||
@@ -1749,37 +1731,35 @@ void LD6002BComponent::clear_target_state_() {
|
||||
}
|
||||
}
|
||||
|
||||
void LD6002BComponent::set_switch_state(SwitchType type, bool state) {
|
||||
switch (type) {
|
||||
case SwitchType::LOW_POWER:
|
||||
this->low_power_enabled_ = state;
|
||||
this->low_power_reported_ = true;
|
||||
this->send_control_command_(state ? CMD_LOW_POWER_ON : CMD_LOW_POWER_OFF);
|
||||
this->update_work_mode_fallback_();
|
||||
break;
|
||||
case SwitchType::POINT_CLOUD:
|
||||
this->point_cloud_enabled_ = state;
|
||||
this->send_control_command_(state ? CMD_POINT_CLOUD_ON : CMD_POINT_CLOUD_OFF);
|
||||
#ifdef USE_SWITCH
|
||||
void LD6002BComponent::set_switch_state(switch_::Switch *entity, bool state) {
|
||||
if (entity == this->low_power_switch_) {
|
||||
this->low_power_enabled_ = state;
|
||||
this->low_power_reported_ = true;
|
||||
this->send_control_command_(state ? CMD_LOW_POWER_ON : CMD_LOW_POWER_OFF);
|
||||
this->update_work_mode_fallback_();
|
||||
} else if (entity == this->point_cloud_switch_) {
|
||||
this->point_cloud_enabled_ = state;
|
||||
this->send_control_command_(state ? CMD_POINT_CLOUD_ON : CMD_POINT_CLOUD_OFF);
|
||||
#ifdef USE_SENSOR
|
||||
// The count only moves while the stream runs, so the last one would stand as
|
||||
// a live reading. The dedup sentinel is cleared with it: the same count is
|
||||
// new again when the stream comes back.
|
||||
if (!state && this->point_count_sensor_ != nullptr && this->last_point_count_ != 0xFFFFFFFF) {
|
||||
this->point_count_sensor_->publish_state(NAN);
|
||||
this->last_point_count_ = 0xFFFFFFFF;
|
||||
}
|
||||
// The count only moves while the stream runs, so the last one would stand as
|
||||
// a live reading. The dedup sentinel is cleared with it: the same count is
|
||||
// new again when the stream comes back.
|
||||
if (!state && this->point_count_sensor_ != nullptr && this->last_point_count_ != 0xFFFFFFFF) {
|
||||
this->point_count_sensor_->publish_state(NAN);
|
||||
this->last_point_count_ = 0xFFFFFFFF;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case SwitchType::TARGET_DISPLAY:
|
||||
this->target_display_enabled_ = state;
|
||||
this->send_control_command_(state ? CMD_TARGET_DISPLAY_ON : CMD_TARGET_DISPLAY_OFF);
|
||||
if (!state) {
|
||||
// Every target entity is fed by the reports this just stopped.
|
||||
this->clear_target_state_();
|
||||
}
|
||||
break;
|
||||
} else { // Only registered entities call this, so the last one needs no compare
|
||||
this->target_display_enabled_ = state;
|
||||
this->send_control_command_(state ? CMD_TARGET_DISPLAY_ON : CMD_TARGET_DISPLAY_OFF);
|
||||
if (!state) {
|
||||
// Every target entity is fed by the reports this just stopped.
|
||||
this->clear_target_state_();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void LD6002BComponent::press_button(ButtonType type) {
|
||||
switch (type) {
|
||||
|
||||
@@ -40,32 +40,6 @@ static constexpr size_t DEFAULT_MAX_DATA_LEN_POINT_CLOUD = 4096;
|
||||
// Largest protocol payload is TYPE_SET_AREA: int32 area id + 6 floats = 28 bytes.
|
||||
static constexpr size_t CMD_MAX_DATA_LEN = 28;
|
||||
|
||||
enum class NumberType : uint8_t {
|
||||
HOLD_DELAY,
|
||||
Z_MIN,
|
||||
Z_MAX,
|
||||
LOW_POWER_SLEEP,
|
||||
AREA_X_MIN,
|
||||
AREA_X_MAX,
|
||||
AREA_Y_MIN,
|
||||
AREA_Y_MAX,
|
||||
AREA_Z_MIN,
|
||||
AREA_Z_MAX,
|
||||
};
|
||||
|
||||
enum class SelectType : uint8_t {
|
||||
SENSITIVITY,
|
||||
TRIGGER_SPEED,
|
||||
INSTALLATION_MODE,
|
||||
AREA_ID,
|
||||
};
|
||||
|
||||
enum class SwitchType : uint8_t {
|
||||
LOW_POWER,
|
||||
POINT_CLOUD,
|
||||
TARGET_DISPLAY,
|
||||
};
|
||||
|
||||
enum class ButtonType : uint8_t {
|
||||
APPLY_AREA,
|
||||
AUTO_INTERFERENCE,
|
||||
@@ -264,9 +238,15 @@ class LD6002BComponent : public Component, public uart::UARTDevice {
|
||||
void set_target_display_switch(switch_::Switch *sw) { this->target_display_switch_ = sw; }
|
||||
#endif
|
||||
|
||||
void set_number_value(NumberType type, float value);
|
||||
void set_select_value(SelectType type, size_t index);
|
||||
void set_switch_state(SwitchType type, bool state);
|
||||
#ifdef USE_NUMBER
|
||||
void set_number_value(number::Number *entity, float value);
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void set_select_value(select::Select *entity, size_t index);
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
void set_switch_state(switch_::Switch *entity, bool state);
|
||||
#endif
|
||||
void press_button(ButtonType type);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -15,7 +15,7 @@ from esphome.const import (
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import LD6002BComponent, ld6002b_ns
|
||||
from .. import LD6002BComponent, validate_unique_settings
|
||||
from ..const import (
|
||||
CONF_APPLY_AREA,
|
||||
CONF_AREA_CONFIG,
|
||||
@@ -32,32 +32,29 @@ from ..const import (
|
||||
|
||||
DEPENDENCIES = ["ld6002b"]
|
||||
|
||||
LD6002BNumber = ld6002b_ns.class_("LD6002BNumber", number.Number)
|
||||
NumberType = ld6002b_ns.enum("NumberType", is_class=True)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LD6002B_ID): cv.use_id(LD6002BComponent),
|
||||
cv.Optional(CONF_HOLD_DELAY): number.number_schema(
|
||||
LD6002BNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_SECOND,
|
||||
device_class=DEVICE_CLASS_DURATION,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(CONF_Z_MIN): number.number_schema(
|
||||
LD6002BNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_METER,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(CONF_Z_MAX): number.number_schema(
|
||||
LD6002BNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_METER,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(CONF_LOW_POWER_SLEEP_TIME): number.number_schema(
|
||||
LD6002BNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_MILLISECOND,
|
||||
device_class=DEVICE_CLASS_DURATION,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
@@ -65,37 +62,37 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_AREA_CONFIG): cv.Schema(
|
||||
{
|
||||
cv.Optional(KEY_X_MIN): number.number_schema(
|
||||
LD6002BNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_METER,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(KEY_X_MAX): number.number_schema(
|
||||
LD6002BNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_METER,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(KEY_Y_MIN): number.number_schema(
|
||||
LD6002BNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_METER,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(KEY_Y_MAX): number.number_schema(
|
||||
LD6002BNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_METER,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(CONF_Z_MIN): number.number_schema(
|
||||
LD6002BNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_METER,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(CONF_Z_MAX): number.number_schema(
|
||||
LD6002BNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_METER,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
@@ -107,6 +104,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
def final_validate(config: ConfigType) -> None:
|
||||
validate_unique_settings(number.DOMAIN, config)
|
||||
if config.get(CONF_AREA_CONFIG) is None:
|
||||
return
|
||||
|
||||
@@ -139,42 +137,34 @@ FINAL_VALIDATE_SCHEMA = final_validate
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_LD6002B_ID])
|
||||
|
||||
for key, number_type, setter, min_value, max_value, step in (
|
||||
(CONF_HOLD_DELAY, NumberType.HOLD_DELAY, "set_hold_delay_number", 0, 65535, 1),
|
||||
(CONF_Z_MIN, NumberType.Z_MIN, "set_z_min_number", -10, 10, 0.1),
|
||||
(CONF_Z_MAX, NumberType.Z_MAX, "set_z_max_number", -10, 10, 0.1),
|
||||
# 0x0205 carries a uint32 of milliseconds; the vendor documents 500 ms as
|
||||
# the default and no upper bound, so the range ends at a minute rather
|
||||
# than at a default the module is free to be sleeping past.
|
||||
(
|
||||
CONF_LOW_POWER_SLEEP_TIME,
|
||||
NumberType.LOW_POWER_SLEEP,
|
||||
"set_low_power_sleep_number",
|
||||
0,
|
||||
60000,
|
||||
100,
|
||||
),
|
||||
):
|
||||
if conf := config.get(key):
|
||||
n = await number.new_number(
|
||||
conf, number_type, min_value=min_value, max_value=max_value, step=step
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD6002B_ID])
|
||||
cg.add(getattr(hub, setter)(n))
|
||||
control = {"apply": "set_number_value(entity, value)", "publish": "before"}
|
||||
numbers = number.sub_numbers(config, parent=hub)
|
||||
await numbers(
|
||||
CONF_HOLD_DELAY,
|
||||
hub.set_hold_delay_number,
|
||||
min_value=0,
|
||||
max_value=65535,
|
||||
step=1,
|
||||
**control,
|
||||
)
|
||||
meters = {"min_value": -10, "max_value": 10, "step": 0.1, **control}
|
||||
await numbers(CONF_Z_MIN, hub.set_z_min_number, **meters)
|
||||
await numbers(CONF_Z_MAX, hub.set_z_max_number, **meters)
|
||||
# 0x0205 is a uint32 of ms with no documented maximum; cap it at a minute
|
||||
await numbers(
|
||||
CONF_LOW_POWER_SLEEP_TIME,
|
||||
hub.set_low_power_sleep_number,
|
||||
min_value=0,
|
||||
max_value=60000,
|
||||
step=100,
|
||||
**control,
|
||||
)
|
||||
|
||||
if area_config := config.get(CONF_AREA_CONFIG):
|
||||
for key, number_type, setter in (
|
||||
(KEY_X_MIN, NumberType.AREA_X_MIN, "set_area_x_min_number"),
|
||||
(KEY_X_MAX, NumberType.AREA_X_MAX, "set_area_x_max_number"),
|
||||
(KEY_Y_MIN, NumberType.AREA_Y_MIN, "set_area_y_min_number"),
|
||||
(KEY_Y_MAX, NumberType.AREA_Y_MAX, "set_area_y_max_number"),
|
||||
(CONF_Z_MIN, NumberType.AREA_Z_MIN, "set_area_z_min_number"),
|
||||
(CONF_Z_MAX, NumberType.AREA_Z_MAX, "set_area_z_max_number"),
|
||||
):
|
||||
if conf := area_config.get(key):
|
||||
n = await number.new_number(
|
||||
conf, number_type, min_value=-10, max_value=10, step=0.1
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD6002B_ID])
|
||||
cg.add(getattr(hub, setter)(n))
|
||||
area = number.sub_numbers(area_config, parent=hub)
|
||||
await area(KEY_X_MIN, hub.set_area_x_min_number, **meters)
|
||||
await area(KEY_X_MAX, hub.set_area_x_max_number, **meters)
|
||||
await area(KEY_Y_MIN, hub.set_area_y_min_number, **meters)
|
||||
await area(KEY_Y_MAX, hub.set_area_y_max_number, **meters)
|
||||
await area(CONF_Z_MIN, hub.set_area_z_min_number, **meters)
|
||||
await area(CONF_Z_MAX, hub.set_area_z_max_number, **meters)
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "ld6002b_number.h"
|
||||
|
||||
namespace esphome::ld6002b {
|
||||
|
||||
void LD6002BNumber::control(float value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_number_value(this->type_, value);
|
||||
}
|
||||
|
||||
} // namespace esphome::ld6002b
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "../ld6002b.h"
|
||||
|
||||
namespace esphome::ld6002b {
|
||||
|
||||
class LD6002BNumber : public number::Number, public Parented<LD6002BComponent> {
|
||||
public:
|
||||
explicit LD6002BNumber(NumberType type) : type_(type) {}
|
||||
|
||||
protected:
|
||||
void control(float value) override;
|
||||
|
||||
NumberType type_;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld6002b
|
||||
@@ -1,17 +1,16 @@
|
||||
from functools import partial
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import select
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_AREA_ID, CONF_SENSITIVITY, ENTITY_CATEGORY_CONFIG
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import LD6002BComponent, ld6002b_ns
|
||||
from .. import LD6002BComponent, validate_unique_settings
|
||||
from ..const import CONF_INSTALLATION_MODE, CONF_LD6002B_ID, CONF_TRIGGER_SPEED
|
||||
|
||||
DEPENDENCIES = ["ld6002b"]
|
||||
|
||||
LD6002BSelect = ld6002b_ns.class_("LD6002BSelect", select.Select)
|
||||
SelectType = ld6002b_ns.enum("SelectType", is_class=True)
|
||||
|
||||
AREA_ID_OPTIONS = [
|
||||
"interference_area_0",
|
||||
"interference_area_1",
|
||||
@@ -27,49 +26,46 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LD6002B_ID): cv.use_id(LD6002BComponent),
|
||||
cv.Optional(CONF_SENSITIVITY): select.select_schema(
|
||||
LD6002BSelect, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
select.ApplySelect, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
),
|
||||
cv.Optional(CONF_TRIGGER_SPEED): select.select_schema(
|
||||
LD6002BSelect, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
select.ApplySelect, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
),
|
||||
cv.Optional(CONF_INSTALLATION_MODE): select.select_schema(
|
||||
LD6002BSelect, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
select.ApplySelect, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
),
|
||||
cv.Optional(CONF_AREA_ID): select.select_schema(
|
||||
LD6002BSelect, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
select.ApplySelect, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
SELECT_MAP = (
|
||||
(
|
||||
CONF_SENSITIVITY,
|
||||
SelectType.SENSITIVITY,
|
||||
"set_sensitivity_select",
|
||||
["low", "medium", "high"],
|
||||
),
|
||||
(
|
||||
CONF_TRIGGER_SPEED,
|
||||
SelectType.TRIGGER_SPEED,
|
||||
"set_trigger_speed_select",
|
||||
["slow", "medium", "fast"],
|
||||
),
|
||||
(
|
||||
CONF_INSTALLATION_MODE,
|
||||
SelectType.INSTALLATION_MODE,
|
||||
"set_installation_select",
|
||||
["top", "side"],
|
||||
),
|
||||
(CONF_AREA_ID, SelectType.AREA_ID, "set_area_id_select", AREA_ID_OPTIONS),
|
||||
)
|
||||
FINAL_VALIDATE_SCHEMA = partial(validate_unique_settings, select.DOMAIN)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_LD6002B_ID])
|
||||
|
||||
for key, select_type, setter, options in SELECT_MAP:
|
||||
if conf := config.get(key):
|
||||
s = await select.new_select(conf, select_type, options=options)
|
||||
await cg.register_parented(s, config[CONF_LD6002B_ID])
|
||||
cg.add(getattr(hub, setter)(s))
|
||||
control = {"apply": "set_select_value(entity, index)", "publish": "before"}
|
||||
selects = select.sub_selects(config, parent=hub)
|
||||
await selects(
|
||||
CONF_SENSITIVITY,
|
||||
hub.set_sensitivity_select,
|
||||
options=["low", "medium", "high"],
|
||||
**control,
|
||||
)
|
||||
await selects(
|
||||
CONF_TRIGGER_SPEED,
|
||||
hub.set_trigger_speed_select,
|
||||
options=["slow", "medium", "fast"],
|
||||
**control,
|
||||
)
|
||||
await selects(
|
||||
CONF_INSTALLATION_MODE,
|
||||
hub.set_installation_select,
|
||||
options=["top", "side"],
|
||||
**control,
|
||||
)
|
||||
await selects(
|
||||
CONF_AREA_ID, hub.set_area_id_select, options=AREA_ID_OPTIONS, **control
|
||||
)
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "ld6002b_select.h"
|
||||
|
||||
namespace esphome::ld6002b {
|
||||
|
||||
void LD6002BSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_select_value(this->type_, index);
|
||||
}
|
||||
|
||||
} // namespace esphome::ld6002b
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../ld6002b.h"
|
||||
|
||||
namespace esphome::ld6002b {
|
||||
|
||||
class LD6002BSelect : public select::Select, public Parented<LD6002BComponent> {
|
||||
public:
|
||||
explicit LD6002BSelect(SelectType type) : type_(type) {}
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
|
||||
SelectType type_;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld6002b
|
||||
@@ -1,10 +1,12 @@
|
||||
from functools import partial
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import switch
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import DEVICE_CLASS_SWITCH, ENTITY_CATEGORY_CONFIG
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import LD6002BComponent, ld6002b_ns
|
||||
from .. import LD6002BComponent, validate_unique_settings
|
||||
from ..const import (
|
||||
CONF_LD6002B_ID,
|
||||
CONF_LOW_POWER,
|
||||
@@ -14,9 +16,6 @@ from ..const import (
|
||||
|
||||
DEPENDENCIES = ["ld6002b"]
|
||||
|
||||
LD6002BSwitch = ld6002b_ns.class_("LD6002BSwitch", switch.Switch)
|
||||
SwitchType = ld6002b_ns.enum("SwitchType", is_class=True)
|
||||
|
||||
# None of these three carry an inversion. They name what the module is doing, not
|
||||
# how something is wired to it, so an inverted one would only report the opposite
|
||||
# of the truth -- and the boot restore, which applies a state nothing reports back,
|
||||
@@ -25,19 +24,19 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LD6002B_ID): cv.use_id(LD6002BComponent),
|
||||
cv.Optional(CONF_LOW_POWER): switch.switch_schema(
|
||||
LD6002BSwitch,
|
||||
switch.ApplySwitch,
|
||||
block_inverted=True,
|
||||
device_class=DEVICE_CLASS_SWITCH,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(CONF_POINT_CLOUD): switch.switch_schema(
|
||||
LD6002BSwitch,
|
||||
switch.ApplySwitch,
|
||||
block_inverted=True,
|
||||
device_class=DEVICE_CLASS_SWITCH,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
cv.Optional(CONF_TARGET_DISPLAY): switch.switch_schema(
|
||||
LD6002BSwitch,
|
||||
switch.ApplySwitch,
|
||||
block_inverted=True,
|
||||
device_class=DEVICE_CLASS_SWITCH,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
@@ -47,15 +46,13 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = partial(validate_unique_settings, switch.DOMAIN)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_LD6002B_ID])
|
||||
|
||||
for key, switch_type, setter in (
|
||||
(CONF_LOW_POWER, SwitchType.LOW_POWER, "set_low_power_switch"),
|
||||
(CONF_POINT_CLOUD, SwitchType.POINT_CLOUD, "set_point_cloud_switch"),
|
||||
(CONF_TARGET_DISPLAY, SwitchType.TARGET_DISPLAY, "set_target_display_switch"),
|
||||
):
|
||||
if conf := config.get(key):
|
||||
s = await switch.new_switch(conf, switch_type)
|
||||
await cg.register_parented(s, config[CONF_LD6002B_ID])
|
||||
cg.add(getattr(hub, setter)(s))
|
||||
control = {"apply": "set_switch_state(entity, state)", "publish": "after"}
|
||||
switches = switch.sub_switches(config, parent=hub)
|
||||
await switches(CONF_LOW_POWER, hub.set_low_power_switch, **control)
|
||||
await switches(CONF_POINT_CLOUD, hub.set_point_cloud_switch, **control)
|
||||
await switches(CONF_TARGET_DISPLAY, hub.set_target_display_switch, **control)
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "ld6002b_switch.h"
|
||||
|
||||
namespace esphome::ld6002b {
|
||||
|
||||
void LD6002BSwitch::write_state(bool state) {
|
||||
this->parent_->set_switch_state(this->type_, state);
|
||||
this->publish_state(state);
|
||||
}
|
||||
|
||||
} // namespace esphome::ld6002b
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "../ld6002b.h"
|
||||
|
||||
namespace esphome::ld6002b {
|
||||
|
||||
class LD6002BSwitch : public switch_::Switch, public Parented<LD6002BComponent> {
|
||||
public:
|
||||
explicit LD6002BSwitch(SwitchType type) : type_(type) {}
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
|
||||
SwitchType type_;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld6002b
|
||||
@@ -26,7 +26,7 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.core import CORE
|
||||
from esphome.core.config import BOARD_MAX_LENGTH
|
||||
from esphome.helpers import copy_file_if_changed
|
||||
from esphome.helpers import copy_file_if_changed, write_file
|
||||
from esphome.platformio.toolchain import copy_ccache_script
|
||||
from esphome.storage_json import StorageJSON
|
||||
|
||||
@@ -656,6 +656,26 @@ async def component_to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
|
||||
|
||||
# Called by the OTA upload through the platforms' ota_image hook
|
||||
def device_ota_image(firmware: Path) -> Path:
|
||||
"""The file to send for an app update: the UF2 without the images the
|
||||
device skips, written next to the build output, or ``firmware`` itself
|
||||
when there is nothing to drop."""
|
||||
from .ota_uf2 import device_ota_uf2
|
||||
|
||||
data = firmware.read_bytes()
|
||||
if (trimmed := device_ota_uf2(data)) is None:
|
||||
return firmware
|
||||
image = CORE.relative_pioenvs_path(CORE.name, "firmware.ota.uf2")
|
||||
write_file(image, trimmed)
|
||||
_LOGGER.info(
|
||||
"Sending the images this device writes: %s of %s bytes",
|
||||
len(trimmed),
|
||||
len(data),
|
||||
)
|
||||
return image
|
||||
|
||||
|
||||
# Called by writer.py
|
||||
def copy_files() -> None:
|
||||
script_dir = Path(__file__).parent
|
||||
|
||||
Loaded 100 of 226 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user