1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-20 08:46:01 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
J. Nick Koston
f478e09972 [network] Fix uninitialized type field in IPAddress esp_ip4_addr_t constructor 2025-11-19 18:12:18 -06:00
30 changed files with 84 additions and 337 deletions

View File

@@ -460,7 +460,6 @@ esphome/components/st7735/* @SenexCrenshaw
esphome/components/st7789v/* @kbx81
esphome/components/st7920/* @marsjan155
esphome/components/statsd/* @Links2004
esphome/components/stts22h/* @B48D81EFCC
esphome/components/substitutions/* @esphome/core
esphome/components/sun/* @OttoWinter
esphome/components/sun_gtil2/* @Mat931

View File

@@ -1535,13 +1535,8 @@ bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
#ifdef USE_API_HOMEASSISTANT_STATES
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
for (auto &it : this->parent_->get_state_subs()) {
// Compare entity_id and attribute with message fields
bool entity_match = (strcmp(it.entity_id_, msg.entity_id.c_str()) == 0);
bool attribute_match = (it.attribute_ != nullptr && strcmp(it.attribute_, msg.attribute.c_str()) == 0) ||
(it.attribute_ == nullptr && msg.attribute.empty());
if (entity_match && attribute_match) {
it.callback_(msg.state);
if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
it.callback(msg.state);
}
}
}
@@ -1878,12 +1873,12 @@ void APIConnection::process_state_subscriptions_() {
const auto &it = subs[this->state_subs_at_];
SubscribeHomeAssistantStateResponse resp;
resp.set_entity_id(StringRef(it.entity_id_));
resp.set_entity_id(StringRef(it.entity_id));
// Avoid string copy by using the const char* pointer if it exists
resp.set_attribute(it.attribute_ != nullptr ? StringRef(it.attribute_) : StringRef(""));
// Avoid string copy by directly using the optional's value if it exists
resp.set_attribute(it.attribute.has_value() ? StringRef(it.attribute.value()) : StringRef(""));
resp.once = it.once_;
resp.once = it.once;
if (this->send_message(resp, SubscribeHomeAssistantStateResponse::MESSAGE_TYPE)) {
this->state_subs_at_++;
}

View File

@@ -431,56 +431,25 @@ void APIServer::handle_action_response(uint32_t call_id, bool success, const std
#endif // USE_API_HOMEASSISTANT_SERVICES
#ifdef USE_API_HOMEASSISTANT_STATES
// Helper to add subscription (reduces duplication)
void APIServer::add_state_subscription_(const char *entity_id, const char *attribute,
std::function<void(std::string)> f, bool once) {
this->state_subs_.push_back(HomeAssistantStateSubscription{
.entity_id_ = entity_id, .attribute_ = attribute, .callback_ = std::move(f), .once_ = once,
// entity_id_copy_ and attribute_copy_ remain nullptr (no heap allocation)
});
}
// Helper to add subscription with heap-allocated strings (reduces duplication)
void APIServer::add_state_subscription_(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f, bool once) {
HomeAssistantStateSubscription sub;
// Allocate heap storage for the strings
sub.entity_id_copy_ = std::make_unique<std::string>(std::move(entity_id));
sub.entity_id_ = sub.entity_id_copy_->c_str();
if (attribute.has_value()) {
sub.attribute_copy_ = std::make_unique<std::string>(std::move(attribute.value()));
sub.attribute_ = sub.attribute_copy_->c_str();
} else {
sub.attribute_ = nullptr;
}
sub.callback_ = std::move(f);
sub.once_ = once;
this->state_subs_.push_back(std::move(sub));
}
// New const char* overload (for internal components - zero allocation)
void APIServer::subscribe_home_assistant_state(const char *entity_id, const char *attribute,
std::function<void(std::string)> f) {
this->add_state_subscription_(entity_id, attribute, std::move(f), false);
}
void APIServer::get_home_assistant_state(const char *entity_id, const char *attribute,
std::function<void(std::string)> f) {
this->add_state_subscription_(entity_id, attribute, std::move(f), true);
}
// Existing std::string overload (for custom_api_device.h - heap allocation)
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), false);
this->state_subs_.push_back(HomeAssistantStateSubscription{
.entity_id = std::move(entity_id),
.attribute = std::move(attribute),
.callback = std::move(f),
.once = false,
});
}
void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), true);
}
this->state_subs_.push_back(HomeAssistantStateSubscription{
.entity_id = std::move(entity_id),
.attribute = std::move(attribute),
.callback = std::move(f),
.once = true,
});
};
const std::vector<APIServer::HomeAssistantStateSubscription> &APIServer::get_state_subs() const {
return this->state_subs_;

View File

@@ -154,27 +154,16 @@ class APIServer : public Component, public Controller {
#ifdef USE_API_HOMEASSISTANT_STATES
struct HomeAssistantStateSubscription {
const char *entity_id_; // Pointer to flash (internal) or heap (external)
const char *attribute_; // Pointer to flash or nullptr (nullptr means no attribute)
std::function<void(std::string)> callback_;
bool once_;
// Storage for external components using std::string API (custom_api_device.h)
// These are only allocated when using the std::string overload
std::unique_ptr<std::string> entity_id_copy_;
std::unique_ptr<std::string> attribute_copy_;
std::string entity_id;
optional<std::string> attribute;
std::function<void(std::string)> callback;
bool once;
};
// New const char* overload (for internal components - zero allocation)
void subscribe_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(std::string)> f);
void get_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(std::string)> f);
// Existing std::string overload (for custom_api_device.h - heap allocation)
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f);
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f);
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
#endif
#ifdef USE_API_SERVICES
@@ -196,13 +185,6 @@ class APIServer : public Component, public Controller {
bool update_noise_psk_(const SavedNoisePsk &new_psk, const LogString *save_log_msg, const LogString *fail_log_msg,
const psk_t &active_psk, bool make_active);
#endif // USE_API_NOISE
#ifdef USE_API_HOMEASSISTANT_STATES
// Helper methods to reduce code duplication
void add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(std::string)> f,
bool once);
void add_state_subscription_(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f, bool once);
#endif // USE_API_HOMEASSISTANT_STATES
// Pointers and pointer-like types first (4 bytes each)
std::unique_ptr<socket::Socket> socket_ = nullptr;
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER

View File

@@ -337,7 +337,7 @@ void Graph::draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_of
return;
/// Plot border
if (legend_->border_) {
if (this->border_) {
int w = legend_->width_;
int h = legend_->height_;
buff->horizontal_line(x_offset, y_offset, w, color);

View File

@@ -19,10 +19,11 @@ void HomeassistantBinarySensor::setup() {
case PARSE_ON:
case PARSE_OFF:
bool new_state = val == PARSE_ON;
if (this->attribute_ != nullptr) {
ESP_LOGD(TAG, "'%s::%s': Got attribute state %s", this->entity_id_, this->attribute_, ONOFF(new_state));
if (this->attribute_.has_value()) {
ESP_LOGD(TAG, "'%s::%s': Got attribute state %s", this->entity_id_.c_str(),
this->attribute_.value().c_str(), ONOFF(new_state));
} else {
ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_, ONOFF(new_state));
ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_.c_str(), ONOFF(new_state));
}
if (this->initial_) {
this->publish_initial_state(new_state);
@@ -36,9 +37,9 @@ void HomeassistantBinarySensor::setup() {
}
void HomeassistantBinarySensor::dump_config() {
LOG_BINARY_SENSOR("", "Homeassistant Binary Sensor", this);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_);
if (this->attribute_ != nullptr) {
ESP_LOGCONFIG(TAG, " Attribute: '%s'", this->attribute_);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str());
if (this->attribute_.has_value()) {
ESP_LOGCONFIG(TAG, " Attribute: '%s'", this->attribute_.value().c_str());
}
}
float HomeassistantBinarySensor::get_setup_priority() const { return setup_priority::AFTER_WIFI; }

View File

@@ -8,15 +8,15 @@ namespace homeassistant {
class HomeassistantBinarySensor : public binary_sensor::BinarySensor, public Component {
public:
void set_entity_id(const char *entity_id) { this->entity_id_ = entity_id; }
void set_attribute(const char *attribute) { this->attribute_ = attribute; }
void set_entity_id(const std::string &entity_id) { entity_id_ = entity_id; }
void set_attribute(const std::string &attribute) { attribute_ = attribute; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
protected:
const char *entity_id_{nullptr};
const char *attribute_{nullptr};
std::string entity_id_;
optional<std::string> attribute_;
bool initial_{true};
};

View File

@@ -12,21 +12,21 @@ static const char *const TAG = "homeassistant.number";
void HomeassistantNumber::state_changed_(const std::string &state) {
auto number_value = parse_number<float>(state);
if (!number_value.has_value()) {
ESP_LOGW(TAG, "'%s': Can't convert '%s' to number!", this->entity_id_, state.c_str());
ESP_LOGW(TAG, "'%s': Can't convert '%s' to number!", this->entity_id_.c_str(), state.c_str());
this->publish_state(NAN);
return;
}
if (this->state == number_value.value()) {
return;
}
ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_, state.c_str());
ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_.c_str(), state.c_str());
this->publish_state(number_value.value());
}
void HomeassistantNumber::min_retrieved_(const std::string &min) {
auto min_value = parse_number<float>(min);
if (!min_value.has_value()) {
ESP_LOGE(TAG, "'%s': Can't convert 'min' value '%s' to number!", this->entity_id_, min.c_str());
ESP_LOGE(TAG, "'%s': Can't convert 'min' value '%s' to number!", this->entity_id_.c_str(), min.c_str());
return;
}
ESP_LOGD(TAG, "'%s': Min retrieved: %s", get_name().c_str(), min.c_str());
@@ -36,7 +36,7 @@ void HomeassistantNumber::min_retrieved_(const std::string &min) {
void HomeassistantNumber::max_retrieved_(const std::string &max) {
auto max_value = parse_number<float>(max);
if (!max_value.has_value()) {
ESP_LOGE(TAG, "'%s': Can't convert 'max' value '%s' to number!", this->entity_id_, max.c_str());
ESP_LOGE(TAG, "'%s': Can't convert 'max' value '%s' to number!", this->entity_id_.c_str(), max.c_str());
return;
}
ESP_LOGD(TAG, "'%s': Max retrieved: %s", get_name().c_str(), max.c_str());
@@ -46,7 +46,7 @@ void HomeassistantNumber::max_retrieved_(const std::string &max) {
void HomeassistantNumber::step_retrieved_(const std::string &step) {
auto step_value = parse_number<float>(step);
if (!step_value.has_value()) {
ESP_LOGE(TAG, "'%s': Can't convert 'step' value '%s' to number!", this->entity_id_, step.c_str());
ESP_LOGE(TAG, "'%s': Can't convert 'step' value '%s' to number!", this->entity_id_.c_str(), step.c_str());
return;
}
ESP_LOGD(TAG, "'%s': Step Retrieved %s", get_name().c_str(), step.c_str());
@@ -55,19 +55,22 @@ void HomeassistantNumber::step_retrieved_(const std::string &step) {
void HomeassistantNumber::setup() {
api::global_api_server->subscribe_home_assistant_state(
this->entity_id_, nullptr, std::bind(&HomeassistantNumber::state_changed_, this, std::placeholders::_1));
this->entity_id_, nullopt, std::bind(&HomeassistantNumber::state_changed_, this, std::placeholders::_1));
api::global_api_server->get_home_assistant_state(
this->entity_id_, "min", std::bind(&HomeassistantNumber::min_retrieved_, this, std::placeholders::_1));
this->entity_id_, optional<std::string>("min"),
std::bind(&HomeassistantNumber::min_retrieved_, this, std::placeholders::_1));
api::global_api_server->get_home_assistant_state(
this->entity_id_, "max", std::bind(&HomeassistantNumber::max_retrieved_, this, std::placeholders::_1));
this->entity_id_, optional<std::string>("max"),
std::bind(&HomeassistantNumber::max_retrieved_, this, std::placeholders::_1));
api::global_api_server->get_home_assistant_state(
this->entity_id_, "step", std::bind(&HomeassistantNumber::step_retrieved_, this, std::placeholders::_1));
this->entity_id_, optional<std::string>("step"),
std::bind(&HomeassistantNumber::step_retrieved_, this, std::placeholders::_1));
}
void HomeassistantNumber::dump_config() {
LOG_NUMBER("", "Homeassistant Number", this);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str());
}
float HomeassistantNumber::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }

View File

@@ -11,7 +11,7 @@ namespace homeassistant {
class HomeassistantNumber : public number::Number, public Component {
public:
void set_entity_id(const char *entity_id) { this->entity_id_ = entity_id; }
void set_entity_id(const std::string &entity_id) { this->entity_id_ = entity_id; }
void setup() override;
void dump_config() override;
@@ -25,7 +25,7 @@ class HomeassistantNumber : public number::Number, public Component {
void control(float value) override;
const char *entity_id_{nullptr};
std::string entity_id_;
};
} // namespace homeassistant
} // namespace esphome

View File

@@ -12,24 +12,25 @@ void HomeassistantSensor::setup() {
this->entity_id_, this->attribute_, [this](const std::string &state) {
auto val = parse_number<float>(state);
if (!val.has_value()) {
ESP_LOGW(TAG, "'%s': Can't convert '%s' to number!", this->entity_id_, state.c_str());
ESP_LOGW(TAG, "'%s': Can't convert '%s' to number!", this->entity_id_.c_str(), state.c_str());
this->publish_state(NAN);
return;
}
if (this->attribute_ != nullptr) {
ESP_LOGD(TAG, "'%s::%s': Got attribute state %.2f", this->entity_id_, this->attribute_, *val);
if (this->attribute_.has_value()) {
ESP_LOGD(TAG, "'%s::%s': Got attribute state %.2f", this->entity_id_.c_str(),
this->attribute_.value().c_str(), *val);
} else {
ESP_LOGD(TAG, "'%s': Got state %.2f", this->entity_id_, *val);
ESP_LOGD(TAG, "'%s': Got state %.2f", this->entity_id_.c_str(), *val);
}
this->publish_state(*val);
});
}
void HomeassistantSensor::dump_config() {
LOG_SENSOR("", "Homeassistant Sensor", this);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_);
if (this->attribute_ != nullptr) {
ESP_LOGCONFIG(TAG, " Attribute: '%s'", this->attribute_);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str());
if (this->attribute_.has_value()) {
ESP_LOGCONFIG(TAG, " Attribute: '%s'", this->attribute_.value().c_str());
}
}
float HomeassistantSensor::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }

View File

@@ -8,15 +8,15 @@ namespace homeassistant {
class HomeassistantSensor : public sensor::Sensor, public Component {
public:
void set_entity_id(const char *entity_id) { this->entity_id_ = entity_id; }
void set_attribute(const char *attribute) { this->attribute_ = attribute; }
void set_entity_id(const std::string &entity_id) { entity_id_ = entity_id; }
void set_attribute(const std::string &attribute) { attribute_ = attribute; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
protected:
const char *entity_id_{nullptr};
const char *attribute_{nullptr};
std::string entity_id_;
optional<std::string> attribute_;
};
} // namespace homeassistant

View File

@@ -10,7 +10,7 @@ static const char *const TAG = "homeassistant.switch";
using namespace esphome::switch_;
void HomeassistantSwitch::setup() {
api::global_api_server->subscribe_home_assistant_state(this->entity_id_, nullptr, [this](const std::string &state) {
api::global_api_server->subscribe_home_assistant_state(this->entity_id_, nullopt, [this](const std::string &state) {
auto val = parse_on_off(state.c_str());
switch (val) {
case PARSE_NONE:
@@ -20,7 +20,7 @@ void HomeassistantSwitch::setup() {
case PARSE_ON:
case PARSE_OFF:
bool new_state = val == PARSE_ON;
ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_, ONOFF(new_state));
ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_.c_str(), ONOFF(new_state));
this->publish_state(new_state);
break;
}
@@ -29,7 +29,7 @@ void HomeassistantSwitch::setup() {
void HomeassistantSwitch::dump_config() {
LOG_SWITCH("", "Homeassistant Switch", this);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str());
}
float HomeassistantSwitch::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }

View File

@@ -8,14 +8,14 @@ namespace homeassistant {
class HomeassistantSwitch : public switch_::Switch, public Component {
public:
void set_entity_id(const char *entity_id) { this->entity_id_ = entity_id; }
void set_entity_id(const std::string &entity_id) { this->entity_id_ = entity_id; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
protected:
void write_state(bool state) override;
const char *entity_id_{nullptr};
std::string entity_id_;
};
} // namespace homeassistant

View File

@@ -10,19 +10,20 @@ static const char *const TAG = "homeassistant.text_sensor";
void HomeassistantTextSensor::setup() {
api::global_api_server->subscribe_home_assistant_state(
this->entity_id_, this->attribute_, [this](const std::string &state) {
if (this->attribute_ != nullptr) {
ESP_LOGD(TAG, "'%s::%s': Got attribute state '%s'", this->entity_id_, this->attribute_, state.c_str());
if (this->attribute_.has_value()) {
ESP_LOGD(TAG, "'%s::%s': Got attribute state '%s'", this->entity_id_.c_str(),
this->attribute_.value().c_str(), state.c_str());
} else {
ESP_LOGD(TAG, "'%s': Got state '%s'", this->entity_id_, state.c_str());
ESP_LOGD(TAG, "'%s': Got state '%s'", this->entity_id_.c_str(), state.c_str());
}
this->publish_state(state);
});
}
void HomeassistantTextSensor::dump_config() {
LOG_TEXT_SENSOR("", "Homeassistant Text Sensor", this);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_);
if (this->attribute_ != nullptr) {
ESP_LOGCONFIG(TAG, " Attribute: '%s'", this->attribute_);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str());
if (this->attribute_.has_value()) {
ESP_LOGCONFIG(TAG, " Attribute: '%s'", this->attribute_.value().c_str());
}
}
float HomeassistantTextSensor::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }

View File

@@ -8,15 +8,15 @@ namespace homeassistant {
class HomeassistantTextSensor : public text_sensor::TextSensor, public Component {
public:
void set_entity_id(const char *entity_id) { this->entity_id_ = entity_id; }
void set_attribute(const char *attribute) { this->attribute_ = attribute; }
void set_entity_id(const std::string &entity_id) { entity_id_ = entity_id; }
void set_attribute(const std::string &attribute) { attribute_ = attribute; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
protected:
const char *entity_id_{nullptr};
const char *attribute_{nullptr};
std::string entity_id_;
optional<std::string> attribute_;
};
} // namespace homeassistant

View File

@@ -81,7 +81,12 @@ struct IPAddress {
ip_addr_.type = IPADDR_TYPE_V6;
}
#endif /* LWIP_IPV6 */
IPAddress(esp_ip4_addr_t *other_ip) { memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(esp_ip4_addr_t)); }
IPAddress(esp_ip4_addr_t *other_ip) {
memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(esp_ip4_addr_t));
#if LWIP_IPV6
ip_addr_.type = IPADDR_TYPE_V4;
#endif
}
IPAddress(esp_ip_addr_t *other_ip) {
#if LWIP_IPV6
memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip_addr_));

