mirror of
https://github.com/esphome/esphome.git
synced 2026-05-27 20:53:46 +08:00
[core] Pack entity string properties into PROGMEM-indexed uint8_t fields (#14171)
This commit is contained in:
@@ -186,8 +186,8 @@ ALARM_CONTROL_PANEL_CONDITION_SCHEMA = maybe_simple_id(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("alarm_control_panel")
|
||||||
async def setup_alarm_control_panel_core_(var, config):
|
async def setup_alarm_control_panel_core_(var, config):
|
||||||
await setup_entity(var, config, "alarm_control_panel")
|
|
||||||
for conf in config.get(CONF_ON_STATE, []):
|
for conf in config.get(CONF_ON_STATE, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [], conf)
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
|||||||
@@ -773,9 +773,9 @@ uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection
|
|||||||
uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
||||||
auto *number = static_cast<number::Number *>(entity);
|
auto *number = static_cast<number::Number *>(entity);
|
||||||
ListEntitiesNumberResponse msg;
|
ListEntitiesNumberResponse msg;
|
||||||
msg.unit_of_measurement = number->traits.get_unit_of_measurement_ref();
|
msg.unit_of_measurement = number->get_unit_of_measurement_ref();
|
||||||
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
|
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
|
||||||
msg.device_class = number->traits.get_device_class_ref();
|
msg.device_class = number->get_device_class_ref();
|
||||||
msg.min_value = number->traits.get_min_value();
|
msg.min_value = number->traits.get_min_value();
|
||||||
msg.max_value = number->traits.get_max_value();
|
msg.max_value = number->traits.get_max_value();
|
||||||
msg.step = number->traits.get_step();
|
msg.step = number->traits.get_step();
|
||||||
|
|||||||
@@ -60,7 +60,11 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_WINDOW,
|
DEVICE_CLASS_WINDOW,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import (
|
||||||
|
entity_duplicate_validator,
|
||||||
|
setup_device_class,
|
||||||
|
setup_entity,
|
||||||
|
)
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.util import Registry
|
from esphome.util import Registry
|
||||||
|
|
||||||
@@ -604,11 +608,9 @@ async def _build_binary_sensor_automations(var, config):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("binary_sensor")
|
||||||
async def setup_binary_sensor_core_(var, config):
|
async def setup_binary_sensor_core_(var, config):
|
||||||
await setup_entity(var, config, "binary_sensor")
|
setup_device_class(config)
|
||||||
|
|
||||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
|
||||||
cg.add(var.set_device_class(device_class))
|
|
||||||
trigger = config.get(CONF_TRIGGER_ON_INITIAL_STATE, False) or config.get(
|
trigger = config.get(CONF_TRIGGER_ON_INITIAL_STATE, False) or config.get(
|
||||||
CONF_PUBLISH_INITIAL_STATE, False
|
CONF_PUBLISH_INITIAL_STATE, False
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ void log_binary_sensor(const char *tag, const char *prefix, const char *type, Bi
|
|||||||
* The sub classes should notify the front-end of new states via the publish_state() method which
|
* The sub classes should notify the front-end of new states via the publish_state() method which
|
||||||
* handles inverted inputs for you.
|
* handles inverted inputs for you.
|
||||||
*/
|
*/
|
||||||
class BinarySensor : public StatefulEntityBase<bool>, public EntityBase_DeviceClass {
|
class BinarySensor : public StatefulEntityBase<bool> {
|
||||||
public:
|
public:
|
||||||
explicit BinarySensor(){};
|
explicit BinarySensor(){};
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,11 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_UPDATE,
|
DEVICE_CLASS_UPDATE,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import (
|
||||||
|
entity_duplicate_validator,
|
||||||
|
setup_device_class,
|
||||||
|
setup_entity,
|
||||||
|
)
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
@@ -84,15 +88,13 @@ def button_schema(
|
|||||||
return _BUTTON_SCHEMA.extend(schema)
|
return _BUTTON_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("button")
|
||||||
async def setup_button_core_(var, config):
|
async def setup_button_core_(var, config):
|
||||||
await setup_entity(var, config, "button")
|
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_PRESS, []):
|
for conf in config.get(CONF_ON_PRESS, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [], conf)
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
|
||||||
if device_class := config.get(CONF_DEVICE_CLASS):
|
setup_device_class(config)
|
||||||
cg.add(var.set_device_class(device_class))
|
|
||||||
|
|
||||||
if mqtt_id := config.get(CONF_MQTT_ID):
|
if mqtt_id := config.get(CONF_MQTT_ID):
|
||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void log_button(const char *tag, const char *prefix, const char *type, Button *o
|
|||||||
*
|
*
|
||||||
* A button is just a momentary switch that does not have a state, only a trigger.
|
* A button is just a momentary switch that does not have a state, only a trigger.
|
||||||
*/
|
*/
|
||||||
class Button : public EntityBase, public EntityBase_DeviceClass {
|
class Button : public EntityBase {
|
||||||
public:
|
public:
|
||||||
/** Press this button. This is called by the front-end.
|
/** Press this button. This is called by the front-end.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -268,9 +268,8 @@ def climate_schema(
|
|||||||
return _CLIMATE_SCHEMA.extend(schema)
|
return _CLIMATE_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("climate")
|
||||||
async def setup_climate_core_(var, config):
|
async def setup_climate_core_(var, config):
|
||||||
await setup_entity(var, config, "climate")
|
|
||||||
|
|
||||||
visual = config[CONF_VISUAL]
|
visual = config[CONF_VISUAL]
|
||||||
if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None:
|
if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None:
|
||||||
cg.add_define("USE_CLIMATE_VISUAL_OVERRIDES")
|
cg.add_define("USE_CLIMATE_VISUAL_OVERRIDES")
|
||||||
|
|||||||
@@ -37,7 +37,11 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_WINDOW,
|
DEVICE_CLASS_WINDOW,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import (
|
||||||
|
entity_duplicate_validator,
|
||||||
|
setup_device_class,
|
||||||
|
setup_entity,
|
||||||
|
)
|
||||||
from esphome.cpp_generator import MockObj, MockObjClass
|
from esphome.cpp_generator import MockObj, MockObjClass
|
||||||
from esphome.types import ConfigType, TemplateArgsType
|
from esphome.types import ConfigType, TemplateArgsType
|
||||||
|
|
||||||
@@ -190,11 +194,9 @@ def cover_schema(
|
|||||||
return _COVER_SCHEMA.extend(schema)
|
return _COVER_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("cover")
|
||||||
async def setup_cover_core_(var, config):
|
async def setup_cover_core_(var, config):
|
||||||
await setup_entity(var, config, "cover")
|
setup_device_class(config)
|
||||||
|
|
||||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
|
||||||
cg.add(var.set_device_class(device_class))
|
|
||||||
|
|
||||||
if CONF_ON_OPEN in config:
|
if CONF_ON_OPEN in config:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ const LogString *cover_operation_to_str(CoverOperation op);
|
|||||||
* to control all values of the cover. Also implement get_traits() to return what operations
|
* to control all values of the cover. Also implement get_traits() to return what operations
|
||||||
* the cover supports.
|
* the cover supports.
|
||||||
*/
|
*/
|
||||||
class Cover : public EntityBase, public EntityBase_DeviceClass {
|
class Cover : public EntityBase {
|
||||||
public:
|
public:
|
||||||
explicit Cover();
|
explicit Cover();
|
||||||
|
|
||||||
|
|||||||
@@ -134,9 +134,8 @@ def datetime_schema(class_: MockObjClass) -> cv.Schema:
|
|||||||
return _DATETIME_SCHEMA.extend(schema)
|
return _DATETIME_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("datetime")
|
||||||
async def setup_datetime_core_(var, config):
|
async def setup_datetime_core_(var, config):
|
||||||
await setup_entity(var, config, "datetime")
|
|
||||||
|
|
||||||
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ void arch_init() {
|
|||||||
void HOT arch_feed_wdt() { esp_task_wdt_reset(); }
|
void HOT arch_feed_wdt() { esp_task_wdt_reset(); }
|
||||||
|
|
||||||
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
||||||
|
const char *progmem_read_ptr(const char *const *addr) { return *addr; }
|
||||||
uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
||||||
uint32_t arch_get_cpu_cycle_count() { return esp_cpu_get_cycle_count(); }
|
uint32_t arch_get_cpu_cycle_count() { return esp_cpu_get_cycle_count(); }
|
||||||
uint32_t arch_get_cpu_freq_hz() {
|
uint32_t arch_get_cpu_freq_hz() {
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ void HOT arch_feed_wdt() { system_soft_wdt_feed(); }
|
|||||||
uint8_t progmem_read_byte(const uint8_t *addr) {
|
uint8_t progmem_read_byte(const uint8_t *addr) {
|
||||||
return pgm_read_byte(addr); // NOLINT
|
return pgm_read_byte(addr); // NOLINT
|
||||||
}
|
}
|
||||||
|
const char *progmem_read_ptr(const char *const *addr) {
|
||||||
|
return reinterpret_cast<const char *>(pgm_read_ptr(addr)); // NOLINT
|
||||||
|
}
|
||||||
uint16_t progmem_read_uint16(const uint16_t *addr) {
|
uint16_t progmem_read_uint16(const uint16_t *addr) {
|
||||||
return pgm_read_word(addr); // NOLINT
|
return pgm_read_word(addr); // NOLINT
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,11 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_MOTION,
|
DEVICE_CLASS_MOTION,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import (
|
||||||
|
entity_duplicate_validator,
|
||||||
|
setup_device_class,
|
||||||
|
setup_entity,
|
||||||
|
)
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
CODEOWNERS = ["@nohat"]
|
CODEOWNERS = ["@nohat"]
|
||||||
@@ -85,17 +89,15 @@ def event_schema(
|
|||||||
return _EVENT_SCHEMA.extend(schema)
|
return _EVENT_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("event")
|
||||||
async def setup_event_core_(var, config, *, event_types: list[str]):
|
async def setup_event_core_(var, config, *, event_types: list[str]):
|
||||||
await setup_entity(var, config, "event")
|
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_EVENT, []):
|
for conf in config.get(CONF_ON_EVENT, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [(cg.StringRef, "event_type")], conf)
|
await automation.build_automation(trigger, [(cg.StringRef, "event_type")], conf)
|
||||||
|
|
||||||
cg.add(var.set_event_types(event_types))
|
cg.add(var.set_event_types(event_types))
|
||||||
|
|
||||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
setup_device_class(config)
|
||||||
cg.add(var.set_device_class(device_class))
|
|
||||||
|
|
||||||
if mqtt_id := config.get(CONF_MQTT_ID):
|
if mqtt_id := config.get(CONF_MQTT_ID):
|
||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace event {
|
|||||||
LOG_ENTITY_DEVICE_CLASS(TAG, prefix, *(obj)); \
|
LOG_ENTITY_DEVICE_CLASS(TAG, prefix, *(obj)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
class Event : public EntityBase, public EntityBase_DeviceClass {
|
class Event : public EntityBase {
|
||||||
public:
|
public:
|
||||||
void trigger(const std::string &event_type);
|
void trigger(const std::string &event_type);
|
||||||
|
|
||||||
|
|||||||
@@ -222,9 +222,8 @@ def validate_preset_modes(value):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("fan")
|
||||||
async def setup_fan_core_(var, config):
|
async def setup_fan_core_(var, config):
|
||||||
await setup_entity(var, config, "fan")
|
|
||||||
|
|
||||||
cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE]))
|
cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE]))
|
||||||
|
|
||||||
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ void HOT arch_feed_wdt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
||||||
|
const char *progmem_read_ptr(const char *const *addr) { return *addr; }
|
||||||
uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
||||||
uint32_t arch_get_cpu_cycle_count() {
|
uint32_t arch_get_cpu_cycle_count() {
|
||||||
struct timespec spec;
|
struct timespec spec;
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ def infrared_schema(class_: type[cg.MockObjClass]) -> cv.Schema:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("infrared")
|
||||||
async def setup_infrared_core_(var: cg.Pvariable, config: ConfigType) -> None:
|
async def setup_infrared_core_(var: cg.Pvariable, config: ConfigType) -> None:
|
||||||
"""Set up core infrared configuration."""
|
"""Set up core infrared configuration."""
|
||||||
await setup_entity(var, config, "infrared")
|
|
||||||
|
|
||||||
|
|
||||||
async def register_infrared(var: cg.Pvariable, config: ConfigType) -> None:
|
async def register_infrared(var: cg.Pvariable, config: ConfigType) -> None:
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ void HOT arch_feed_wdt() { lt_wdt_feed(); }
|
|||||||
uint32_t arch_get_cpu_cycle_count() { return lt_cpu_get_cycle_count(); }
|
uint32_t arch_get_cpu_cycle_count() { return lt_cpu_get_cycle_count(); }
|
||||||
uint32_t arch_get_cpu_freq_hz() { return lt_cpu_get_freq(); }
|
uint32_t arch_get_cpu_freq_hz() { return lt_cpu_get_freq(); }
|
||||||
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
||||||
|
const char *progmem_read_ptr(const char *const *addr) { return *addr; }
|
||||||
uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -243,9 +243,8 @@ def validate_color_temperature_channels(value):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
async def setup_light_core_(light_var, output_var, config):
|
@setup_entity("light")
|
||||||
await setup_entity(light_var, config, "light")
|
async def setup_light_core_(light_var, config, output_var):
|
||||||
|
|
||||||
cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE]))
|
cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE]))
|
||||||
|
|
||||||
if (initial_state_config := config.get(CONF_INITIAL_STATE)) is not None:
|
if (initial_state_config := config.get(CONF_INITIAL_STATE)) is not None:
|
||||||
@@ -312,7 +311,7 @@ async def register_light(output_var, config):
|
|||||||
cg.add(cg.App.register_light(light_var))
|
cg.add(cg.App.register_light(light_var))
|
||||||
CORE.register_platform_component("light", light_var)
|
CORE.register_platform_component("light", light_var)
|
||||||
await cg.register_component(light_var, config)
|
await cg.register_component(light_var, config)
|
||||||
await setup_light_core_(light_var, output_var, config)
|
await setup_light_core_(light_var, config, output_var)
|
||||||
|
|
||||||
|
|
||||||
async def new_light(config, *args):
|
async def new_light(config, *args):
|
||||||
|
|||||||
@@ -91,9 +91,8 @@ def lock_schema(
|
|||||||
return _LOCK_SCHEMA.extend(schema)
|
return _LOCK_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("lock")
|
||||||
async def _setup_lock_core(var, config):
|
async def _setup_lock_core(var, config):
|
||||||
await setup_entity(var, config, "lock")
|
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_LOCK, []):
|
for conf in config.get(CONF_ON_LOCK, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [], conf)
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ VolumeSetAction = media_player_ns.class_(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("media_player")
|
||||||
async def setup_media_player_core_(var, config):
|
async def setup_media_player_core_(var, config):
|
||||||
await setup_entity(var, config, "media_player")
|
|
||||||
for conf_key, _ in _STATE_TRIGGERS:
|
for conf_key, _ in _STATE_TRIGGERS:
|
||||||
for conf in config.get(conf_key, []):
|
for conf in config.get(conf_key, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon
|
|||||||
root[MQTT_MAX] = traits.get_max_value();
|
root[MQTT_MAX] = traits.get_max_value();
|
||||||
root[MQTT_STEP] = traits.get_step();
|
root[MQTT_STEP] = traits.get_step();
|
||||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
const auto unit_of_measurement = this->number_->traits.get_unit_of_measurement_ref();
|
const auto unit_of_measurement = this->number_->get_unit_of_measurement_ref();
|
||||||
if (!unit_of_measurement.empty()) {
|
if (!unit_of_measurement.empty()) {
|
||||||
root[MQTT_UNIT_OF_MEASUREMENT] = unit_of_measurement;
|
root[MQTT_UNIT_OF_MEASUREMENT] = unit_of_measurement;
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon
|
|||||||
root[MQTT_MODE] =
|
root[MQTT_MODE] =
|
||||||
NumberMqttModeStrings::get_progmem_str(static_cast<uint8_t>(mode), static_cast<uint8_t>(NUMBER_MODE_BOX));
|
NumberMqttModeStrings::get_progmem_str(static_cast<uint8_t>(mode), static_cast<uint8_t>(NUMBER_MODE_BOX));
|
||||||
}
|
}
|
||||||
const auto device_class = this->number_->traits.get_device_class_ref();
|
const auto device_class = this->number_->get_device_class_ref();
|
||||||
if (!device_class.empty()) {
|
if (!device_class.empty()) {
|
||||||
root[MQTT_DEVICE_CLASS] = device_class;
|
root[MQTT_DEVICE_CLASS] = device_class;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,12 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_WIND_SPEED,
|
DEVICE_CLASS_WIND_SPEED,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import (
|
||||||
|
entity_duplicate_validator,
|
||||||
|
setup_device_class,
|
||||||
|
setup_entity,
|
||||||
|
setup_unit_of_measurement,
|
||||||
|
)
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
@@ -257,11 +262,10 @@ async def _build_number_automations(var, config):
|
|||||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("number")
|
||||||
async def setup_number_core_(
|
async def setup_number_core_(
|
||||||
var, config, *, min_value: float, max_value: float, step: float
|
var, config, *, min_value: float, max_value: float, step: float
|
||||||
):
|
):
|
||||||
await setup_entity(var, config, "number")
|
|
||||||
|
|
||||||
cg.add(var.traits.set_min_value(min_value))
|
cg.add(var.traits.set_min_value(min_value))
|
||||||
cg.add(var.traits.set_max_value(max_value))
|
cg.add(var.traits.set_max_value(max_value))
|
||||||
cg.add(var.traits.set_step(step))
|
cg.add(var.traits.set_step(step))
|
||||||
@@ -273,10 +277,8 @@ async def setup_number_core_(
|
|||||||
|
|
||||||
CORE.add_job(_build_number_automations, var, config)
|
CORE.add_job(_build_number_automations, var, config)
|
||||||
|
|
||||||
if (unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)) is not None:
|
setup_device_class(config)
|
||||||
cg.add(var.traits.set_unit_of_measurement(unit_of_measurement))
|
setup_unit_of_measurement(config)
|
||||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
|
||||||
cg.add(var.traits.set_device_class(device_class))
|
|
||||||
|
|
||||||
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ void log_number(const char *tag, const char *prefix, const char *type, Number *o
|
|||||||
|
|
||||||
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
|
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
|
||||||
LOG_ENTITY_ICON(tag, prefix, *obj);
|
LOG_ENTITY_ICON(tag, prefix, *obj);
|
||||||
LOG_ENTITY_UNIT_OF_MEASUREMENT(tag, prefix, obj->traits);
|
LOG_ENTITY_UNIT_OF_MEASUREMENT(tag, prefix, *obj);
|
||||||
LOG_ENTITY_DEVICE_CLASS(tag, prefix, obj->traits);
|
LOG_ENTITY_DEVICE_CLASS(tag, prefix, *obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Number::publish_state(float state) {
|
void Number::publish_state(float state) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/entity_base.h"
|
#include <cmath>
|
||||||
#include "esphome/core/helpers.h"
|
#include <cstdint>
|
||||||
|
|
||||||
namespace esphome::number {
|
namespace esphome::number {
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ enum NumberMode : uint8_t {
|
|||||||
NUMBER_MODE_SLIDER = 2,
|
NUMBER_MODE_SLIDER = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
class NumberTraits : public EntityBase_DeviceClass, public EntityBase_UnitOfMeasurement {
|
class NumberTraits {
|
||||||
public:
|
public:
|
||||||
// Set/get the number value boundaries.
|
// Set/get the number value boundaries.
|
||||||
void set_min_value(float min_value) { min_value_ = min_value; }
|
void set_min_value(float min_value) { min_value_ = min_value; }
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ void HOT arch_feed_wdt() { watchdog_update(); }
|
|||||||
uint8_t progmem_read_byte(const uint8_t *addr) {
|
uint8_t progmem_read_byte(const uint8_t *addr) {
|
||||||
return pgm_read_byte(addr); // NOLINT
|
return pgm_read_byte(addr); // NOLINT
|
||||||
}
|
}
|
||||||
|
const char *progmem_read_ptr(const char *const *addr) {
|
||||||
|
return reinterpret_cast<const char *>(pgm_read_ptr(addr)); // NOLINT
|
||||||
|
}
|
||||||
uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
||||||
uint32_t HOT arch_get_cpu_cycle_count() { return ulMainGetRunTimeCounterValue(); }
|
uint32_t HOT arch_get_cpu_cycle_count() { return ulMainGetRunTimeCounterValue(); }
|
||||||
uint32_t arch_get_cpu_freq_hz() { return RP2040::f_cpu(); }
|
uint32_t arch_get_cpu_freq_hz() { return RP2040::f_cpu(); }
|
||||||
|
|||||||
@@ -92,9 +92,8 @@ def select_schema(
|
|||||||
return _SELECT_SCHEMA.extend(schema)
|
return _SELECT_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("select")
|
||||||
async def setup_select_core_(var, config, *, options: list[str]):
|
async def setup_select_core_(var, config, *, options: list[str]):
|
||||||
await setup_entity(var, config, "select")
|
|
||||||
|
|
||||||
cg.add(var.traits.set_options(options))
|
cg.add(var.traits.set_options(options))
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_VALUE, []):
|
for conf in config.get(CONF_ON_VALUE, []):
|
||||||
|
|||||||
@@ -106,7 +106,12 @@ from esphome.const import (
|
|||||||
ENTITY_CATEGORY_CONFIG,
|
ENTITY_CATEGORY_CONFIG,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import (
|
||||||
|
entity_duplicate_validator,
|
||||||
|
setup_device_class,
|
||||||
|
setup_entity,
|
||||||
|
setup_unit_of_measurement,
|
||||||
|
)
|
||||||
from esphome.cpp_generator import MockObj, MockObjClass
|
from esphome.cpp_generator import MockObj, MockObjClass
|
||||||
from esphome.util import Registry
|
from esphome.util import Registry
|
||||||
|
|
||||||
@@ -908,15 +913,12 @@ async def _build_sensor_automations(var, config):
|
|||||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("sensor")
|
||||||
async def setup_sensor_core_(var, config):
|
async def setup_sensor_core_(var, config):
|
||||||
await setup_entity(var, config, "sensor")
|
setup_device_class(config)
|
||||||
|
setup_unit_of_measurement(config)
|
||||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
|
||||||
cg.add(var.set_device_class(device_class))
|
|
||||||
if (state_class := config.get(CONF_STATE_CLASS)) is not None:
|
if (state_class := config.get(CONF_STATE_CLASS)) is not None:
|
||||||
cg.add(var.set_state_class(state_class))
|
cg.add(var.set_state_class(state_class))
|
||||||
if (unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)) is not None:
|
|
||||||
cg.add(var.set_unit_of_measurement(unit_of_measurement))
|
|
||||||
if (accuracy_decimals := config.get(CONF_ACCURACY_DECIMALS)) is not None:
|
if (accuracy_decimals := config.get(CONF_ACCURACY_DECIMALS)) is not None:
|
||||||
cg.add(var.set_accuracy_decimals(accuracy_decimals))
|
cg.add(var.set_accuracy_decimals(accuracy_decimals))
|
||||||
# Only set force_update if True (default is False)
|
# Only set force_update if True (default is False)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const LogString *state_class_to_string(StateClass state_class);
|
|||||||
*
|
*
|
||||||
* A sensor has unit of measurement and can use publish_state to send out a new value with the specified accuracy.
|
* A sensor has unit of measurement and can use publish_state to send out a new value with the specified accuracy.
|
||||||
*/
|
*/
|
||||||
class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBase_UnitOfMeasurement {
|
class Sensor : public EntityBase {
|
||||||
public:
|
public:
|
||||||
explicit Sensor();
|
explicit Sensor();
|
||||||
|
|
||||||
|
|||||||
@@ -567,7 +567,7 @@ void Sprinkler::set_valve_run_duration(const optional<size_t> valve_number, cons
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto call = this->valve_[valve_number.value()].run_duration_number->make_call();
|
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_ref() == MIN_STR) {
|
if (this->valve_[valve_number.value()].run_duration_number->get_unit_of_measurement_ref() == MIN_STR) {
|
||||||
call.set_value(run_duration.value() / 60.0);
|
call.set_value(run_duration.value() / 60.0);
|
||||||
} else {
|
} else {
|
||||||
call.set_value(run_duration.value());
|
call.set_value(run_duration.value());
|
||||||
@@ -649,7 +649,7 @@ uint32_t Sprinkler::valve_run_duration(const size_t valve_number) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (this->valve_[valve_number].run_duration_number != nullptr) {
|
if (this->valve_[valve_number].run_duration_number != nullptr) {
|
||||||
if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement_ref() == MIN_STR) {
|
if (this->valve_[valve_number].run_duration_number->get_unit_of_measurement_ref() == MIN_STR) {
|
||||||
return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state * 60));
|
return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state * 60));
|
||||||
} else {
|
} else {
|
||||||
return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state));
|
return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state));
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_SWITCH,
|
DEVICE_CLASS_SWITCH,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import (
|
||||||
|
entity_duplicate_validator,
|
||||||
|
setup_device_class,
|
||||||
|
setup_entity,
|
||||||
|
)
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
@@ -154,9 +158,8 @@ async def _build_switch_automations(var, config):
|
|||||||
await automation.build_automation(trigger, [], conf)
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("switch")
|
||||||
async def setup_switch_core_(var, config):
|
async def setup_switch_core_(var, config):
|
||||||
await setup_entity(var, config, "switch")
|
|
||||||
|
|
||||||
if (inverted := config.get(CONF_INVERTED)) is not None:
|
if (inverted := config.get(CONF_INVERTED)) is not None:
|
||||||
cg.add(var.set_inverted(inverted))
|
cg.add(var.set_inverted(inverted))
|
||||||
|
|
||||||
@@ -169,8 +172,7 @@ async def setup_switch_core_(var, config):
|
|||||||
if web_server_config := config.get(CONF_WEB_SERVER):
|
if web_server_config := config.get(CONF_WEB_SERVER):
|
||||||
await web_server.add_entity_config(var, web_server_config)
|
await web_server.add_entity_config(var, web_server_config)
|
||||||
|
|
||||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
setup_device_class(config)
|
||||||
cg.add(var.set_device_class(device_class))
|
|
||||||
|
|
||||||
cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE]))
|
cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE]))
|
||||||
await zigbee.setup_switch(var, config)
|
await zigbee.setup_switch(var, config)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ enum SwitchRestoreMode : uint8_t {
|
|||||||
* A switch is basically just a combination of a binary sensor (for reporting switch values)
|
* A switch is basically just a combination of a binary sensor (for reporting switch values)
|
||||||
* and a write_state method that writes a state to the hardware.
|
* and a write_state method that writes a state to the hardware.
|
||||||
*/
|
*/
|
||||||
class Switch : public EntityBase, public EntityBase_DeviceClass {
|
class Switch : public EntityBase {
|
||||||
public:
|
public:
|
||||||
explicit Switch();
|
explicit Switch();
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ def text_schema(
|
|||||||
return _TEXT_SCHEMA.extend(schema)
|
return _TEXT_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("text")
|
||||||
async def setup_text_core_(
|
async def setup_text_core_(
|
||||||
var,
|
var,
|
||||||
config,
|
config,
|
||||||
@@ -92,8 +93,6 @@ async def setup_text_core_(
|
|||||||
max_length: int | None,
|
max_length: int | None,
|
||||||
pattern: str | None,
|
pattern: str | None,
|
||||||
):
|
):
|
||||||
await setup_entity(var, config, "text")
|
|
||||||
|
|
||||||
cg.add(var.traits.set_min_length(min_length))
|
cg.add(var.traits.set_min_length(min_length))
|
||||||
cg.add(var.traits.set_max_length(max_length))
|
cg.add(var.traits.set_max_length(max_length))
|
||||||
if pattern is not None:
|
if pattern is not None:
|
||||||
|
|||||||
@@ -21,7 +21,11 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_TIMESTAMP,
|
DEVICE_CLASS_TIMESTAMP,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import (
|
||||||
|
entity_duplicate_validator,
|
||||||
|
setup_device_class,
|
||||||
|
setup_entity,
|
||||||
|
)
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.util import Registry
|
from esphome.util import Registry
|
||||||
|
|
||||||
@@ -208,11 +212,9 @@ async def _build_text_sensor_automations(var, config):
|
|||||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("text_sensor")
|
||||||
async def setup_text_sensor_core_(var, config):
|
async def setup_text_sensor_core_(var, config):
|
||||||
await setup_entity(var, config, "text_sensor")
|
setup_device_class(config)
|
||||||
|
|
||||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
|
||||||
cg.add(var.set_device_class(device_class))
|
|
||||||
|
|
||||||
if config.get(CONF_FILTERS): # must exist and not be empty
|
if config.get(CONF_FILTERS): # must exist and not be empty
|
||||||
cg.add_define("USE_TEXT_SENSOR_FILTER")
|
cg.add_define("USE_TEXT_SENSOR_FILTER")
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ void log_text_sensor(const char *tag, const char *prefix, const char *type, Text
|
|||||||
public: \
|
public: \
|
||||||
void set_##name##_text_sensor(text_sensor::TextSensor *text_sensor) { this->name##_text_sensor_ = text_sensor; }
|
void set_##name##_text_sensor(text_sensor::TextSensor *text_sensor) { this->name##_text_sensor_ = text_sensor; }
|
||||||
|
|
||||||
class TextSensor : public EntityBase, public EntityBase_DeviceClass {
|
class TextSensor : public EntityBase {
|
||||||
public:
|
public:
|
||||||
std::string state;
|
std::string state;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ from esphome.const import (
|
|||||||
ENTITY_CATEGORY_CONFIG,
|
ENTITY_CATEGORY_CONFIG,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import (
|
||||||
|
entity_duplicate_validator,
|
||||||
|
setup_device_class,
|
||||||
|
setup_entity,
|
||||||
|
)
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
CODEOWNERS = ["@jesserockz"]
|
CODEOWNERS = ["@jesserockz"]
|
||||||
@@ -87,11 +91,9 @@ def update_schema(
|
|||||||
return _UPDATE_SCHEMA.extend(schema)
|
return _UPDATE_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("update")
|
||||||
async def setup_update_core_(var, config):
|
async def setup_update_core_(var, config):
|
||||||
await setup_entity(var, config, "update")
|
setup_device_class(config)
|
||||||
|
|
||||||
if device_class_config := config.get(CONF_DEVICE_CLASS):
|
|
||||||
cg.add(var.set_device_class(device_class_config))
|
|
||||||
|
|
||||||
if on_update_available := config.get(CONF_ON_UPDATE_AVAILABLE):
|
if on_update_available := config.get(CONF_ON_UPDATE_AVAILABLE):
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ enum UpdateState : uint8_t {
|
|||||||
|
|
||||||
const LogString *update_state_to_string(UpdateState state);
|
const LogString *update_state_to_string(UpdateState state);
|
||||||
|
|
||||||
class UpdateEntity : public EntityBase, public EntityBase_DeviceClass {
|
class UpdateEntity : public EntityBase {
|
||||||
public:
|
public:
|
||||||
void publish_state();
|
void publish_state();
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_WATER,
|
DEVICE_CLASS_WATER,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import (
|
||||||
|
entity_duplicate_validator,
|
||||||
|
setup_device_class,
|
||||||
|
setup_entity,
|
||||||
|
)
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
@@ -129,11 +133,9 @@ def valve_schema(
|
|||||||
return _VALVE_SCHEMA.extend(schema)
|
return _VALVE_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("valve")
|
||||||
async def _setup_valve_core(var, config):
|
async def _setup_valve_core(var, config):
|
||||||
await setup_entity(var, config, "valve")
|
setup_device_class(config)
|
||||||
|
|
||||||
if device_class_config := config.get(CONF_DEVICE_CLASS):
|
|
||||||
cg.add(var.set_device_class(device_class_config))
|
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_OPEN, []):
|
for conf in config.get(CONF_ON_OPEN, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ const LogString *valve_operation_to_str(ValveOperation op);
|
|||||||
* to control all values of the valve. Also implement get_traits() to return what operations
|
* to control all values of the valve. Also implement get_traits() to return what operations
|
||||||
* the valve supports.
|
* the valve supports.
|
||||||
*/
|
*/
|
||||||
class Valve : public EntityBase, public EntityBase_DeviceClass {
|
class Valve : public EntityBase {
|
||||||
public:
|
public:
|
||||||
explicit Valve();
|
explicit Valve();
|
||||||
|
|
||||||
|
|||||||
@@ -69,10 +69,9 @@ def water_heater_schema(
|
|||||||
return _WATER_HEATER_SCHEMA.extend(schema)
|
return _WATER_HEATER_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
@setup_entity("water_heater")
|
||||||
async def setup_water_heater_core_(var: cg.Pvariable, config: ConfigType) -> None:
|
async def setup_water_heater_core_(var: cg.Pvariable, config: ConfigType) -> None:
|
||||||
"""Set up the core water heater properties in C++."""
|
"""Set up the core water heater properties in C++."""
|
||||||
await setup_entity(var, config, "water_heater")
|
|
||||||
|
|
||||||
visual = config[CONF_VISUAL]
|
visual = config[CONF_VISUAL]
|
||||||
if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None:
|
if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None:
|
||||||
cg.add_define("USE_WATER_HEATER_VISUAL_OVERRIDES")
|
cg.add_define("USE_WATER_HEATER_VISUAL_OVERRIDES")
|
||||||
|
|||||||
@@ -1139,7 +1139,7 @@ json::SerializationBuffer<> WebServer::number_json_(number::Number *obj, float v
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
const auto uom_ref = obj->traits.get_unit_of_measurement_ref();
|
const auto uom_ref = obj->get_unit_of_measurement_ref();
|
||||||
const int8_t accuracy = step_to_accuracy_decimals(obj->traits.get_step());
|
const int8_t accuracy = step_to_accuracy_decimals(obj->traits.get_step());
|
||||||
|
|
||||||
// Need two buffers: one for value, one for state with UOM
|
// Need two buffers: one for value, one for state with UOM
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ void arch_restart() { sys_reboot(SYS_REBOOT_COLD); }
|
|||||||
uint32_t arch_get_cpu_cycle_count() { return k_cycle_get_32(); }
|
uint32_t arch_get_cpu_cycle_count() { return k_cycle_get_32(); }
|
||||||
uint32_t arch_get_cpu_freq_hz() { return sys_clock_hw_cycles_per_sec(); }
|
uint32_t arch_get_cpu_freq_hz() { return sys_clock_hw_cycles_per_sec(); }
|
||||||
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
||||||
|
const char *progmem_read_ptr(const char *const *addr) { return *addr; }
|
||||||
uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
||||||
|
|
||||||
Mutex::Mutex() {
|
Mutex::Mutex() {
|
||||||
|
|||||||
@@ -44,7 +44,9 @@
|
|||||||
#define USE_DEEP_SLEEP
|
#define USE_DEEP_SLEEP
|
||||||
#define USE_DEVICES
|
#define USE_DEVICES
|
||||||
#define USE_DISPLAY
|
#define USE_DISPLAY
|
||||||
|
#define USE_ENTITY_DEVICE_CLASS
|
||||||
#define USE_ENTITY_ICON
|
#define USE_ENTITY_ICON
|
||||||
|
#define USE_ENTITY_UNIT_OF_MEASUREMENT
|
||||||
#define USE_ESP32_CAMERA_JPEG_CONVERSION
|
#define USE_ESP32_CAMERA_JPEG_CONVERSION
|
||||||
#define USE_ESP32_HOSTED
|
#define USE_ESP32_HOSTED
|
||||||
#define USE_ESP32_IMPROV_STATE_CALLBACK
|
#define USE_ESP32_IMPROV_STATE_CALLBACK
|
||||||
|
|||||||
@@ -45,24 +45,42 @@ void EntityBase::set_name(const char *name, uint32_t object_id_hash) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entity Icon
|
// Weak default lookup functions — overridden by generated code in main.cpp
|
||||||
std::string EntityBase::get_icon() const {
|
__attribute__((weak)) const char *entity_device_class_lookup(uint8_t) { return ""; }
|
||||||
#ifdef USE_ENTITY_ICON
|
__attribute__((weak)) const char *entity_uom_lookup(uint8_t) { return ""; }
|
||||||
if (this->icon_c_str_ == nullptr) {
|
__attribute__((weak)) const char *entity_icon_lookup(uint8_t) { return ""; }
|
||||||
return "";
|
|
||||||
}
|
// Entity device class (from index)
|
||||||
return this->icon_c_str_;
|
StringRef EntityBase::get_device_class_ref() const {
|
||||||
|
#ifdef USE_ENTITY_DEVICE_CLASS
|
||||||
|
return StringRef(entity_device_class_lookup(this->device_class_idx_));
|
||||||
#else
|
#else
|
||||||
return "";
|
return StringRef(entity_device_class_lookup(0));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
void EntityBase::set_icon(const char *icon) {
|
std::string EntityBase::get_device_class() const { return std::string(this->get_device_class_ref().c_str()); }
|
||||||
#ifdef USE_ENTITY_ICON
|
|
||||||
this->icon_c_str_ = icon;
|
// Entity unit of measurement (from index)
|
||||||
|
StringRef EntityBase::get_unit_of_measurement_ref() const {
|
||||||
|
#ifdef USE_ENTITY_UNIT_OF_MEASUREMENT
|
||||||
|
return StringRef(entity_uom_lookup(this->uom_idx_));
|
||||||
#else
|
#else
|
||||||
// No-op when USE_ENTITY_ICON is not defined
|
return StringRef(entity_uom_lookup(0));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
std::string EntityBase::get_unit_of_measurement() const {
|
||||||
|
return std::string(this->get_unit_of_measurement_ref().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entity icon (from index)
|
||||||
|
StringRef EntityBase::get_icon_ref() const {
|
||||||
|
#ifdef USE_ENTITY_ICON
|
||||||
|
return StringRef(entity_icon_lookup(this->icon_idx_));
|
||||||
|
#else
|
||||||
|
return StringRef(entity_icon_lookup(0));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
std::string EntityBase::get_icon() const { return std::string(this->get_icon_ref().c_str()); }
|
||||||
|
|
||||||
// Entity Object ID - computed on-demand from name
|
// Entity Object ID - computed on-demand from name
|
||||||
std::string EntityBase::get_object_id() const {
|
std::string EntityBase::get_object_id() const {
|
||||||
@@ -134,24 +152,6 @@ ESPPreferenceObject EntityBase::make_entity_preference_(size_t size, uint32_t ve
|
|||||||
return global_preferences->make_preference(size, key);
|
return global_preferences->make_preference(size, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EntityBase_DeviceClass::get_device_class() {
|
|
||||||
if (this->device_class_ == nullptr) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return this->device_class_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityBase_DeviceClass::set_device_class(const char *device_class) { this->device_class_ = device_class; }
|
|
||||||
|
|
||||||
std::string EntityBase_UnitOfMeasurement::get_unit_of_measurement() {
|
|
||||||
if (this->unit_of_measurement_ == nullptr)
|
|
||||||
return "";
|
|
||||||
return this->unit_of_measurement_;
|
|
||||||
}
|
|
||||||
void EntityBase_UnitOfMeasurement::set_unit_of_measurement(const char *unit_of_measurement) {
|
|
||||||
this->unit_of_measurement_ = unit_of_measurement;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_ENTITY_ICON
|
#ifdef USE_ENTITY_ICON
|
||||||
void log_entity_icon(const char *tag, const char *prefix, const EntityBase &obj) {
|
void log_entity_icon(const char *tag, const char *prefix, const EntityBase &obj) {
|
||||||
if (!obj.get_icon_ref().empty()) {
|
if (!obj.get_icon_ref().empty()) {
|
||||||
@@ -160,13 +160,13 @@ void log_entity_icon(const char *tag, const char *prefix, const EntityBase &obj)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void log_entity_device_class(const char *tag, const char *prefix, const EntityBase_DeviceClass &obj) {
|
void log_entity_device_class(const char *tag, const char *prefix, const EntityBase &obj) {
|
||||||
if (!obj.get_device_class_ref().empty()) {
|
if (!obj.get_device_class_ref().empty()) {
|
||||||
ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj.get_device_class_ref().c_str());
|
ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj.get_device_class_ref().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_entity_unit_of_measurement(const char *tag, const char *prefix, const EntityBase_UnitOfMeasurement &obj) {
|
void log_entity_unit_of_measurement(const char *tag, const char *prefix, const EntityBase &obj) {
|
||||||
if (!obj.get_unit_of_measurement_ref().empty()) {
|
if (!obj.get_unit_of_measurement_ref().empty()) {
|
||||||
ESP_LOGCONFIG(tag, "%s Unit of Measurement: '%s'", prefix, obj.get_unit_of_measurement_ref().c_str());
|
ESP_LOGCONFIG(tag, "%s Unit of Measurement: '%s'", prefix, obj.get_unit_of_measurement_ref().c_str());
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-52
@@ -14,6 +14,12 @@
|
|||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
|
// Extern lookup functions for entity string tables.
|
||||||
|
// Generated code provides strong definitions; weak defaults return "".
|
||||||
|
extern const char *entity_device_class_lookup(uint8_t index);
|
||||||
|
extern const char *entity_uom_lookup(uint8_t index);
|
||||||
|
extern const char *entity_icon_lookup(uint8_t index);
|
||||||
|
|
||||||
// Maximum device name length - keep in sync with validate_hostname() in esphome/core/config.py
|
// Maximum device name length - keep in sync with validate_hostname() in esphome/core/config.py
|
||||||
static constexpr size_t ESPHOME_DEVICE_NAME_MAX_LEN = 31;
|
static constexpr size_t ESPHOME_DEVICE_NAME_MAX_LEN = 31;
|
||||||
|
|
||||||
@@ -89,20 +95,41 @@ class EntityBase {
|
|||||||
this->flags_.entity_category = static_cast<uint8_t>(entity_category);
|
this->flags_.entity_category = static_cast<uint8_t>(entity_category);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set entity string table indices — one call per entity from codegen.
|
||||||
|
// Packed: [23..16] icon | [15..8] UoM | [7..0] device_class (each 8 bits)
|
||||||
|
void set_entity_strings([[maybe_unused]] uint32_t packed) {
|
||||||
|
#ifdef USE_ENTITY_DEVICE_CLASS
|
||||||
|
this->device_class_idx_ = packed & 0xFF;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ENTITY_UNIT_OF_MEASUREMENT
|
||||||
|
this->uom_idx_ = (packed >> 8) & 0xFF;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ENTITY_ICON
|
||||||
|
this->icon_idx_ = (packed >> 16) & 0xFF;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get device class as StringRef (from packed index)
|
||||||
|
StringRef get_device_class_ref() const;
|
||||||
|
/// Get the device class as std::string (deprecated, prefer get_device_class_ref())
|
||||||
|
ESPDEPRECATED("Use get_device_class_ref() instead for better performance (avoids string copy). Will be removed in "
|
||||||
|
"ESPHome 2026.9.0",
|
||||||
|
"2026.3.0")
|
||||||
|
std::string get_device_class() const;
|
||||||
|
// Get unit of measurement as StringRef (from packed index)
|
||||||
|
StringRef get_unit_of_measurement_ref() const;
|
||||||
|
/// Get the unit of measurement as std::string (deprecated, prefer get_unit_of_measurement_ref())
|
||||||
|
ESPDEPRECATED("Use get_unit_of_measurement_ref() instead for better performance (avoids string copy). Will be "
|
||||||
|
"removed in ESPHome 2026.9.0",
|
||||||
|
"2026.3.0")
|
||||||
|
std::string get_unit_of_measurement() const;
|
||||||
|
|
||||||
// Get/set this entity's icon
|
// Get/set this entity's icon
|
||||||
ESPDEPRECATED(
|
ESPDEPRECATED(
|
||||||
"Use get_icon_ref() instead for better performance (avoids string copy). Will be removed in ESPHome 2026.5.0",
|
"Use get_icon_ref() instead for better performance (avoids string copy). Will be removed in ESPHome 2026.5.0",
|
||||||
"2025.11.0")
|
"2025.11.0")
|
||||||
std::string get_icon() const;
|
std::string get_icon() const;
|
||||||
void set_icon(const char *icon);
|
StringRef get_icon_ref() const;
|
||||||
StringRef get_icon_ref() const {
|
|
||||||
static constexpr auto EMPTY_STRING = StringRef::from_lit("");
|
|
||||||
#ifdef USE_ENTITY_ICON
|
|
||||||
return this->icon_c_str_ == nullptr ? EMPTY_STRING : StringRef(this->icon_c_str_);
|
|
||||||
#else
|
|
||||||
return EMPTY_STRING;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
// Get/set this entity's device id
|
// Get/set this entity's device id
|
||||||
@@ -173,9 +200,6 @@ class EntityBase {
|
|||||||
void calc_object_id_();
|
void calc_object_id_();
|
||||||
|
|
||||||
StringRef name_;
|
StringRef name_;
|
||||||
#ifdef USE_ENTITY_ICON
|
|
||||||
const char *icon_c_str_{nullptr};
|
|
||||||
#endif
|
|
||||||
uint32_t object_id_hash_{};
|
uint32_t object_id_hash_{};
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
Device *device_{};
|
Device *device_{};
|
||||||
@@ -190,44 +214,16 @@ class EntityBase {
|
|||||||
uint8_t entity_category : 2; // Supports up to 4 categories
|
uint8_t entity_category : 2; // Supports up to 4 categories
|
||||||
uint8_t reserved : 2; // Reserved for future use
|
uint8_t reserved : 2; // Reserved for future use
|
||||||
} flags_{};
|
} flags_{};
|
||||||
};
|
// String table indices — packed into the 3 padding bytes after flags_
|
||||||
|
#ifdef USE_ENTITY_DEVICE_CLASS
|
||||||
class EntityBase_DeviceClass { // NOLINT(readability-identifier-naming)
|
uint8_t device_class_idx_{};
|
||||||
public:
|
#endif
|
||||||
/// Get the device class, using the manual override if set.
|
#ifdef USE_ENTITY_UNIT_OF_MEASUREMENT
|
||||||
ESPDEPRECATED("Use get_device_class_ref() instead for better performance (avoids string copy). Will be removed in "
|
uint8_t uom_idx_{};
|
||||||
"ESPHome 2026.5.0",
|
#endif
|
||||||
"2025.11.0")
|
#ifdef USE_ENTITY_ICON
|
||||||
std::string get_device_class();
|
uint8_t icon_idx_{};
|
||||||
/// Manually set the device class.
|
#endif
|
||||||
void set_device_class(const char *device_class);
|
|
||||||
/// Get the device class as StringRef
|
|
||||||
StringRef get_device_class_ref() const {
|
|
||||||
static constexpr auto EMPTY_STRING = StringRef::from_lit("");
|
|
||||||
return this->device_class_ == nullptr ? EMPTY_STRING : StringRef(this->device_class_);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
const char *device_class_{nullptr}; ///< Device class override
|
|
||||||
};
|
|
||||||
|
|
||||||
class EntityBase_UnitOfMeasurement { // NOLINT(readability-identifier-naming)
|
|
||||||
public:
|
|
||||||
/// Get the unit of measurement, using the manual override if set.
|
|
||||||
ESPDEPRECATED("Use get_unit_of_measurement_ref() instead for better performance (avoids string copy). Will be "
|
|
||||||
"removed in ESPHome 2026.5.0",
|
|
||||||
"2025.11.0")
|
|
||||||
std::string get_unit_of_measurement();
|
|
||||||
/// Manually set the unit of measurement.
|
|
||||||
void set_unit_of_measurement(const char *unit_of_measurement);
|
|
||||||
/// Get the unit of measurement as StringRef
|
|
||||||
StringRef get_unit_of_measurement_ref() const {
|
|
||||||
static constexpr auto EMPTY_STRING = StringRef::from_lit("");
|
|
||||||
return this->unit_of_measurement_ == nullptr ? EMPTY_STRING : StringRef(this->unit_of_measurement_);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
const char *unit_of_measurement_{nullptr}; ///< Unit of measurement override
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Log entity icon if set (for use in dump_config)
|
/// Log entity icon if set (for use in dump_config)
|
||||||
@@ -240,10 +236,10 @@ inline void log_entity_icon(const char *, const char *, const EntityBase &) {}
|
|||||||
#endif
|
#endif
|
||||||
/// Log entity device class if set (for use in dump_config)
|
/// Log entity device class if set (for use in dump_config)
|
||||||
#define LOG_ENTITY_DEVICE_CLASS(tag, prefix, obj) log_entity_device_class(tag, prefix, obj)
|
#define LOG_ENTITY_DEVICE_CLASS(tag, prefix, obj) log_entity_device_class(tag, prefix, obj)
|
||||||
void log_entity_device_class(const char *tag, const char *prefix, const EntityBase_DeviceClass &obj);
|
void log_entity_device_class(const char *tag, const char *prefix, const EntityBase &obj);
|
||||||
/// Log entity unit of measurement if set (for use in dump_config)
|
/// Log entity unit of measurement if set (for use in dump_config)
|
||||||
#define LOG_ENTITY_UNIT_OF_MEASUREMENT(tag, prefix, obj) log_entity_unit_of_measurement(tag, prefix, obj)
|
#define LOG_ENTITY_UNIT_OF_MEASUREMENT(tag, prefix, obj) log_entity_unit_of_measurement(tag, prefix, obj)
|
||||||
void log_entity_unit_of_measurement(const char *tag, const char *prefix, const EntityBase_UnitOfMeasurement &obj);
|
void log_entity_unit_of_measurement(const char *tag, const char *prefix, const EntityBase &obj);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An entity that has a state.
|
* An entity that has a state.
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
import functools
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_DEVICE_CLASS,
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
CONF_DISABLED_BY_DEFAULT,
|
CONF_DISABLED_BY_DEFAULT,
|
||||||
CONF_ENTITY_CATEGORY,
|
CONF_ENTITY_CATEGORY,
|
||||||
@@ -11,15 +14,184 @@ from esphome.const import (
|
|||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_INTERNAL,
|
CONF_INTERNAL,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
|
CONF_UNIT_OF_MEASUREMENT,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, ID
|
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
|
||||||
from esphome.cpp_generator import MockObj, add, get_variable
|
from esphome.cpp_generator import MockObj, RawStatement, add, get_variable
|
||||||
import esphome.final_validate as fv
|
import esphome.final_validate as fv
|
||||||
from esphome.helpers import fnv1_hash_object_id, sanitize, snake_case
|
from esphome.helpers import cpp_string_escape, fnv1_hash_object_id, sanitize, snake_case
|
||||||
from esphome.types import ConfigType, EntityMetadata
|
from esphome.types import ConfigType, EntityMetadata
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DOMAIN = "entity_string_pool"
|
||||||
|
|
||||||
|
# Private config keys for storing registered string indices
|
||||||
|
_KEY_DC_IDX = "_entity_dc_idx"
|
||||||
|
_KEY_UOM_IDX = "_entity_uom_idx"
|
||||||
|
_KEY_ICON_IDX = "_entity_icon_idx"
|
||||||
|
|
||||||
|
# Bit layout for set_entity_strings(packed) — must match C++ setter in entity_base.h:
|
||||||
|
# [23..16] icon (8 bits) | [15..8] UoM (8 bits) | [7..0] device_class (8 bits)
|
||||||
|
_DC_SHIFT = 0
|
||||||
|
_UOM_SHIFT = 8
|
||||||
|
_ICON_SHIFT = 16
|
||||||
|
|
||||||
|
# Maximum unique strings per category (8-bit index, 0 = not set)
|
||||||
|
_MAX_DEVICE_CLASSES = 0xFF # 255
|
||||||
|
_MAX_UNITS = 0xFF # 255
|
||||||
|
_MAX_ICONS = 0xFF # 255
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EntityStringPool:
|
||||||
|
"""Pool of entity string properties for PROGMEM pointer tables.
|
||||||
|
|
||||||
|
Strings are registered during to_code() and assigned 1-based indices.
|
||||||
|
Index 0 means "not set" (empty string). At render time, the pool
|
||||||
|
generates C++ PROGMEM pointer table + lookup function per category.
|
||||||
|
"""
|
||||||
|
|
||||||
|
device_classes: dict[str, int] = field(default_factory=dict)
|
||||||
|
units: dict[str, int] = field(default_factory=dict)
|
||||||
|
icons: dict[str, int] = field(default_factory=dict)
|
||||||
|
tables_registered: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
def _get_pool() -> EntityStringPool:
|
||||||
|
"""Get or create the entity string pool from CORE.data."""
|
||||||
|
if DOMAIN not in CORE.data:
|
||||||
|
CORE.data[DOMAIN] = EntityStringPool()
|
||||||
|
return CORE.data[DOMAIN]
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_tables_registered() -> None:
|
||||||
|
"""Schedule the table generation job (once)."""
|
||||||
|
pool = _get_pool()
|
||||||
|
if pool.tables_registered:
|
||||||
|
return
|
||||||
|
pool.tables_registered = True
|
||||||
|
CORE.add_job(_generate_tables_job)
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_category_code(
|
||||||
|
table_var: str,
|
||||||
|
lookup_fn: str,
|
||||||
|
strings: dict[str, int],
|
||||||
|
) -> str:
|
||||||
|
"""Generate C++ code for one string category (PROGMEM pointer table + lookup).
|
||||||
|
|
||||||
|
Uses a PROGMEM array of string pointers. On ESP8266, pointers are stored
|
||||||
|
in flash (via PROGMEM) and read with progmem_read_ptr(). String literals
|
||||||
|
themselves remain in RAM but benefit from linker string deduplication.
|
||||||
|
Index 0 means "not set" and returns empty string.
|
||||||
|
"""
|
||||||
|
if not strings:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
sorted_strings = sorted(strings.items(), key=lambda x: x[1])
|
||||||
|
entries = ", ".join(cpp_string_escape(s) for s, _ in sorted_strings)
|
||||||
|
count = len(sorted_strings)
|
||||||
|
|
||||||
|
return (
|
||||||
|
f"static const char *const {table_var}[] PROGMEM = {{{entries}}};\n"
|
||||||
|
f"const char *{lookup_fn}(uint8_t index) {{\n"
|
||||||
|
f' if (index == 0 || index > {count}) return "";\n'
|
||||||
|
f" return progmem_read_ptr(&{table_var}[index - 1]);\n"
|
||||||
|
f"}}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
_CATEGORY_CONFIGS = (
|
||||||
|
("ENTITY_DC_TABLE", "entity_device_class_lookup", "device_classes"),
|
||||||
|
("ENTITY_UOM_TABLE", "entity_uom_lookup", "units"),
|
||||||
|
("ENTITY_ICON_TABLE", "entity_icon_lookup", "icons"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@coroutine_with_priority(CoroPriority.FINAL)
|
||||||
|
async def _generate_tables_job() -> None:
|
||||||
|
"""Generate all entity string table C++ code as a FINAL-priority job.
|
||||||
|
|
||||||
|
Runs after all component to_code() calls have registered their strings.
|
||||||
|
"""
|
||||||
|
pool = _get_pool()
|
||||||
|
parts = ["namespace esphome {"]
|
||||||
|
for table_var, lookup_fn, attr in _CATEGORY_CONFIGS:
|
||||||
|
code = _generate_category_code(table_var, lookup_fn, getattr(pool, attr))
|
||||||
|
if code:
|
||||||
|
parts.append(code)
|
||||||
|
parts.append("} // namespace esphome")
|
||||||
|
cg.add_global(RawStatement("\n".join(parts)))
|
||||||
|
|
||||||
|
|
||||||
|
def _register_string(
|
||||||
|
value: str, category: dict[str, int], max_count: int, category_name: str
|
||||||
|
) -> int:
|
||||||
|
"""Register a string in a category dict and return its 1-based index.
|
||||||
|
|
||||||
|
Returns 0 if value is empty/None (meaning "not set").
|
||||||
|
"""
|
||||||
|
if not value:
|
||||||
|
return 0
|
||||||
|
if value in category:
|
||||||
|
return category[value]
|
||||||
|
idx = len(category) + 1
|
||||||
|
if idx > max_count:
|
||||||
|
raise ValueError(
|
||||||
|
f"Too many unique {category_name} values (max {max_count}), got {idx}: '{value}'"
|
||||||
|
)
|
||||||
|
category[value] = idx
|
||||||
|
_ensure_tables_registered()
|
||||||
|
return idx
|
||||||
|
|
||||||
|
|
||||||
|
def register_device_class(value: str) -> int:
|
||||||
|
"""Register a device_class string and return its 1-based index."""
|
||||||
|
return _register_string(
|
||||||
|
value, _get_pool().device_classes, _MAX_DEVICE_CLASSES, "device_class"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register_unit_of_measurement(value: str) -> int:
|
||||||
|
"""Register a unit_of_measurement string and return its 1-based index."""
|
||||||
|
return _register_string(value, _get_pool().units, _MAX_UNITS, "unit_of_measurement")
|
||||||
|
|
||||||
|
|
||||||
|
def register_icon(value: str) -> int:
|
||||||
|
"""Register an icon string and return its 1-based index."""
|
||||||
|
return _register_string(value, _get_pool().icons, _MAX_ICONS, "icon")
|
||||||
|
|
||||||
|
|
||||||
|
def setup_device_class(config: ConfigType) -> None:
|
||||||
|
"""Register config's device_class and store its index for finalize_entity_strings."""
|
||||||
|
idx = register_device_class(config.get(CONF_DEVICE_CLASS, ""))
|
||||||
|
if idx:
|
||||||
|
cg.add_define("USE_ENTITY_DEVICE_CLASS")
|
||||||
|
config[_KEY_DC_IDX] = idx
|
||||||
|
|
||||||
|
|
||||||
|
def setup_unit_of_measurement(config: ConfigType) -> None:
|
||||||
|
"""Register config's unit_of_measurement and store its index for finalize_entity_strings."""
|
||||||
|
idx = register_unit_of_measurement(config.get(CONF_UNIT_OF_MEASUREMENT, ""))
|
||||||
|
if idx:
|
||||||
|
cg.add_define("USE_ENTITY_UNIT_OF_MEASUREMENT")
|
||||||
|
config[_KEY_UOM_IDX] = idx
|
||||||
|
|
||||||
|
|
||||||
|
def finalize_entity_strings(var: MockObj, config: ConfigType) -> None:
|
||||||
|
"""Emit a single set_entity_strings() call with all packed indices.
|
||||||
|
|
||||||
|
Call this at the end of each component's setup function, after
|
||||||
|
setup_entity() and any register_device_class/register_unit_of_measurement calls.
|
||||||
|
"""
|
||||||
|
dc_idx = config.get(_KEY_DC_IDX, 0)
|
||||||
|
uom_idx = config.get(_KEY_UOM_IDX, 0)
|
||||||
|
icon_idx = config.get(_KEY_ICON_IDX, 0)
|
||||||
|
packed = (dc_idx << _DC_SHIFT) | (uom_idx << _UOM_SHIFT) | (icon_idx << _ICON_SHIFT)
|
||||||
|
if packed != 0:
|
||||||
|
add(var.set_entity_strings(packed))
|
||||||
|
|
||||||
|
|
||||||
def get_base_entity_object_id(
|
def get_base_entity_object_id(
|
||||||
name: str, friendly_name: str | None, device_name: str | None = None
|
name: str, friendly_name: str | None, device_name: str | None = None
|
||||||
@@ -64,8 +236,48 @@ def get_base_entity_object_id(
|
|||||||
return sanitize(snake_case(base_str))
|
return sanitize(snake_case(base_str))
|
||||||
|
|
||||||
|
|
||||||
async def setup_entity(var: MockObj, config: ConfigType, platform: str) -> None:
|
def setup_entity(var_or_platform, config=None, platform=None):
|
||||||
"""Set up generic properties of an Entity.
|
"""Set up entity properties — works as both decorator and direct call.
|
||||||
|
|
||||||
|
Decorator mode::
|
||||||
|
|
||||||
|
@setup_entity("sensor")
|
||||||
|
async def setup_sensor_core_(var, config):
|
||||||
|
setup_device_class(config)
|
||||||
|
setup_unit_of_measurement(config)
|
||||||
|
...
|
||||||
|
|
||||||
|
Direct call mode (for entities with no extra string properties)::
|
||||||
|
|
||||||
|
await setup_entity(var, config, "camera")
|
||||||
|
"""
|
||||||
|
if isinstance(var_or_platform, str) and config is None:
|
||||||
|
# Decorator mode: @setup_entity("sensor")
|
||||||
|
platform = var_or_platform
|
||||||
|
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@functools.wraps(func)
|
||||||
|
async def wrapper(
|
||||||
|
var: MockObj, config: ConfigType, *args, **kwargs
|
||||||
|
) -> None:
|
||||||
|
await _setup_entity_impl(var, config, platform)
|
||||||
|
await func(var, config, *args, **kwargs)
|
||||||
|
finalize_entity_strings(var, config)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
# Direct call mode: await setup_entity(var, config, "camera")
|
||||||
|
async def _do() -> None:
|
||||||
|
await _setup_entity_impl(var_or_platform, config, platform)
|
||||||
|
finalize_entity_strings(var_or_platform, config)
|
||||||
|
|
||||||
|
return _do()
|
||||||
|
|
||||||
|
|
||||||
|
async def _setup_entity_impl(var: MockObj, config: ConfigType, platform: str) -> None:
|
||||||
|
"""Set up generic properties of an Entity (internal implementation).
|
||||||
|
|
||||||
This function sets up the common entity properties like name, icon,
|
This function sets up the common entity properties like name, icon,
|
||||||
entity category, etc.
|
entity category, etc.
|
||||||
@@ -92,12 +304,15 @@ async def setup_entity(var: MockObj, config: ConfigType, platform: str) -> None:
|
|||||||
add(var.set_disabled_by_default(True))
|
add(var.set_disabled_by_default(True))
|
||||||
if CONF_INTERNAL in config:
|
if CONF_INTERNAL in config:
|
||||||
add(var.set_internal(config[CONF_INTERNAL]))
|
add(var.set_internal(config[CONF_INTERNAL]))
|
||||||
|
icon_idx = 0
|
||||||
if CONF_ICON in config:
|
if CONF_ICON in config:
|
||||||
# Add USE_ENTITY_ICON define when icons are used
|
# Add USE_ENTITY_ICON define when icons are used
|
||||||
cg.add_define("USE_ENTITY_ICON")
|
cg.add_define("USE_ENTITY_ICON")
|
||||||
add(var.set_icon(config[CONF_ICON]))
|
icon_idx = register_icon(config[CONF_ICON])
|
||||||
if CONF_ENTITY_CATEGORY in config:
|
if CONF_ENTITY_CATEGORY in config:
|
||||||
add(var.set_entity_category(config[CONF_ENTITY_CATEGORY]))
|
add(var.set_entity_category(config[CONF_ENTITY_CATEGORY]))
|
||||||
|
# Store icon index for finalize_entity_strings
|
||||||
|
config[_KEY_ICON_IDX] = icon_idx
|
||||||
|
|
||||||
|
|
||||||
def inherit_property_from(property_to_inherit, parent_id_property, transform=None):
|
def inherit_property_from(property_to_inherit, parent_id_property, transform=None):
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ void arch_feed_wdt();
|
|||||||
uint32_t arch_get_cpu_cycle_count();
|
uint32_t arch_get_cpu_cycle_count();
|
||||||
uint32_t arch_get_cpu_freq_hz();
|
uint32_t arch_get_cpu_freq_hz();
|
||||||
uint8_t progmem_read_byte(const uint8_t *addr);
|
uint8_t progmem_read_byte(const uint8_t *addr);
|
||||||
|
const char *progmem_read_ptr(const char *const *addr);
|
||||||
uint16_t progmem_read_uint16(const uint16_t *addr);
|
uint16_t progmem_read_uint16(const uint16_t *addr);
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ def clang_options(idedata):
|
|||||||
"-Dpgm_read_byte_near(s)=(*(const uint8_t *)(s))",
|
"-Dpgm_read_byte_near(s)=(*(const uint8_t *)(s))",
|
||||||
"-Dpgm_read_word(s)=(*(const uint16_t *)(s))",
|
"-Dpgm_read_word(s)=(*(const uint16_t *)(s))",
|
||||||
"-Dpgm_read_dword(s)=(*(const uint32_t *)(s))",
|
"-Dpgm_read_dword(s)=(*(const uint32_t *)(s))",
|
||||||
|
"-Dpgm_read_ptr(s)=(*(const void *const *)(s))",
|
||||||
"-DPROGMEM=",
|
"-DPROGMEM=",
|
||||||
"-DPGM_P=const char *",
|
"-DPGM_P=const char *",
|
||||||
"-DPSTR(s)=(s)",
|
"-DPSTR(s)=(s)",
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ def test_sensor_device_class_set(generate_main):
|
|||||||
main_cpp = generate_main("tests/component_tests/sensor/test_sensor.yaml")
|
main_cpp = generate_main("tests/component_tests/sensor/test_sensor.yaml")
|
||||||
|
|
||||||
# Then
|
# Then
|
||||||
assert 's_1->set_device_class("voltage");' in main_cpp
|
assert "s_1->set_entity_strings(" in main_cpp
|
||||||
|
|||||||
@@ -54,5 +54,5 @@ def test_text_sensor_device_class_set(generate_main):
|
|||||||
main_cpp = generate_main("tests/component_tests/text_sensor/test_text_sensor.yaml")
|
main_cpp = generate_main("tests/component_tests/text_sensor/test_text_sensor.yaml")
|
||||||
|
|
||||||
# Then
|
# Then
|
||||||
assert 'ts_2->set_device_class("timestamp");' in main_cpp
|
assert "ts_2->set_entity_strings(" in main_cpp
|
||||||
assert 'ts_3->set_device_class("date");' in main_cpp
|
assert "ts_3->set_entity_strings(" in main_cpp
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user