mirror of
https://github.com/home-assistant/core.git
synced 2025-08-30 18:01:31 +02:00
Add multiple NICs in govee_light_local (#128123)
This commit is contained in:
@@ -9,6 +9,7 @@ import logging
|
|||||||
|
|
||||||
from govee_local_api.controller import LISTENING_PORT
|
from govee_local_api.controller import LISTENING_PORT
|
||||||
|
|
||||||
|
from homeassistant.components import network
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
@@ -23,12 +24,24 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: GoveeLocalConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: GoveeLocalConfigEntry) -> bool:
|
||||||
"""Set up Govee light local from a config entry."""
|
"""Set up Govee light local from a config entry."""
|
||||||
coordinator = GoveeLocalApiCoordinator(hass, entry)
|
|
||||||
|
# Get source IPs for all enabled adapters
|
||||||
|
source_ips = await network.async_get_enabled_source_ips(hass)
|
||||||
|
_LOGGER.debug("Enabled source IPs: %s", source_ips)
|
||||||
|
|
||||||
|
coordinator: GoveeLocalApiCoordinator = GoveeLocalApiCoordinator(
|
||||||
|
hass=hass, config_entry=entry, source_ips=source_ips
|
||||||
|
)
|
||||||
|
|
||||||
async def await_cleanup():
|
async def await_cleanup():
|
||||||
cleanup_complete: asyncio.Event = coordinator.cleanup()
|
cleanup_complete_events: [asyncio.Event] = coordinator.cleanup()
|
||||||
with suppress(TimeoutError):
|
with suppress(TimeoutError):
|
||||||
await asyncio.wait_for(cleanup_complete.wait(), 1)
|
await asyncio.gather(
|
||||||
|
*[
|
||||||
|
asyncio.wait_for(cleanup_complete_event.wait(), 1)
|
||||||
|
for cleanup_complete_event in cleanup_complete_events
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
entry.async_on_unload(await_cleanup)
|
entry.async_on_unload(await_cleanup)
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
from ipaddress import IPv4Address, IPv6Address
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from govee_local_api import GoveeController
|
from govee_local_api import GoveeController
|
||||||
@@ -23,15 +24,13 @@ from .const import (
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def _async_has_devices(hass: HomeAssistant) -> bool:
|
async def _async_discover(
|
||||||
"""Return if there are devices that can be discovered."""
|
hass: HomeAssistant, adapter_ip: IPv4Address | IPv6Address
|
||||||
|
) -> bool:
|
||||||
adapter = await network.async_get_source_ip(hass, network.PUBLIC_TARGET_IP)
|
|
||||||
|
|
||||||
controller: GoveeController = GoveeController(
|
controller: GoveeController = GoveeController(
|
||||||
loop=hass.loop,
|
loop=hass.loop,
|
||||||
logger=_LOGGER,
|
logger=_LOGGER,
|
||||||
listening_address=adapter,
|
listening_address=str(adapter_ip),
|
||||||
broadcast_address=CONF_MULTICAST_ADDRESS_DEFAULT,
|
broadcast_address=CONF_MULTICAST_ADDRESS_DEFAULT,
|
||||||
broadcast_port=CONF_TARGET_PORT_DEFAULT,
|
broadcast_port=CONF_TARGET_PORT_DEFAULT,
|
||||||
listening_port=CONF_LISTENING_PORT_DEFAULT,
|
listening_port=CONF_LISTENING_PORT_DEFAULT,
|
||||||
@@ -41,9 +40,10 @@ async def _async_has_devices(hass: HomeAssistant) -> bool:
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
_LOGGER.debug("Starting discovery with IP %s", adapter_ip)
|
||||||
await controller.start()
|
await controller.start()
|
||||||
except OSError as ex:
|
except OSError as ex:
|
||||||
_LOGGER.error("Start failed, errno: %d", ex.errno)
|
_LOGGER.error("Start failed on IP %s, errno: %d", adapter_ip, ex.errno)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -51,16 +51,34 @@ async def _async_has_devices(hass: HomeAssistant) -> bool:
|
|||||||
while not controller.devices:
|
while not controller.devices:
|
||||||
await asyncio.sleep(delay=1)
|
await asyncio.sleep(delay=1)
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
_LOGGER.debug("No devices found")
|
_LOGGER.debug("No devices found with IP %s", adapter_ip)
|
||||||
|
|
||||||
devices_count = len(controller.devices)
|
devices_count = len(controller.devices)
|
||||||
cleanup_complete: asyncio.Event = controller.cleanup()
|
cleanup_complete_events: list[asyncio.Event] = []
|
||||||
with suppress(TimeoutError):
|
with suppress(TimeoutError):
|
||||||
await asyncio.wait_for(cleanup_complete.wait(), 1)
|
await asyncio.gather(
|
||||||
|
*[
|
||||||
|
asyncio.wait_for(cleanup_complete_event.wait(), 1)
|
||||||
|
for cleanup_complete_event in cleanup_complete_events
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return devices_count > 0
|
return devices_count > 0
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_has_devices(hass: HomeAssistant) -> bool:
|
||||||
|
"""Return if there are devices that can be discovered."""
|
||||||
|
|
||||||
|
# Get source IPs for all enabled adapters
|
||||||
|
source_ips = await network.async_get_enabled_source_ips(hass)
|
||||||
|
_LOGGER.debug("Enabled source IPs: %s", source_ips)
|
||||||
|
|
||||||
|
# Run discovery on every IPv4 address and gather results
|
||||||
|
results = await asyncio.gather(*[_async_discover(hass, ip) for ip in source_ips])
|
||||||
|
|
||||||
|
return any(results)
|
||||||
|
|
||||||
|
|
||||||
config_entry_flow.register_discovery_flow(
|
config_entry_flow.register_discovery_flow(
|
||||||
DOMAIN, "Govee light local", _async_has_devices
|
DOMAIN, "Govee light local", _async_has_devices
|
||||||
)
|
)
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
from ipaddress import IPv4Address, IPv6Address
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from govee_local_api import GoveeController, GoveeDevice
|
from govee_local_api import GoveeController, GoveeDevice
|
||||||
@@ -11,7 +12,6 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_DISCOVERY_INTERVAL_DEFAULT,
|
|
||||||
CONF_LISTENING_PORT_DEFAULT,
|
CONF_LISTENING_PORT_DEFAULT,
|
||||||
CONF_MULTICAST_ADDRESS_DEFAULT,
|
CONF_MULTICAST_ADDRESS_DEFAULT,
|
||||||
CONF_TARGET_PORT_DEFAULT,
|
CONF_TARGET_PORT_DEFAULT,
|
||||||
@@ -26,10 +26,11 @@ type GoveeLocalConfigEntry = ConfigEntry[GoveeLocalApiCoordinator]
|
|||||||
class GoveeLocalApiCoordinator(DataUpdateCoordinator[list[GoveeDevice]]):
|
class GoveeLocalApiCoordinator(DataUpdateCoordinator[list[GoveeDevice]]):
|
||||||
"""Govee light local coordinator."""
|
"""Govee light local coordinator."""
|
||||||
|
|
||||||
config_entry: GoveeLocalConfigEntry
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, hass: HomeAssistant, config_entry: GoveeLocalConfigEntry
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: GoveeLocalConfigEntry,
|
||||||
|
source_ips: list[IPv4Address | IPv6Address],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize my coordinator."""
|
"""Initialize my coordinator."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
@@ -40,32 +41,40 @@ class GoveeLocalApiCoordinator(DataUpdateCoordinator[list[GoveeDevice]]):
|
|||||||
update_interval=SCAN_INTERVAL,
|
update_interval=SCAN_INTERVAL,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._controller = GoveeController(
|
self._controllers: list[GoveeController] = [
|
||||||
loop=hass.loop,
|
GoveeController(
|
||||||
logger=_LOGGER,
|
loop=hass.loop,
|
||||||
broadcast_address=CONF_MULTICAST_ADDRESS_DEFAULT,
|
logger=_LOGGER,
|
||||||
broadcast_port=CONF_TARGET_PORT_DEFAULT,
|
listening_address=str(source_ip),
|
||||||
listening_port=CONF_LISTENING_PORT_DEFAULT,
|
broadcast_address=CONF_MULTICAST_ADDRESS_DEFAULT,
|
||||||
discovery_enabled=True,
|
broadcast_port=CONF_TARGET_PORT_DEFAULT,
|
||||||
discovery_interval=CONF_DISCOVERY_INTERVAL_DEFAULT,
|
listening_port=CONF_LISTENING_PORT_DEFAULT,
|
||||||
discovered_callback=None,
|
discovery_enabled=True,
|
||||||
update_enabled=False,
|
discovery_interval=1,
|
||||||
)
|
update_enabled=False,
|
||||||
|
)
|
||||||
|
for source_ip in source_ips
|
||||||
|
]
|
||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
"""Start the Govee coordinator."""
|
"""Start the Govee coordinator."""
|
||||||
await self._controller.start()
|
|
||||||
self._controller.send_update_message()
|
for controller in self._controllers:
|
||||||
|
await controller.start()
|
||||||
|
controller.send_update_message()
|
||||||
|
|
||||||
async def set_discovery_callback(
|
async def set_discovery_callback(
|
||||||
self, callback: Callable[[GoveeDevice, bool], bool]
|
self, callback: Callable[[GoveeDevice, bool], bool]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set discovery callback for automatic Govee light discovery."""
|
"""Set discovery callback for automatic Govee light discovery."""
|
||||||
self._controller.set_device_discovered_callback(callback)
|
|
||||||
|
|
||||||
def cleanup(self) -> asyncio.Event:
|
for controller in self._controllers:
|
||||||
"""Stop and cleanup the cooridinator."""
|
controller.set_device_discovered_callback(callback)
|
||||||
return self._controller.cleanup()
|
|
||||||
|
def cleanup(self) -> list[asyncio.Event]:
|
||||||
|
"""Stop and cleanup the coordinator."""
|
||||||
|
|
||||||
|
return [controller.cleanup() for controller in self._controllers]
|
||||||
|
|
||||||
async def turn_on(self, device: GoveeDevice) -> None:
|
async def turn_on(self, device: GoveeDevice) -> None:
|
||||||
"""Turn on the light."""
|
"""Turn on the light."""
|
||||||
@@ -96,8 +105,14 @@ class GoveeLocalApiCoordinator(DataUpdateCoordinator[list[GoveeDevice]]):
|
|||||||
@property
|
@property
|
||||||
def devices(self) -> list[GoveeDevice]:
|
def devices(self) -> list[GoveeDevice]:
|
||||||
"""Return a list of discovered Govee devices."""
|
"""Return a list of discovered Govee devices."""
|
||||||
return self._controller.devices
|
|
||||||
|
devices: list[GoveeDevice] = []
|
||||||
|
for controller in self._controllers:
|
||||||
|
devices = devices + controller.devices
|
||||||
|
return devices
|
||||||
|
|
||||||
async def _async_update_data(self) -> list[GoveeDevice]:
|
async def _async_update_data(self) -> list[GoveeDevice]:
|
||||||
self._controller.send_update_message()
|
for controller in self._controllers:
|
||||||
return self._controller.devices
|
controller.send_update_message()
|
||||||
|
|
||||||
|
return self.devices
|
||||||
|
Reference in New Issue
Block a user