Use a single source of data for switchbot (#84215)
parent
6e612a45ff
commit
7c13e7cdfd
|
@ -54,7 +54,7 @@ async def async_setup_entry(
|
||||||
coordinator: SwitchbotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator: SwitchbotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
SwitchBotBinarySensor(coordinator, binary_sensor)
|
SwitchBotBinarySensor(coordinator, binary_sensor)
|
||||||
for binary_sensor in coordinator.data["data"]
|
for binary_sensor in coordinator.device.parsed_data
|
||||||
if binary_sensor in BINARY_SENSOR_TYPES
|
if binary_sensor in BINARY_SENSOR_TYPES
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -77,4 +77,4 @@ class SwitchBotBinarySensor(SwitchbotEntity, BinarySensorEntity):
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
return self.data["data"][self._sensor]
|
return self.parsed_data[self._sensor]
|
||||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||||
import asyncio
|
import asyncio
|
||||||
import contextlib
|
import contextlib
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import switchbot
|
import switchbot
|
||||||
|
@ -25,14 +25,6 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
DEVICE_STARTUP_TIMEOUT = 30
|
DEVICE_STARTUP_TIMEOUT = 30
|
||||||
|
|
||||||
|
|
||||||
def flatten_sensors_data(sensor):
|
|
||||||
"""Deconstruct SwitchBot library temp object C/Fº readings from dictionary."""
|
|
||||||
if "temp" in sensor["data"]:
|
|
||||||
sensor["data"]["temperature"] = sensor["data"]["temp"]["c"]
|
|
||||||
|
|
||||||
return sensor
|
|
||||||
|
|
||||||
|
|
||||||
class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator):
|
class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator):
|
||||||
"""Class to manage fetching switchbot data."""
|
"""Class to manage fetching switchbot data."""
|
||||||
|
|
||||||
|
@ -57,7 +49,6 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator):
|
||||||
)
|
)
|
||||||
self.ble_device = ble_device
|
self.ble_device = ble_device
|
||||||
self.device = device
|
self.device = device
|
||||||
self.data: dict[str, Any] = {}
|
|
||||||
self.device_name = device_name
|
self.device_name = device_name
|
||||||
self.base_unique_id = base_unique_id
|
self.base_unique_id = base_unique_id
|
||||||
self.model = model
|
self.model = model
|
||||||
|
@ -88,11 +79,12 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator):
|
||||||
return
|
return
|
||||||
if "modelName" in adv.data:
|
if "modelName" in adv.data:
|
||||||
self._ready_event.set()
|
self._ready_event.set()
|
||||||
_LOGGER.debug("%s: Switchbot data: %s", self.ble_device.address, self.data)
|
_LOGGER.debug(
|
||||||
|
"%s: Switchbot data: %s", self.ble_device.address, self.device.data
|
||||||
|
)
|
||||||
if not self.device.advertisement_changed(adv) and not self._was_unavailable:
|
if not self.device.advertisement_changed(adv) and not self._was_unavailable:
|
||||||
return
|
return
|
||||||
self._was_unavailable = False
|
self._was_unavailable = False
|
||||||
self.data = flatten_sensors_data(adv.data)
|
|
||||||
self.device.update_from_advertisement(adv)
|
self.device.update_from_advertisement(adv)
|
||||||
super()._async_handle_bluetooth_event(service_info, change)
|
super()._async_handle_bluetooth_event(service_info, change)
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ class SwitchBotCurtainEntity(SwitchbotEntity, CoverEntity, RestoreEntity):
|
||||||
@callback
|
@callback
|
||||||
def _handle_coordinator_update(self) -> None:
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Handle updated data from the coordinator."""
|
"""Handle updated data from the coordinator."""
|
||||||
self._attr_current_cover_position = self.data["data"]["position"]
|
self._attr_current_cover_position = self.parsed_data["position"]
|
||||||
self._attr_is_closed = self.data["data"]["position"] <= 20
|
self._attr_is_closed = self.parsed_data["position"] <= 20
|
||||||
self._attr_is_opening = self.data["data"]["inMotion"]
|
self._attr_is_opening = self.parsed_data["inMotion"]
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""An abstract class common to all Switchbot entities."""
|
"""An abstract class common to all Switchbot entities."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from abc import abstractmethod
|
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
@ -54,15 +53,37 @@ class SwitchbotEntity(PassiveBluetoothCoordinatorEntity):
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self) -> dict[str, Any]:
|
def parsed_data(self) -> dict[str, Any]:
|
||||||
"""Return coordinator data for this entity."""
|
"""Return parsed device data for this entity."""
|
||||||
return self.coordinator.data
|
return self.coordinator.device.parsed_data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self) -> Mapping[Any, Any]:
|
def extra_state_attributes(self) -> Mapping[Any, Any]:
|
||||||
"""Return the state attributes."""
|
"""Return the state attributes."""
|
||||||
return {"last_run_success": self._last_run_success}
|
return {"last_run_success": self._last_run_success}
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_update_attrs(self) -> None:
|
||||||
|
"""Update the entity attributes."""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _handle_coordinator_update(self) -> None:
|
||||||
|
"""Handle data update."""
|
||||||
|
self._async_update_attrs()
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Register callbacks."""
|
||||||
|
self.async_on_remove(self._device.subscribe(self._handle_coordinator_update))
|
||||||
|
return await super().async_added_to_hass()
|
||||||
|
|
||||||
|
async def async_update(self) -> None:
|
||||||
|
"""Update the entity.
|
||||||
|
|
||||||
|
Only used by the generic entity update service.
|
||||||
|
"""
|
||||||
|
await self._device.update()
|
||||||
|
|
||||||
|
|
||||||
class SwitchbotSwitchedEntity(SwitchbotEntity, ToggleEntity):
|
class SwitchbotSwitchedEntity(SwitchbotEntity, ToggleEntity):
|
||||||
"""Base class for Switchbot entities that can be turned on and off."""
|
"""Base class for Switchbot entities that can be turned on and off."""
|
||||||
|
@ -86,29 +107,3 @@ class SwitchbotSwitchedEntity(SwitchbotEntity, ToggleEntity):
|
||||||
if self._last_run_success:
|
if self._last_run_success:
|
||||||
self._attr_is_on = False
|
self._attr_is_on = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
class SwitchbotSubscribeEntity(SwitchbotEntity):
|
|
||||||
"""Base class for Switchbot entities that use subscribe."""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def _async_update_attrs(self) -> None:
|
|
||||||
"""Update the entity attributes."""
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def _handle_coordinator_update(self) -> None:
|
|
||||||
"""Handle data update."""
|
|
||||||
self._async_update_attrs()
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
|
||||||
"""Register callbacks."""
|
|
||||||
self.async_on_remove(self._device.subscribe(self._handle_coordinator_update))
|
|
||||||
return await super().async_added_to_hass()
|
|
||||||
|
|
||||||
async def async_update(self) -> None:
|
|
||||||
"""Update the entity.
|
|
||||||
|
|
||||||
Only used by the generic entity update service.
|
|
||||||
"""
|
|
||||||
await self._device.update()
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ from homeassistant.util.color import (
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import SwitchbotDataUpdateCoordinator
|
from .coordinator import SwitchbotDataUpdateCoordinator
|
||||||
from .entity import SwitchbotSubscribeEntity
|
from .entity import SwitchbotEntity
|
||||||
|
|
||||||
SWITCHBOT_COLOR_MODE_TO_HASS = {
|
SWITCHBOT_COLOR_MODE_TO_HASS = {
|
||||||
SwitchBotColorMode.RGB: ColorMode.RGB,
|
SwitchBotColorMode.RGB: ColorMode.RGB,
|
||||||
|
@ -42,7 +42,7 @@ async def async_setup_entry(
|
||||||
async_add_entities([SwitchbotLightEntity(coordinator)])
|
async_add_entities([SwitchbotLightEntity(coordinator)])
|
||||||
|
|
||||||
|
|
||||||
class SwitchbotLightEntity(SwitchbotSubscribeEntity, LightEntity):
|
class SwitchbotLightEntity(SwitchbotEntity, LightEntity):
|
||||||
"""Representation of switchbot light bulb."""
|
"""Representation of switchbot light bulb."""
|
||||||
|
|
||||||
_device: SwitchbotBaseLight
|
_device: SwitchbotBaseLight
|
||||||
|
|
|
@ -89,11 +89,8 @@ async def async_setup_entry(
|
||||||
"""Set up Switchbot sensor based on a config entry."""
|
"""Set up Switchbot sensor based on a config entry."""
|
||||||
coordinator: SwitchbotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator: SwitchbotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
entities = [
|
entities = [
|
||||||
SwitchBotSensor(
|
SwitchBotSensor(coordinator, sensor)
|
||||||
coordinator,
|
for sensor in coordinator.device.parsed_data
|
||||||
sensor,
|
|
||||||
)
|
|
||||||
for sensor in coordinator.data["data"]
|
|
||||||
if sensor in SENSOR_TYPES
|
if sensor in SENSOR_TYPES
|
||||||
]
|
]
|
||||||
entities.append(SwitchbotRSSISensor(coordinator, "rssi"))
|
entities.append(SwitchbotRSSISensor(coordinator, "rssi"))
|
||||||
|
@ -117,7 +114,7 @@ class SwitchBotSensor(SwitchbotEntity, SensorEntity):
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> str | int | None:
|
def native_value(self) -> str | int | None:
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
return self.data["data"][self._sensor]
|
return self.parsed_data[self._sensor]
|
||||||
|
|
||||||
|
|
||||||
class SwitchbotRSSISensor(SwitchBotSensor):
|
class SwitchbotRSSISensor(SwitchBotSensor):
|
||||||
|
|
Loading…
Reference in New Issue