mirror of
https://github.com/home-assistant/core.git
synced 2025-07-09 14:27:07 +00:00
Use asyncio in XiaomiAqara instead of threading (#74979)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
84a812ad05
commit
ade4fcaebd
@ -1,12 +1,13 @@
|
|||||||
"""Support for Xiaomi Gateways."""
|
"""Support for Xiaomi Gateways."""
|
||||||
|
import asyncio
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from xiaomi_gateway import XiaomiGateway, XiaomiGatewayDiscovery
|
from xiaomi_gateway import AsyncXiaomiGatewayMulticast, XiaomiGateway
|
||||||
|
|
||||||
from homeassistant.components import persistent_notification
|
from homeassistant.components import persistent_notification
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_BATTERY_LEVEL,
|
ATTR_BATTERY_LEVEL,
|
||||||
ATTR_DEVICE_ID,
|
ATTR_DEVICE_ID,
|
||||||
@ -34,6 +35,8 @@ from .const import (
|
|||||||
DEFAULT_DISCOVERY_RETRY,
|
DEFAULT_DISCOVERY_RETRY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
GATEWAYS_KEY,
|
GATEWAYS_KEY,
|
||||||
|
KEY_SETUP_LOCK,
|
||||||
|
KEY_UNSUB_STOP,
|
||||||
LISTENER_KEY,
|
LISTENER_KEY,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -143,6 +146,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up the xiaomi aqara components from a config entry."""
|
"""Set up the xiaomi aqara components from a config entry."""
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
setup_lock = hass.data[DOMAIN].setdefault(KEY_SETUP_LOCK, asyncio.Lock())
|
||||||
hass.data[DOMAIN].setdefault(GATEWAYS_KEY, {})
|
hass.data[DOMAIN].setdefault(GATEWAYS_KEY, {})
|
||||||
|
|
||||||
# Connect to Xiaomi Aqara Gateway
|
# Connect to Xiaomi Aqara Gateway
|
||||||
@ -158,24 +162,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
)
|
)
|
||||||
hass.data[DOMAIN][GATEWAYS_KEY][entry.entry_id] = xiaomi_gateway
|
hass.data[DOMAIN][GATEWAYS_KEY][entry.entry_id] = xiaomi_gateway
|
||||||
|
|
||||||
gateway_discovery = hass.data[DOMAIN].setdefault(
|
async with setup_lock:
|
||||||
LISTENER_KEY,
|
if LISTENER_KEY not in hass.data[DOMAIN]:
|
||||||
XiaomiGatewayDiscovery(hass.add_job, [], entry.data[CONF_INTERFACE]),
|
multicast = AsyncXiaomiGatewayMulticast(
|
||||||
|
interface=entry.data[CONF_INTERFACE]
|
||||||
)
|
)
|
||||||
|
hass.data[DOMAIN][LISTENER_KEY] = multicast
|
||||||
|
|
||||||
if len(hass.data[DOMAIN][GATEWAYS_KEY]) == 1:
|
|
||||||
# start listining for local pushes (only once)
|
# start listining for local pushes (only once)
|
||||||
await hass.async_add_executor_job(gateway_discovery.listen)
|
await multicast.start_listen()
|
||||||
|
|
||||||
# register stop callback to shutdown listining for local pushes
|
# register stop callback to shutdown listining for local pushes
|
||||||
|
@callback
|
||||||
def stop_xiaomi(event):
|
def stop_xiaomi(event):
|
||||||
"""Stop Xiaomi Socket."""
|
"""Stop Xiaomi Socket."""
|
||||||
_LOGGER.debug("Shutting down Xiaomi Gateway Listener")
|
_LOGGER.debug("Shutting down Xiaomi Gateway Listener")
|
||||||
gateway_discovery.stop_listen()
|
multicast.stop_listen()
|
||||||
|
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_xiaomi)
|
unsub = hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_xiaomi)
|
||||||
|
hass.data[DOMAIN][KEY_UNSUB_STOP] = unsub
|
||||||
|
|
||||||
gateway_discovery.gateways[entry.data[CONF_HOST]] = xiaomi_gateway
|
multicast = hass.data[DOMAIN][LISTENER_KEY]
|
||||||
|
multicast.register_gateway(entry.data[CONF_HOST], xiaomi_gateway.multicast_callback)
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Gateway with host '%s' connected, listening for broadcasts",
|
"Gateway with host '%s' connected, listening for broadcasts",
|
||||||
entry.data[CONF_HOST],
|
entry.data[CONF_HOST],
|
||||||
@ -201,23 +209,32 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
if entry.data[CONF_KEY] is not None:
|
if config_entry.data[CONF_KEY] is not None:
|
||||||
platforms = GATEWAY_PLATFORMS
|
platforms = GATEWAY_PLATFORMS
|
||||||
else:
|
else:
|
||||||
platforms = GATEWAY_PLATFORMS_NO_KEY
|
platforms = GATEWAY_PLATFORMS_NO_KEY
|
||||||
|
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, platforms)
|
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||||
|
config_entry, platforms
|
||||||
|
)
|
||||||
if unload_ok:
|
if unload_ok:
|
||||||
hass.data[DOMAIN][GATEWAYS_KEY].pop(entry.entry_id)
|
hass.data[DOMAIN][GATEWAYS_KEY].pop(config_entry.entry_id)
|
||||||
|
|
||||||
if len(hass.data[DOMAIN][GATEWAYS_KEY]) == 0:
|
loaded_entries = [
|
||||||
|
entry
|
||||||
|
for entry in hass.config_entries.async_entries(DOMAIN)
|
||||||
|
if entry.state == ConfigEntryState.LOADED
|
||||||
|
]
|
||||||
|
if len(loaded_entries) == 1:
|
||||||
# No gateways left, stop Xiaomi socket
|
# No gateways left, stop Xiaomi socket
|
||||||
|
unsub_stop = hass.data[DOMAIN].pop(KEY_UNSUB_STOP)
|
||||||
|
unsub_stop()
|
||||||
hass.data[DOMAIN].pop(GATEWAYS_KEY)
|
hass.data[DOMAIN].pop(GATEWAYS_KEY)
|
||||||
_LOGGER.debug("Shutting down Xiaomi Gateway Listener")
|
_LOGGER.debug("Shutting down Xiaomi Gateway Listener")
|
||||||
gateway_discovery = hass.data[DOMAIN].pop(LISTENER_KEY)
|
multicast = hass.data[DOMAIN].pop(LISTENER_KEY)
|
||||||
await hass.async_add_executor_job(gateway_discovery.stop_listen)
|
multicast.stop_listen()
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
@ -262,12 +279,9 @@ class XiaomiDevice(Entity):
|
|||||||
self._is_gateway = False
|
self._is_gateway = False
|
||||||
self._device_id = self._sid
|
self._device_id = self._sid
|
||||||
|
|
||||||
def _add_push_data_job(self, *args):
|
|
||||||
self.hass.add_job(self.push_data, *args)
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Start unavailability tracking."""
|
"""Start unavailability tracking."""
|
||||||
self._xiaomi_hub.callbacks[self._sid].append(self._add_push_data_job)
|
self._xiaomi_hub.callbacks[self._sid].append(self.push_data)
|
||||||
self._async_track_unavailable()
|
self._async_track_unavailable()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -287,7 +287,7 @@ class XiaomiMotionSensor(XiaomiBinarySensor):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.entity_id is not None:
|
if self.entity_id is not None:
|
||||||
self._hass.bus.fire(
|
self._hass.bus.async_fire(
|
||||||
"xiaomi_aqara.motion", {"entity_id": self.entity_id}
|
"xiaomi_aqara.motion", {"entity_id": self.entity_id}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -473,7 +473,7 @@ class XiaomiVibration(XiaomiBinarySensor):
|
|||||||
_LOGGER.warning("Unsupported movement_type detected: %s", value)
|
_LOGGER.warning("Unsupported movement_type detected: %s", value)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.hass.bus.fire(
|
self.hass.bus.async_fire(
|
||||||
"xiaomi_aqara.movement",
|
"xiaomi_aqara.movement",
|
||||||
{"entity_id": self.entity_id, "movement_type": value},
|
{"entity_id": self.entity_id, "movement_type": value},
|
||||||
)
|
)
|
||||||
@ -533,7 +533,7 @@ class XiaomiButton(XiaomiBinarySensor):
|
|||||||
_LOGGER.warning("Unsupported click_type detected: %s", value)
|
_LOGGER.warning("Unsupported click_type detected: %s", value)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._hass.bus.fire(
|
self._hass.bus.async_fire(
|
||||||
"xiaomi_aqara.click",
|
"xiaomi_aqara.click",
|
||||||
{"entity_id": self.entity_id, "click_type": click_type},
|
{"entity_id": self.entity_id, "click_type": click_type},
|
||||||
)
|
)
|
||||||
@ -570,7 +570,7 @@ class XiaomiCube(XiaomiBinarySensor):
|
|||||||
def parse_data(self, data, raw_data):
|
def parse_data(self, data, raw_data):
|
||||||
"""Parse data sent by gateway."""
|
"""Parse data sent by gateway."""
|
||||||
if self._data_key in data:
|
if self._data_key in data:
|
||||||
self._hass.bus.fire(
|
self._hass.bus.async_fire(
|
||||||
"xiaomi_aqara.cube_action",
|
"xiaomi_aqara.cube_action",
|
||||||
{"entity_id": self.entity_id, "action_type": data[self._data_key]},
|
{"entity_id": self.entity_id, "action_type": data[self._data_key]},
|
||||||
)
|
)
|
||||||
@ -582,7 +582,7 @@ class XiaomiCube(XiaomiBinarySensor):
|
|||||||
if isinstance(data["rotate"], int)
|
if isinstance(data["rotate"], int)
|
||||||
else data["rotate"].replace(",", ".")
|
else data["rotate"].replace(",", ".")
|
||||||
)
|
)
|
||||||
self._hass.bus.fire(
|
self._hass.bus.async_fire(
|
||||||
"xiaomi_aqara.cube_action",
|
"xiaomi_aqara.cube_action",
|
||||||
{
|
{
|
||||||
"entity_id": self.entity_id,
|
"entity_id": self.entity_id,
|
||||||
@ -598,7 +598,7 @@ class XiaomiCube(XiaomiBinarySensor):
|
|||||||
if isinstance(data["rotate_degree"], int)
|
if isinstance(data["rotate_degree"], int)
|
||||||
else data["rotate_degree"].replace(",", ".")
|
else data["rotate_degree"].replace(",", ".")
|
||||||
)
|
)
|
||||||
self._hass.bus.fire(
|
self._hass.bus.async_fire(
|
||||||
"xiaomi_aqara.cube_action",
|
"xiaomi_aqara.cube_action",
|
||||||
{
|
{
|
||||||
"entity_id": self.entity_id,
|
"entity_id": self.entity_id,
|
||||||
|
@ -106,7 +106,7 @@ class XiaomiAqaraFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
return await self.async_step_settings()
|
return await self.async_step_settings()
|
||||||
|
|
||||||
# Discover Xiaomi Aqara Gateways in the netwerk to get required SIDs.
|
# Discover Xiaomi Aqara Gateways in the netwerk to get required SIDs.
|
||||||
xiaomi = XiaomiGatewayDiscovery(self.hass.add_job, [], self.interface)
|
xiaomi = XiaomiGatewayDiscovery(self.interface)
|
||||||
try:
|
try:
|
||||||
await self.hass.async_add_executor_job(xiaomi.discover_gateways)
|
await self.hass.async_add_executor_job(xiaomi.discover_gateways)
|
||||||
except gaierror:
|
except gaierror:
|
||||||
|
@ -4,6 +4,8 @@ DOMAIN = "xiaomi_aqara"
|
|||||||
|
|
||||||
GATEWAYS_KEY = "gateways"
|
GATEWAYS_KEY = "gateways"
|
||||||
LISTENER_KEY = "listener"
|
LISTENER_KEY = "listener"
|
||||||
|
KEY_UNSUB_STOP = "unsub_stop"
|
||||||
|
KEY_SETUP_LOCK = "setup_lock"
|
||||||
|
|
||||||
ZEROCONF_GATEWAY = "lumi-gateway"
|
ZEROCONF_GATEWAY = "lumi-gateway"
|
||||||
ZEROCONF_ACPARTNER = "lumi-acpartner"
|
ZEROCONF_ACPARTNER = "lumi-acpartner"
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Xiaomi Gateway (Aqara)",
|
"name": "Xiaomi Gateway (Aqara)",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/xiaomi_aqara",
|
"documentation": "https://www.home-assistant.io/integrations/xiaomi_aqara",
|
||||||
"requirements": ["PyXiaomiGateway==0.13.4"],
|
"requirements": ["PyXiaomiGateway==0.14.1"],
|
||||||
"after_dependencies": ["discovery"],
|
"after_dependencies": ["discovery"],
|
||||||
"codeowners": ["@danielhiversen", "@syssi"],
|
"codeowners": ["@danielhiversen", "@syssi"],
|
||||||
"zeroconf": ["_miio._udp.local."],
|
"zeroconf": ["_miio._udp.local."],
|
||||||
|
@ -50,7 +50,7 @@ PyTurboJPEG==1.6.7
|
|||||||
PyViCare==2.17.0
|
PyViCare==2.17.0
|
||||||
|
|
||||||
# homeassistant.components.xiaomi_aqara
|
# homeassistant.components.xiaomi_aqara
|
||||||
PyXiaomiGateway==0.13.4
|
PyXiaomiGateway==0.14.1
|
||||||
|
|
||||||
# homeassistant.components.remember_the_milk
|
# homeassistant.components.remember_the_milk
|
||||||
RtmAPI==0.7.2
|
RtmAPI==0.7.2
|
||||||
|
@ -46,7 +46,7 @@ PyTurboJPEG==1.6.7
|
|||||||
PyViCare==2.17.0
|
PyViCare==2.17.0
|
||||||
|
|
||||||
# homeassistant.components.xiaomi_aqara
|
# homeassistant.components.xiaomi_aqara
|
||||||
PyXiaomiGateway==0.13.4
|
PyXiaomiGateway==0.14.1
|
||||||
|
|
||||||
# homeassistant.components.remember_the_milk
|
# homeassistant.components.remember_the_milk
|
||||||
RtmAPI==0.7.2
|
RtmAPI==0.7.2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user