Files
devmel_hass_airsend/custom_components/airsend/__init__.py
T
38decibel 7009d6ee89 feat: complete migration to config flow and bugfixes (@38decibel)
* feat: migrate to config flow with device registry and brand icons

- Migrate all platforms to async_setup_entry (config flow)
- Each device now appears as a real HA device in the device registry
- Add device_info to all entities (cover, switch, button, sensor, binary_sensor)
- Add config_flow.py with airsend.yaml + !secret resolution
- Add strings.json for UI labels
- Fix unique_id bug (was splitting each character of the name)
- Split binary_sensor.py from sensor.py
- Bump version to 4.0

* Add icon for local brand

* Revise installation steps for AirSend integration

* Correction du crash websocket

raise Exception remplacé par return False

* Update README with AirSend configuration steps

Added instructions for configuring AirSend in Home Assistant.

* Update README with clearer instructions for YAML

Clarified instructions for editing the airsend.yaml file.

* Add files via upload

* Add English translation for AirSend configuration

* Add fr translations

* Update screenshot following latest version

* Fixe unavailable and new type light

Corrections de bugs
500 avec wait: true non fatal

Nouveau type
light.py — lumière dimmable (type 4100)
2026-03-31 18:02:14 +02:00

109 lines
3.5 KiB
Python

"""The AirSend component."""
import os
import logging
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.typing import ConfigType
from homeassistant.const import CONF_INTERNAL_URL
from homeassistant.components.hassio import get_addons_info
DOMAIN = "airsend"
AS_PLATFORMS = ["cover", "switch", "button", "light", "sensor", "binary_sensor"]
_LOGGER = logging.getLogger(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Legacy YAML setup — no longer used for device loading."""
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up AirSend from a config entry."""
from .coordinator import AirSendCoordinator
from .device import Device
hass.data.setdefault(DOMAIN, {})
internal_url = entry.data.get(CONF_INTERNAL_URL, "")
devices_config = entry.data.get("devices", {})
coordinators = {}
for name, options in devices_config.items():
device = Device(name, options, internal_url)
coordinator = AirSendCoordinator(hass, device)
coordinators[name] = coordinator
hass.data[DOMAIN][entry.entry_id] = {
"entry": entry.data,
"coordinators": coordinators,
}
await hass.config_entries.async_forward_entry_setups(entry, AS_PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, AS_PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
def load_airsend_yaml(hass: HomeAssistant) -> dict:
"""Load airsend.yaml resolving !secret tags via secrets.yaml."""
import yaml as _yaml
config_dir = hass.config.config_dir
path = os.path.join(config_dir, "airsend.yaml")
if not os.path.exists(path):
_LOGGER.error("airsend.yaml not found at %s", path)
return {}
secrets = {}
secrets_path = os.path.join(config_dir, "secrets.yaml")
if os.path.exists(secrets_path):
try:
with open(secrets_path, "r", encoding="utf-8") as f:
secrets = _yaml.safe_load(f) or {}
except Exception as e:
_LOGGER.warning("Could not load secrets.yaml: %s", e)
def secret_constructor(loader, node):
key = loader.construct_scalar(node)
value = secrets.get(key)
if value is None:
_LOGGER.warning("Secret '%s' not found in secrets.yaml", key)
return ""
return value
loader_class = type("SecretLoader", (_yaml.SafeLoader,), {})
loader_class.add_constructor("!secret", secret_constructor)
try:
with open(path, "r", encoding="utf-8") as f:
data = _yaml.load(f, Loader=loader_class) # noqa: S506
_LOGGER.debug("airsend.yaml loaded with %d keys", len(data) if data else 0)
return data or {}
except Exception as e:
_LOGGER.error("Failed to load airsend.yaml: %s", e)
return {}
async def get_internal_url(hass: HomeAssistant) -> str:
"""Auto-detect internal URL of the AirSend addon."""
try:
addons_info = get_addons_info(hass)
for name, options in addons_info.items():
if "_airsend" in name:
ip = options.get("ip_address")
if ip:
return "http://" + str(ip) + ":33863/"
except Exception:
pass
return ""