View File

@@ -61,18 +61,9 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::stri
server->sin6_family = AF_INET6;
server->sin6_port = htons(port);
#ifdef USE_SOCKET_IMPL_BSD_SOCKETS
// Use standard inet_pton for BSD sockets
if (inet_pton(AF_INET6, ip_address.c_str(), &server->sin6_addr) != 1) {
errno = EINVAL;
return 0;
}
#else
// Use LWIP-specific functions
ip6_addr_t ip6;
inet6_aton(ip_address.c_str(), &ip6);
memcpy(server->sin6_addr.un.u32_addr, ip6.addr, sizeof(ip6.addr));
#endif
return sizeof(sockaddr_in6);
}
#endif /* USE_NETWORK_IPV6 */

View File

@@ -1 +0,0 @@
CODEOWNERS = ["@B48D81EFCC"]

View File

@@ -1,33 +0,0 @@
import esphome.codegen as cg
from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import (
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
)
DEPENDENCIES = ["i2c"]
sensor_ns = cg.esphome_ns.namespace("stts22h")
stts22h = sensor_ns.class_(
"STTS22HComponent", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
sensor.sensor_schema(
stts22h,
accuracy_decimals=2,
unit_of_measurement=UNIT_CELSIUS,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x3C))
)
async def to_code(config):
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)

