HomeAssistant GeraeteApplikationen

Aus TippvomTibb
Zur Navigation springen Zur Suche springen

Motivation

Ein Fehler in der FoxESS - Modbus (FOXESS) Integration in HomeAssistant (leider habe ich vergessen einen Screenshot zu machen) gab den Hinweis auf fehlerhafte Register.

Da ich auf der Seite FOX ESS Photovoltaik-Anlage eine komplette Registerliste meines H3 Pro erstellt habe, wollte ich diese mit der FoxESS - Modbus (FOXESS) Integration abgleichen, um dem Fehler auf die Schliche zu kommen.

Im Verzeichnis homeassistant/custom_components/foxess_modbus gibt es aber leider keine schoene Liste mit allen Registern als Referenz zum Abgleich.

So bin ich dadurch gezwungen worden mich mal mit der GeraeteIntegrationsProgrammierung zu beschaeftigen.

Orientierung

Minimalster Aufbau der GeraeteIntegrationsDateien:

config/
└── custom_components/
    └── mydevice/
        ├── __init__.py             Initialisierung
        ├── manifest.json           Sie beschreibt die Integration.
        ├── const.py                Hier landen sämtliche Konstanten. Dadurch vermeidet man "Magic Strings".
        ├── config_flow.py          Heute werden nahezu alle Integrationen über den Einrichtungsdialog konfiguriert.
        ├── coordinator.py          DataUpdateCoordinator Dadurch wird verhindert, dass jeder Sensor einzeln das Gerät anspricht.
        ├── sensor.py               Ein Sensor liest nur Daten aus dem Coordinator.
        ├── switch.py
        ├── button.py
        ├── number.py               Haeufig wird hier Zahl als Uebersetzung verwendet. Besser ist allerdings Wert.
        ...
        ├── diagnostics.py
        ├── services.yaml
        ├── strings.json
        └── translations/
            ├── en.json
            └── de.json

Warum client.py fehlt weisz ich noch nicht.

Je nach Funktion gibt es unterschiedliche Klassen:

Typ	Klasse
Sensor	SensorEntity
Binärsensor	BinarySensorEntity
Schalter	SwitchEntity
Button	ButtonEntity
Auswahl	SelectEntity
Zahlen	NumberEntity
Texte	TextEntity
Licht	LightEntity
Lüfter	FanEntity
Klima	ClimateEntity
Update	UpdateEntity
Vorhang CoverEntity
(Tür-)Schließer LockEntity
Staubsauger VacuumEntity
MediaPlayer Media PlayerEntity
Kamera CameraEntity
Alarm Control PanelEntity
Device Tracker Entity
Update Entity
Weather Entity
Text Entity
Siren Entity
Valve Entity

Erlaeuterungen:
*Sensor: Measures numerical states like temperature, humidity, or power consumption.
*Binary Sensor: Monitors two-state conditions like on/off, open/closed, or detected/clear (e.g., motion or door sensors).
*Switch: Toggles devices completely on or off.Button: Sends momentary trigger actions to a device without holding a state (e.g., rebooting a device).
*Number: Allows you to input or slide custom values (e.g., volume level, target motor position).
*Select: Provides a dropdown menu of specific options (e.g., HVAC modes, preset colors).Light: Controls brightness, color, and temperature for illumination devices.
*Fan: Controls speed, oscillation, and direction.
*Cover: Manages doors, garage doors, blinds, and window shades.Lock: Monitors and controls door locks (locked/unlocked).
*Climate: Regulates thermostats and HVAC systems.Vacuum: Operates robotic cleaners (start, dock, pause).
*Media Player: Controls TVs, speakers, and media streams (play, pause, volume).
*Camera: Streams live video feeds or takes snapshots from smart cameras.
*Alarm Control Panel: Manages arming, disarming, and triggering states for security systems.
*Device Tracker: Monitors the location of people, cars, or connected network devices.
*Update: Tracks and manages pending firmware or software updates for your devices.

Zu jeder der Dateien gibt es unter https://github.com/home-assistant/core/tree/dev/homeassistant/components/demo eine Demo.

Alle Entitaeten sollten von einer gemeinsamen Basisklasse (entity.py) erben und ihre Daten ausschlieszlich ueber einen DataUpdateCoordinator beziehen. Das entspricht der aktuellen Home-Assistant-Architektur und erleichtert Wartung und Erweiterung.

