|  | 
|  | 1 | +"""The iNELS integration.""" | 
|  | 2 | + | 
|  | 3 | +from __future__ import annotations | 
|  | 4 | + | 
|  | 5 | +from collections.abc import Callable | 
|  | 6 | +from dataclasses import dataclass | 
|  | 7 | +from typing import Any | 
|  | 8 | + | 
|  | 9 | +from inelsmqtt import InelsMqtt | 
|  | 10 | +from inelsmqtt.devices import Device | 
|  | 11 | +from inelsmqtt.discovery import InelsDiscovery | 
|  | 12 | + | 
|  | 13 | +from homeassistant.components import mqtt as ha_mqtt | 
|  | 14 | +from homeassistant.components.mqtt import ( | 
|  | 15 | +    ReceiveMessage, | 
|  | 16 | +    async_prepare_subscribe_topics, | 
|  | 17 | +    async_subscribe_topics, | 
|  | 18 | +    async_unsubscribe_topics, | 
|  | 19 | +) | 
|  | 20 | +from homeassistant.config_entries import ConfigEntry | 
|  | 21 | +from homeassistant.core import HomeAssistant, callback | 
|  | 22 | +from homeassistant.exceptions import ConfigEntryNotReady | 
|  | 23 | + | 
|  | 24 | +from .const import LOGGER, PLATFORMS | 
|  | 25 | + | 
|  | 26 | +type InelsConfigEntry = ConfigEntry[InelsData] | 
|  | 27 | + | 
|  | 28 | + | 
|  | 29 | +@dataclass | 
|  | 30 | +class InelsData: | 
|  | 31 | +    """Represents the data structure for INELS runtime data.""" | 
|  | 32 | + | 
|  | 33 | +    mqtt: InelsMqtt | 
|  | 34 | +    devices: list[Device] | 
|  | 35 | + | 
|  | 36 | + | 
|  | 37 | +async def async_setup_entry(hass: HomeAssistant, entry: InelsConfigEntry) -> bool: | 
|  | 38 | +    """Set up iNELS from a config entry.""" | 
|  | 39 | + | 
|  | 40 | +    async def mqtt_publish(topic: str, payload: str, qos: int, retain: bool) -> None: | 
|  | 41 | +        """Publish an MQTT message using the Home Assistant MQTT client.""" | 
|  | 42 | +        await ha_mqtt.async_publish(hass, topic, payload, qos, retain) | 
|  | 43 | + | 
|  | 44 | +    async def mqtt_subscribe( | 
|  | 45 | +        sub_state: dict[str, Any] | None, | 
|  | 46 | +        topic: str, | 
|  | 47 | +        callback_func: Callable[[str, str], None], | 
|  | 48 | +    ) -> dict[str, Any]: | 
|  | 49 | +        """Subscribe to MQTT topics using the Home Assistant MQTT client.""" | 
|  | 50 | + | 
|  | 51 | +        @callback | 
|  | 52 | +        def mqtt_message_received(msg: ReceiveMessage) -> None: | 
|  | 53 | +            """Handle iNELS mqtt messages.""" | 
|  | 54 | +            # Payload is always str at runtime since we don't set encoding=None | 
|  | 55 | +            # HA uses UTF-8 by default | 
|  | 56 | +            callback_func(msg.topic, msg.payload)  # type: ignore[arg-type] | 
|  | 57 | + | 
|  | 58 | +        topics = { | 
|  | 59 | +            "inels_subscribe_topic": { | 
|  | 60 | +                "topic": topic, | 
|  | 61 | +                "msg_callback": mqtt_message_received, | 
|  | 62 | +            } | 
|  | 63 | +        } | 
|  | 64 | + | 
|  | 65 | +        sub_state = async_prepare_subscribe_topics(hass, sub_state, topics) | 
|  | 66 | +        await async_subscribe_topics(hass, sub_state) | 
|  | 67 | +        return sub_state | 
|  | 68 | + | 
|  | 69 | +    async def mqtt_unsubscribe(sub_state: dict[str, Any]) -> None: | 
|  | 70 | +        async_unsubscribe_topics(hass, sub_state) | 
|  | 71 | + | 
|  | 72 | +    if not await ha_mqtt.async_wait_for_mqtt_client(hass): | 
|  | 73 | +        LOGGER.error("MQTT integration not available") | 
|  | 74 | +        raise ConfigEntryNotReady("MQTT integration not available") | 
|  | 75 | + | 
|  | 76 | +    inels_mqtt = InelsMqtt(mqtt_publish, mqtt_subscribe, mqtt_unsubscribe) | 
|  | 77 | +    devices: list[Device] = await InelsDiscovery(inels_mqtt).start() | 
|  | 78 | + | 
|  | 79 | +    # If no devices are discovered, continue with the setup | 
|  | 80 | +    if not devices: | 
|  | 81 | +        LOGGER.info("No devices discovered") | 
|  | 82 | + | 
|  | 83 | +    entry.runtime_data = InelsData(mqtt=inels_mqtt, devices=devices) | 
|  | 84 | + | 
|  | 85 | +    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) | 
|  | 86 | + | 
|  | 87 | +    return True | 
|  | 88 | + | 
|  | 89 | + | 
|  | 90 | +async def async_unload_entry(hass: HomeAssistant, entry: InelsConfigEntry) -> bool: | 
|  | 91 | +    """Unload a config entry.""" | 
|  | 92 | +    await entry.runtime_data.mqtt.unsubscribe_topics() | 
|  | 93 | +    entry.runtime_data.mqtt.unsubscribe_listeners() | 
|  | 94 | + | 
|  | 95 | +    return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) | 
0 commit comments