View File

@@ -1,101 +0,0 @@
#include "esphome/core/log.h"
#include "stts22h.h"
namespace esphome::stts22h {
static const char *const TAG = "stts22h";
static const uint8_t WHOAMI_REG = 0x01;
static const uint8_t CTRL_REG = 0x04;
static const uint8_t TEMPERATURE_REG = 0x06;
// CTRL_REG flags
static const uint8_t LOW_ODR_CTRL_ENABLE_FLAG = 0x80; // Flag to enable low ODR mode in CTRL_REG
static const uint8_t FREERUN_CTRL_ENABLE_FLAG = 0x04; // Flag to enable FREERUN mode in CTRL_REG
static const uint8_t ADD_INC_ENABLE_FLAG = 0x08; // Flag to enable ADD_INC (IF_ADD_INC) mode in CTRL_REG
static const uint8_t WHOAMI_STTS22H_IDENTIFICATION = 0xA0; // ID value of STTS22H in WHOAMI_REG
static const float SENSOR_SCALE = 0.01f; // Sensor resolution in degrees Celsius
void STTS22HComponent::setup() {
// Check if device is a STTS22H
if (!this->is_stts22h_sensor_()) {
this->mark_failed("Device is not a STTS22H sensor");
return;
}
this->initialize_sensor_();
}
void STTS22HComponent::update() {
if (this->is_failed()) {
return;
}
this->publish_state(this->read_temperature_());
}
void STTS22HComponent::dump_config() {
LOG_SENSOR("", "STTS22H", this);
LOG_I2C_DEVICE(this);
LOG_UPDATE_INTERVAL(this);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
}
float STTS22HComponent::read_temperature_() {
uint8_t temp_reg_value[2];
if (this->read_register(TEMPERATURE_REG, temp_reg_value, 2) != i2c::NO_ERROR) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
return NAN;
}
// Combine the two bytes into a single 16-bit signed integer
// The STTS22H temperature data is in two's complement format
int16_t temp_raw_value = static_cast<int16_t>(encode_uint16(temp_reg_value[1], temp_reg_value[0]));
return temp_raw_value * SENSOR_SCALE; // Apply sensor resolution
}
bool STTS22HComponent::is_stts22h_sensor_() {
uint8_t whoami_value;
if (this->read_register(WHOAMI_REG, &whoami_value, 1) != i2c::NO_ERROR) {
this->mark_failed(ESP_LOG_MSG_COMM_FAIL);
return false;
}
if (whoami_value != WHOAMI_STTS22H_IDENTIFICATION) {
this->mark_failed("Unexpected WHOAMI identifier. Sensor is not a STTS22H");
return false;
}
return true;
}
void STTS22HComponent::initialize_sensor_() {
// Read current CTRL_REG configuration
uint8_t ctrl_value;
if (this->read_register(CTRL_REG, &ctrl_value, 1) != i2c::NO_ERROR) {
this->mark_failed(ESP_LOG_MSG_COMM_FAIL);
return;
}
// Enable low ODR mode and enable ADD_INC
// Before low ODR mode can be used,
// FREERUN bit must be cleared (see sensor documentation)
ctrl_value &= ~FREERUN_CTRL_ENABLE_FLAG; // Clear FREERUN bit
if (this->write_register(CTRL_REG, &ctrl_value, 1) != i2c::NO_ERROR) {
this->mark_failed(ESP_LOG_MSG_COMM_FAIL);
return;
}
// Enable LOW ODR mode and ADD_INC
ctrl_value |= LOW_ODR_CTRL_ENABLE_FLAG | ADD_INC_ENABLE_FLAG; // Set LOW ODR bit and ADD_INC bit
if (this->write_register(CTRL_REG, &ctrl_value, 1) != i2c::NO_ERROR) {
this->mark_failed(ESP_LOG_MSG_COMM_FAIL);
return;
}
}
} // namespace esphome::stts22h

