Compare commits

...
Author SHA1 Message Date
J. Nick Koston 37e7597791 [veml6040] Zero the channel counts so clang-tidy sees them set 2026-10-22 15:27:47 -10:00
J. Nick Koston 608bad93e8 [dfrobot_sen0609] Use core parse_number<float> 2026-10-22 15:03:17 -10:00
J. Nick Koston 3d293b0cab [esp32_ulp] Give mark_failed a reason 2026-10-22 15:02:39 -10:00
J. Nick Koston 397cad4bb9 [dfrobot_sen0465_base] Use pow10_int and drop the dump_config failure line 2026-10-22 15:02:27 -10:00
J. Nick Koston 8e1498b2c6 [cw2017] Use status_comm_result and SUB_SENSOR 2026-10-22 15:02:00 -10:00
J. Nick Koston d1c42968c1 [veml6040] Use read_byte_16_le, status_comm_result, SUB_SENSOR and options() 2026-10-22 15:01:39 -10:00
J. Nick Koston a56a7603dd Merge remote-tracking branch 'origin/dev' into recent-components-py-helpers 2026-10-22 15:01:09 -10:00
J. Nick Koston 47f1da0a8d [components] Use options() and sub_text_sensors in recently added components
Build the dfrobot_sen0465 alarm types and mics_5524 conversion models with
options(), and register the systa_bus error code text sensor with
sub_text_sensors. The generated C++ is unchanged.
2026-10-17 17:32:55 -10:00
11 changed files with 49 additions and 99 deletions

No files matched your search

