Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using DataCoordinator instead of direct update #134087

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions homeassistant/components/vesync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
from .const import (
DOMAIN,
SERVICE_UPDATE_DEVS,
VS_COORDINATOR,
VS_DISCOVERY,
VS_FANS,
VS_LIGHTS,
VS_MANAGER,
VS_SENSORS,
VS_SWITCHES,
)
from .coordinator import VeSyncDataCoordinator

PLATFORMS = [Platform.FAN, Platform.LIGHT, Platform.SENSOR, Platform.SWITCH]

Expand Down Expand Up @@ -48,6 +50,11 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
hass.data[DOMAIN] = {}
hass.data[DOMAIN][VS_MANAGER] = manager

coordinator = VeSyncDataCoordinator(hass, manager)

# Store coordinator at domain level since only single integration instance is permitted.
hass.data[DOMAIN][VS_COORDINATOR] = coordinator

switches = hass.data[DOMAIN][VS_SWITCHES] = []
fans = hass.data[DOMAIN][VS_FANS] = []
lights = hass.data[DOMAIN][VS_LIGHTS] = []
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/vesync/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
VS_FANS = "fans"
VS_LIGHTS = "lights"
VS_SENSORS = "sensors"
VS_COORDINATOR = "coordinator"
VS_MANAGER = "manager"