Weitere hilfreiche Informationen erhaelt man unter:

NB: Ein anderes Thema ist noch die API. Das wird wahrscheinlich ne eigene Seite werden.

Die Home Assistant API-Integration stellt eine RESTful-API bereit und ermoeglicht die Interaktion mit einer Home Assistant-Instanz, die im Headless-Modus laeuft. Diese Integration basiert auf der HTTP-Integration. 

Sobald man in den Quellcode von Komponenten (custom_components; GeraeteIntegrationen) eintaucht, benoetigt man zum Verstaendnis die Developer Docs https://developers.home-assistant.io/docs/development_index

Start

https://developers.home-assistant.io/docs/creating_component_index


__init__.py

"""Lab Device integration."""
from __future__ import annotations    # siehe unten

from homeassistant.config_entries import ConfigEntry  # siehe unten
from homeassistant.core import HomeAssistant          # siehe unten

from .api import LabDeviceApi                  # api.py   (. vor Libraryname bedeutet im gleichen Verzeichnis)
from .const import DOMAIN, PLATFORMS           # const.py
from .coordinator import LabDeviceCoordinator  # coordinator.py 


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # HomeAssistant, ConfigEntry Datentyp Rueckgabewert true/false
    """Set up Lab Device from a config entry."""
    api = LabDeviceApi(name=entry.title)               # in api.py definiert
    coordinator = LabDeviceCoordinator(hass, api)      # in coordinator.py definiert
    await coordinator.async_config_entry_first_refresh()  # Die Methode `async_config_entry_first_refresh()` ist eine wichtige Funktion im DataUpdateCoordinator von Home Assistant. Sie ruft die initialen Daten für eine Integration ab, bevor deren Entitaeten eingerichtet werden.

    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator # https://developers.home-assistant.io/blog/2024/05/01/improved-hass-data-typing?_highlight=hass.data.setdefault
    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) # https://developers.home-assistant.io/blog/2024/06/12/async_forward_entry_setups/
    return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # HomeAssistant, ConfigEntry Datentyp Rueckgabewert true/false
    """Unload Lab Device."""
    unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)  # https://developers.home-assistant.io/docs/core/integration-quality-scale/rules/config-entry-unloading/
    if unload_ok:
        hass.data[DOMAIN].pop(entry.entry_id)
    return unload_ok


__future__

Ist kein Bestandteil von Home Assistant selbst, sondern ein spezielles Python-Modul. Es ermoeglicht, Sprachfunktionen einer neueren Python-Version bereits in aelteren Python-Versionen zu verwenden.

Die dunder (Double Undescore) sind für Python reserviert (Magic Methods).

Mehr zum Thema.HomeAssistant Programmierung (dunder) future (dunder)


RegisterEinbindung

FoxAPI (Modbus TCP / RS485)
            │
            ▼
FoxCoordinator
            │
            ├── sensor.py
            │      Leistung
            │      Spannung
            │      SOC
            │      Temperaturen
            │
            ├── number.py
            │      Ladeleistung
            │      Entladeleistung
            │
            ├── switch.py
            │      Backup-Modus
            │      Zwangsladung
            │
            ├── select.py
            │      Betriebsmodus
            │
            └── button.py
                   Register neu laden

sensor.py

Ich fange mal mit den Sensoren an.

"""Sensor platform for foxess_modbus."""

import logging

from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .common.types import HassData
from .const import DOMAIN
from .entities.connection_status_sensor import ConnectionStatusSensor
from .inverter_profiles import create_entities

_LOGGER = logging.getLogger(__package__)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_devices: AddEntitiesCallback) -> None:
    """Setup sensor platform."""

    hass_data: HassData = hass.data[DOMAIN]
    controllers = hass_data[entry.entry_id]["controllers"]

    for controller in controllers:
        async_add_devices([ConnectionStatusSensor(controller)])
        # We have to add sensors which don't depend on other sensors, before we add the sensors which *do* depend on
        # other sensors (like the integration sensors), otherwise HA crashes when trying to create the IntegrationSensor
        # because it can't find the sensor it depends on. See https://github.com/nathanmarlor/foxess_modbus/issues/886
        async_add_devices(create_entities(SensorEntity, controller, filter_depends_on_other_entites=False))
        async_add_devices(create_entities(SensorEntity, controller, filter_depends_on_other_entites=True))