View File

@@ -1,21 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome::stts22h {
class STTS22HComponent : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void update() override;
void dump_config() override;
protected:
void initialize_sensor_();
bool is_stts22h_sensor_();
float read_temperature_();
};
} // namespace esphome::stts22h

View File

@@ -1,4 +0,0 @@
sensor:
- platform: stts22h
name: Temperature
update_interval: 15s

View File

@@ -1,4 +0,0 @@
packages:
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
<<: !include common.yaml

View File

@@ -1,4 +0,0 @@
packages:
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
<<: !include common.yaml

View File

@@ -1,4 +0,0 @@
packages:
i2c: !include ../../test_build_components/common/i2c/nrf52.yaml
<<: !include common.yaml

View File

@@ -1,4 +0,0 @@
packages:
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
<<: !include common.yaml

View File

@@ -5,7 +5,6 @@ host:
# This is required for CustomAPIDevice to work
api:
custom_services: true
homeassistant_states: true
# Also test that YAML services still work
actions:
- action: test_yaml_service

View File

@@ -17,10 +17,6 @@ void CustomAPIDeviceComponent::setup() {
// Test array types
register_service(&CustomAPIDeviceComponent::on_service_with_arrays, "custom_service_with_arrays",
{"bool_array", "int_array", "float_array", "string_array"});
// Test Home Assistant state subscription using std::string API (custom_api_device.h)
// This tests the backward compatibility of the std::string overloads
subscribe_homeassistant_state(&CustomAPIDeviceComponent::on_ha_state_changed, std::string("sensor.custom_test"));
}
void CustomAPIDeviceComponent::on_test_service() { ESP_LOGI(TAG, "Custom test service called!"); }
@@ -52,11 +48,6 @@ void CustomAPIDeviceComponent::on_service_with_arrays(std::vector<bool> bool_arr
}
}
void CustomAPIDeviceComponent::on_ha_state_changed(std::string entity_id, std::string state) {
ESP_LOGI(TAG, "Home Assistant state changed for %s: %s", entity_id.c_str(), state.c_str());
ESP_LOGI(TAG, "This subscription uses std::string API for backward compatibility");
}
} // namespace custom_api_device_component
} // namespace esphome
#endif // USE_API

