EntityBase: Move ObjectId to Flash (#4569)

* Move EntityBase Object Id from memory to flash.

* Sprinkler use common `setup_entity` method.

* Remove `EntityBase` from Sprinkler.

* Support for entity names set to None

* change so gh PR picks up commit.

---------

Co-authored-by: Your Name <you@example.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Fabian 2023-03-28 11:00:34 +02:00 committed by GitHub
parent 922344811f
commit 3ac7bf3761
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 82 additions and 8 deletions

View File

@ -16,7 +16,6 @@ void EntityBase::set_name(const char *name) {
} else { } else {
this->has_own_name_ = true; this->has_own_name_ = true;
} }
this->calc_object_id_();
} }
// Entity Internal // Entity Internal
@ -41,13 +40,37 @@ EntityCategory EntityBase::get_entity_category() const { return this->entity_cat
void EntityBase::set_entity_category(EntityCategory entity_category) { this->entity_category_ = entity_category; } void EntityBase::set_entity_category(EntityCategory entity_category) { this->entity_category_ = entity_category; }
// Entity Object ID // Entity Object ID
const std::string &EntityBase::get_object_id() { return this->object_id_; } std::string EntityBase::get_object_id() const {
// Check if `App.get_friendly_name()` is constant or dynamic.
if (!this->has_own_name_ && App.is_name_add_mac_suffix_enabled()) {
// `App.get_friendly_name()` is dynamic.
return str_sanitize(str_snake_case(App.get_friendly_name()));
} else {
// `App.get_friendly_name()` is constant.
if (this->object_id_c_str_ == nullptr) {
return "";
}
return this->object_id_c_str_;
}
}
void EntityBase::set_object_id(const char *object_id) {
this->object_id_c_str_ = object_id;
this->calc_object_id_();
}
// Calculate Object ID Hash from Entity Name // Calculate Object ID Hash from Entity Name
void EntityBase::calc_object_id_() { void EntityBase::calc_object_id_() {
this->object_id_ = str_sanitize(str_snake_case(this->name_)); // Check if `App.get_friendly_name()` is constant or dynamic.
// FNV-1 hash if (!this->has_own_name_ && App.is_name_add_mac_suffix_enabled()) {
this->object_id_hash_ = fnv1_hash(this->object_id_); // `App.get_friendly_name()` is dynamic.
const auto object_id = str_sanitize(str_snake_case(App.get_friendly_name()));
// FNV-1 hash
this->object_id_hash_ = fnv1_hash(object_id);
} else {
// `App.get_friendly_name()` is constant.
// FNV-1 hash
this->object_id_hash_ = fnv1_hash(this->object_id_c_str_);
}
} }
uint32_t EntityBase::get_object_id_hash() { return this->object_id_hash_; } uint32_t EntityBase::get_object_id_hash() { return this->object_id_hash_; }

View File

@ -22,8 +22,9 @@ class EntityBase {
// Get whether this Entity has its own name or it should use the device friendly_name. // Get whether this Entity has its own name or it should use the device friendly_name.
bool has_own_name() const { return this->has_own_name_; } bool has_own_name() const { return this->has_own_name_; }
// Get the sanitized name of this Entity as an ID. Caching it internally. // Get the sanitized name of this Entity as an ID.
const std::string &get_object_id(); std::string get_object_id() const;
void set_object_id(const char *object_id);
// Get the unique Object ID of this Entity // Get the unique Object ID of this Entity
uint32_t get_object_id_hash(); uint32_t get_object_id_hash();
@ -54,7 +55,7 @@ class EntityBase {
StringRef name_; StringRef name_;
bool has_own_name_{false}; bool has_own_name_{false};
std::string object_id_; const char *object_id_c_str_{nullptr};
const char *icon_c_str_{nullptr}; const char *icon_c_str_{nullptr};
uint32_t object_id_hash_; uint32_t object_id_hash_;
bool internal_{false}; bool internal_{false};

View File

@ -20,6 +20,7 @@ from esphome.types import ConfigType, ConfigFragmentType
from esphome.cpp_generator import add, get_variable from esphome.cpp_generator import add, get_variable
from esphome.cpp_types import App from esphome.cpp_types import App
from esphome.util import Registry, RegistryEntry from esphome.util import Registry, RegistryEntry
from esphome.helpers import snake_case, sanitize
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -101,6 +102,10 @@ async def register_parented(var, value):
async def setup_entity(var, config): async def setup_entity(var, config):
"""Set up generic properties of an Entity""" """Set up generic properties of an Entity"""
add(var.set_name(config[CONF_NAME])) add(var.set_name(config[CONF_NAME]))
if not config[CONF_NAME]:
add(var.set_object_id(sanitize(snake_case(CORE.friendly_name))))
else:
add(var.set_object_id(sanitize(snake_case(config[CONF_NAME]))))
add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config: if CONF_INTERNAL in config:
add(var.set_internal(config[CONF_INTERNAL])) add(var.set_internal(config[CONF_INTERNAL]))

View File

@ -7,6 +7,7 @@ from pathlib import Path
from typing import Union from typing import Union
import tempfile import tempfile
from urllib.parse import urlparse from urllib.parse import urlparse
import re
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -334,3 +335,13 @@ def add_class_to_obj(value, cls):
if type(value) is type_: # pylint: disable=unidiomatic-typecheck if type(value) is type_: # pylint: disable=unidiomatic-typecheck
return add_class_to_obj(func(value), cls) return add_class_to_obj(func(value), cls)
raise raise
def snake_case(value):
"""Same behaviour as `helpers.cpp` method `str_snake_case`."""
return value.replace(" ", "_").lower()
def sanitize(value):
"""Same behaviour as `helpers.cpp` method `str_sanitize`."""
return re.sub("[^-_0-9a-zA-Z]", r"", value)

View File

@ -229,3 +229,37 @@ def test_file_compare(fixture_path, file1, file2, expected):
actual = helpers.file_compare(path1, path2) actual = helpers.file_compare(path1, path2)
assert actual == expected assert actual == expected
@pytest.mark.parametrize(
"text, expected",
(
("foo", "foo"),
("foo bar", "foo_bar"),
("foo Bar", "foo_bar"),
("foo BAR", "foo_bar"),
("foo.bar", "foo.bar"),
("fooBAR", "foobar"),
("Foo-bar_EEK", "foo-bar_eek"),
(" foo", "__foo"),
),
)
def test_snake_case(text, expected):
actual = helpers.snake_case(text)
assert actual == expected
@pytest.mark.parametrize(
"text, expected",
(
("foo_bar", "foo_bar"),
('!"§$%&/()=?foo_bar', "foo_bar"),
('foo_!"§$%&/()=?bar', "foo_bar"),
('foo_bar!"§$%&/()=?', "foo_bar"),
),
)
def test_sanitize(text, expected):
actual = helpers.sanitize(text)
assert actual == expected