[core] Improve CORE.data documentation with dataclass pattern (#12170)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -402,35 +402,45 @@ This document provides essential context for AI models interacting with this pro
|
||||
_use_feature = True
|
||||
```
|
||||
|
||||
**Good Pattern (CORE.data with Helpers):**
|
||||
**Bad Pattern (Flat Keys):**
|
||||
```python
|
||||
# Don't do this - keys should be namespaced under component domain
|
||||
MY_FEATURE_KEY = "my_component_feature"
|
||||
CORE.data[MY_FEATURE_KEY] = True
|
||||
```
|
||||
|
||||
**Good Pattern (dataclass):**
|
||||
```python
|
||||
from dataclasses import dataclass, field
|
||||
from esphome.core import CORE
|
||||
|
||||
# Keys for CORE.data storage
|
||||
COMPONENT_STATE_KEY = "my_component_state"
|
||||
USE_FEATURE_KEY = "my_component_use_feature"
|
||||
DOMAIN = "my_component"
|
||||
|
||||
def _get_component_state() -> list:
|
||||
"""Get component state from CORE.data."""
|
||||
return CORE.data.setdefault(COMPONENT_STATE_KEY, [])
|
||||
@dataclass
|
||||
class MyComponentData:
|
||||
feature_enabled: bool = False
|
||||
item_count: int = 0
|
||||
items: list[str] = field(default_factory=list)
|
||||
|
||||
def _get_use_feature() -> bool | None:
|
||||
"""Get feature flag from CORE.data."""
|
||||
return CORE.data.get(USE_FEATURE_KEY)
|
||||
def _get_data() -> MyComponentData:
|
||||
if DOMAIN not in CORE.data:
|
||||
CORE.data[DOMAIN] = MyComponentData()
|
||||
return CORE.data[DOMAIN]
|
||||
|
||||
def _set_use_feature(value: bool) -> None:
|
||||
"""Set feature flag in CORE.data."""
|
||||
CORE.data[USE_FEATURE_KEY] = value
|
||||
def request_feature() -> None:
|
||||
_get_data().feature_enabled = True
|
||||
|
||||
def enable_feature():
|
||||
_set_use_feature(True)
|
||||
def add_item(item: str) -> None:
|
||||
_get_data().items.append(item)
|
||||
```
|
||||
|
||||
If you need a real-world example, search for components that use `@dataclass` with `CORE.data` in the codebase. Note: Some components may use `TypedDict` for dictionary-based storage; both patterns are acceptable depending on your needs.
|
||||
|
||||
**Why this matters:**
|
||||
- Module-level globals persist between compilation runs if the dashboard doesn't fork/exec
|
||||
- `CORE.data` automatically clears between runs
|
||||
- Typed helper functions provide better IDE support and maintainability
|
||||
- Encapsulation makes state management explicit and testable
|
||||
- Namespacing under `DOMAIN` prevents key collisions between components
|
||||
- `@dataclass` provides type safety and cleaner attribute access
|
||||
|
||||
* **Security:** Be mindful of security when making changes to the API, web server, or any other network-related code. Do not hardcode secrets or keys.
|
||||
|
||||
|
||||
@@ -541,8 +541,22 @@ class EsphomeCore:
|
||||
self.friendly_name: str | None = None
|
||||
# The area / zone of the node
|
||||
self.area: str | None = None
|
||||
# Additional data components can store temporary data in
|
||||
# The first key to this dict should always be the integration name
|
||||
# Additional data components can store temporary data in.
|
||||
# This dict is cleared between compilation runs.
|
||||
#
|
||||
# Usage pattern (use @dataclass for type safety):
|
||||
# DOMAIN = "my_component"
|
||||
#
|
||||
# @dataclass
|
||||
# class MyComponentData:
|
||||
# feature_enabled: bool = False
|
||||
#
|
||||
# def _get_data() -> MyComponentData:
|
||||
# if DOMAIN not in CORE.data:
|
||||
# CORE.data[DOMAIN] = MyComponentData()
|
||||
# return CORE.data[DOMAIN]
|
||||
#
|
||||
# The first key should always be the component domain name (DOMAIN constant).
|
||||
self.data = {}
|
||||
# The relative path to the configuration YAML
|
||||
self.config_path: Path | None = None
|
||||
|
||||
Reference in New Issue
Block a user