+7 -12
View File
@@ -163,26 +163,21 @@ void CW2017::update() {
if (!this->ready_)
return;
bool ok = true;
if (this->battery_level_sensor_ != nullptr) {
uint8_t soc = 0;
if (this->read_byte(REG_SOC, &soc) && soc <= 100) {
ok = this->read_byte(REG_SOC, &soc) && soc <= 100;
if (ok)
this->battery_level_sensor_->publish_state(soc);
} else {
this->status_set_warning();
return;
}
}
if (this->battery_voltage_sensor_ != nullptr) {
if (ok && this->battery_voltage_sensor_ != nullptr) {
uint16_t raw = 0;
if (this->read_byte_16(REG_VCELL, &raw)) {
ok = this->read_byte_16(REG_VCELL, &raw);
if (ok)
this->battery_voltage_sensor_->publish_state((raw & VCELL_MASK) * VCELL_VOLTS_PER_LSB);
} else {
this->status_set_warning();
return;
}
}
this->status_clear_warning();
this->status_comm_result(ok);
}
void CW2017::dump_config() {
+2 -4
View File
@@ -12,8 +12,8 @@ class CW2017 : public PollingComponent, public i2c::I2CDevice {
public:
explicit CW2017(const uint8_t *profile) : profile_(profile) {}
void set_battery_level_sensor(sensor::Sensor *sensor) { this->battery_level_sensor_ = sensor; }
void set_battery_voltage_sensor(sensor::Sensor *sensor) { this->battery_voltage_sensor_ = sensor; }
SUB_SENSOR(battery_level)
SUB_SENSOR(battery_voltage)
void setup() override;
void update() override;
@@ -29,8 +29,6 @@ class CW2017 : public PollingComponent, public i2c::I2CDevice {
void wait_ready_(uint8_t attempt);
const uint8_t *profile_;
sensor::Sensor *battery_level_sensor_{nullptr};
sensor::Sensor *battery_voltage_sensor_{nullptr};
bool ready_{false};
bool restart_pending_{false};
uint8_t init_attempts_{0};
@@ -34,10 +34,7 @@ DFRobotSEN0465Component = dfrobot_sen0465_base_ns.class_(
"DFRobotSEN0465Component", cg.PollingComponent
)
AlarmType = dfrobot_sen0465_base_ns.enum("AlarmType", is_class=True)
ALARM_TYPES = {
"LOW": AlarmType.ALARM_TYPE_LOW,
"HIGH": AlarmType.ALARM_TYPE_HIGH,
}
ALARM_TYPES = AlarmType.options("LOW", "HIGH", prefix="ALARM_TYPE_")
# One piece of the temperature compensation from the DFRobot_MultiGasSensor library:
# for low < T <= high, compensated = raw / (a * T + b) - (c * T + d)
@@ -72,10 +72,7 @@ void DFRobotSEN0465Component::dump_config() {
if (this->alarm_type_ == AlarmType::ALARM_TYPE_OFF) {
ESP_LOGCONFIG(TAG, " Alarm: off");
} else {
float scale = 1.0f;
for (uint8_t i = 0; i < this->alarm_decimals_; i++)
scale *= 10.0f;
const float threshold = this->alarm_threshold_ / scale;
const float threshold = this->alarm_threshold_ / pow10_int(this->alarm_decimals_);
ESP_LOGCONFIG(TAG, " Alarm: %s %.*f",
this->alarm_type_ == AlarmType::ALARM_TYPE_HIGH ? LOG_STR_LITERAL("above") : LOG_STR_LITERAL("below"),
this->alarm_decimals_, threshold);
@@ -83,9 +80,6 @@ void DFRobotSEN0465Component::dump_config() {
if (this->setup_incomplete_) {
ESP_LOGCONFIG(TAG, " Setup incomplete: yes");
}
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Concentration", this->concentration_sensor_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
@@ -228,23 +222,14 @@ void DFRobotSEN0465Component::handle_data_(const uint8_t *frame) {
return;
}
float concentration = encode_uint16(frame[2], frame[3]);
switch (frame[5]) {
case 0:
break;
case 1:
concentration *= 0.1f;
break;
case 2:
concentration *= 0.01f;
break;
default:
ESP_LOGW(TAG, "Invalid number of decimal places: %u", frame[5]);
if (this->concentration_sensor_ != nullptr)
this->concentration_sensor_->publish_state(NAN);
this->status_set_warning();
return;
if (frame[5] > 2) {
ESP_LOGW(TAG, "Invalid number of decimal places: %u", frame[5]);
if (this->concentration_sensor_ != nullptr)
this->concentration_sensor_->publish_state(NAN);
this->status_set_warning();
return;
}
float concentration = encode_uint16(frame[2], frame[3]) * pow10_int(-frame[5]);
#ifdef USE_DFROBOT_SEN0465_TEMPERATURE_COMPENSATION
if (!this->compensation_.empty())
@@ -95,12 +95,6 @@ static bool answer_means_done(Operation operation, const char *line) {
}
}
static bool parse_float(const char *str, float &value) {
char *end;
value = strtof(str, &end);
return end != str && *end == '\0';
}
static const LogString *operation_to_str(Operation operation) {
switch (operation) {
case Operation::OPERATION_STOP:
@@ -286,9 +280,6 @@ void DFRobotSEN0609Component::handle_frame_(char *line) {
return;
}
char *fields[FRAME_FIELDS];
float distance;
float speed;
float energy;
if (split_fields(line + 7, fields, FRAME_FIELDS) < FRAME_FIELDS) {
this->log_bad_frame_(LOG_STR("Malformed target frame"), line);
return;
@@ -308,7 +299,10 @@ void DFRobotSEN0609Component::handle_frame_(char *line) {
#endif
return;
}
if (!parse_float(fields[2], distance) || !parse_float(fields[3], speed) || !parse_float(fields[4], energy)) {
const optional<float> distance = parse_number<float>(fields[2]);
const optional<float> speed = parse_number<float>(fields[3]);
const optional<float> energy = parse_number<float>(fields[4]);
if (!distance.has_value() || !speed.has_value() || !energy.has_value()) {
this->log_bad_frame_(LOG_STR("Malformed target frame"), line);
return;
}
@@ -317,7 +311,7 @@ void DFRobotSEN0609Component::handle_frame_(char *line) {
this->has_target_binary_sensor_->publish_state(true);
#endif
#ifdef USE_SENSOR
this->publish_target_(distance, speed, energy);
this->publish_target_(*distance, *speed, *energy);
#endif
}
+2 -2
View File
@@ -38,7 +38,7 @@ void ESP32ULP::setup() {
esp_err_t err = ulp_set_wakeup_period(0, this->wakeup_period_us_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "ulp_set_wakeup_period failed: %s", esp_err_to_name(err));
this->mark_failed();
this->mark_failed(LOG_STR("Wakeup period not set"));
return;
}
}
@@ -48,7 +48,7 @@ void ESP32ULP::setup() {
if (esp_reset_reason() == ESP_RST_DEEPSLEEP && s_program_loaded) {
this->loaded_ = true;
} else if (this->run_on_boot_ && !this->load_and_run()) {
this->mark_failed();
this->mark_failed(LOG_STR("Program did not start"));
return;
}
if (this->wakeup_from_sleep_) {
+3 -4
View File
@@ -43,10 +43,9 @@ CONF_RATIO_SENSOR = "ratio_sensor"
CONF_RS_SENSOR = "rs_sensor"
CONF_VOLTAGE_SENSOR = "voltage_sensor"
CONVERSION_MODELS = {
"dfrobot": ConversionModel.CONVERSION_MODEL_DFROBOT,
"datasheet": ConversionModel.CONVERSION_MODEL_DATASHEET,
}
CONVERSION_MODELS = ConversionModel.options(
"dfrobot", "datasheet", prefix="CONVERSION_MODEL_"
)
# Divider guard limits (input max, pin max) in volts by target platform. ESP32 and RP2: VDD and
# the absolute maximum VDD + 0.3 V; a bare ESP8266 A0 measures 0 - 1 V, boards with an onboard
@@ -36,9 +36,9 @@ async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
if config[CONF_MODEL] == CONF_SYSTASOLAR_AQUA and CONF_ERROR_CODE in config:
sens = await text_sensor.new_text_sensor(config[CONF_ERROR_CODE])
cg.add(var.set_error_code_text_sensor(sens))
# The schema only lets a model's own keys through, so unconfigured ones are skipped here
text_sensors = text_sensor.sub_text_sensors(config)
await text_sensors(CONF_ERROR_CODE, var.set_error_code_text_sensor)
systa_bus = await cg.get_variable(config[CONF_SYSTA_BUS_ID])
await register_systa_bus_listener(systa_bus, var)
+9 -8
View File
@@ -32,14 +32,15 @@ VEML6040Component = veml6040_ns.class_(
)
VEML6040IntegrationTime = veml6040_ns.enum("VEML6040IntegrationTime")
VEML6040_INTEGRATION_TIMES = {
"40ms": VEML6040IntegrationTime.VEML6040_INTEGRATION_TIME_40MS,
"80ms": VEML6040IntegrationTime.VEML6040_INTEGRATION_TIME_80MS,
"160ms": VEML6040IntegrationTime.VEML6040_INTEGRATION_TIME_160MS,
"320ms": VEML6040IntegrationTime.VEML6040_INTEGRATION_TIME_320MS,
"640ms": VEML6040IntegrationTime.VEML6040_INTEGRATION_TIME_640MS,
"1280ms": VEML6040IntegrationTime.VEML6040_INTEGRATION_TIME_1280MS,
}
VEML6040_INTEGRATION_TIMES = VEML6040IntegrationTime.options(
"40ms",
"80ms",
"160ms",
"320ms",
"640ms",
"1280ms",
prefix="VEML6040_INTEGRATION_TIME_",
)
color_channel_schema = sensor.sensor_schema(
icon=ICON_LIGHTBULB,
+3 -15
View File
@@ -1,5 +1,4 @@
#include "veml6040.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cinttypes>
@@ -60,25 +59,14 @@ void VEML6040Component::dump_config() {
LOG_SENSOR(" ", "Color temperature", this->color_temperature_sensor_);
}
bool VEML6040Component::read_channel_(uint8_t reg, uint16_t &counts) {
uint8_t data[2];
if (this->read_register(reg, data, sizeof data) != i2c::ERROR_OK)
return false;
counts = encode_uint16(data[1], data[0]);
return true;
}
void VEML6040Component::update() {
if (!this->data_ready_)
return;
uint16_t red, green, blue, white;
if (!this->read_channel_(R_DATA_REG, red) || !this->read_channel_(G_DATA_REG, green) ||
!this->read_channel_(B_DATA_REG, blue) || !this->read_channel_(W_DATA_REG, white)) {
this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
uint16_t red{0}, green{0}, blue{0}, white{0};
if (!this->status_comm_result(this->read_byte_16_le(R_DATA_REG, &red) && this->read_byte_16_le(G_DATA_REG, &green) &&
this->read_byte_16_le(B_DATA_REG, &blue) && this->read_byte_16_le(W_DATA_REG, &white)))
return;
}
this->status_clear_warning();
ESP_LOGV(TAG, "Counts: red=%u green=%u blue=%u white=%u", red, green, blue, white);
+6 -13
View File
@@ -29,23 +29,16 @@ class VEML6040Component final : public PollingComponent, public i2c::I2CDevice {
void dump_config() override;
void update() override;
void set_red_sensor(sensor::Sensor *sensor) { this->red_sensor_ = sensor; }
void set_green_sensor(sensor::Sensor *sensor) { this->green_sensor_ = sensor; }
void set_blue_sensor(sensor::Sensor *sensor) { this->blue_sensor_ = sensor; }
void set_white_sensor(sensor::Sensor *sensor) { this->white_sensor_ = sensor; }
void set_illuminance_sensor(sensor::Sensor *sensor) { this->illuminance_sensor_ = sensor; }
void set_color_temperature_sensor(sensor::Sensor *sensor) { this->color_temperature_sensor_ = sensor; }
SUB_SENSOR(red)
SUB_SENSOR(green)
SUB_SENSOR(blue)
SUB_SENSOR(white)
SUB_SENSOR(illuminance)
SUB_SENSOR(color_temperature)
protected:
bool read_channel_(uint8_t reg, uint16_t &counts);
uint32_t integration_time_ms_() const { return 40u << this->integration_time_; }
sensor::Sensor *red_sensor_{nullptr};
sensor::Sensor *green_sensor_{nullptr};
sensor::Sensor *blue_sensor_{nullptr};
sensor::Sensor *white_sensor_{nullptr};
sensor::Sensor *illuminance_sensor_{nullptr};
sensor::Sensor *color_temperature_sensor_{nullptr};
const float glass_attenuation_factor_;
const float color_temperature_offset_;
const VEML6040IntegrationTime integration_time_;