Skip to content
Closed
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
1 change: 1 addition & 0 deletions homeassistant/components/vesync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
Platform.TIME,
Platform.UPDATE,
]

Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/vesync/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@
"display": {
"name": "Display"
}
},
"time": {
"remaining_time": {
"name": "Remaining time"
}
}
},
"services": {
Expand Down
101 changes: 101 additions & 0 deletions homeassistant/components/vesync/time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""Support for VeSync time entities."""

from collections.abc import Callable
from dataclasses import dataclass
import logging

from pyvesync.base_devices.vesyncbasedevice import VeSyncBaseDevice
from pyvesync.device_container import DeviceContainer

from homeassistant.components.time import TimeEntity, TimeEntityDescription, time
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .common import is_air_fryer
from .const import VS_DEVICES, VS_DISCOVERY
from .coordinator import VesyncConfigEntry, VeSyncDataCoordinator
from .entity import VeSyncBaseEntity

_LOGGER = logging.getLogger(__name__)

PARALLEL_UPDATES = 0


@dataclass(frozen=True, kw_only=True)
class VeSyncTimeEntityDescription(TimeEntityDescription):
"""Class to describe a Vesync number entity."""

exists_fn: Callable[[VeSyncBaseDevice], bool] = lambda _: True
value_fn: Callable[[VeSyncBaseDevice], float]


TIME_DESCRIPTIONS: list[VeSyncTimeEntityDescription] = [
VeSyncTimeEntityDescription(
key="remaining_time",
translation_key="remaining_time",
exists_fn=is_air_fryer,
value_fn=lambda device: device.state.remaining_time,
)
]


async def async_setup_entry(
hass: HomeAssistant,
config_entry: VesyncConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up time entities."""

coordinator = config_entry.runtime_data

@callback
def discover(devices: list[VeSyncBaseDevice]) -> None:
"""Add new devices to platform."""
_setup_entities(devices, async_add_entities, coordinator)

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

_setup_entities(
config_entry.runtime_data.manager.devices, async_add_entities, coordinator
)


@callback
def _setup_entities(
devices: DeviceContainer | list[VeSyncBaseDevice],
async_add_entities: AddConfigEntryEntitiesCallback,
coordinator: VeSyncDataCoordinator,
) -> None:
"""Add time entities."""

async_add_entities(
VeSyncTimeEntity(dev, description, coordinator)
for dev in devices
for description in TIME_DESCRIPTIONS
if description.exists_fn(dev)
)


class VeSyncTimeEntity(VeSyncBaseEntity, TimeEntity):
"""A class to set numeric options on Vesync device."""

entity_description: VeSyncTimeEntityDescription

def __init__(
self,
device: VeSyncBaseDevice,
description: VeSyncTimeEntityDescription,
coordinator: VeSyncDataCoordinator,
) -> None:
"""Initialize the VeSync time device."""
super().__init__(device, coordinator)
self.entity_description = description
self._attr_unique_id = f"{super().unique_id}-{description.key}"

@property
def native_value(self) -> time:
"""Return the value reported by the number."""
return self.entity_description.value_fn(self.device)

Check failure on line 101 in homeassistant/components/vesync/time.py

View workflow job for this annotation

GitHub Actions / Check mypy

Incompatible return value type (got "float", expected "time") [return-value]
138 changes: 0 additions & 138 deletions tests/components/vesync/snapshots/test_sensor.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -783,51 +783,6 @@
'unique_id': 'CS158Cooking-preheat_set_time',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'cooking_end',
'cooking',
'cooking_stop',
'heating',
'preheat_end',
'preheat_stop',
'pull_out',
'standby',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.cs158_af_air_fryer_cooking_cooking_status',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Cooking status',
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Cooking status',
'platform': 'vesync',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'cook_status',
'unique_id': 'CS158Cooking-cook_status',
'unit_of_measurement': None,
}),
])
# ---
# name: test_sensor_state[CS158-AF Air Fryer Cooking][sensor.cs158_af_air_fryer_cooking_cooking_set_temperature]
Expand Down Expand Up @@ -861,30 +816,6 @@
'state': '15',
})
# ---
# name: test_sensor_state[CS158-AF Air Fryer Cooking][sensor.cs158_af_air_fryer_cooking_cooking_status]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'CS158-AF Air Fryer Cooking Cooking status',
'options': list([
'cooking_end',
'cooking',
'cooking_stop',
'heating',
'preheat_end',
'preheat_stop',
'pull_out',
'standby',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.cs158_af_air_fryer_cooking_cooking_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'cooking',
})
# ---
# name: test_sensor_state[CS158-AF Air Fryer Cooking][sensor.cs158_af_air_fryer_cooking_current_temperature]
StateSnapshot({
'attributes': ReadOnlyDict({
Expand Down Expand Up @@ -1097,51 +1028,6 @@
'unique_id': 'CS158Standby-preheat_set_time',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'cooking_end',
'cooking',
'cooking_stop',
'heating',
'preheat_end',
'preheat_stop',
'pull_out',
'standby',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.cs158_af_air_fryer_standby_cooking_status',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Cooking status',
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Cooking status',
'platform': 'vesync',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'cook_status',
'unique_id': 'CS158Standby-cook_status',
'unit_of_measurement': None,
}),
])
# ---
# name: test_sensor_state[CS158-AF Air Fryer Standby][sensor.cs158_af_air_fryer_standby_cooking_set_temperature]
Expand Down Expand Up @@ -1174,30 +1060,6 @@
'state': 'unknown',
})
# ---
# name: test_sensor_state[CS158-AF Air Fryer Standby][sensor.cs158_af_air_fryer_standby_cooking_status]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'CS158-AF Air Fryer Standby Cooking status',
'options': list([
'cooking_end',
'cooking',
'cooking_stop',
'heating',
'preheat_end',
'preheat_stop',
'pull_out',
'standby',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.cs158_af_air_fryer_standby_cooking_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'standby',
})
# ---
# name: test_sensor_state[CS158-AF Air Fryer Standby][sensor.cs158_af_air_fryer_standby_current_temperature]
StateSnapshot({
'attributes': ReadOnlyDict({
Expand Down
Loading
Loading