fix: standardise sensor module structure and docs

- Use multi-line ensure_python_modules list format in ens210.py,
  matching the established pattern from ina219.py
- Fix auto_install_packages indentation in ina219.py docstring
- Remove smbus2 from pyproject.toml core dependencies; sensor packages
  are handled at runtime via ensure_python_modules/auto_install_packages
- Update docs/adding_sensors.md template and guidance to match

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Joshua Mesilane
2026-05-13 16:33:37 +10:00
parent 3f7b6d5cdc
commit 7865e9cb4b
4 changed files with 12 additions and 5 deletions
+6 -2
View File
@@ -58,7 +58,11 @@ class MySensor(SensorBase):
self.some_option = self.settings.get("some_option", "default")
self.available = False
if not self.ensure_python_modules([("import_name", "pip-package-name")]):
if not self.ensure_python_modules(
[
("import_name", "pip-package-name"),
]
):
return # logs a warning; sensor will report unavailable
try:
@@ -87,7 +91,7 @@ Key rules:
- **`sensor_type`** class attribute must match the string passed to `@SensorRegistry.register`.
- **`self.settings`** is the `settings:` block from the sensor's config entry (a plain dict).
- **`ensure_python_modules`** handles missing dependencies gracefully. Pass a list of `(import_name, pip_package)` tuples. Returns `False` and logs a warning if any are missing and `auto_install_packages` is `false`; installs them if `true`.
- **`ensure_python_modules`** handles missing dependencies gracefully. Pass a multi-line list of `(import_name, pip_package)` tuples. Returns `False` and logs a warning if any are missing and `auto_install_packages` is `false`; installs them via pip if `true`. Sensor-specific packages belong here — do **not** add them to `pyproject.toml`.
- **`_read`** must return a flat `dict[str, Any]`. The base class wraps it in a standard envelope (`name`, `type`, `ok`, `timestamp`, `data`, optional `error`).
- **`_read`** must raise `RuntimeError` on failure — the base class catches it, marks `ok=False`, and logs it without crashing the polling loop.
- All hardware initialisation belongs in `__init__`, not in `_read`. Keep `_read` fast.
-1
View File
@@ -39,7 +39,6 @@ dependencies = [
"pyserial>=3.5",
"pyjwt>=2.8.0",
"ws4py>=0.6.0",
"smbus2>=0.4.0",
]
+5 -1
View File
@@ -42,7 +42,11 @@ class ENS210Sensor(SensorBase):
self._poll_attempts = max(1, int(float(self.settings.get("read_timeout_seconds", 1.0)) / self._poll_interval))
self.available = False
if not self.ensure_python_modules([("smbus2", "smbus2")]):
if not self.ensure_python_modules(
[
("smbus2", "smbus2"),
]
):
return
try:
+1 -1
View File
@@ -7,7 +7,7 @@ Config example:
- type: ina219
name: "power_monitor"
enabled: true
auto_install_packages: false
auto_install_packages: false
settings:
i2c_address: 0x40 # Default INA219 I2C address
max_expected_amps: 2.0