View File

@@ -22,9 +22,6 @@ class CustomAPIDeviceComponent : public Component, public CustomAPIDevice {
void on_service_with_arrays(std::vector<bool> bool_array, std::vector<int32_t> int_array,
std::vector<float> float_array, std::vector<std::string> string_array);
// Test Home Assistant state subscription with std::string API
void on_ha_state_changed(std::string entity_id, std::string state);
};
} // namespace custom_api_device_component

View File

@@ -38,7 +38,6 @@ async def test_api_custom_services(
custom_service_future = loop.create_future()
custom_args_future = loop.create_future()
custom_arrays_future = loop.create_future()
ha_state_future = loop.create_future()
# Patterns to match in logs
yaml_service_pattern = re.compile(r"YAML service called")
@@ -51,9 +50,6 @@ async def test_api_custom_services(
custom_arrays_pattern = re.compile(
r"Array service called with 2 bools, 3 ints, 2 floats, 2 strings"
)
ha_state_pattern = re.compile(
r"This subscription uses std::string API for backward compatibility"
)
def check_output(line: str) -> None:
"""Check log output for expected messages."""
@@ -69,8 +65,6 @@ async def test_api_custom_services(
custom_args_future.set_result(True)
elif not custom_arrays_future.done() and custom_arrays_pattern.search(line):
custom_arrays_future.set_result(True)
elif not ha_state_future.done() and ha_state_pattern.search(line):
ha_state_future.set_result(True)
# Run with log monitoring
async with (
@@ -204,8 +198,3 @@ async def test_api_custom_services(
},
)
await asyncio.wait_for(custom_arrays_future, timeout=5.0)
# Test Home Assistant state subscription (std::string API backward compatibility)
# This verifies that custom_api_device.h can still use std::string overloads
client.send_home_assistant_state("sensor.custom_test", "", "42.5")
await asyncio.wait_for(ha_state_future, timeout=5.0)