mirror of
https://github.com/esphome/esphome.git
synced 2025-11-09 03:21:52 +00:00
Compare commits
29 Commits
api_shrink
...
parition_c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a2fc8aa51 | ||
|
|
c77bb3b269 | ||
|
|
79d1a558af | ||
|
|
a5bf55b6ac | ||
|
|
85d2565f25 | ||
|
|
4f08f0750a | ||
|
|
3c41e080c5 | ||
|
|
7c30d57391 | ||
|
|
182e106bfa | ||
|
|
d0b399d771 | ||
|
|
5d20e3a3b4 | ||
|
|
ba5fa7c10a | ||
|
|
5cdb891b58 | ||
|
|
26607713bb | ||
|
|
895d76ca03 | ||
|
|
74187845b7 | ||
|
|
822eacfd77 | ||
|
|
ab5d8f67ae | ||
|
|
83f30a64ed | ||
|
|
5eea7bdb44 | ||
|
|
bdfd88441a | ||
|
|
20b6e0d5c2 | ||
|
|
ce5e608863 | ||
|
|
aa5795c019 | ||
|
|
00c0854323 | ||
|
|
be006ecadd | ||
|
|
b08419fa47 | ||
|
|
d36ef050a9 | ||
|
|
df53ff7afe |
@@ -51,7 +51,79 @@ This document provides essential context for AI models interacting with this pro
|
||||
|
||||
* **Naming Conventions:**
|
||||
* **Python:** Follows PEP 8. Use clear, descriptive names following snake_case.
|
||||
* **C++:** Follows the Google C++ Style Guide.
|
||||
* **C++:** Follows the Google C++ Style Guide with these specifics (following clang-tidy conventions):
|
||||
- Function, method, and variable names: `lower_snake_case`
|
||||
- Class/struct/enum names: `UpperCamelCase`
|
||||
- Top-level constants (global/namespace scope): `UPPER_SNAKE_CASE`
|
||||
- Function-local constants: `lower_snake_case`
|
||||
- Protected/private fields: `lower_snake_case_with_trailing_underscore_`
|
||||
- Favor descriptive names over abbreviations
|
||||
|
||||
* **C++ Field Visibility:**
|
||||
* **Prefer `protected`:** Use `protected` for most class fields to enable extensibility and testing. Fields should be `lower_snake_case_with_trailing_underscore_`.
|
||||
* **Use `private` for safety-critical cases:** Use `private` visibility when direct field access could introduce bugs or violate invariants:
|
||||
1. **Pointer lifetime issues:** When setters validate and store pointers from known lists to prevent dangling references.
|
||||
```cpp
|
||||
// Helper to find matching string in vector and return its pointer
|
||||
inline const char *vector_find(const std::vector<const char *> &vec, const char *value) {
|
||||
for (const char *item : vec) {
|
||||
if (strcmp(item, value) == 0)
|
||||
return item;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class ClimateDevice {
|
||||
public:
|
||||
void set_custom_fan_modes(std::initializer_list<const char *> modes) {
|
||||
this->custom_fan_modes_ = modes;
|
||||
this->active_custom_fan_mode_ = nullptr; // Reset when modes change
|
||||
}
|
||||
bool set_custom_fan_mode(const char *mode) {
|
||||
// Find mode in supported list and store that pointer (not the input pointer)
|
||||
const char *validated_mode = vector_find(this->custom_fan_modes_, mode);
|
||||
if (validated_mode != nullptr) {
|
||||
this->active_custom_fan_mode_ = validated_mode;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
std::vector<const char *> custom_fan_modes_; // Pointers to string literals in flash
|
||||
const char *active_custom_fan_mode_{nullptr}; // Must point to entry in custom_fan_modes_
|
||||
};
|
||||
```
|
||||
2. **Invariant coupling:** When multiple fields must remain synchronized to prevent buffer overflows or data corruption.
|
||||
```cpp
|
||||
class Buffer {
|
||||
public:
|
||||
void resize(size_t new_size) {
|
||||
auto new_data = std::make_unique<uint8_t[]>(new_size);
|
||||
if (this->data_) {
|
||||
std::memcpy(new_data.get(), this->data_.get(), std::min(this->size_, new_size));
|
||||
}
|
||||
this->data_ = std::move(new_data);
|
||||
this->size_ = new_size; // Must stay in sync with data_
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<uint8_t[]> data_;
|
||||
size_t size_{0}; // Must match allocated size of data_
|
||||
};
|
||||
```
|
||||
3. **Resource management:** When setters perform cleanup or registration operations that derived classes might skip.
|
||||
* **Provide `protected` accessor methods:** When derived classes need controlled access to `private` members.
|
||||
|
||||
* **C++ Preprocessor Directives:**
|
||||
* **Avoid `#define` for constants:** Using `#define` for constants is discouraged and should be replaced with `const` variables or enums.
|
||||
* **Use `#define` only for:**
|
||||
- Conditional compilation (`#ifdef`, `#ifndef`)
|
||||
- Compile-time sizes calculated during Python code generation (e.g., configuring `std::array` or `StaticVector` dimensions via `cg.add_define()`)
|
||||
|
||||
* **C++ Additional Conventions:**
|
||||
* **Member access:** Prefix all class member access with `this->` (e.g., `this->value_` not `value_`)
|
||||
* **Indentation:** Use spaces (two per indentation level), not tabs
|
||||
* **Type aliases:** Prefer `using type_t = int;` over `typedef int type_t;`
|
||||
* **Line length:** Wrap lines at no more than 120 characters
|
||||
|
||||
* **Component Structure:**
|
||||
* **Standard Files:**
|
||||
@@ -368,3 +440,45 @@ This document provides essential context for AI models interacting with this pro
|
||||
* **Python:** When adding a new Python dependency, add it to the appropriate `requirements*.txt` file and `pyproject.toml`.
|
||||
* **C++ / PlatformIO:** When adding a new C++ dependency, add it to `platformio.ini` and use `cg.add_library`.
|
||||
* **Build Flags:** Use `cg.add_build_flag(...)` to add compiler flags.
|
||||
|
||||
## 8. Public API and Breaking Changes
|
||||
|
||||
* **Public C++ API:**
|
||||
* **Components**: Only documented features at [esphome.io](https://esphome.io) are public API. Undocumented `public` members are internal.
|
||||
* **Core/Base Classes** (`esphome/core/`, `Component`, `Sensor`, etc.): All `public` members are public API.
|
||||
* **Components with Global Accessors** (`global_api_server`, etc.): All `public` members are public API (except config setters).
|
||||
|
||||
* **Public Python API:**
|
||||
* All documented configuration options at [esphome.io](https://esphome.io) are public API.
|
||||
* Python code in `esphome/core/` actively used by existing core components is considered stable API.
|
||||
* Other Python code is internal unless explicitly documented for external component use.
|
||||
|
||||
* **Breaking Changes Policy:**
|
||||
* Aim for **6-month deprecation window** when possible
|
||||
* Clean breaks allowed for: signature changes, deep refactorings, resource constraints
|
||||
* Must document migration path in PR description (generates release notes)
|
||||
* Blog post required for core/base class changes or significant architectural changes
|
||||
* Full details: https://developers.esphome.io/contributing/code/#public-api-and-breaking-changes
|
||||
|
||||
* **Breaking Change Checklist:**
|
||||
- [ ] Clear justification (RAM/flash savings, architectural improvement)
|
||||
- [ ] Explored non-breaking alternatives
|
||||
- [ ] Added deprecation warnings if possible (use `ESPDEPRECATED` macro for C++)
|
||||
- [ ] Documented migration path in PR description with before/after examples
|
||||
- [ ] Updated all internal usage and esphome-docs
|
||||
- [ ] Tested backward compatibility during deprecation period
|
||||
|
||||
* **Deprecation Pattern (C++):**
|
||||
```cpp
|
||||
// Remove before 2026.6.0
|
||||
ESPDEPRECATED("Use new_method() instead. Removed in 2026.6.0", "2025.12.0")
|
||||
void old_method() { this->new_method(); }
|
||||
```
|
||||
|
||||
* **Deprecation Pattern (Python):**
|
||||
```python
|
||||
# Remove before 2026.6.0
|
||||
if CONF_OLD_KEY in config:
|
||||
_LOGGER.warning(f"'{CONF_OLD_KEY}' deprecated, use '{CONF_NEW_KEY}'. Removed in 2026.6.0")
|
||||
config[CONF_NEW_KEY] = config.pop(CONF_OLD_KEY) # Auto-migrate
|
||||
```
|
||||
|
||||
@@ -11,7 +11,7 @@ ci:
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.14.3
|
||||
rev: v0.14.4
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -290,6 +290,7 @@ esphome/components/mcp23x17_base/* @jesserockz
|
||||
esphome/components/mcp23xxx_base/* @jesserockz
|
||||
esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||
esphome/components/mcp3204/* @rsumner
|
||||
esphome/components/mcp3221/* @philippderdiedas
|
||||
esphome/components/mcp4461/* @p1ngb4ck
|
||||
esphome/components/mcp4728/* @berfenger
|
||||
esphome/components/mcp47a1/* @jesserockz
|
||||
|
||||
@@ -2147,7 +2147,7 @@ message ListEntitiesEventResponse {
|
||||
EntityCategory entity_category = 7;
|
||||
string device_class = 8;
|
||||
|
||||
repeated string event_types = 9;
|
||||
repeated string event_types = 9 [(container_pointer_no_template) = "FixedVector<const char *>"];
|
||||
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
|
||||
}
|
||||
message EventResponse {
|
||||
|
||||
@@ -1310,8 +1310,7 @@ uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *c
|
||||
auto *event = static_cast<event::Event *>(entity);
|
||||
ListEntitiesEventResponse msg;
|
||||
msg.set_device_class(event->get_device_class_ref());
|
||||
for (const auto &event_type : event->get_event_types())
|
||||
msg.event_types.push_back(event_type);
|
||||
msg.event_types = &event->get_event_types();
|
||||
return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||
is_single);
|
||||
}
|
||||
|
||||
@@ -2877,8 +2877,8 @@ void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(6, this->disabled_by_default);
|
||||
buffer.encode_uint32(7, static_cast<uint32_t>(this->entity_category));
|
||||
buffer.encode_string(8, this->device_class_ref_);
|
||||
for (auto &it : this->event_types) {
|
||||
buffer.encode_string(9, it, true);
|
||||
for (const char *it : *this->event_types) {
|
||||
buffer.encode_string(9, it, strlen(it), true);
|
||||
}
|
||||
#ifdef USE_DEVICES
|
||||
buffer.encode_uint32(10, this->device_id);
|
||||
@@ -2894,9 +2894,9 @@ void ListEntitiesEventResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_bool(1, this->disabled_by_default);
|
||||
size.add_uint32(1, static_cast<uint32_t>(this->entity_category));
|
||||
size.add_length(1, this->device_class_ref_.size());
|
||||
if (!this->event_types.empty()) {
|
||||
for (const auto &it : this->event_types) {
|
||||
size.add_length_force(1, it.size());
|
||||
if (!this->event_types->empty()) {
|
||||
for (const char *it : *this->event_types) {
|
||||
size.add_length_force(1, strlen(it));
|
||||
}
|
||||
}
|
||||
#ifdef USE_DEVICES
|
||||
|
||||
@@ -2788,7 +2788,7 @@ class ListEntitiesEventResponse final : public InfoResponseProtoMessage {
|
||||
#endif
|
||||
StringRef device_class_ref_{};
|
||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||
std::vector<std::string> event_types{};
|
||||
const FixedVector<const char *> *event_types{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
|
||||
@@ -2053,7 +2053,7 @@ void ListEntitiesEventResponse::dump_to(std::string &out) const {
|
||||
dump_field(out, "disabled_by_default", this->disabled_by_default);
|
||||
dump_field(out, "entity_category", static_cast<enums::EntityCategory>(this->entity_category));
|
||||
dump_field(out, "device_class", this->device_class_ref_);
|
||||
for (const auto &it : this->event_types) {
|
||||
for (const auto &it : *this->event_types) {
|
||||
dump_field(out, "event_types", it, 4);
|
||||
}
|
||||
#ifdef USE_DEVICES
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
namespace esphome::api {
|
||||
|
||||
#ifdef USE_API_SERVICES
|
||||
template<typename T, typename... Ts> class CustomAPIDeviceService : public UserServiceBase<Ts...> {
|
||||
template<typename T, typename... Ts> class CustomAPIDeviceService : public UserServiceDynamic<Ts...> {
|
||||
public:
|
||||
CustomAPIDeviceService(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names, T *obj,
|
||||
void (T::*callback)(Ts...))
|
||||
: UserServiceBase<Ts...>(name, arg_names), obj_(obj), callback_(callback) {}
|
||||
: UserServiceDynamic<Ts...>(name, arg_names), obj_(obj), callback_(callback) {}
|
||||
|
||||
protected:
|
||||
void execute(Ts... x) override { (this->obj_->*this->callback_)(x...); } // NOLINT
|
||||
|
||||
@@ -23,11 +23,13 @@ template<typename T> T get_execute_arg_value(const ExecuteServiceArgument &arg);
|
||||
|
||||
template<typename T> enums::ServiceArgType to_service_arg_type();
|
||||
|
||||
// Base class for YAML-defined services (most common case)
|
||||
// Stores only pointers to string literals in flash - no heap allocation
|
||||
template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||
public:
|
||||
UserServiceBase(std::string name, const std::array<std::string, sizeof...(Ts)> &arg_names)
|
||||
: name_(std::move(name)), arg_names_(arg_names) {
|
||||
this->key_ = fnv1_hash(this->name_);
|
||||
UserServiceBase(const char *name, const std::array<const char *, sizeof...(Ts)> &arg_names)
|
||||
: name_(name), arg_names_(arg_names) {
|
||||
this->key_ = fnv1_hash(name);
|
||||
}
|
||||
|
||||
ListEntitiesServicesResponse encode_list_service_response() override {
|
||||
@@ -47,7 +49,7 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||
bool execute_service(const ExecuteServiceRequest &req) override {
|
||||
if (req.key != this->key_)
|
||||
return false;
|
||||
if (req.args.size() != this->arg_names_.size())
|
||||
if (req.args.size() != sizeof...(Ts))
|
||||
return false;
|
||||
this->execute_(req.args, typename gens<sizeof...(Ts)>::type());
|
||||
return true;
|
||||
@@ -59,14 +61,60 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||
this->execute((get_execute_arg_value<Ts>(args[S]))...);
|
||||
}
|
||||
|
||||
std::string name_;
|
||||
// Pointers to string literals in flash - no heap allocation
|
||||
const char *name_;
|
||||
std::array<const char *, sizeof...(Ts)> arg_names_;
|
||||
uint32_t key_{0};
|
||||
};
|
||||
|
||||
// Separate class for custom_api_device services (rare case)
|
||||
// Stores copies of runtime-generated names
|
||||
template<typename... Ts> class UserServiceDynamic : public UserServiceDescriptor {
|
||||
public:
|
||||
UserServiceDynamic(std::string name, const std::array<std::string, sizeof...(Ts)> &arg_names)
|
||||
: name_(std::move(name)), arg_names_(arg_names) {
|
||||
this->key_ = fnv1_hash(this->name_.c_str());
|
||||
}
|
||||
|
||||
ListEntitiesServicesResponse encode_list_service_response() override {
|
||||
ListEntitiesServicesResponse msg;
|
||||
msg.set_name(StringRef(this->name_));
|
||||
msg.key = this->key_;
|
||||
std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
|
||||
msg.args.init(sizeof...(Ts));
|
||||
for (size_t i = 0; i < sizeof...(Ts); i++) {
|
||||
auto &arg = msg.args.emplace_back();
|
||||
arg.type = arg_types[i];
|
||||
arg.set_name(StringRef(this->arg_names_[i]));
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool execute_service(const ExecuteServiceRequest &req) override {
|
||||
if (req.key != this->key_)
|
||||
return false;
|
||||
if (req.args.size() != sizeof...(Ts))
|
||||
return false;
|
||||
this->execute_(req.args, typename gens<sizeof...(Ts)>::type());
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void execute(Ts... x) = 0;
|
||||
template<typename ArgsContainer, int... S> void execute_(const ArgsContainer &args, seq<S...> type) {
|
||||
this->execute((get_execute_arg_value<Ts>(args[S]))...);
|
||||
}
|
||||
|
||||
// Heap-allocated strings for runtime-generated names
|
||||
std::string name_;
|
||||
std::array<std::string, sizeof...(Ts)> arg_names_;
|
||||
uint32_t key_{0};
|
||||
};
|
||||
|
||||
template<typename... Ts> class UserServiceTrigger : public UserServiceBase<Ts...>, public Trigger<Ts...> {
|
||||
public:
|
||||
UserServiceTrigger(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names)
|
||||
// Constructor for static names (YAML-defined services - used by code generator)
|
||||
UserServiceTrigger(const char *name, const std::array<const char *, sizeof...(Ts)> &arg_names)
|
||||
: UserServiceBase<Ts...>(name, arg_names) {}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -9,6 +9,7 @@ BYTE_ORDER_BIG = "big_endian"
|
||||
CONF_COLOR_DEPTH = "color_depth"
|
||||
CONF_DRAW_ROUNDING = "draw_rounding"
|
||||
CONF_ENABLED = "enabled"
|
||||
CONF_IGNORE_NOT_FOUND = "ignore_not_found"
|
||||
CONF_ON_RECEIVE = "on_receive"
|
||||
CONF_ON_STATE_CHANGE = "on_state_change"
|
||||
CONF_REQUEST_HEADERS = "request_headers"
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace demo {
|
||||
|
||||
class DemoSelect : public select::Select, public Component {
|
||||
protected:
|
||||
void control(const std::string &value) override { this->publish_state(value); }
|
||||
void control(size_t index) override { this->publish_state(index); }
|
||||
};
|
||||
|
||||
} // namespace demo
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace es8388 {
|
||||
|
||||
void ADCInputMicSelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_adc_input_mic(static_cast<AdcInputMicLine>(this->index_of(value).value()));
|
||||
void ADCInputMicSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_adc_input_mic(static_cast<AdcInputMicLine>(index));
|
||||
}
|
||||
|
||||
} // namespace es8388
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace es8388 {
|
||||
|
||||
class ADCInputMicSelect : public select::Select, public Parented<ES8388> {
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace es8388
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace es8388 {
|
||||
|
||||
void DacOutputSelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_dac_output(static_cast<DacOutputLine>(this->index_of(value).value()));
|
||||
void DacOutputSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_dac_output(static_cast<DacOutputLine>(index));
|
||||
}
|
||||
|
||||
} // namespace es8388
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace es8388 {
|
||||
|
||||
class DacOutputSelect : public select::Select, public Parented<ES8388> {
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace es8388
|
||||
|
||||
@@ -8,11 +8,11 @@ namespace event {
|
||||
static const char *const TAG = "event";
|
||||
|
||||
void Event::trigger(const std::string &event_type) {
|
||||
// Linear search - faster than std::set for small datasets (1-5 items typical)
|
||||
const std::string *found = nullptr;
|
||||
for (const auto &type : this->types_) {
|
||||
if (type == event_type) {
|
||||
found = &type;
|
||||
// Linear search with strcmp - faster than std::set for small datasets (1-5 items typical)
|
||||
const char *found = nullptr;
|
||||
for (const char *type : this->types_) {
|
||||
if (strcmp(type, event_type.c_str()) == 0) {
|
||||
found = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -20,11 +20,27 @@ void Event::trigger(const std::string &event_type) {
|
||||
ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str());
|
||||
return;
|
||||
}
|
||||
last_event_type = found;
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), last_event_type->c_str());
|
||||
this->last_event_type_ = found;
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), this->last_event_type_);
|
||||
this->event_callback_.call(event_type);
|
||||
}
|
||||
|
||||
void Event::set_event_types(const FixedVector<const char *> &event_types) {
|
||||
this->types_.init(event_types.size());
|
||||
for (const char *type : event_types) {
|
||||
this->types_.push_back(type);
|
||||
}
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
|
||||
void Event::set_event_types(const std::vector<const char *> &event_types) {
|
||||
this->types_.init(event_types.size());
|
||||
for (const char *type : event_types) {
|
||||
this->types_.push_back(type);
|
||||
}
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
|
||||
void Event::add_on_event_callback(std::function<void(const std::string &event_type)> &&callback) {
|
||||
this->event_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
@@ -22,16 +24,39 @@ namespace event {
|
||||
|
||||
class Event : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
const std::string *last_event_type;
|
||||
|
||||
void trigger(const std::string &event_type);
|
||||
void set_event_types(const std::initializer_list<std::string> &event_types) { this->types_ = event_types; }
|
||||
const FixedVector<std::string> &get_event_types() const { return this->types_; }
|
||||
|
||||
/// Set the event types supported by this event (from initializer list).
|
||||
void set_event_types(std::initializer_list<const char *> event_types) {
|
||||
this->types_ = event_types;
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
/// Set the event types supported by this event (from FixedVector).
|
||||
void set_event_types(const FixedVector<const char *> &event_types);
|
||||
/// Set the event types supported by this event (from vector).
|
||||
void set_event_types(const std::vector<const char *> &event_types);
|
||||
|
||||
// Deleted overloads to catch incorrect std::string usage at compile time with clear error messages
|
||||
void set_event_types(std::initializer_list<std::string> event_types) = delete;
|
||||
void set_event_types(const FixedVector<std::string> &event_types) = delete;
|
||||
void set_event_types(const std::vector<std::string> &event_types) = delete;
|
||||
|
||||
/// Return the event types supported by this event.
|
||||
const FixedVector<const char *> &get_event_types() const { return this->types_; }
|
||||
|
||||
/// Return the last triggered event type (pointer to string in types_), or nullptr if no event triggered yet.
|
||||
const char *get_last_event_type() const { return this->last_event_type_; }
|
||||
|
||||
void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback);
|
||||
|
||||
protected:
|
||||
CallbackManager<void(const std::string &event_type)> event_callback_;
|
||||
FixedVector<std::string> types_;
|
||||
FixedVector<const char *> types_;
|
||||
|
||||
private:
|
||||
/// Last triggered event type - must point to entry in types_ to ensure valid lifetime.
|
||||
/// Set by trigger() after validation, reset to nullptr when types_ changes.
|
||||
const char *last_event_type_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace event
|
||||
|
||||
@@ -62,7 +62,6 @@ void GDK101Component::dump_config() {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR(" ", "Firmware Version", this->fw_version_sensor_);
|
||||
LOG_SENSOR(" ", "Average Radaition Dose per 1 minute", this->rad_1m_sensor_);
|
||||
LOG_SENSOR(" ", "Average Radaition Dose per 10 minutes", this->rad_10m_sensor_);
|
||||
LOG_SENSOR(" ", "Status", this->status_sensor_);
|
||||
@@ -72,6 +71,10 @@ void GDK101Component::dump_config() {
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
LOG_BINARY_SENSOR(" ", "Vibration Status", this->vibration_binary_sensor_);
|
||||
#endif // USE_BINARY_SENSOR
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
LOG_TEXT_SENSOR(" ", "Firmware Version", this->fw_version_text_sensor_);
|
||||
#endif // USE_TEXT_SENSOR
|
||||
}
|
||||
|
||||
float GDK101Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
@@ -153,18 +156,18 @@ bool GDK101Component::read_status_(uint8_t *data) {
|
||||
}
|
||||
|
||||
bool GDK101Component::read_fw_version_(uint8_t *data) {
|
||||
#ifdef USE_SENSOR
|
||||
if (this->fw_version_sensor_ != nullptr) {
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->fw_version_text_sensor_ != nullptr) {
|
||||
if (!this->read_bytes(GDK101_REG_READ_FIRMWARE, data, 2)) {
|
||||
ESP_LOGE(TAG, "Updating GDK101 failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
const float fw_version = data[0] + (data[1] / 10.0f);
|
||||
const std::string fw_version_str = str_sprintf("%d.%d", data[0], data[1]);
|
||||
|
||||
this->fw_version_sensor_->publish_state(fw_version);
|
||||
this->fw_version_text_sensor_->publish_state(fw_version_str);
|
||||
}
|
||||
#endif // USE_SENSOR
|
||||
#endif // USE_TEXT_SENSOR
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif // USE_BINARY_SENSOR
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#endif // USE_TEXT_SENSOR
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -25,12 +28,14 @@ class GDK101Component : public PollingComponent, public i2c::I2CDevice {
|
||||
SUB_SENSOR(rad_1m)
|
||||
SUB_SENSOR(rad_10m)
|
||||
SUB_SENSOR(status)
|
||||
SUB_SENSOR(fw_version)
|
||||
SUB_SENSOR(measurement_duration)
|
||||
#endif // USE_SENSOR
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
SUB_BINARY_SENSOR(vibration)
|
||||
#endif // USE_BINARY_SENSOR
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
SUB_TEXT_SENSOR(fw_version)
|
||||
#endif // USE_TEXT_SENSOR
|
||||
|
||||
public:
|
||||
void setup() override;
|
||||
|
||||
@@ -40,9 +40,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_VERSION): sensor.sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
accuracy_decimals=1,
|
||||
cv.Optional(CONF_VERSION): cv.invalid(
|
||||
"The 'version' option has been moved to the `text_sensor` component."
|
||||
),
|
||||
cv.Optional(CONF_STATUS): sensor.sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
@@ -71,10 +70,6 @@ async def to_code(config):
|
||||
sens = await sensor.new_sensor(radiation_dose_per_10m)
|
||||
cg.add(hub.set_rad_10m_sensor(sens))
|
||||
|
||||
if version_config := config.get(CONF_VERSION):
|
||||
sens = await sensor.new_sensor(version_config)
|
||||
cg.add(hub.set_fw_version_sensor(sens))
|
||||
|
||||
if status_config := config.get(CONF_STATUS):
|
||||
sens = await sensor.new_sensor(status_config)
|
||||
cg.add(hub.set_status_sensor(sens))
|
||||
|
||||
23
esphome/components/gdk101/text_sensor.py
Normal file
23
esphome/components/gdk101/text_sensor.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_VERSION, ENTITY_CATEGORY_DIAGNOSTIC, ICON_CHIP
|
||||
|
||||
from . import CONF_GDK101_ID, GDK101Component
|
||||
|
||||
DEPENDENCIES = ["gdk101"]
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_GDK101_ID): cv.use_id(GDK101Component),
|
||||
cv.Required(CONF_VERSION): text_sensor.text_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, icon=ICON_CHIP
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_GDK101_ID])
|
||||
var = await text_sensor.new_text_sensor(config[CONF_VERSION])
|
||||
cg.add(hub.set_fw_version_text_sensor(var))
|
||||
@@ -235,7 +235,7 @@ void GraphLegend::init(Graph *g) {
|
||||
std::string valstr =
|
||||
value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals());
|
||||
if (this->units_) {
|
||||
valstr += trace->sensor_->get_unit_of_measurement();
|
||||
valstr += trace->sensor_->get_unit_of_measurement_ref();
|
||||
}
|
||||
this->font_value_->measure(valstr.c_str(), &fw, &fos, &fbl, &fh);
|
||||
if (fw > valw)
|
||||
@@ -371,7 +371,7 @@ void Graph::draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_of
|
||||
std::string valstr =
|
||||
value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals());
|
||||
if (legend_->units_) {
|
||||
valstr += trace->sensor_->get_unit_of_measurement();
|
||||
valstr += trace->sensor_->get_unit_of_measurement_ref();
|
||||
}
|
||||
buff->printf(xv, yv, legend_->font_value_, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", valstr.c_str());
|
||||
ESP_LOGV(TAG, " value: %s", valstr.c_str());
|
||||
|
||||
@@ -3,6 +3,8 @@ import re
|
||||
from esphome import config_validation as cv
|
||||
from esphome.const import CONF_ARGS, CONF_FORMAT
|
||||
|
||||
CONF_IF_NAN = "if_nan"
|
||||
|
||||
lv_uses = {
|
||||
"USER_DATA",
|
||||
"LOG",
|
||||
@@ -21,23 +23,48 @@ lv_fonts_used = set()
|
||||
esphome_fonts_used = set()
|
||||
lvgl_components_required = set()
|
||||
|
||||
|
||||
def validate_printf(value):
|
||||
cfmt = r"""
|
||||
# noqa
|
||||
f_regex = re.compile(
|
||||
r"""
|
||||
( # start of capture group 1
|
||||
% # literal "%"
|
||||
(?:[-+0 #]{0,5}) # optional flags
|
||||
[-+0 #]{0,5} # optional flags
|
||||
(?:\d+|\*)? # width
|
||||
(?:\.(?:\d+|\*))? # precision
|
||||
(?:h|l|ll|w|I|I32|I64)? # size
|
||||
f # type
|
||||
)
|
||||
""",
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
# noqa
|
||||
c_regex = re.compile(
|
||||
r"""
|
||||
( # start of capture group 1
|
||||
% # literal "%"
|
||||
[-+0 #]{0,5} # optional flags
|
||||
(?:\d+|\*)? # width
|
||||
(?:\.(?:\d+|\*))? # precision
|
||||
(?:h|l|ll|w|I|I32|I64)? # size
|
||||
[cCdiouxXeEfgGaAnpsSZ] # type
|
||||
)
|
||||
""" # noqa
|
||||
matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.VERBOSE)
|
||||
""",
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
def validate_printf(value):
|
||||
format_string = value[CONF_FORMAT]
|
||||
matches = c_regex.findall(format_string)
|
||||
if len(matches) != len(value[CONF_ARGS]):
|
||||
raise cv.Invalid(
|
||||
f"Found {len(matches)} printf-patterns ({', '.join(matches)}), but {len(value[CONF_ARGS])} args were given!"
|
||||
)
|
||||
|
||||
if value.get(CONF_IF_NAN) and len(f_regex.findall(format_string)) != 1:
|
||||
raise cv.Invalid(
|
||||
"Use of 'if_nan' requires a single valid printf-pattern of type %f"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,13 @@ from .defines import (
|
||||
call_lambda,
|
||||
literal,
|
||||
)
|
||||
from .helpers import add_lv_use, esphome_fonts_used, lv_fonts_used, requires_component
|
||||
from .helpers import (
|
||||
CONF_IF_NAN,
|
||||
add_lv_use,
|
||||
esphome_fonts_used,
|
||||
lv_fonts_used,
|
||||
requires_component,
|
||||
)
|
||||
from .types import lv_font_t, lv_gradient_t
|
||||
|
||||
opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER")
|
||||
@@ -412,7 +418,13 @@ class TextValidator(LValidator):
|
||||
str_args = [str(x) for x in value[CONF_ARGS]]
|
||||
arg_expr = cg.RawExpression(",".join(str_args))
|
||||
format_str = cpp_string_escape(format_str)
|
||||
return literal(f"str_sprintf({format_str}, {arg_expr}).c_str()")
|
||||
sprintf_str = f"str_sprintf({format_str}, {arg_expr}).c_str()"
|
||||
if nanval := value.get(CONF_IF_NAN):
|
||||
nanval = cpp_string_escape(nanval)
|
||||
return literal(
|
||||
f"(std::isfinite({arg_expr}) ? {sprintf_str} : {nanval})"
|
||||
)
|
||||
return literal(sprintf_str)
|
||||
if time_format := value.get(CONF_TIME_FORMAT):
|
||||
source = value[CONF_TIME]
|
||||
if isinstance(source, Lambda):
|
||||
|
||||
@@ -20,7 +20,7 @@ from esphome.core.config import StartupTrigger
|
||||
|
||||
from . import defines as df, lv_validation as lvalid
|
||||
from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR
|
||||
from .helpers import requires_component, validate_printf
|
||||
from .helpers import CONF_IF_NAN, requires_component, validate_printf
|
||||
from .layout import (
|
||||
FLEX_OBJ_SCHEMA,
|
||||
GRID_CELL_SCHEMA,
|
||||
@@ -54,6 +54,7 @@ PRINTF_TEXT_SCHEMA = cv.All(
|
||||
{
|
||||
cv.Required(CONF_FORMAT): cv.string,
|
||||
cv.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_),
|
||||
cv.Optional(CONF_IF_NAN): cv.string,
|
||||
},
|
||||
),
|
||||
validate_printf,
|
||||
|
||||
@@ -41,16 +41,16 @@ class LVGLSelect : public select::Select, public Component {
|
||||
}
|
||||
|
||||
void publish() {
|
||||
this->publish_state(this->widget_->get_selected_text());
|
||||
auto index = this->widget_->get_selected_index();
|
||||
this->publish_state(index);
|
||||
if (this->restore_) {
|
||||
auto index = this->widget_->get_selected_index();
|
||||
this->pref_.save(&index);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override {
|
||||
this->widget_->set_selected_text(value, this->anim_);
|
||||
void control(size_t index) override {
|
||||
this->widget_->set_selected_index(index, this->anim_);
|
||||
this->publish();
|
||||
}
|
||||
void set_options_() {
|
||||
|
||||
1
esphome/components/mcp3221/__init__.py
Normal file
1
esphome/components/mcp3221/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@philippderdiedas"]
|
||||
31
esphome/components/mcp3221/mcp3221_sensor.cpp
Normal file
31
esphome/components/mcp3221/mcp3221_sensor.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "mcp3221_sensor.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mcp3221 {
|
||||
|
||||
static const char *const TAG = "mcp3221";
|
||||
|
||||
float MCP3221Sensor::sample() {
|
||||
uint8_t data[2];
|
||||
if (this->read(data, 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Read failed");
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
}
|
||||
this->status_clear_warning();
|
||||
|
||||
uint16_t value = encode_uint16(data[0], data[1]);
|
||||
float voltage = value * this->reference_voltage_ / 4096.0f;
|
||||
|
||||
return voltage;
|
||||
}
|
||||
|
||||
void MCP3221Sensor::update() {
|
||||
float v = this->sample();
|
||||
this->publish_state(v);
|
||||
}
|
||||
|
||||
} // namespace mcp3221
|
||||
} // namespace esphome
|
||||
28
esphome/components/mcp3221/mcp3221_sensor.h
Normal file
28
esphome/components/mcp3221/mcp3221_sensor.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace mcp3221 {
|
||||
|
||||
class MCP3221Sensor : public sensor::Sensor,
|
||||
public PollingComponent,
|
||||
public voltage_sampler::VoltageSampler,
|
||||
public i2c::I2CDevice {
|
||||
public:
|
||||
void set_reference_voltage(float reference_voltage) { this->reference_voltage_ = reference_voltage; }
|
||||
void update() override;
|
||||
float sample() override;
|
||||
|
||||
protected:
|
||||
float reference_voltage_;
|
||||
};
|
||||
|
||||
} // namespace mcp3221
|
||||
} // namespace esphome
|
||||
49
esphome/components/mcp3221/sensor.py
Normal file
49
esphome/components/mcp3221/sensor.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c, sensor, voltage_sampler
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_REFERENCE_VOLTAGE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_SCALE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["voltage_sampler"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
|
||||
mcp3221_ns = cg.esphome_ns.namespace("mcp3221")
|
||||
MCP3221Sensor = mcp3221_ns.class_(
|
||||
"MCP3221Sensor",
|
||||
sensor.Sensor,
|
||||
voltage_sampler.VoltageSampler,
|
||||
cg.PollingComponent,
|
||||
i2c.I2CDevice,
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
MCP3221Sensor,
|
||||
icon=ICON_SCALE,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_REFERENCE_VOLTAGE, default="3.3V"): cv.voltage,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x48))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await sensor.new_sensor(config)
|
||||
cg.add(var.set_reference_voltage(config[CONF_REFERENCE_VOLTAGE]))
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
@@ -37,8 +37,6 @@ MDNS_STATIC_CONST_CHAR(SERVICE_TCP, "_tcp");
|
||||
MDNS_STATIC_CONST_CHAR(VALUE_VERSION, ESPHOME_VERSION);
|
||||
|
||||
void MDNSComponent::compile_records_(StaticVector<MDNSService, MDNS_SERVICE_COUNT> &services) {
|
||||
this->hostname_ = App.get_name();
|
||||
|
||||
// IMPORTANT: The #ifdef blocks below must match COMPONENTS_WITH_MDNS_SERVICES
|
||||
// in mdns/__init__.py. If you add a new service here, update both locations.
|
||||
|
||||
@@ -179,7 +177,7 @@ void MDNSComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"mDNS:\n"
|
||||
" Hostname: %s",
|
||||
this->hostname_.c_str());
|
||||
App.get_name().c_str());
|
||||
#ifdef USE_MDNS_STORE_SERVICES
|
||||
ESP_LOGV(TAG, " Services:");
|
||||
for (const auto &service : this->services_) {
|
||||
|
||||
@@ -76,7 +76,6 @@ class MDNSComponent : public Component {
|
||||
#ifdef USE_MDNS_STORE_SERVICES
|
||||
StaticVector<MDNSService, MDNS_SERVICE_COUNT> services_{};
|
||||
#endif
|
||||
std::string hostname_;
|
||||
void compile_records_(StaticVector<MDNSService, MDNS_SERVICE_COUNT> &services);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#if defined(USE_ESP32) && defined(USE_MDNS)
|
||||
|
||||
#include <mdns.h>
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "mdns_component.h"
|
||||
@@ -27,8 +28,9 @@ void MDNSComponent::setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
mdns_hostname_set(this->hostname_.c_str());
|
||||
mdns_instance_name_set(this->hostname_.c_str());
|
||||
const char *hostname = App.get_name().c_str();
|
||||
mdns_hostname_set(hostname);
|
||||
mdns_instance_name_set(hostname);
|
||||
|
||||
for (const auto &service : services) {
|
||||
auto txt_records = std::make_unique<mdns_txt_item_t[]>(service.txt_records.size());
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <ESP8266mDNS.h>
|
||||
#include "esphome/components/network/ip_address.h"
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "mdns_component.h"
|
||||
@@ -20,7 +21,7 @@ void MDNSComponent::setup() {
|
||||
this->compile_records_(services);
|
||||
#endif
|
||||
|
||||
MDNS.begin(this->hostname_.c_str());
|
||||
MDNS.begin(App.get_name().c_str());
|
||||
|
||||
for (const auto &service : services) {
|
||||
// Strip the leading underscore from the proto and service_type. While it is
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "esphome/components/network/ip_address.h"
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "mdns_component.h"
|
||||
|
||||
@@ -20,7 +21,7 @@ void MDNSComponent::setup() {
|
||||
this->compile_records_(services);
|
||||
#endif
|
||||
|
||||
MDNS.begin(this->hostname_.c_str());
|
||||
MDNS.begin(App.get_name().c_str());
|
||||
|
||||
for (const auto &service : services) {
|
||||
// Strip the leading underscore from the proto and service_type. While it is
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "esphome/components/network/ip_address.h"
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "mdns_component.h"
|
||||
|
||||
@@ -20,7 +21,7 @@ void MDNSComponent::setup() {
|
||||
this->compile_records_(services);
|
||||
#endif
|
||||
|
||||
MDNS.begin(this->hostname_.c_str());
|
||||
MDNS.begin(App.get_name().c_str());
|
||||
|
||||
for (const auto &service : services) {
|
||||
// Strip the leading underscore from the proto and service_type. While it is
|
||||
|
||||
@@ -28,8 +28,9 @@ void ModbusSelect::parse_and_publish(const std::vector<uint8_t> &data) {
|
||||
|
||||
if (map_it != this->mapping_.cend()) {
|
||||
size_t idx = std::distance(this->mapping_.cbegin(), map_it);
|
||||
new_state = std::string(this->option_at(idx));
|
||||
ESP_LOGV(TAG, "Found option %s for value %lld", new_state->c_str(), value);
|
||||
ESP_LOGV(TAG, "Found option %s for value %lld", this->option_at(idx), value);
|
||||
this->publish_state(idx);
|
||||
return;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No option found for mapping %lld", value);
|
||||
}
|
||||
@@ -40,19 +41,16 @@ void ModbusSelect::parse_and_publish(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
}
|
||||
|
||||
void ModbusSelect::control(const std::string &value) {
|
||||
auto idx = this->index_of(value);
|
||||
if (!idx.has_value()) {
|
||||
ESP_LOGW(TAG, "Invalid option '%s'", value.c_str());
|
||||
return;
|
||||
}
|
||||
optional<int64_t> mapval = this->mapping_[idx.value()];
|
||||
ESP_LOGD(TAG, "Found value %lld for option '%s'", *mapval, value.c_str());
|
||||
void ModbusSelect::control(size_t index) {
|
||||
optional<int64_t> mapval = this->mapping_[index];
|
||||
const char *option = this->option_at(index);
|
||||
ESP_LOGD(TAG, "Found value %lld for option '%s'", *mapval, option);
|
||||
|
||||
std::vector<uint16_t> data;
|
||||
|
||||
if (this->write_transform_func_.has_value()) {
|
||||
auto val = (*this->write_transform_func_)(this, value, *mapval, data);
|
||||
// Transform func requires string parameter for backward compatibility
|
||||
auto val = (*this->write_transform_func_)(this, std::string(option), *mapval, data);
|
||||
if (val.has_value()) {
|
||||
mapval = *val;
|
||||
ESP_LOGV(TAG, "write_lambda returned mapping value %lld", *mapval);
|
||||
@@ -85,7 +83,7 @@ void ModbusSelect::control(const std::string &value) {
|
||||
this->parent_->queue_command(write_cmd);
|
||||
|
||||
if (this->optimistic_)
|
||||
this->publish_state(value);
|
||||
this->publish_state(index);
|
||||
}
|
||||
|
||||
} // namespace modbus_controller
|
||||
|
||||
@@ -38,7 +38,7 @@ class ModbusSelect : public Component, public select::Select, public SensorItem
|
||||
|
||||
void dump_config() override;
|
||||
void parse_and_publish(const std::vector<uint8_t> &data) override;
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
|
||||
protected:
|
||||
std::vector<int64_t> mapping_{};
|
||||
|
||||
@@ -36,7 +36,7 @@ void MQTTAlarmControlPanelComponent::setup() {
|
||||
} else if (strcasecmp(payload.c_str(), "TRIGGERED") == 0) {
|
||||
call.triggered();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name().c_str(), payload.c_str());
|
||||
ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name_().c_str(), payload.c_str());
|
||||
}
|
||||
call.perform();
|
||||
});
|
||||
|
||||
@@ -30,9 +30,12 @@ MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor
|
||||
}
|
||||
|
||||
void MQTTBinarySensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
if (!this->binary_sensor_->get_device_class().empty())
|
||||
root[MQTT_DEVICE_CLASS] = this->binary_sensor_->get_device_class();
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
const auto device_class = this->binary_sensor_->get_device_class_ref();
|
||||
if (!device_class.empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = device_class;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
if (this->binary_sensor_->is_status_binary_sensor())
|
||||
root[MQTT_PAYLOAD_ON] = mqtt::global_mqtt_client->get_availability().payload_available;
|
||||
if (this->binary_sensor_->is_status_binary_sensor())
|
||||
|
||||
@@ -20,7 +20,7 @@ void MQTTButtonComponent::setup() {
|
||||
if (payload == "PRESS") {
|
||||
this->button_->press();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str());
|
||||
ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str());
|
||||
this->status_momentary_warning("state", 5000);
|
||||
}
|
||||
});
|
||||
@@ -33,8 +33,9 @@ void MQTTButtonComponent::dump_config() {
|
||||
void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
config.state_topic = false;
|
||||
if (!this->button_->get_device_class().empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = this->button_->get_device_class();
|
||||
const auto device_class = this->button_->get_device_class_ref();
|
||||
if (!device_class.empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = device_class;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
}
|
||||
|
||||
@@ -64,11 +64,11 @@ bool MQTTComponent::send_discovery_() {
|
||||
const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info();
|
||||
|
||||
if (discovery_info.clean) {
|
||||
ESP_LOGV(TAG, "'%s': Cleaning discovery", this->friendly_name().c_str());
|
||||
ESP_LOGV(TAG, "'%s': Cleaning discovery", this->friendly_name_().c_str());
|
||||
return global_mqtt_client->publish(this->get_discovery_topic_(discovery_info), "", 0, this->qos_, true);
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str());
|
||||
ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name_().c_str());
|
||||
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
return global_mqtt_client->publish_json(
|
||||
@@ -85,12 +85,16 @@ bool MQTTComponent::send_discovery_() {
|
||||
}
|
||||
|
||||
// Fields from EntityBase
|
||||
root[MQTT_NAME] = this->get_entity()->has_own_name() ? this->friendly_name() : "";
|
||||
root[MQTT_NAME] = this->get_entity()->has_own_name() ? this->friendly_name_() : "";
|
||||
|
||||
if (this->is_disabled_by_default())
|
||||
if (this->is_disabled_by_default_())
|
||||
root[MQTT_ENABLED_BY_DEFAULT] = false;
|
||||
if (!this->get_icon().empty())
|
||||
root[MQTT_ICON] = this->get_icon();
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
const auto icon_ref = this->get_icon_ref_();
|
||||
if (!icon_ref.empty()) {
|
||||
root[MQTT_ICON] = icon_ref;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
|
||||
const auto entity_category = this->get_entity()->get_entity_category();
|
||||
switch (entity_category) {
|
||||
@@ -122,7 +126,7 @@ bool MQTTComponent::send_discovery_() {
|
||||
const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info();
|
||||
if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) {
|
||||
char friendly_name_hash[9];
|
||||
sprintf(friendly_name_hash, "%08" PRIx32, fnv1_hash(this->friendly_name()));
|
||||
sprintf(friendly_name_hash, "%08" PRIx32, fnv1_hash(this->friendly_name_()));
|
||||
friendly_name_hash[8] = 0; // ensure the hash-string ends with null
|
||||
root[MQTT_UNIQUE_ID] = get_mac_address() + "-" + this->component_type() + "-" + friendly_name_hash;
|
||||
} else {
|
||||
@@ -184,7 +188,7 @@ bool MQTTComponent::is_discovery_enabled() const {
|
||||
}
|
||||
|
||||
std::string MQTTComponent::get_default_object_id_() const {
|
||||
return str_sanitize(str_snake_case(this->friendly_name()));
|
||||
return str_sanitize(str_snake_case(this->friendly_name_()));
|
||||
}
|
||||
|
||||
void MQTTComponent::subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos) {
|
||||
@@ -268,9 +272,9 @@ void MQTTComponent::schedule_resend_state() { this->resend_state_ = true; }
|
||||
bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connected(); }
|
||||
|
||||
// Pull these properties from EntityBase if not overridden
|
||||
std::string MQTTComponent::friendly_name() const { return this->get_entity()->get_name(); }
|
||||
std::string MQTTComponent::get_icon() const { return this->get_entity()->get_icon(); }
|
||||
bool MQTTComponent::is_disabled_by_default() const { return this->get_entity()->is_disabled_by_default(); }
|
||||
std::string MQTTComponent::friendly_name_() const { return this->get_entity()->get_name(); }
|
||||
StringRef MQTTComponent::get_icon_ref_() const { return this->get_entity()->get_icon_ref(); }
|
||||
bool MQTTComponent::is_disabled_by_default_() const { return this->get_entity()->is_disabled_by_default(); }
|
||||
bool MQTTComponent::is_internal() {
|
||||
if (this->has_custom_state_topic_) {
|
||||
// If the custom state_topic is null, return true as it is internal and should not publish
|
||||
|
||||
@@ -165,13 +165,13 @@ class MQTTComponent : public Component {
|
||||
virtual const EntityBase *get_entity() const = 0;
|
||||
|
||||
/// Get the friendly name of this MQTT component.
|
||||
virtual std::string friendly_name() const;
|
||||
std::string friendly_name_() const;
|
||||
|
||||
/// Get the icon field of this component
|
||||
virtual std::string get_icon() const;
|
||||
/// Get the icon field of this component as StringRef
|
||||
StringRef get_icon_ref_() const;
|
||||
|
||||
/// Get whether the underlying Entity is disabled by default
|
||||
virtual bool is_disabled_by_default() const;
|
||||
bool is_disabled_by_default_() const;
|
||||
|
||||
/// Get the MQTT topic that new states will be shared to.
|
||||
std::string get_state_topic_() const;
|
||||
|
||||
@@ -67,9 +67,12 @@ void MQTTCoverComponent::dump_config() {
|
||||
}
|
||||
}
|
||||
void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
if (!this->cover_->get_device_class().empty())
|
||||
root[MQTT_DEVICE_CLASS] = this->cover_->get_device_class();
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
const auto device_class = this->cover_->get_device_class_ref();
|
||||
if (!device_class.empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = device_class;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
|
||||
auto traits = this->cover_->get_traits();
|
||||
if (traits.get_is_assumed_state()) {
|
||||
|
||||
@@ -21,8 +21,12 @@ void MQTTEventComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConf
|
||||
for (const auto &event_type : this->event_->get_event_types())
|
||||
event_types.add(event_type);
|
||||
|
||||
if (!this->event_->get_device_class().empty())
|
||||
root[MQTT_DEVICE_CLASS] = this->event_->get_device_class();
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
const auto device_class = this->event_->get_device_class_ref();
|
||||
if (!device_class.empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = device_class;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
|
||||
config.command_topic = false;
|
||||
}
|
||||
@@ -34,8 +38,8 @@ void MQTTEventComponent::setup() {
|
||||
void MQTTEventComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Event '%s': ", this->event_->get_name().c_str());
|
||||
ESP_LOGCONFIG(TAG, "Event Types: ");
|
||||
for (const auto &event_type : this->event_->get_event_types()) {
|
||||
ESP_LOGCONFIG(TAG, "- %s", event_type.c_str());
|
||||
for (const char *event_type : this->event_->get_event_types()) {
|
||||
ESP_LOGCONFIG(TAG, "- %s", event_type);
|
||||
}
|
||||
LOG_MQTT_COMPONENT(true, true);
|
||||
}
|
||||
|
||||
@@ -24,15 +24,15 @@ void MQTTFanComponent::setup() {
|
||||
auto val = parse_on_off(payload.c_str());
|
||||
switch (val) {
|
||||
case PARSE_ON:
|
||||
ESP_LOGD(TAG, "'%s' Turning Fan ON.", this->friendly_name().c_str());
|
||||
ESP_LOGD(TAG, "'%s' Turning Fan ON.", this->friendly_name_().c_str());
|
||||
this->state_->turn_on().perform();
|
||||
break;
|
||||
case PARSE_OFF:
|
||||
ESP_LOGD(TAG, "'%s' Turning Fan OFF.", this->friendly_name().c_str());
|
||||
ESP_LOGD(TAG, "'%s' Turning Fan OFF.", this->friendly_name_().c_str());
|
||||
this->state_->turn_off().perform();
|
||||
break;
|
||||
case PARSE_TOGGLE:
|
||||
ESP_LOGD(TAG, "'%s' Toggling Fan.", this->friendly_name().c_str());
|
||||
ESP_LOGD(TAG, "'%s' Toggling Fan.", this->friendly_name_().c_str());
|
||||
this->state_->toggle().perform();
|
||||
break;
|
||||
case PARSE_NONE:
|
||||
@@ -48,11 +48,11 @@ void MQTTFanComponent::setup() {
|
||||
auto val = parse_on_off(payload.c_str(), "forward", "reverse");
|
||||
switch (val) {
|
||||
case PARSE_ON:
|
||||
ESP_LOGD(TAG, "'%s': Setting direction FORWARD", this->friendly_name().c_str());
|
||||
ESP_LOGD(TAG, "'%s': Setting direction FORWARD", this->friendly_name_().c_str());
|
||||
this->state_->make_call().set_direction(fan::FanDirection::FORWARD).perform();
|
||||
break;
|
||||
case PARSE_OFF:
|
||||
ESP_LOGD(TAG, "'%s': Setting direction REVERSE", this->friendly_name().c_str());
|
||||
ESP_LOGD(TAG, "'%s': Setting direction REVERSE", this->friendly_name_().c_str());
|
||||
this->state_->make_call().set_direction(fan::FanDirection::REVERSE).perform();
|
||||
break;
|
||||
case PARSE_TOGGLE:
|
||||
@@ -75,11 +75,11 @@ void MQTTFanComponent::setup() {
|
||||
auto val = parse_on_off(payload.c_str(), "oscillate_on", "oscillate_off");
|
||||
switch (val) {
|
||||
case PARSE_ON:
|
||||
ESP_LOGD(TAG, "'%s': Setting oscillating ON", this->friendly_name().c_str());
|
||||
ESP_LOGD(TAG, "'%s': Setting oscillating ON", this->friendly_name_().c_str());
|
||||
this->state_->make_call().set_oscillating(true).perform();
|
||||
break;
|
||||
case PARSE_OFF:
|
||||
ESP_LOGD(TAG, "'%s': Setting oscillating OFF", this->friendly_name().c_str());
|
||||
ESP_LOGD(TAG, "'%s': Setting oscillating OFF", this->friendly_name_().c_str());
|
||||
this->state_->make_call().set_oscillating(false).perform();
|
||||
break;
|
||||
case PARSE_TOGGLE:
|
||||
|
||||
@@ -24,7 +24,7 @@ void MQTTLockComponent::setup() {
|
||||
} else if (strcasecmp(payload.c_str(), "OPEN") == 0) {
|
||||
this->lock_->open();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str());
|
||||
ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str());
|
||||
this->status_momentary_warning("state", 5000);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -44,8 +44,11 @@ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon
|
||||
root[MQTT_MIN] = traits.get_min_value();
|
||||
root[MQTT_MAX] = traits.get_max_value();
|
||||
root[MQTT_STEP] = traits.get_step();
|
||||
if (!this->number_->traits.get_unit_of_measurement().empty())
|
||||
root[MQTT_UNIT_OF_MEASUREMENT] = this->number_->traits.get_unit_of_measurement();
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
const auto unit_of_measurement = this->number_->traits.get_unit_of_measurement_ref();
|
||||
if (!unit_of_measurement.empty()) {
|
||||
root[MQTT_UNIT_OF_MEASUREMENT] = unit_of_measurement;
|
||||
}
|
||||
switch (this->number_->traits.get_mode()) {
|
||||
case NUMBER_MODE_AUTO:
|
||||
break;
|
||||
@@ -56,8 +59,11 @@ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon
|
||||
root[MQTT_MODE] = "slider";
|
||||
break;
|
||||
}
|
||||
if (!this->number_->traits.get_device_class().empty())
|
||||
root[MQTT_DEVICE_CLASS] = this->number_->traits.get_device_class();
|
||||
const auto device_class = this->number_->traits.get_device_class_ref();
|
||||
if (!device_class.empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = device_class;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
|
||||
config.command_topic = true;
|
||||
}
|
||||
|
||||
@@ -44,13 +44,17 @@ void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire
|
||||
void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; }
|
||||
|
||||
void MQTTSensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
if (!this->sensor_->get_device_class().empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class();
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
const auto device_class = this->sensor_->get_device_class_ref();
|
||||
if (!device_class.empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = device_class;
|
||||
}
|
||||
|
||||
if (!this->sensor_->get_unit_of_measurement().empty())
|
||||
root[MQTT_UNIT_OF_MEASUREMENT] = this->sensor_->get_unit_of_measurement();
|
||||
const auto unit_of_measurement = this->sensor_->get_unit_of_measurement_ref();
|
||||
if (!unit_of_measurement.empty()) {
|
||||
root[MQTT_UNIT_OF_MEASUREMENT] = unit_of_measurement;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
|
||||
if (this->get_expire_after() > 0)
|
||||
root[MQTT_EXPIRE_AFTER] = this->get_expire_after() / 1000;
|
||||
|
||||
@@ -29,7 +29,7 @@ void MQTTSwitchComponent::setup() {
|
||||
break;
|
||||
case PARSE_NONE:
|
||||
default:
|
||||
ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str());
|
||||
ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str());
|
||||
this->status_momentary_warning("state", 5000);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,12 @@ using namespace esphome::text_sensor;
|
||||
|
||||
MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : sensor_(sensor) {}
|
||||
void MQTTTextSensor::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
if (!this->sensor_->get_device_class().empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class();
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
const auto device_class = this->sensor_->get_device_class_ref();
|
||||
if (!device_class.empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = device_class;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
config.command_topic = false;
|
||||
}
|
||||
void MQTTTextSensor::setup() {
|
||||
|
||||
@@ -20,7 +20,7 @@ void MQTTUpdateComponent::setup() {
|
||||
if (payload == "INSTALL") {
|
||||
this->update_->perform();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s': Received unknown update payload: %s", this->friendly_name().c_str(), payload.c_str());
|
||||
ESP_LOGW(TAG, "'%s': Received unknown update payload: %s", this->friendly_name_().c_str(), payload.c_str());
|
||||
this->status_momentary_warning("state", 5000);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -49,10 +49,12 @@ void MQTTValveComponent::dump_config() {
|
||||
}
|
||||
}
|
||||
void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
if (!this->valve_->get_device_class().empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = this->valve_->get_device_class();
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
const auto device_class = this->valve_->get_device_class_ref();
|
||||
if (!device_class.empty()) {
|
||||
root[MQTT_DEVICE_CLASS] = device_class;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
|
||||
auto traits = this->valve_->get_traits();
|
||||
if (traits.get_is_assumed_state()) {
|
||||
|
||||
@@ -9,7 +9,7 @@ from esphome.components.esp32 import (
|
||||
from esphome.components.mdns import MDNSComponent, enable_mdns_storage
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ENABLE_IPV6, CONF_ID, CONF_USE_ADDRESS
|
||||
from esphome.core import CORE
|
||||
from esphome.core import CORE, TimePeriodMilliseconds
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -22,6 +22,7 @@ from .const import (
|
||||
CONF_NETWORK_KEY,
|
||||
CONF_NETWORK_NAME,
|
||||
CONF_PAN_ID,
|
||||
CONF_POLL_PERIOD,
|
||||
CONF_PSKC,
|
||||
CONF_SRP_ID,
|
||||
CONF_TLV,
|
||||
@@ -89,7 +90,7 @@ def set_sdkconfig_options(config):
|
||||
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT", True)
|
||||
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT_MAX_SERVICES", 5)
|
||||
|
||||
# TODO: Add suport for sleepy end devices
|
||||
# TODO: Add suport for synchronized sleepy end devices (SSED)
|
||||
add_idf_sdkconfig_option(f"CONFIG_OPENTHREAD_{config.get(CONF_DEVICE_TYPE)}", True)
|
||||
|
||||
|
||||
@@ -113,6 +114,17 @@ _CONNECTION_SCHEMA = cv.Schema(
|
||||
def _validate(config: ConfigType) -> ConfigType:
|
||||
if CONF_USE_ADDRESS not in config:
|
||||
config[CONF_USE_ADDRESS] = f"{CORE.name}.local"
|
||||
device_type = config.get(CONF_DEVICE_TYPE)
|
||||
poll_period = config.get(CONF_POLL_PERIOD)
|
||||
if (
|
||||
device_type == "FTD"
|
||||
and poll_period
|
||||
and poll_period > TimePeriodMilliseconds(milliseconds=0)
|
||||
):
|
||||
raise cv.Invalid(
|
||||
f"{CONF_POLL_PERIOD} can only be used with {CONF_DEVICE_TYPE}: MTD"
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
@@ -135,6 +147,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_FORCE_DATASET): cv.boolean,
|
||||
cv.Optional(CONF_TLV): cv.string_strict,
|
||||
cv.Optional(CONF_USE_ADDRESS): cv.string_strict,
|
||||
cv.Optional(CONF_POLL_PERIOD): cv.positive_time_period_milliseconds,
|
||||
}
|
||||
).extend(_CONNECTION_SCHEMA),
|
||||
cv.has_exactly_one_key(CONF_NETWORK_KEY, CONF_TLV),
|
||||
@@ -167,6 +180,8 @@ async def to_code(config):
|
||||
ot = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(ot.set_use_address(config[CONF_USE_ADDRESS]))
|
||||
await cg.register_component(ot, config)
|
||||
if (poll_period := config.get(CONF_POLL_PERIOD)) is not None:
|
||||
cg.add(ot.set_poll_period(poll_period))
|
||||
|
||||
srp = cg.new_Pvariable(config[CONF_SRP_ID])
|
||||
mdns_component = await cg.get_variable(config[CONF_MDNS_ID])
|
||||
|
||||
@@ -6,6 +6,7 @@ CONF_MESH_LOCAL_PREFIX = "mesh_local_prefix"
|
||||
CONF_NETWORK_NAME = "network_name"
|
||||
CONF_NETWORK_KEY = "network_key"
|
||||
CONF_PAN_ID = "pan_id"
|
||||
CONF_POLL_PERIOD = "poll_period"
|
||||
CONF_PSKC = "pskc"
|
||||
CONF_SRP_ID = "srp_id"
|
||||
CONF_TLV = "tlv"
|
||||
|
||||
@@ -29,6 +29,23 @@ OpenThreadComponent *global_openthread_component = // NOLINT(cppcoreguidelines-
|
||||
|
||||
OpenThreadComponent::OpenThreadComponent() { global_openthread_component = this; }
|
||||
|
||||
void OpenThreadComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Open Thread:");
|
||||
#if CONFIG_OPENTHREAD_FTD
|
||||
ESP_LOGCONFIG(TAG, " Device Type: FTD");
|
||||
#elif CONFIG_OPENTHREAD_MTD
|
||||
ESP_LOGCONFIG(TAG, " Device Type: MTD");
|
||||
// TBD: Synchronized Sleepy End Device
|
||||
if (this->poll_period > 0) {
|
||||
ESP_LOGCONFIG(TAG, " Device is configured as Sleepy End Device (SED)");
|
||||
uint32_t duration = this->poll_period / 1000;
|
||||
ESP_LOGCONFIG(TAG, " Poll Period: %" PRIu32 "s", duration);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " Device is configured as Minimal End Device (MED)");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OpenThreadComponent::is_connected() {
|
||||
auto lock = InstanceLock::try_acquire(100);
|
||||
if (!lock) {
|
||||
|
||||
@@ -22,6 +22,7 @@ class OpenThreadComponent : public Component {
|
||||
public:
|
||||
OpenThreadComponent();
|
||||
~OpenThreadComponent();
|
||||
void dump_config() override;
|
||||
void setup() override;
|
||||
bool teardown() override;
|
||||
float get_setup_priority() const override { return setup_priority::WIFI; }
|
||||
@@ -35,6 +36,9 @@ class OpenThreadComponent : public Component {
|
||||
|
||||
const char *get_use_address() const;
|
||||
void set_use_address(const char *use_address);
|
||||
#if CONFIG_OPENTHREAD_MTD
|
||||
void set_poll_period(uint32_t poll_period) { this->poll_period = poll_period; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
std::optional<otIp6Address> get_omr_address_(InstanceLock &lock);
|
||||
@@ -46,6 +50,9 @@ class OpenThreadComponent : public Component {
|
||||
// Stores a pointer to a string literal (static storage duration).
|
||||
// ONLY set from Python-generated code with string literals - never dynamic strings.
|
||||
const char *use_address_{""};
|
||||
#if CONFIG_OPENTHREAD_MTD
|
||||
uint32_t poll_period{0};
|
||||
#endif
|
||||
};
|
||||
|
||||
extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
@@ -105,6 +105,32 @@ void OpenThreadComponent::ot_main() {
|
||||
esp_cli_custom_command_init();
|
||||
#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
|
||||
|
||||
otLinkModeConfig link_mode_config = {0};
|
||||
#if CONFIG_OPENTHREAD_FTD
|
||||
link_mode_config.mRxOnWhenIdle = true;
|
||||
link_mode_config.mDeviceType = true;
|
||||
link_mode_config.mNetworkData = true;
|
||||
#elif CONFIG_OPENTHREAD_MTD
|
||||
if (this->poll_period > 0) {
|
||||
if (otLinkSetPollPeriod(esp_openthread_get_instance(), this->poll_period) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to set OpenThread pollperiod.");
|
||||
}
|
||||
uint32_t link_polling_period = otLinkGetPollPeriod(esp_openthread_get_instance());
|
||||
ESP_LOGD(TAG, "Link Polling Period: %d", link_polling_period);
|
||||
}
|
||||
link_mode_config.mRxOnWhenIdle = this->poll_period == 0;
|
||||
link_mode_config.mDeviceType = false;
|
||||
link_mode_config.mNetworkData = false;
|
||||
#endif
|
||||
|
||||
if (otThreadSetLinkMode(esp_openthread_get_instance(), link_mode_config) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to set OpenThread linkmode.");
|
||||
}
|
||||
link_mode_config = otThreadGetLinkMode(esp_openthread_get_instance());
|
||||
ESP_LOGD(TAG, "Link Mode Device Type: %s", link_mode_config.mDeviceType ? "true" : "false");
|
||||
ESP_LOGD(TAG, "Link Mode Network Data: %s", link_mode_config.mNetworkData ? "true" : "false");
|
||||
ESP_LOGD(TAG, "Link Mode RX On When Idle: %s", link_mode_config.mRxOnWhenIdle ? "true" : "false");
|
||||
|
||||
// Run the main loop
|
||||
#if CONFIG_OPENTHREAD_CLI
|
||||
esp_openthread_cli_create_task();
|
||||
|
||||
@@ -158,7 +158,7 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor
|
||||
stream->print(ESPHOME_F("\",name=\""));
|
||||
stream->print(relabel_name_(obj).c_str());
|
||||
stream->print(ESPHOME_F("\",unit=\""));
|
||||
stream->print(obj->get_unit_of_measurement().c_str());
|
||||
stream->print(obj->get_unit_of_measurement_ref().c_str());
|
||||
stream->print(ESPHOME_F("\"} "));
|
||||
stream->print(value_accuracy_to_string(obj->state, obj->get_accuracy_decimals()).c_str());
|
||||
stream->print(ESPHOME_F("\n"));
|
||||
|
||||
@@ -2,6 +2,7 @@ import logging
|
||||
import textwrap
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.const import CONF_IGNORE_NOT_FOUND
|
||||
from esphome.components.esp32 import (
|
||||
CONF_CPU_FREQUENCY,
|
||||
CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES,
|
||||
@@ -123,6 +124,7 @@ def get_config_schema(config):
|
||||
cv.Optional(CONF_ENABLE_ECC, default=False): cv.boolean,
|
||||
cv.Optional(CONF_SPEED, default=speeds[0]): cv.one_of(*speeds, upper=True),
|
||||
cv.Optional(CONF_DISABLED, default=False): cv.boolean,
|
||||
cv.Optional(CONF_IGNORE_NOT_FOUND, default=True): cv.boolean,
|
||||
}
|
||||
)(config)
|
||||
|
||||
@@ -147,7 +149,9 @@ async def to_code(config):
|
||||
add_idf_sdkconfig_option("CONFIG_SPIRAM", True)
|
||||
add_idf_sdkconfig_option("CONFIG_SPIRAM_USE", True)
|
||||
add_idf_sdkconfig_option("CONFIG_SPIRAM_USE_CAPS_ALLOC", True)
|
||||
add_idf_sdkconfig_option("CONFIG_SPIRAM_IGNORE_NOTFOUND", True)
|
||||
add_idf_sdkconfig_option(
|
||||
"CONFIG_SPIRAM_IGNORE_NOTFOUND", config[CONF_IGNORE_NOT_FOUND]
|
||||
)
|
||||
|
||||
add_idf_sdkconfig_option(f"CONFIG_SPIRAM_MODE_{SDK_MODES[config[CONF_MODE]]}", True)
|
||||
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace seeed_mr24hpc1 {
|
||||
|
||||
void ExistenceBoundarySelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
auto index = this->index_of(value);
|
||||
if (index.has_value()) {
|
||||
this->parent_->set_existence_boundary(index.value());
|
||||
}
|
||||
void ExistenceBoundarySelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_existence_boundary(index);
|
||||
}
|
||||
|
||||
} // namespace seeed_mr24hpc1
|
||||
|
||||
@@ -11,7 +11,7 @@ class ExistenceBoundarySelect : public select::Select, public Parented<MR24HPC1C
|
||||
ExistenceBoundarySelect() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace seeed_mr24hpc1
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace seeed_mr24hpc1 {
|
||||
|
||||
void MotionBoundarySelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
auto index = this->index_of(value);
|
||||
if (index.has_value()) {
|
||||
this->parent_->set_motion_boundary(index.value());
|
||||
}
|
||||
void MotionBoundarySelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_motion_boundary(index);
|
||||
}
|
||||
|
||||
} // namespace seeed_mr24hpc1
|
||||
|
||||
@@ -11,7 +11,7 @@ class MotionBoundarySelect : public select::Select, public Parented<MR24HPC1Comp
|
||||
MotionBoundarySelect() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace seeed_mr24hpc1
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace seeed_mr24hpc1 {
|
||||
|
||||
void SceneModeSelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
auto index = this->index_of(value);
|
||||
if (index.has_value()) {
|
||||
this->parent_->set_scene_mode(index.value());
|
||||
}
|
||||
void SceneModeSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_scene_mode(index);
|
||||
}
|
||||
|
||||
} // namespace seeed_mr24hpc1
|
||||
|
||||
@@ -11,7 +11,7 @@ class SceneModeSelect : public select::Select, public Parented<MR24HPC1Component
|
||||
SceneModeSelect() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace seeed_mr24hpc1
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace seeed_mr24hpc1 {
|
||||
|
||||
void UnmanTimeSelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
auto index = this->index_of(value);
|
||||
if (index.has_value()) {
|
||||
this->parent_->set_unman_time(index.value());
|
||||
}
|
||||
void UnmanTimeSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_unman_time(index);
|
||||
}
|
||||
|
||||
} // namespace seeed_mr24hpc1
|
||||
|
||||
@@ -11,7 +11,7 @@ class UnmanTimeSelect : public select::Select, public Parented<MR24HPC1Component
|
||||
UnmanTimeSelect() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace seeed_mr24hpc1
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace seeed_mr60fda2 {
|
||||
|
||||
void HeightThresholdSelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
auto index = this->index_of(value);
|
||||
if (index.has_value()) {
|
||||
this->parent_->set_height_threshold(index.value());
|
||||
}
|
||||
void HeightThresholdSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_height_threshold(index);
|
||||
}
|
||||
|
||||
} // namespace seeed_mr60fda2
|
||||
|
||||
@@ -11,7 +11,7 @@ class HeightThresholdSelect : public select::Select, public Parented<MR60FDA2Com
|
||||
HeightThresholdSelect() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace seeed_mr60fda2
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace seeed_mr60fda2 {
|
||||
|
||||
void InstallHeightSelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
auto index = this->index_of(value);
|
||||
if (index.has_value()) {
|
||||
this->parent_->set_install_height(index.value());
|
||||
}
|
||||
void InstallHeightSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_install_height(index);
|
||||
}
|
||||
|
||||
} // namespace seeed_mr60fda2
|
||||
|
||||
@@ -11,7 +11,7 @@ class InstallHeightSelect : public select::Select, public Parented<MR60FDA2Compo
|
||||
InstallHeightSelect() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace seeed_mr60fda2
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace seeed_mr60fda2 {
|
||||
|
||||
void SensitivitySelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
auto index = this->index_of(value);
|
||||
if (index.has_value()) {
|
||||
this->parent_->set_sensitivity(index.value());
|
||||
}
|
||||
void SensitivitySelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_sensitivity(index);
|
||||
}
|
||||
|
||||
} // namespace seeed_mr60fda2
|
||||
|
||||
@@ -11,7 +11,7 @@ class SensitivitySelect : public select::Select, public Parented<MR60FDA2Compone
|
||||
SensitivitySelect() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace seeed_mr60fda2
|
||||
|
||||
@@ -35,7 +35,7 @@ class Select : public EntityBase {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
/// @deprecated Use current_option() instead. This member will be removed in ESPHome 2026.5.0.
|
||||
__attribute__((deprecated("Use current_option() instead of .state. Will be removed in 2026.5.0")))
|
||||
ESPDEPRECATED("Use current_option() instead of .state. Will be removed in 2026.5.0", "2025.11.0")
|
||||
std::string state{};
|
||||
|
||||
Select() = default;
|
||||
|
||||
@@ -72,9 +72,9 @@ StateClass Sensor::get_state_class() {
|
||||
|
||||
void Sensor::publish_state(float state) {
|
||||
this->raw_state = state;
|
||||
if (this->raw_callback_) {
|
||||
this->raw_callback_->call(state);
|
||||
}
|
||||
|
||||
// Call raw callbacks (before filters)
|
||||
this->callbacks_.call_first(this->raw_count_, state);
|
||||
|
||||
ESP_LOGV(TAG, "'%s': Received new state %f", this->name_.c_str(), state);
|
||||
|
||||
@@ -85,12 +85,12 @@ void Sensor::publish_state(float state) {
|
||||
}
|
||||
}
|
||||
|
||||
void Sensor::add_on_state_callback(std::function<void(float)> &&callback) { this->callback_.add(std::move(callback)); }
|
||||
void Sensor::add_on_state_callback(std::function<void(float)> &&callback) {
|
||||
this->callbacks_.add_second(std::move(callback));
|
||||
}
|
||||
|
||||
void Sensor::add_on_raw_state_callback(std::function<void(float)> &&callback) {
|
||||
if (!this->raw_callback_) {
|
||||
this->raw_callback_ = make_unique<CallbackManager<void(float)>>();
|
||||
}
|
||||
this->raw_callback_->add(std::move(callback));
|
||||
this->callbacks_.add_first(std::move(callback), &this->raw_count_);
|
||||
}
|
||||
|
||||
void Sensor::add_filter(Filter *filter) {
|
||||
@@ -130,7 +130,13 @@ void Sensor::internal_send_state_to_frontend(float state) {
|
||||
this->state = state;
|
||||
ESP_LOGD(TAG, "'%s': Sending state %.5f %s with %d decimals of accuracy", this->get_name().c_str(), state,
|
||||
this->get_unit_of_measurement_ref().c_str(), this->get_accuracy_decimals());
|
||||
this->callback_.call(state);
|
||||
|
||||
// Call filtered callbacks (after filters)
|
||||
this->callbacks_.call_second(this->raw_count_, state);
|
||||
|
||||
#if defined(USE_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_sensor_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace sensor
|
||||
|
||||
@@ -124,8 +124,7 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa
|
||||
void internal_send_state_to_frontend(float state);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<CallbackManager<void(float)>> raw_callback_; ///< Storage for raw state callbacks (lazy allocated).
|
||||
CallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.
|
||||
PartitionedCallbackManager<void(float)> callbacks_;
|
||||
|
||||
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
||||
|
||||
@@ -140,6 +139,8 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa
|
||||
uint8_t force_update : 1;
|
||||
uint8_t reserved : 5; // Reserved for future use
|
||||
} sensor_flags_{};
|
||||
|
||||
uint8_t raw_count_{0}; ///< Number of raw callbacks (partition point in callbacks_ vector)
|
||||
};
|
||||
|
||||
} // namespace sensor
|
||||
|
||||
@@ -172,16 +172,7 @@ class LWIPRawImpl : public Socket {
|
||||
errno = ECONNRESET;
|
||||
return "";
|
||||
}
|
||||
char buffer[50] = {};
|
||||
if (IP_IS_V4_VAL(pcb_->remote_ip)) {
|
||||
inet_ntoa_r(pcb_->remote_ip, buffer, sizeof(buffer));
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
else if (IP_IS_V6_VAL(pcb_->remote_ip)) {
|
||||
inet6_ntoa_r(pcb_->remote_ip, buffer, sizeof(buffer));
|
||||
}
|
||||
#endif
|
||||
return std::string(buffer);
|
||||
return this->format_ip_address_(pcb_->remote_ip);
|
||||
}
|
||||
int getsockname(struct sockaddr *name, socklen_t *addrlen) override {
|
||||
if (pcb_ == nullptr) {
|
||||
@@ -199,16 +190,7 @@ class LWIPRawImpl : public Socket {
|
||||
errno = ECONNRESET;
|
||||
return "";
|
||||
}
|
||||
char buffer[50] = {};
|
||||
if (IP_IS_V4_VAL(pcb_->local_ip)) {
|
||||
inet_ntoa_r(pcb_->local_ip, buffer, sizeof(buffer));
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
else if (IP_IS_V6_VAL(pcb_->local_ip)) {
|
||||
inet6_ntoa_r(pcb_->local_ip, buffer, sizeof(buffer));
|
||||
}
|
||||
#endif
|
||||
return std::string(buffer);
|
||||
return this->format_ip_address_(pcb_->local_ip);
|
||||
}
|
||||
int getsockopt(int level, int optname, void *optval, socklen_t *optlen) override {
|
||||
if (pcb_ == nullptr) {
|
||||
@@ -499,6 +481,19 @@ class LWIPRawImpl : public Socket {
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string format_ip_address_(const ip_addr_t &ip) {
|
||||
char buffer[50] = {};
|
||||
if (IP_IS_V4_VAL(ip)) {
|
||||
inet_ntoa_r(ip, buffer, sizeof(buffer));
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
else if (IP_IS_V6_VAL(ip)) {
|
||||
inet6_ntoa_r(ip, buffer, sizeof(buffer));
|
||||
}
|
||||
#endif
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen) {
|
||||
if (family_ == AF_INET) {
|
||||
if (*addrlen < sizeof(struct sockaddr_in)) {
|
||||
|
||||
@@ -650,7 +650,7 @@ void Sprinkler::set_valve_run_duration(const optional<size_t> valve_number, cons
|
||||
return;
|
||||
}
|
||||
auto call = this->valve_[valve_number.value()].run_duration_number->make_call();
|
||||
if (this->valve_[valve_number.value()].run_duration_number->traits.get_unit_of_measurement() == MIN_STR) {
|
||||
if (this->valve_[valve_number.value()].run_duration_number->traits.get_unit_of_measurement_ref() == MIN_STR) {
|
||||
call.set_value(run_duration.value() / 60.0);
|
||||
} else {
|
||||
call.set_value(run_duration.value());
|
||||
@@ -732,7 +732,7 @@ uint32_t Sprinkler::valve_run_duration(const size_t valve_number) {
|
||||
return 0;
|
||||
}
|
||||
if (this->valve_[valve_number].run_duration_number != nullptr) {
|
||||
if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement() == MIN_STR) {
|
||||
if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement_ref() == MIN_STR) {
|
||||
return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state * 60));
|
||||
} else {
|
||||
return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state));
|
||||
|
||||
@@ -49,7 +49,7 @@ struct SensorInfo {
|
||||
uint8_t store_index;
|
||||
};
|
||||
|
||||
class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, public Component {
|
||||
class TemplateAlarmControlPanel final : public alarm_control_panel::AlarmControlPanel, public Component {
|
||||
public:
|
||||
TemplateAlarmControlPanel();
|
||||
void dump_config() override;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor {
|
||||
class TemplateBinarySensor final : public Component, public binary_sensor::BinarySensor {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateButton : public button::Button {
|
||||
class TemplateButton final : public button::Button {
|
||||
public:
|
||||
// Implements the abstract `press_action` but the `on_press` trigger already handles the press.
|
||||
void press_action() override{};
|
||||
|
||||
@@ -14,7 +14,7 @@ enum TemplateCoverRestoreMode {
|
||||
COVER_RESTORE_AND_CALL,
|
||||
};
|
||||
|
||||
class TemplateCover : public cover::Cover, public Component {
|
||||
class TemplateCover final : public cover::Cover, public Component {
|
||||
public:
|
||||
TemplateCover();
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateDate : public datetime::DateEntity, public PollingComponent {
|
||||
class TemplateDate final : public datetime::DateEntity, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponent {
|
||||
class TemplateDateTime final : public datetime::DateTimeEntity, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateTime : public datetime::TimeEntity, public PollingComponent {
|
||||
class TemplateTime final : public datetime::TimeEntity, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateEvent : public Component, public event::Event {};
|
||||
class TemplateEvent final : public Component, public event::Event {};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateFan : public Component, public fan::Fan {
|
||||
class TemplateFan final : public Component, public fan::Fan {
|
||||
public:
|
||||
TemplateFan() {}
|
||||
void setup() override;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateLock : public lock::Lock, public Component {
|
||||
class TemplateLock final : public lock::Lock, public Component {
|
||||
public:
|
||||
TemplateLock();
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateNumber : public number::Number, public PollingComponent {
|
||||
class TemplateNumber final : public number::Number, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateBinaryOutput : public output::BinaryOutput {
|
||||
class TemplateBinaryOutput final : public output::BinaryOutput {
|
||||
public:
|
||||
Trigger<bool> *get_trigger() const { return trigger_; }
|
||||
|
||||
@@ -17,7 +17,7 @@ class TemplateBinaryOutput : public output::BinaryOutput {
|
||||
Trigger<bool> *trigger_ = new Trigger<bool>();
|
||||
};
|
||||
|
||||
class TemplateFloatOutput : public output::FloatOutput {
|
||||
class TemplateFloatOutput final : public output::FloatOutput {
|
||||
public:
|
||||
Trigger<float> *get_trigger() const { return trigger_; }
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateSelect : public select::Select, public PollingComponent {
|
||||
class TemplateSelect final : public select::Select, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateSensor : public sensor::Sensor, public PollingComponent {
|
||||
class TemplateSensor final : public sensor::Sensor, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateSwitch : public switch_::Switch, public Component {
|
||||
class TemplateSwitch final : public switch_::Switch, public Component {
|
||||
public:
|
||||
TemplateSwitch();
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ template<uint8_t SZ> class TextSaver : public TemplateTextSaverBase {
|
||||
}
|
||||
};
|
||||
|
||||
class TemplateText : public text::Text, public PollingComponent {
|
||||
class TemplateText final : public text::Text, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent {
|
||||
class TemplateTextSensor final : public text_sensor::TextSensor, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ enum TemplateValveRestoreMode {
|
||||
VALVE_RESTORE_AND_CALL,
|
||||
};
|
||||
|
||||
class TemplateValve : public valve::Valve, public Component {
|
||||
class TemplateValve final : public valve::Valve, public Component {
|
||||
public:
|
||||
TemplateValve();
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ void log_text_sensor(const char *tag, const char *prefix, const char *type, Text
|
||||
|
||||
void TextSensor::publish_state(const std::string &state) {
|
||||
this->raw_state = state;
|
||||
if (this->raw_callback_) {
|
||||
this->raw_callback_->call(state);
|
||||
}
|
||||
|
||||
// Call raw callbacks (before filters)
|
||||
this->callbacks_.call_first(this->raw_count_, state);
|
||||
|
||||
ESP_LOGV(TAG, "'%s': Received new state %s", this->name_.c_str(), state.c_str());
|
||||
|
||||
@@ -68,13 +68,11 @@ void TextSensor::clear_filters() {
|
||||
}
|
||||
|
||||
void TextSensor::add_on_state_callback(std::function<void(std::string)> callback) {
|
||||
this->callback_.add(std::move(callback));
|
||||
this->callbacks_.add_second(std::move(callback));
|
||||
}
|
||||
|
||||
void TextSensor::add_on_raw_state_callback(std::function<void(std::string)> callback) {
|
||||
if (!this->raw_callback_) {
|
||||
this->raw_callback_ = make_unique<CallbackManager<void(std::string)>>();
|
||||
}
|
||||
this->raw_callback_->add(std::move(callback));
|
||||
this->callbacks_.add_first(std::move(callback), &this->raw_count_);
|
||||
}
|
||||
|
||||
std::string TextSensor::get_state() const { return this->state; }
|
||||
@@ -83,7 +81,13 @@ void TextSensor::internal_send_state_to_frontend(const std::string &state) {
|
||||
this->state = state;
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s': Sending state '%s'", this->name_.c_str(), state.c_str());
|
||||
this->callback_.call(state);
|
||||
|
||||
// Call filtered callbacks (after filters)
|
||||
this->callbacks_.call_second(this->raw_count_, state);
|
||||
|
||||
#if defined(USE_TEXT_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_text_sensor_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace text_sensor
|
||||
|
||||
@@ -58,11 +58,11 @@ class TextSensor : public EntityBase, public EntityBase_DeviceClass {
|
||||
void internal_send_state_to_frontend(const std::string &state);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<CallbackManager<void(std::string)>>
|
||||
raw_callback_; ///< Storage for raw state callbacks (lazy allocated).
|
||||
CallbackManager<void(std::string)> callback_; ///< Storage for filtered state callbacks.
|
||||
PartitionedCallbackManager<void(std::string)> callbacks_;
|
||||
|
||||
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
||||
|
||||
uint8_t raw_count_{0}; ///< Number of raw callbacks (partition point in callbacks_ vector)
|
||||
};
|
||||
|
||||
} // namespace text_sensor
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user