DEV_TYPE_TO_HA = {
Expand Down
40 changes: 40 additions & 0 deletions homeassistant/components/vesync/coordinator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Representation of VeSync data coordinator."""

from __future__ import annotations

from datetime import timedelta
import logging
from typing import Any

from pyvesync import VeSync

from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

_LOGGER = logging.getLogger(__name__)


class VeSyncDataCoordinator(DataUpdateCoordinator):
"""Class representing data coordinator for VeSync devices."""

def __init__(self, hass: HomeAssistant, manager: VeSync) -> None:
"""Initialize."""
self._manager = manager

super().__init__(
hass,
_LOGGER,
name="VeSyncDataCoordinator",
update_interval=timedelta(seconds=30),
)

async def _async_update_data(self) -> dict[str, Any]:
"""Fetch data from API endpoint."""

try:
# Using `update_all_devices` instead of `update` to avoid fetching device list every time.
return await self.hass.async_add_executor_job(
self._manager.update_all_devices
)
except Exception as error:
raise UpdateFailed(error) from error

Check warning on line 40 in homeassistant/components/vesync/coordinator.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/vesync/coordinator.py#L39-L40

Added lines #L39 - L40 were not covered by tests
20 changes: 15 additions & 5 deletions homeassistant/components/vesync/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,24 @@

from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity, ToggleEntity
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)

from .const import DOMAIN


class VeSyncBaseEntity(Entity):
class VeSyncBaseEntity(CoordinatorEntity, Entity):
"""Base class for VeSync Entity Representations."""

_attr_has_entity_name = True

def __init__(self, device: VeSyncBaseDevice) -> None:
def __init__(
self, device: VeSyncBaseDevice, coordinator: DataUpdateCoordinator
) -> None:
"""Initialize the VeSync device."""
super().__init__(coordinator)
self.device = device
self._attr_unique_id = self.base_unique_id

Expand Down Expand Up @@ -46,9 +53,12 @@ def device_info(self) -> DeviceInfo:
sw_version=self.device.current_firm_version,
)

def update(self) -> None:
"""Update vesync device."""
self.device.update()
async def async_added_to_hass(self) -> None:
"""When entity is added to hass."""
await super().async_added_to_hass()
self.async_on_remove(
self.coordinator.async_add_listener(self.async_write_ha_state)
)


class VeSyncDevice(VeSyncBaseEntity, ToggleEntity):
Expand Down
32 changes: 24 additions & 8 deletions homeassistant/components/vesync/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,28 @@
import math
from typing import Any

from pyvesync.vesyncbasedevice import VeSyncBaseDevice

from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util.percentage import (
percentage_to_ranged_value,
ranged_value_to_percentage,
)
from homeassistant.util.scaling import int_states_in_range

from .const import DEV_TYPE_TO_HA, DOMAIN, SKU_TO_BASE_DEVICE, VS_DISCOVERY, VS_FANS
from .const import (
DEV_TYPE_TO_HA,
DOMAIN,
SKU_TO_BASE_DEVICE,
VS_COORDINATOR,
VS_DISCOVERY,
VS_FANS,
)
from .entity import VeSyncDevice

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -56,25 +66,31 @@
) -> None:
"""Set up the VeSync fan platform."""

coordinator = hass.data[DOMAIN][VS_COORDINATOR]

@callback
def discover(devices):
"""Add new devices to platform."""
_setup_entities(devices, async_add_entities)
_setup_entities(devices, async_add_entities, coordinator)

Check warning on line 74 in homeassistant/components/vesync/fan.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/vesync/fan.py#L74

Added line #L74 was not covered by tests

config_entry.async_on_unload(
async_dispatcher_connect(hass, VS_DISCOVERY.format(VS_FANS), discover)
)

_setup_entities(hass.data[DOMAIN][VS_FANS], async_add_entities)
_setup_entities(hass.data[DOMAIN][VS_FANS], async_add_entities, coordinator)


@callback
def _setup_entities(devices, async_add_entities):
def _setup_entities(
devices: list[VeSyncBaseDevice],
async_add_entities,
coordinator: DataUpdateCoordinator,
):
"""Check if device is online and add entity."""
entities = []
for dev in devices:
if DEV_TYPE_TO_HA.get(SKU_TO_BASE_DEVICE.get(dev.device_type)) == "fan":
entities.append(VeSyncFanHA(dev))
if DEV_TYPE_TO_HA.get(SKU_TO_BASE_DEVICE.get(dev.device_type, "")) == "fan":
entities.append(VeSyncFanHA(dev, coordinator))
else:
_LOGGER.warning(
"%s - Unknown device type - %s", dev.device_name, dev.device_type
Expand All @@ -96,9 +112,9 @@
_attr_name = None
_attr_translation_key = "vesync"

def __init__(self, fan) -> None:
def __init__(self, fan, coordinator: DataUpdateCoordinator) -> None:
"""Initialize the VeSync fan device."""
super().__init__(fan)
super().__init__(fan, coordinator)
self.smartfan = fan

@property
Expand Down
23 changes: 16 additions & 7 deletions homeassistant/components/vesync/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import logging
from typing import Any

from pyvesync.vesyncbasedevice import VeSyncBaseDevice

from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP_KELVIN,
Expand All @@ -13,9 +15,10 @@
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util import color as color_util

from .const import DEV_TYPE_TO_HA, DOMAIN, VS_DISCOVERY, VS_LIGHTS
from .const import DEV_TYPE_TO_HA, DOMAIN, VS_COORDINATOR, VS_DISCOVERY, VS_LIGHTS
from .entity import VeSyncDevice

_LOGGER = logging.getLogger(__name__)
Expand All @@ -30,27 +33,33 @@
) -> None:
"""Set up lights."""

coordinator = hass.data[DOMAIN][VS_COORDINATOR]

@callback
def discover(devices):
"""Add new devices to platform."""
_setup_entities(devices, async_add_entities)
_setup_entities(devices, async_add_entities, coordinator)

Check warning on line 41 in homeassistant/components/vesync/light.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/vesync/light.py#L41

Added line #L41 was not covered by tests

config_entry.async_on_unload(
async_dispatcher_connect(hass, VS_DISCOVERY.format(VS_LIGHTS), discover)
)

_setup_entities(hass.data[DOMAIN][VS_LIGHTS], async_add_entities)
_setup_entities(hass.data[DOMAIN][VS_LIGHTS], async_add_entities, coordinator)


@callback
def _setup_entities(devices, async_add_entities):
def _setup_entities(
devices: list[VeSyncBaseDevice],
async_add_entities,
coordinator: DataUpdateCoordinator,
):
"""Check if device is online and add entity."""
entities = []
entities: list[VeSyncBaseLight] = []
for dev in devices:
if DEV_TYPE_TO_HA.get(dev.device_type) in ("walldimmer", "bulb-dimmable"):
entities.append(VeSyncDimmableLightHA(dev))
entities.append(VeSyncDimmableLightHA(dev, coordinator))
elif DEV_TYPE_TO_HA.get(dev.device_type) in ("bulb-tunable-white",):
entities.append(VeSyncTunableWhiteLightHA(dev))
entities.append(VeSyncTunableWhiteLightHA(dev, coordinator))
else:
_LOGGER.debug(
"%s - Unknown device type - %s", dev.device_name, dev.device_type
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/vesync/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/vesync",
"iot_class": "cloud_polling",
"loggers": ["pyvesync"],
"requirements": ["pyvesync==2.1.14"]
"requirements": ["pyvesync==2.1.15"]
}
31 changes: 24 additions & 7 deletions homeassistant/components/vesync/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from dataclasses import dataclass
import logging

from pyvesync.vesyncbasedevice import VeSyncBaseDevice
from pyvesync.vesyncfan import VeSyncAirBypass
from pyvesync.vesyncoutlet import VeSyncOutlet
from pyvesync.vesyncswitch import VeSyncSwitch
Expand All @@ -29,8 +30,16 @@
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType

from .const import DEV_TYPE_TO_HA, DOMAIN, SKU_TO_BASE_DEVICE, VS_DISCOVERY, VS_SENSORS
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from .const import (
DEV_TYPE_TO_HA,
DOMAIN,
SKU_TO_BASE_DEVICE,
VS_COORDINATOR,
VS_DISCOVERY,
VS_SENSORS,
)
from .entity import VeSyncBaseEntity

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -187,24 +196,31 @@
) -> None:
"""Set up switches."""

coordinator = hass.data[DOMAIN][VS_COORDINATOR]

@callback
def discover(devices):
"""Add new devices to platform."""
_setup_entities(devices, async_add_entities)
_setup_entities(devices, async_add_entities, coordinator)

Check warning on line 204 in homeassistant/components/vesync/sensor.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/vesync/sensor.py#L204

Added line #L204 was not covered by tests

config_entry.async_on_unload(
async_dispatcher_connect(hass, VS_DISCOVERY.format(VS_SENSORS), discover)
)

_setup_entities(hass.data[DOMAIN][VS_SENSORS], async_add_entities)
_setup_entities(hass.data[DOMAIN][VS_SENSORS], async_add_entities, coordinator)


@callback
def _setup_entities(devices, async_add_entities):
def _setup_entities(
devices: list[VeSyncBaseDevice],
async_add_entities,
coordinator: DataUpdateCoordinator,
):
"""Check if device is online and add entity."""

async_add_entities(
(
VeSyncSensorEntity(dev, description)
VeSyncSensorEntity(dev, description, coordinator)
for dev in devices
for description in SENSORS
if description.exists_fn(dev)
Expand All @@ -222,9 +238,10 @@
self,
device: VeSyncAirBypass | VeSyncOutlet | VeSyncSwitch,
description: VeSyncSensorEntityDescription,
coordinator,
) -> None:
"""Initialize the VeSync outlet device."""
super().__init__(device)
super().__init__(device, coordinator)
self.entity_description = description
self._attr_unique_id = f"{super().unique_id}-{description.key}"

Expand Down
Loading