Add flow and rain sensor support to Hydrawise (#116303)
* Add flow and rain sensor support to Hydrawise * Address comments * Cleanup * Review comments * Address review comments * Added tests * Add icon translations * Add snapshot tests * Clean up binary sensor * Mypy cleanup * Another mypy error * Reviewer feedback * Clear next_cycle sensor when the value is unknown * Reviewer feedback * Reviewer feedback * Remove assert * Restructure switches, sensors, and binary sensors * Reviewer feedback * Reviewer feedback
This commit is contained in:
parent
a3248ccff9
commit
14fcf7be8e
14 changed files with 1307 additions and 149 deletions
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorDeviceClass,
|
BinarySensorDeviceClass,
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
|
@ -15,22 +18,40 @@ from .const import DOMAIN
|
||||||
from .coordinator import HydrawiseDataUpdateCoordinator
|
from .coordinator import HydrawiseDataUpdateCoordinator
|
||||||
from .entity import HydrawiseEntity
|
from .entity import HydrawiseEntity
|
||||||
|
|
||||||
BINARY_SENSOR_STATUS = BinarySensorEntityDescription(
|
|
||||||
key="status",
|
|
||||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
|
||||||
)
|
|
||||||
|
|
||||||
BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = (
|
@dataclass(frozen=True, kw_only=True)
|
||||||
BinarySensorEntityDescription(
|
class HydrawiseBinarySensorEntityDescription(BinarySensorEntityDescription):
|
||||||
key="is_watering",
|
"""Describes Hydrawise binary sensor."""
|
||||||
translation_key="watering",
|
|
||||||
device_class=BinarySensorDeviceClass.MOISTURE,
|
value_fn: Callable[[HydrawiseBinarySensor], bool | None]
|
||||||
|
|
||||||
|
|
||||||
|
CONTROLLER_BINARY_SENSORS: tuple[HydrawiseBinarySensorEntityDescription, ...] = (
|
||||||
|
HydrawiseBinarySensorEntityDescription(
|
||||||
|
key="status",
|
||||||
|
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||||
|
value_fn=lambda status_sensor: status_sensor.coordinator.last_update_success,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
BINARY_SENSOR_KEYS: list[str] = [
|
RAIN_SENSOR_BINARY_SENSOR: tuple[HydrawiseBinarySensorEntityDescription, ...] = (
|
||||||
desc.key for desc in (BINARY_SENSOR_STATUS, *BINARY_SENSOR_TYPES)
|
HydrawiseBinarySensorEntityDescription(
|
||||||
]
|
key="rain_sensor",
|
||||||
|
translation_key="rain_sensor",
|
||||||
|
device_class=BinarySensorDeviceClass.MOISTURE,
|
||||||
|
value_fn=lambda rain_sensor: rain_sensor.sensor.status.active,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
ZONE_BINARY_SENSORS: tuple[HydrawiseBinarySensorEntityDescription, ...] = (
|
||||||
|
HydrawiseBinarySensorEntityDescription(
|
||||||
|
key="is_watering",
|
||||||
|
translation_key="watering",
|
||||||
|
device_class=BinarySensorDeviceClass.RUNNING,
|
||||||
|
value_fn=lambda watering_sensor: watering_sensor.zone.scheduled_runs.current_run
|
||||||
|
is not None,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
|
@ -42,15 +63,27 @@ async def async_setup_entry(
|
||||||
coordinator: HydrawiseDataUpdateCoordinator = hass.data[DOMAIN][
|
coordinator: HydrawiseDataUpdateCoordinator = hass.data[DOMAIN][
|
||||||
config_entry.entry_id
|
config_entry.entry_id
|
||||||
]
|
]
|
||||||
entities = []
|
entities: list[HydrawiseBinarySensor] = []
|
||||||
for controller in coordinator.data.controllers.values():
|
for controller in coordinator.data.controllers.values():
|
||||||
entities.append(
|
entities.extend(
|
||||||
HydrawiseBinarySensor(coordinator, BINARY_SENSOR_STATUS, controller)
|
HydrawiseBinarySensor(coordinator, description, controller)
|
||||||
|
for description in CONTROLLER_BINARY_SENSORS
|
||||||
)
|
)
|
||||||
entities.extend(
|
entities.extend(
|
||||||
HydrawiseBinarySensor(coordinator, description, controller, zone)
|
HydrawiseBinarySensor(
|
||||||
|
coordinator,
|
||||||
|
description,
|
||||||
|
controller,
|
||||||
|
sensor_id=sensor.id,
|
||||||
|
)
|
||||||
|
for sensor in controller.sensors
|
||||||
|
for description in RAIN_SENSOR_BINARY_SENSOR
|
||||||
|
if "rain sensor" in sensor.model.name.lower()
|
||||||
|
)
|
||||||
|
entities.extend(
|
||||||
|
HydrawiseBinarySensor(coordinator, description, controller, zone_id=zone.id)
|
||||||
for zone in controller.zones
|
for zone in controller.zones
|
||||||
for description in BINARY_SENSOR_TYPES
|
for description in ZONE_BINARY_SENSORS
|
||||||
)
|
)
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
@ -58,10 +91,8 @@ async def async_setup_entry(
|
||||||
class HydrawiseBinarySensor(HydrawiseEntity, BinarySensorEntity):
|
class HydrawiseBinarySensor(HydrawiseEntity, BinarySensorEntity):
|
||||||
"""A sensor implementation for Hydrawise device."""
|
"""A sensor implementation for Hydrawise device."""
|
||||||
|
|
||||||
|
entity_description: HydrawiseBinarySensorEntityDescription
|
||||||
|
|
||||||
def _update_attrs(self) -> None:
|
def _update_attrs(self) -> None:
|
||||||
"""Update state attributes."""
|
"""Update state attributes."""
|
||||||
if self.entity_description.key == "status":
|
self._attr_is_on = self.entity_description.value_fn(self)
|
||||||
self._attr_is_on = self.coordinator.last_update_success
|
|
||||||
elif self.entity_description.key == "is_watering":
|
|
||||||
assert self.zone is not None
|
|
||||||
self._attr_is_on = self.zone.scheduled_runs.current_run is not None
|
|
||||||
|
|
|
@ -5,11 +5,12 @@ from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from pydrawise import HydrawiseBase
|
from pydrawise import Hydrawise
|
||||||
from pydrawise.schema import Controller, User, Zone
|
from pydrawise.schema import Controller, ControllerWaterUseSummary, Sensor, User, Zone
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
from homeassistant.util.dt import now
|
||||||
|
|
||||||
from .const import DOMAIN, LOGGER
|
from .const import DOMAIN, LOGGER
|
||||||
|
|
||||||
|
@ -21,15 +22,17 @@ class HydrawiseData:
|
||||||
user: User
|
user: User
|
||||||
controllers: dict[int, Controller]
|
controllers: dict[int, Controller]
|
||||||
zones: dict[int, Zone]
|
zones: dict[int, Zone]
|
||||||
|
sensors: dict[int, Sensor]
|
||||||
|
daily_water_use: dict[int, ControllerWaterUseSummary]
|
||||||
|
|
||||||
|
|
||||||
class HydrawiseDataUpdateCoordinator(DataUpdateCoordinator[HydrawiseData]):
|
class HydrawiseDataUpdateCoordinator(DataUpdateCoordinator[HydrawiseData]):
|
||||||
"""The Hydrawise Data Update Coordinator."""
|
"""The Hydrawise Data Update Coordinator."""
|
||||||
|
|
||||||
api: HydrawiseBase
|
api: Hydrawise
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, hass: HomeAssistant, api: HydrawiseBase, scan_interval: timedelta
|
self, hass: HomeAssistant, api: Hydrawise, scan_interval: timedelta
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize HydrawiseDataUpdateCoordinator."""
|
"""Initialize HydrawiseDataUpdateCoordinator."""
|
||||||
super().__init__(hass, LOGGER, name=DOMAIN, update_interval=scan_interval)
|
super().__init__(hass, LOGGER, name=DOMAIN, update_interval=scan_interval)
|
||||||
|
@ -40,8 +43,30 @@ class HydrawiseDataUpdateCoordinator(DataUpdateCoordinator[HydrawiseData]):
|
||||||
user = await self.api.get_user()
|
user = await self.api.get_user()
|
||||||
controllers = {}
|
controllers = {}
|
||||||
zones = {}
|
zones = {}
|
||||||
|
sensors = {}
|
||||||
|
daily_water_use: dict[int, ControllerWaterUseSummary] = {}
|
||||||
for controller in user.controllers:
|
for controller in user.controllers:
|
||||||
controllers[controller.id] = controller
|
controllers[controller.id] = controller
|
||||||
for zone in controller.zones:
|
for zone in controller.zones:
|
||||||
zones[zone.id] = zone
|
zones[zone.id] = zone
|
||||||
return HydrawiseData(user=user, controllers=controllers, zones=zones)
|
for sensor in controller.sensors:
|
||||||
|
sensors[sensor.id] = sensor
|
||||||
|
if any(
|
||||||
|
"flow meter" in sensor.model.name.lower()
|
||||||
|
for sensor in controller.sensors
|
||||||
|
):
|
||||||
|
daily_water_use[controller.id] = await self.api.get_water_use_summary(
|
||||||
|
controller,
|
||||||
|
now().replace(hour=0, minute=0, second=0, microsecond=0),
|
||||||
|
now(),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
daily_water_use[controller.id] = ControllerWaterUseSummary()
|
||||||
|
|
||||||
|
return HydrawiseData(
|
||||||
|
user=user,
|
||||||
|
controllers=controllers,
|
||||||
|
zones=zones,
|
||||||
|
sensors=sensors,
|
||||||
|
daily_water_use=daily_water_use,
|
||||||
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pydrawise.schema import Controller, Zone
|
from pydrawise.schema import Controller, Sensor, Zone
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
|
@ -24,24 +24,42 @@ class HydrawiseEntity(CoordinatorEntity[HydrawiseDataUpdateCoordinator]):
|
||||||
coordinator: HydrawiseDataUpdateCoordinator,
|
coordinator: HydrawiseDataUpdateCoordinator,
|
||||||
description: EntityDescription,
|
description: EntityDescription,
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
zone: Zone | None = None,
|
*,
|
||||||
|
zone_id: int | None = None,
|
||||||
|
sensor_id: int | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Hydrawise entity."""
|
"""Initialize the Hydrawise entity."""
|
||||||
super().__init__(coordinator=coordinator)
|
super().__init__(coordinator=coordinator)
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.zone = zone
|
self.zone_id = zone_id
|
||||||
self._device_id = str(controller.id if zone is None else zone.id)
|
self.sensor_id = sensor_id
|
||||||
|
self._device_id = str(zone_id) if zone_id is not None else str(controller.id)
|
||||||
self._attr_unique_id = f"{self._device_id}_{description.key}"
|
self._attr_unique_id = f"{self._device_id}_{description.key}"
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, self._device_id)},
|
identifiers={(DOMAIN, self._device_id)},
|
||||||
name=controller.name if zone is None else zone.name,
|
name=self.zone.name if zone_id is not None else controller.name,
|
||||||
|
model="Zone"
|
||||||
|
if zone_id is not None
|
||||||
|
else controller.hardware.model.description,
|
||||||
manufacturer=MANUFACTURER,
|
manufacturer=MANUFACTURER,
|
||||||
)
|
)
|
||||||
if zone is not None:
|
if zone_id is not None or sensor_id is not None:
|
||||||
self._attr_device_info["via_device"] = (DOMAIN, str(controller.id))
|
self._attr_device_info["via_device"] = (DOMAIN, str(controller.id))
|
||||||
self._update_attrs()
|
self._update_attrs()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def zone(self) -> Zone:
|
||||||
|
"""Return the entity zone."""
|
||||||
|
assert self.zone_id is not None # needed for mypy
|
||||||
|
return self.coordinator.data.zones[self.zone_id]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sensor(self) -> Sensor:
|
||||||
|
"""Return the entity sensor."""
|
||||||
|
assert self.sensor_id is not None # needed for mypy
|
||||||
|
return self.coordinator.data.sensors[self.sensor_id]
|
||||||
|
|
||||||
def _update_attrs(self) -> None:
|
def _update_attrs(self) -> None:
|
||||||
"""Update state attributes."""
|
"""Update state attributes."""
|
||||||
return # pragma: no cover
|
return # pragma: no cover
|
||||||
|
@ -50,7 +68,5 @@ class HydrawiseEntity(CoordinatorEntity[HydrawiseDataUpdateCoordinator]):
|
||||||
def _handle_coordinator_update(self) -> None:
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Get the latest data and updates the state."""
|
"""Get the latest data and updates the state."""
|
||||||
self.controller = self.coordinator.data.controllers[self.controller.id]
|
self.controller = self.coordinator.data.controllers[self.controller.id]
|
||||||
if self.zone:
|
|
||||||
self.zone = self.coordinator.data.zones[self.zone.id]
|
|
||||||
self._update_attrs()
|
self._update_attrs()
|
||||||
super()._handle_coordinator_update()
|
super()._handle_coordinator_update()
|
||||||
|
|
|
@ -1,8 +1,29 @@
|
||||||
{
|
{
|
||||||
"entity": {
|
"entity": {
|
||||||
"sensor": {
|
"sensor": {
|
||||||
|
"daily_active_water_use": {
|
||||||
|
"default": "mdi:water"
|
||||||
|
},
|
||||||
|
"daily_inactive_water_use": {
|
||||||
|
"default": "mdi:water"
|
||||||
|
},
|
||||||
|
"daily_total_water_use": {
|
||||||
|
"default": "mdi:water"
|
||||||
|
},
|
||||||
|
"next_cycle": {
|
||||||
|
"default": "mdi:clock-outline"
|
||||||
|
},
|
||||||
"watering_time": {
|
"watering_time": {
|
||||||
"default": "mdi:water-pump"
|
"default": "mdi:timer-outline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"binary_sensor": {
|
||||||
|
"rain_sensor": {
|
||||||
|
"default": "mdi:weather-sunny",
|
||||||
|
"state": {
|
||||||
|
"off": "mdi:weather-sunny",
|
||||||
|
"on": "mdi:weather-pouring"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Any
|
||||||
from pydrawise.schema import Zone
|
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
|
@ -12,7 +13,7 @@ from homeassistant.components.sensor import (
|
||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import UnitOfTime
|
from homeassistant.const import UnitOfTime, UnitOfVolume
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
@ -21,22 +22,104 @@ from .const import DOMAIN
|
||||||
from .coordinator import HydrawiseDataUpdateCoordinator
|
from .coordinator import HydrawiseDataUpdateCoordinator
|
||||||
from .entity import HydrawiseEntity
|
from .entity import HydrawiseEntity
|
||||||
|
|
||||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
|
||||||
SensorEntityDescription(
|
@dataclass(frozen=True, kw_only=True)
|
||||||
key="next_cycle",
|
class HydrawiseSensorEntityDescription(SensorEntityDescription):
|
||||||
translation_key="next_cycle",
|
"""Describes Hydrawise binary sensor."""
|
||||||
device_class=SensorDeviceClass.TIMESTAMP,
|
|
||||||
|
value_fn: Callable[[HydrawiseSensor], Any]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_zone_watering_time(sensor: HydrawiseSensor) -> int:
|
||||||
|
if (current_run := sensor.zone.scheduled_runs.current_run) is not None:
|
||||||
|
return int(current_run.remaining_time.total_seconds() / 60)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def _get_zone_next_cycle(sensor: HydrawiseSensor) -> datetime | None:
|
||||||
|
if (next_run := sensor.zone.scheduled_runs.next_run) is not None:
|
||||||
|
return dt_util.as_utc(next_run.start_time)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_zone_daily_active_water_use(sensor: HydrawiseSensor) -> float:
|
||||||
|
"""Get active water use for the zone."""
|
||||||
|
daily_water_summary = sensor.coordinator.data.daily_water_use[sensor.controller.id]
|
||||||
|
return float(daily_water_summary.active_use_by_zone_id.get(sensor.zone.id, 0.0))
|
||||||
|
|
||||||
|
|
||||||
|
def _get_controller_daily_active_water_use(sensor: HydrawiseSensor) -> float:
|
||||||
|
"""Get active water use for the controller."""
|
||||||
|
daily_water_summary = sensor.coordinator.data.daily_water_use[sensor.controller.id]
|
||||||
|
return daily_water_summary.total_active_use
|
||||||
|
|
||||||
|
|
||||||
|
def _get_controller_daily_inactive_water_use(sensor: HydrawiseSensor) -> float | None:
|
||||||
|
"""Get inactive water use for the controller."""
|
||||||
|
daily_water_summary = sensor.coordinator.data.daily_water_use[sensor.controller.id]
|
||||||
|
return daily_water_summary.total_inactive_use
|
||||||
|
|
||||||
|
|
||||||
|
def _get_controller_daily_total_water_use(sensor: HydrawiseSensor) -> float | None:
|
||||||
|
"""Get inactive water use for the controller."""
|
||||||
|
daily_water_summary = sensor.coordinator.data.daily_water_use[sensor.controller.id]
|
||||||
|
return daily_water_summary.total_use
|
||||||
|
|
||||||
|
|
||||||
|
FLOW_CONTROLLER_SENSORS: tuple[HydrawiseSensorEntityDescription, ...] = (
|
||||||
|
HydrawiseSensorEntityDescription(
|
||||||
|
key="daily_total_water_use",
|
||||||
|
translation_key="daily_total_water_use",
|
||||||
|
device_class=SensorDeviceClass.VOLUME,
|
||||||
|
native_unit_of_measurement=UnitOfVolume.GALLONS,
|
||||||
|
suggested_display_precision=1,
|
||||||
|
value_fn=_get_controller_daily_total_water_use,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(
|
HydrawiseSensorEntityDescription(
|
||||||
key="watering_time",
|
key="daily_active_water_use",
|
||||||
translation_key="watering_time",
|
translation_key="daily_active_water_use",
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
device_class=SensorDeviceClass.VOLUME,
|
||||||
|
native_unit_of_measurement=UnitOfVolume.GALLONS,
|
||||||
|
suggested_display_precision=1,
|
||||||
|
value_fn=_get_controller_daily_active_water_use,
|
||||||
|
),
|
||||||
|
HydrawiseSensorEntityDescription(
|
||||||
|
key="daily_inactive_water_use",
|
||||||
|
translation_key="daily_inactive_water_use",
|
||||||
|
device_class=SensorDeviceClass.VOLUME,
|
||||||
|
native_unit_of_measurement=UnitOfVolume.GALLONS,
|
||||||
|
suggested_display_precision=1,
|
||||||
|
value_fn=_get_controller_daily_inactive_water_use,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
|
FLOW_ZONE_SENSORS: tuple[SensorEntityDescription, ...] = (
|
||||||
TWO_YEAR_SECONDS = 60 * 60 * 24 * 365 * 2
|
HydrawiseSensorEntityDescription(
|
||||||
WATERING_TIME_ICON = "mdi:water-pump"
|
key="daily_active_water_use",
|
||||||
|
translation_key="daily_active_water_use",
|
||||||
|
device_class=SensorDeviceClass.VOLUME,
|
||||||
|
native_unit_of_measurement=UnitOfVolume.GALLONS,
|
||||||
|
suggested_display_precision=1,
|
||||||
|
value_fn=_get_zone_daily_active_water_use,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
ZONE_SENSORS: tuple[HydrawiseSensorEntityDescription, ...] = (
|
||||||
|
HydrawiseSensorEntityDescription(
|
||||||
|
key="next_cycle",
|
||||||
|
translation_key="next_cycle",
|
||||||
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
|
value_fn=_get_zone_next_cycle,
|
||||||
|
),
|
||||||
|
HydrawiseSensorEntityDescription(
|
||||||
|
key="watering_time",
|
||||||
|
translation_key="watering_time",
|
||||||
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
|
value_fn=_get_zone_watering_time,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
FLOW_MEASUREMENT_KEYS = [x.key for x in FLOW_CONTROLLER_SENSORS]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
|
@ -48,30 +131,50 @@ async def async_setup_entry(
|
||||||
coordinator: HydrawiseDataUpdateCoordinator = hass.data[DOMAIN][
|
coordinator: HydrawiseDataUpdateCoordinator = hass.data[DOMAIN][
|
||||||
config_entry.entry_id
|
config_entry.entry_id
|
||||||
]
|
]
|
||||||
async_add_entities(
|
entities: list[HydrawiseSensor] = []
|
||||||
HydrawiseSensor(coordinator, description, controller, zone)
|
for controller in coordinator.data.controllers.values():
|
||||||
for controller in coordinator.data.controllers.values()
|
entities.extend(
|
||||||
for zone in controller.zones
|
HydrawiseSensor(coordinator, description, controller, zone_id=zone.id)
|
||||||
for description in SENSOR_TYPES
|
for zone in controller.zones
|
||||||
)
|
for description in ZONE_SENSORS
|
||||||
|
)
|
||||||
|
entities.extend(
|
||||||
|
HydrawiseSensor(coordinator, description, controller, sensor_id=sensor.id)
|
||||||
|
for sensor in controller.sensors
|
||||||
|
for description in FLOW_CONTROLLER_SENSORS
|
||||||
|
if "flow meter" in sensor.model.name.lower()
|
||||||
|
)
|
||||||
|
entities.extend(
|
||||||
|
HydrawiseSensor(
|
||||||
|
coordinator,
|
||||||
|
description,
|
||||||
|
controller,
|
||||||
|
zone_id=zone.id,
|
||||||
|
sensor_id=sensor.id,
|
||||||
|
)
|
||||||
|
for zone in controller.zones
|
||||||
|
for sensor in controller.sensors
|
||||||
|
for description in FLOW_ZONE_SENSORS
|
||||||
|
if "flow meter" in sensor.model.name.lower()
|
||||||
|
)
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class HydrawiseSensor(HydrawiseEntity, SensorEntity):
|
class HydrawiseSensor(HydrawiseEntity, SensorEntity):
|
||||||
"""A sensor implementation for Hydrawise device."""
|
"""A sensor implementation for Hydrawise device."""
|
||||||
|
|
||||||
zone: Zone
|
entity_description: HydrawiseSensorEntityDescription
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self) -> str | None:
|
||||||
|
"""Icon of the entity based on the value."""
|
||||||
|
if (
|
||||||
|
self.entity_description.key in FLOW_MEASUREMENT_KEYS
|
||||||
|
and round(self.state, 2) == 0.0
|
||||||
|
):
|
||||||
|
return "mdi:water-outline"
|
||||||
|
return None
|
||||||
|
|
||||||
def _update_attrs(self) -> None:
|
def _update_attrs(self) -> None:
|
||||||
"""Update state attributes."""
|
"""Update state attributes."""
|
||||||
if self.entity_description.key == "watering_time":
|
self._attr_native_value = self.entity_description.value_fn(self)
|
||||||
if (current_run := self.zone.scheduled_runs.current_run) is not None:
|
|
||||||
self._attr_native_value = int(
|
|
||||||
current_run.remaining_time.total_seconds() / 60
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._attr_native_value = 0
|
|
||||||
elif self.entity_description.key == "next_cycle":
|
|
||||||
if (next_run := self.zone.scheduled_runs.next_run) is not None:
|
|
||||||
self._attr_native_value = dt_util.as_utc(next_run.start_time)
|
|
||||||
else:
|
|
||||||
self._attr_native_value = datetime.max.replace(tzinfo=dt_util.UTC)
|
|
||||||
|
|
|
@ -24,9 +24,21 @@
|
||||||
"binary_sensor": {
|
"binary_sensor": {
|
||||||
"watering": {
|
"watering": {
|
||||||
"name": "Watering"
|
"name": "Watering"
|
||||||
|
},
|
||||||
|
"rain_sensor": {
|
||||||
|
"name": "Rain sensor"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sensor": {
|
"sensor": {
|
||||||
|
"daily_total_water_use": {
|
||||||
|
"name": "Daily total water use"
|
||||||
|
},
|
||||||
|
"daily_active_water_use": {
|
||||||
|
"name": "Daily active water use"
|
||||||
|
},
|
||||||
|
"daily_inactive_water_use": {
|
||||||
|
"name": "Daily inactive water use"
|
||||||
|
},
|
||||||
"next_cycle": {
|
"next_cycle": {
|
||||||
"name": "Next cycle"
|
"name": "Next cycle"
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable, Coroutine
|
||||||
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pydrawise.schema import Zone
|
from pydrawise import Hydrawise, Zone
|
||||||
|
|
||||||
from homeassistant.components.switch import (
|
from homeassistant.components.switch import (
|
||||||
SwitchDeviceClass,
|
SwitchDeviceClass,
|
||||||
|
@ -21,16 +23,37 @@ from .const import DEFAULT_WATERING_TIME, DOMAIN
|
||||||
from .coordinator import HydrawiseDataUpdateCoordinator
|
from .coordinator import HydrawiseDataUpdateCoordinator
|
||||||
from .entity import HydrawiseEntity
|
from .entity import HydrawiseEntity
|
||||||
|
|
||||||
SWITCH_TYPES: tuple[SwitchEntityDescription, ...] = (
|
|
||||||
SwitchEntityDescription(
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class HydrawiseSwitchEntityDescription(SwitchEntityDescription):
|
||||||
|
"""Describes Hydrawise binary sensor."""
|
||||||
|
|
||||||
|
turn_on_fn: Callable[[Hydrawise, Zone], Coroutine[Any, Any, None]]
|
||||||
|
turn_off_fn: Callable[[Hydrawise, Zone], Coroutine[Any, Any, None]]
|
||||||
|
value_fn: Callable[[Zone], bool]
|
||||||
|
|
||||||
|
|
||||||
|
SWITCH_TYPES: tuple[HydrawiseSwitchEntityDescription, ...] = (
|
||||||
|
HydrawiseSwitchEntityDescription(
|
||||||
key="auto_watering",
|
key="auto_watering",
|
||||||
translation_key="auto_watering",
|
translation_key="auto_watering",
|
||||||
device_class=SwitchDeviceClass.SWITCH,
|
device_class=SwitchDeviceClass.SWITCH,
|
||||||
|
value_fn=lambda zone: zone.status.suspended_until is None,
|
||||||
|
turn_on_fn=lambda api, zone: api.resume_zone(zone),
|
||||||
|
turn_off_fn=lambda api, zone: api.suspend_zone(
|
||||||
|
zone, dt_util.now() + timedelta(days=365)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SwitchEntityDescription(
|
HydrawiseSwitchEntityDescription(
|
||||||
key="manual_watering",
|
key="manual_watering",
|
||||||
translation_key="manual_watering",
|
translation_key="manual_watering",
|
||||||
device_class=SwitchDeviceClass.SWITCH,
|
device_class=SwitchDeviceClass.SWITCH,
|
||||||
|
value_fn=lambda zone: zone.scheduled_runs.current_run is not None,
|
||||||
|
turn_on_fn=lambda api, zone: api.start_zone(
|
||||||
|
zone,
|
||||||
|
custom_run_duration=int(DEFAULT_WATERING_TIME.total_seconds()),
|
||||||
|
),
|
||||||
|
turn_off_fn=lambda api, zone: api.stop_zone(zone),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,7 +70,7 @@ async def async_setup_entry(
|
||||||
config_entry.entry_id
|
config_entry.entry_id
|
||||||
]
|
]
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
HydrawiseSwitch(coordinator, description, controller, zone)
|
HydrawiseSwitch(coordinator, description, controller, zone_id=zone.id)
|
||||||
for controller in coordinator.data.controllers.values()
|
for controller in coordinator.data.controllers.values()
|
||||||
for zone in controller.zones
|
for zone in controller.zones
|
||||||
for description in SWITCH_TYPES
|
for description in SWITCH_TYPES
|
||||||
|
@ -57,34 +80,21 @@ async def async_setup_entry(
|
||||||
class HydrawiseSwitch(HydrawiseEntity, SwitchEntity):
|
class HydrawiseSwitch(HydrawiseEntity, SwitchEntity):
|
||||||
"""A switch implementation for Hydrawise device."""
|
"""A switch implementation for Hydrawise device."""
|
||||||
|
|
||||||
|
entity_description: HydrawiseSwitchEntityDescription
|
||||||
zone: Zone
|
zone: Zone
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the device on."""
|
"""Turn the device on."""
|
||||||
if self.entity_description.key == "manual_watering":
|
await self.entity_description.turn_on_fn(self.coordinator.api, self.zone)
|
||||||
await self.coordinator.api.start_zone(
|
|
||||||
self.zone,
|
|
||||||
custom_run_duration=int(DEFAULT_WATERING_TIME.total_seconds()),
|
|
||||||
)
|
|
||||||
elif self.entity_description.key == "auto_watering":
|
|
||||||
await self.coordinator.api.resume_zone(self.zone)
|
|
||||||
self._attr_is_on = True
|
self._attr_is_on = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the device off."""
|
"""Turn the device off."""
|
||||||
if self.entity_description.key == "manual_watering":
|
await self.entity_description.turn_off_fn(self.coordinator.api, self.zone)
|
||||||
await self.coordinator.api.stop_zone(self.zone)
|
|
||||||
elif self.entity_description.key == "auto_watering":
|
|
||||||
await self.coordinator.api.suspend_zone(
|
|
||||||
self.zone, dt_util.now() + timedelta(days=365)
|
|
||||||
)
|
|
||||||
self._attr_is_on = False
|
self._attr_is_on = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
def _update_attrs(self) -> None:
|
def _update_attrs(self) -> None:
|
||||||
"""Update state attributes."""
|
"""Update state attributes."""
|
||||||
if self.entity_description.key == "manual_watering":
|
self._attr_is_on = self.entity_description.value_fn(self.zone)
|
||||||
self._attr_is_on = self.zone.scheduled_runs.current_run is not None
|
|
||||||
elif self.entity_description.key == "auto_watering":
|
|
||||||
self._attr_is_on = self.zone.status.suspended_until is None
|
|
||||||
|
|
|
@ -7,8 +7,14 @@ from unittest.mock import AsyncMock, patch
|
||||||
from pydrawise.schema import (
|
from pydrawise.schema import (
|
||||||
Controller,
|
Controller,
|
||||||
ControllerHardware,
|
ControllerHardware,
|
||||||
|
ControllerWaterUseSummary,
|
||||||
|
CustomSensorTypeEnum,
|
||||||
|
LocalizedValueType,
|
||||||
ScheduledZoneRun,
|
ScheduledZoneRun,
|
||||||
ScheduledZoneRuns,
|
ScheduledZoneRuns,
|
||||||
|
Sensor,
|
||||||
|
SensorModel,
|
||||||
|
SensorStatus,
|
||||||
User,
|
User,
|
||||||
Zone,
|
Zone,
|
||||||
)
|
)
|
||||||
|
@ -53,12 +59,18 @@ def mock_pydrawise(
|
||||||
user: User,
|
user: User,
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
zones: list[Zone],
|
zones: list[Zone],
|
||||||
|
sensors: list[Sensor],
|
||||||
|
controller_water_use_summary: ControllerWaterUseSummary,
|
||||||
) -> Generator[AsyncMock, None, None]:
|
) -> Generator[AsyncMock, None, None]:
|
||||||
"""Mock Hydrawise."""
|
"""Mock Hydrawise."""
|
||||||
with patch("pydrawise.client.Hydrawise", autospec=True) as mock_pydrawise:
|
with patch("pydrawise.client.Hydrawise", autospec=True) as mock_pydrawise:
|
||||||
user.controllers = [controller]
|
user.controllers = [controller]
|
||||||
controller.zones = zones
|
controller.zones = zones
|
||||||
|
controller.sensors = sensors
|
||||||
mock_pydrawise.return_value.get_user.return_value = user
|
mock_pydrawise.return_value.get_user.return_value = user
|
||||||
|
mock_pydrawise.return_value.get_water_use_summary.return_value = (
|
||||||
|
controller_water_use_summary
|
||||||
|
)
|
||||||
yield mock_pydrawise.return_value
|
yield mock_pydrawise.return_value
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,9 +98,50 @@ def controller() -> Controller:
|
||||||
),
|
),
|
||||||
last_contact_time=datetime.fromtimestamp(1693292420),
|
last_contact_time=datetime.fromtimestamp(1693292420),
|
||||||
online=True,
|
online=True,
|
||||||
|
sensors=[],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sensors() -> list[Sensor]:
|
||||||
|
"""Hydrawise sensor fixtures."""
|
||||||
|
return [
|
||||||
|
Sensor(
|
||||||
|
id=337844,
|
||||||
|
name="Rain sensor ",
|
||||||
|
model=SensorModel(
|
||||||
|
id=3318,
|
||||||
|
name="Rain Sensor (normally closed wire)",
|
||||||
|
active=True,
|
||||||
|
off_level=1,
|
||||||
|
off_timer=0,
|
||||||
|
divisor=0.0,
|
||||||
|
flow_rate=0.0,
|
||||||
|
sensor_type=CustomSensorTypeEnum.LEVEL_CLOSED,
|
||||||
|
),
|
||||||
|
status=SensorStatus(water_flow=None, active=False),
|
||||||
|
),
|
||||||
|
Sensor(
|
||||||
|
id=337845,
|
||||||
|
name="Flow meter",
|
||||||
|
model=SensorModel(
|
||||||
|
id=3324,
|
||||||
|
name="1, 1½ or 2 inch NPT Flow Meter",
|
||||||
|
active=True,
|
||||||
|
off_level=0,
|
||||||
|
off_timer=0,
|
||||||
|
divisor=0.52834,
|
||||||
|
flow_rate=3.7854,
|
||||||
|
sensor_type=CustomSensorTypeEnum.FLOW,
|
||||||
|
),
|
||||||
|
status=SensorStatus(
|
||||||
|
water_flow=LocalizedValueType(value=577.0044752010709, unit="gal"),
|
||||||
|
active=None,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def zones() -> list[Zone]:
|
def zones() -> list[Zone]:
|
||||||
"""Hydrawise zone fixtures."""
|
"""Hydrawise zone fixtures."""
|
||||||
|
@ -123,6 +176,18 @@ def zones() -> list[Zone]:
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def controller_water_use_summary() -> ControllerWaterUseSummary:
|
||||||
|
"""Mock water use summary for the controller."""
|
||||||
|
return ControllerWaterUseSummary(
|
||||||
|
total_use=345.6,
|
||||||
|
total_active_use=332.6,
|
||||||
|
total_inactive_use=13.0,
|
||||||
|
active_use_by_zone_id={5965394: 120.1, 5965395: 0.0},
|
||||||
|
unit="gal",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_config_entry_legacy() -> MockConfigEntry:
|
def mock_config_entry_legacy() -> MockConfigEntry:
|
||||||
"""Mock ConfigEntry."""
|
"""Mock ConfigEntry."""
|
||||||
|
|
193
tests/components/hydrawise/snapshots/test_binary_sensor.ambr
Normal file
193
tests/components/hydrawise/snapshots/test_binary_sensor.ambr
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_all_binary_sensors[binary_sensor.home_controller_connectivity-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'binary_sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'binary_sensor.home_controller_connectivity',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <BinarySensorDeviceClass.CONNECTIVITY: 'connectivity'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Connectivity',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': None,
|
||||||
|
'unique_id': '52496_status',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_binary_sensors[binary_sensor.home_controller_connectivity-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'connectivity',
|
||||||
|
'friendly_name': 'Home Controller Connectivity',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'binary_sensor.home_controller_connectivity',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_binary_sensors[binary_sensor.home_controller_rain_sensor-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'binary_sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'binary_sensor.home_controller_rain_sensor',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <BinarySensorDeviceClass.MOISTURE: 'moisture'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Rain sensor',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'rain_sensor',
|
||||||
|
'unique_id': '52496_rain_sensor',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_binary_sensors[binary_sensor.home_controller_rain_sensor-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'moisture',
|
||||||
|
'friendly_name': 'Home Controller Rain sensor',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'binary_sensor.home_controller_rain_sensor',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_binary_sensors[binary_sensor.zone_one_watering-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'binary_sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'binary_sensor.zone_one_watering',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <BinarySensorDeviceClass.RUNNING: 'running'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Watering',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'watering',
|
||||||
|
'unique_id': '5965394_is_watering',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_binary_sensors[binary_sensor.zone_one_watering-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'running',
|
||||||
|
'friendly_name': 'Zone One Watering',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'binary_sensor.zone_one_watering',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_binary_sensors[binary_sensor.zone_two_watering-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'binary_sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'binary_sensor.zone_two_watering',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <BinarySensorDeviceClass.RUNNING: 'running'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Watering',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'watering',
|
||||||
|
'unique_id': '5965395_is_watering',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_binary_sensors[binary_sensor.zone_two_watering-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'running',
|
||||||
|
'friendly_name': 'Zone Two Watering',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'binary_sensor.zone_two_watering',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
469
tests/components/hydrawise/snapshots/test_sensor.ambr
Normal file
469
tests/components/hydrawise/snapshots/test_sensor.ambr
Normal file
|
@ -0,0 +1,469 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_all_sensors[sensor.home_controller_daily_active_water_use-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.home_controller_daily_active_water_use',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
'sensor.private': dict({
|
||||||
|
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Daily active water use',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'daily_active_water_use',
|
||||||
|
'unique_id': '52496_daily_active_water_use',
|
||||||
|
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.home_controller_daily_active_water_use-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'volume',
|
||||||
|
'friendly_name': 'Home Controller Daily active water use',
|
||||||
|
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.home_controller_daily_active_water_use',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '1259.0279593584',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.home_controller_daily_inactive_water_use-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.home_controller_daily_inactive_water_use',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
'sensor.private': dict({
|
||||||
|
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Daily inactive water use',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'daily_inactive_water_use',
|
||||||
|
'unique_id': '52496_daily_inactive_water_use',
|
||||||
|
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.home_controller_daily_inactive_water_use-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'volume',
|
||||||
|
'friendly_name': 'Home Controller Daily inactive water use',
|
||||||
|
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.home_controller_daily_inactive_water_use',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '49.210353192',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.home_controller_daily_total_water_use-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.home_controller_daily_total_water_use',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
'sensor.private': dict({
|
||||||
|
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Daily total water use',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'daily_total_water_use',
|
||||||
|
'unique_id': '52496_daily_total_water_use',
|
||||||
|
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.home_controller_daily_total_water_use-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'volume',
|
||||||
|
'friendly_name': 'Home Controller Daily total water use',
|
||||||
|
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.home_controller_daily_total_water_use',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '1308.2383125504',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_one_daily_active_water_use-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.zone_one_daily_active_water_use',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
'sensor.private': dict({
|
||||||
|
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Daily active water use',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'daily_active_water_use',
|
||||||
|
'unique_id': '5965394_daily_active_water_use',
|
||||||
|
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_one_daily_active_water_use-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'volume',
|
||||||
|
'friendly_name': 'Zone One Daily active water use',
|
||||||
|
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.zone_one_daily_active_water_use',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '454.6279552584',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_one_next_cycle-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.zone_one_next_cycle',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Next cycle',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'next_cycle',
|
||||||
|
'unique_id': '5965394_next_cycle',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_one_next_cycle-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'timestamp',
|
||||||
|
'friendly_name': 'Zone One Next cycle',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.zone_one_next_cycle',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '2023-10-04T19:49:57+00:00',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_one_watering_time-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.zone_one_watering_time',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Watering time',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'watering_time',
|
||||||
|
'unique_id': '5965394_watering_time',
|
||||||
|
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_one_watering_time-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'friendly_name': 'Zone One Watering time',
|
||||||
|
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.zone_one_watering_time',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '0',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_two_daily_active_water_use-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.zone_two_daily_active_water_use',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
'sensor.private': dict({
|
||||||
|
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
|
||||||
|
'original_icon': 'mdi:water-outline',
|
||||||
|
'original_name': 'Daily active water use',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'daily_active_water_use',
|
||||||
|
'unique_id': '5965395_daily_active_water_use',
|
||||||
|
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_two_daily_active_water_use-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'volume',
|
||||||
|
'friendly_name': 'Zone Two Daily active water use',
|
||||||
|
'icon': 'mdi:water-outline',
|
||||||
|
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.zone_two_daily_active_water_use',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '0.0',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_two_next_cycle-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.zone_two_next_cycle',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Next cycle',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'next_cycle',
|
||||||
|
'unique_id': '5965395_next_cycle',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_two_next_cycle-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'timestamp',
|
||||||
|
'friendly_name': 'Zone Two Next cycle',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.zone_two_next_cycle',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_two_watering_time-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'sensor.zone_two_watering_time',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Watering time',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'watering_time',
|
||||||
|
'unique_id': '5965395_watering_time',
|
||||||
|
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_sensors[sensor.zone_two_watering_time-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'friendly_name': 'Zone Two Watering time',
|
||||||
|
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.zone_two_watering_time',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '29',
|
||||||
|
})
|
||||||
|
# ---
|
193
tests/components/hydrawise/snapshots/test_switch.ambr
Normal file
193
tests/components/hydrawise/snapshots/test_switch.ambr
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_all_switches[switch.zone_one_automatic_watering-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.zone_one_automatic_watering',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Automatic watering',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'auto_watering',
|
||||||
|
'unique_id': '5965394_auto_watering',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_switches[switch.zone_one_automatic_watering-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Zone One Automatic watering',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.zone_one_automatic_watering',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_switches[switch.zone_one_manual_watering-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.zone_one_manual_watering',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Manual watering',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'manual_watering',
|
||||||
|
'unique_id': '5965394_manual_watering',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_switches[switch.zone_one_manual_watering-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Zone One Manual watering',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.zone_one_manual_watering',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_switches[switch.zone_two_automatic_watering-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.zone_two_automatic_watering',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Automatic watering',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'auto_watering',
|
||||||
|
'unique_id': '5965395_auto_watering',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_switches[switch.zone_two_automatic_watering-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Zone Two Automatic watering',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.zone_two_automatic_watering',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_switches[switch.zone_two_manual_watering-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.zone_two_manual_watering',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Manual watering',
|
||||||
|
'platform': 'hydrawise',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'manual_watering',
|
||||||
|
'unique_id': '5965395_manual_watering',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_switches[switch.zone_two_manual_watering-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by hydrawise.com',
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Zone Two Manual watering',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.zone_two_manual_watering',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
|
@ -1,34 +1,34 @@
|
||||||
"""Test Hydrawise binary_sensor."""
|
"""Test Hydrawise binary_sensor."""
|
||||||
|
|
||||||
|
from collections.abc import Awaitable, Callable
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
from aiohttp import ClientError
|
from aiohttp import ClientError
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.hydrawise.const import SCAN_INTERVAL
|
from homeassistant.components.hydrawise.const import SCAN_INTERVAL
|
||||||
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||||
|
|
||||||
|
|
||||||
async def test_states(
|
async def test_all_binary_sensors(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_added_config_entry: MockConfigEntry,
|
mock_add_config_entry: Callable[[], Awaitable[MockConfigEntry]],
|
||||||
freezer: FrozenDateTimeFactory,
|
entity_registry: er.EntityRegistry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test binary_sensor states."""
|
"""Test that all binary sensors are working."""
|
||||||
connectivity = hass.states.get("binary_sensor.home_controller_connectivity")
|
with patch(
|
||||||
assert connectivity is not None
|
"homeassistant.components.hydrawise.PLATFORMS",
|
||||||
assert connectivity.state == "on"
|
[Platform.BINARY_SENSOR],
|
||||||
|
):
|
||||||
watering1 = hass.states.get("binary_sensor.zone_one_watering")
|
config_entry = await mock_add_config_entry()
|
||||||
assert watering1 is not None
|
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
|
||||||
assert watering1.state == "off"
|
|
||||||
|
|
||||||
watering2 = hass.states.get("binary_sensor.zone_two_watering")
|
|
||||||
assert watering2 is not None
|
|
||||||
assert watering2.state == "on"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update_data_fails(
|
async def test_update_data_fails(
|
||||||
|
|
|
@ -1,34 +1,33 @@
|
||||||
"""Test Hydrawise sensor."""
|
"""Test Hydrawise sensor."""
|
||||||
|
|
||||||
from collections.abc import Awaitable, Callable
|
from collections.abc import Awaitable, Callable
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
from pydrawise.schema import Controller, Zone
|
||||||
from pydrawise.schema import Zone
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry, snapshot_platform
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.freeze_time("2023-10-01 00:00:00+00:00")
|
@pytest.mark.freeze_time("2023-10-01 00:00:00+00:00")
|
||||||
async def test_states(
|
async def test_all_sensors(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_added_config_entry: MockConfigEntry,
|
mock_add_config_entry: Callable[[], Awaitable[MockConfigEntry]],
|
||||||
freezer: FrozenDateTimeFactory,
|
entity_registry: er.EntityRegistry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test sensor states."""
|
"""Test that all sensors are working."""
|
||||||
watering_time1 = hass.states.get("sensor.zone_one_watering_time")
|
with patch(
|
||||||
assert watering_time1 is not None
|
"homeassistant.components.hydrawise.PLATFORMS",
|
||||||
assert watering_time1.state == "0"
|
[Platform.SENSOR],
|
||||||
|
):
|
||||||
watering_time2 = hass.states.get("sensor.zone_two_watering_time")
|
config_entry = await mock_add_config_entry()
|
||||||
assert watering_time2 is not None
|
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
|
||||||
assert watering_time2.state == "29"
|
|
||||||
|
|
||||||
next_cycle = hass.states.get("sensor.zone_one_next_cycle")
|
|
||||||
assert next_cycle is not None
|
|
||||||
assert next_cycle.state == "2023-10-04T19:49:57+00:00"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.freeze_time("2023-10-01 00:00:00+00:00")
|
@pytest.mark.freeze_time("2023-10-01 00:00:00+00:00")
|
||||||
|
@ -43,4 +42,24 @@ async def test_suspended_state(
|
||||||
|
|
||||||
next_cycle = hass.states.get("sensor.zone_one_next_cycle")
|
next_cycle = hass.states.get("sensor.zone_one_next_cycle")
|
||||||
assert next_cycle is not None
|
assert next_cycle is not None
|
||||||
assert next_cycle.state == "9999-12-31T23:59:59+00:00"
|
assert next_cycle.state == "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_no_sensor_and_water_state2(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
controller: Controller,
|
||||||
|
mock_add_config_entry: Callable[[], Awaitable[MockConfigEntry]],
|
||||||
|
) -> None:
|
||||||
|
"""Test rain sensor, flow sensor, and water use in the absence of flow and rain sensors."""
|
||||||
|
controller.sensors = []
|
||||||
|
await mock_add_config_entry()
|
||||||
|
|
||||||
|
assert hass.states.get("sensor.zone_one_daily_active_water_use") is None
|
||||||
|
assert hass.states.get("sensor.zone_two_daily_active_water_use") is None
|
||||||
|
assert hass.states.get("sensor.home_controller_daily_active_water_use") is None
|
||||||
|
assert hass.states.get("sensor.home_controller_daily_inactive_water_use") is None
|
||||||
|
assert hass.states.get("binary_sensor.home_controller_rain_sensor") is None
|
||||||
|
|
||||||
|
sensor = hass.states.get("binary_sensor.home_controller_connectivity")
|
||||||
|
assert sensor is not None
|
||||||
|
assert sensor.state == "on"
|
||||||
|
|
|
@ -1,40 +1,41 @@
|
||||||
"""Test Hydrawise switch."""
|
"""Test Hydrawise switch."""
|
||||||
|
|
||||||
|
from collections.abc import Awaitable, Callable
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
from pydrawise.schema import Zone
|
from pydrawise.schema import Zone
|
||||||
import pytest
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.hydrawise.const import DEFAULT_WATERING_TIME
|
from homeassistant.components.hydrawise.const import DEFAULT_WATERING_TIME
|
||||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
Platform,
|
||||||
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry, snapshot_platform
|
||||||
|
|
||||||
|
|
||||||
async def test_states(
|
async def test_all_switches(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_added_config_entry: MockConfigEntry,
|
mock_add_config_entry: Callable[[], Awaitable[MockConfigEntry]],
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test switch states."""
|
"""Test that all switches are working."""
|
||||||
watering1 = hass.states.get("switch.zone_one_manual_watering")
|
with patch(
|
||||||
assert watering1 is not None
|
"homeassistant.components.hydrawise.PLATFORMS",
|
||||||
assert watering1.state == "off"
|
[Platform.SWITCH],
|
||||||
|
):
|
||||||
watering2 = hass.states.get("switch.zone_two_manual_watering")
|
config_entry = await mock_add_config_entry()
|
||||||
assert watering2 is not None
|
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
|
||||||
assert watering2.state == "on"
|
|
||||||
|
|
||||||
auto_watering1 = hass.states.get("switch.zone_one_automatic_watering")
|
|
||||||
assert auto_watering1 is not None
|
|
||||||
assert auto_watering1.state == "on"
|
|
||||||
|
|
||||||
auto_watering2 = hass.states.get("switch.zone_two_automatic_watering")
|
|
||||||
assert auto_watering2 is not None
|
|
||||||
assert auto_watering2.state == "on"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_manual_watering_services(
|
async def test_manual_watering_services(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue