diff --git a/homeassistant/components/hydrawise/binary_sensor.py b/homeassistant/components/hydrawise/binary_sensor.py index b8c5dbddc7c..ee41a004a48 100644 --- a/homeassistant/components/hydrawise/binary_sensor.py +++ b/homeassistant/components/hydrawise/binary_sensor.py @@ -2,6 +2,9 @@ from __future__ import annotations +from collections.abc import Callable +from dataclasses import dataclass + from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, BinarySensorEntity, @@ -15,22 +18,40 @@ from .const import DOMAIN from .coordinator import HydrawiseDataUpdateCoordinator from .entity import HydrawiseEntity -BINARY_SENSOR_STATUS = BinarySensorEntityDescription( - key="status", - device_class=BinarySensorDeviceClass.CONNECTIVITY, -) -BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = ( - BinarySensorEntityDescription( - key="is_watering", - translation_key="watering", - device_class=BinarySensorDeviceClass.MOISTURE, +@dataclass(frozen=True, kw_only=True) +class HydrawiseBinarySensorEntityDescription(BinarySensorEntityDescription): + """Describes Hydrawise binary sensor.""" + + 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] = [ - desc.key for desc in (BINARY_SENSOR_STATUS, *BINARY_SENSOR_TYPES) -] +RAIN_SENSOR_BINARY_SENSOR: tuple[HydrawiseBinarySensorEntityDescription, ...] = ( + 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( @@ -42,15 +63,27 @@ async def async_setup_entry( coordinator: HydrawiseDataUpdateCoordinator = hass.data[DOMAIN][ config_entry.entry_id ] - entities = [] + entities: list[HydrawiseBinarySensor] = [] for controller in coordinator.data.controllers.values(): - entities.append( - HydrawiseBinarySensor(coordinator, BINARY_SENSOR_STATUS, controller) + entities.extend( + HydrawiseBinarySensor(coordinator, description, controller) + for description in CONTROLLER_BINARY_SENSORS ) 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 description in BINARY_SENSOR_TYPES + for description in ZONE_BINARY_SENSORS ) async_add_entities(entities) @@ -58,10 +91,8 @@ async def async_setup_entry( class HydrawiseBinarySensor(HydrawiseEntity, BinarySensorEntity): """A sensor implementation for Hydrawise device.""" + entity_description: HydrawiseBinarySensorEntityDescription + def _update_attrs(self) -> None: """Update state attributes.""" - if self.entity_description.key == "status": - 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 + self._attr_is_on = self.entity_description.value_fn(self) diff --git a/homeassistant/components/hydrawise/coordinator.py b/homeassistant/components/hydrawise/coordinator.py index 71922928651..d046dfcc92a 100644 --- a/homeassistant/components/hydrawise/coordinator.py +++ b/homeassistant/components/hydrawise/coordinator.py @@ -5,11 +5,12 @@ from __future__ import annotations from dataclasses import dataclass from datetime import timedelta -from pydrawise import HydrawiseBase -from pydrawise.schema import Controller, User, Zone +from pydrawise import Hydrawise +from pydrawise.schema import Controller, ControllerWaterUseSummary, Sensor, User, Zone from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from homeassistant.util.dt import now from .const import DOMAIN, LOGGER @@ -21,15 +22,17 @@ class HydrawiseData: user: User controllers: dict[int, Controller] zones: dict[int, Zone] + sensors: dict[int, Sensor] + daily_water_use: dict[int, ControllerWaterUseSummary] class HydrawiseDataUpdateCoordinator(DataUpdateCoordinator[HydrawiseData]): """The Hydrawise Data Update Coordinator.""" - api: HydrawiseBase + api: Hydrawise def __init__( - self, hass: HomeAssistant, api: HydrawiseBase, scan_interval: timedelta + self, hass: HomeAssistant, api: Hydrawise, scan_interval: timedelta ) -> None: """Initialize HydrawiseDataUpdateCoordinator.""" super().__init__(hass, LOGGER, name=DOMAIN, update_interval=scan_interval) @@ -40,8 +43,30 @@ class HydrawiseDataUpdateCoordinator(DataUpdateCoordinator[HydrawiseData]): user = await self.api.get_user() controllers = {} zones = {} + sensors = {} + daily_water_use: dict[int, ControllerWaterUseSummary] = {} for controller in user.controllers: controllers[controller.id] = controller for zone in controller.zones: 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, + ) diff --git a/homeassistant/components/hydrawise/entity.py b/homeassistant/components/hydrawise/entity.py index 2ae893887e6..509586ccd31 100644 --- a/homeassistant/components/hydrawise/entity.py +++ b/homeassistant/components/hydrawise/entity.py @@ -2,7 +2,7 @@ from __future__ import annotations -from pydrawise.schema import Controller, Zone +from pydrawise.schema import Controller, Sensor, Zone from homeassistant.core import callback from homeassistant.helpers.device_registry import DeviceInfo @@ -24,24 +24,42 @@ class HydrawiseEntity(CoordinatorEntity[HydrawiseDataUpdateCoordinator]): coordinator: HydrawiseDataUpdateCoordinator, description: EntityDescription, controller: Controller, - zone: Zone | None = None, + *, + zone_id: int | None = None, + sensor_id: int | None = None, ) -> None: """Initialize the Hydrawise entity.""" super().__init__(coordinator=coordinator) self.entity_description = description self.controller = controller - self.zone = zone - self._device_id = str(controller.id if zone is None else zone.id) + self.zone_id = 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_device_info = DeviceInfo( 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, ) - 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._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: """Update state attributes.""" return # pragma: no cover @@ -50,7 +68,5 @@ class HydrawiseEntity(CoordinatorEntity[HydrawiseDataUpdateCoordinator]): def _handle_coordinator_update(self) -> None: """Get the latest data and updates the state.""" self.controller = self.coordinator.data.controllers[self.controller.id] - if self.zone: - self.zone = self.coordinator.data.zones[self.zone.id] self._update_attrs() super()._handle_coordinator_update() diff --git a/homeassistant/components/hydrawise/icons.json b/homeassistant/components/hydrawise/icons.json index 717b5c48357..64deab590da 100644 --- a/homeassistant/components/hydrawise/icons.json +++ b/homeassistant/components/hydrawise/icons.json @@ -1,8 +1,29 @@ { "entity": { "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": { - "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" + } } } } diff --git a/homeassistant/components/hydrawise/sensor.py b/homeassistant/components/hydrawise/sensor.py index 84e9f979878..87dc5e73afe 100644 --- a/homeassistant/components/hydrawise/sensor.py +++ b/homeassistant/components/hydrawise/sensor.py @@ -2,9 +2,10 @@ from __future__ import annotations +from collections.abc import Callable +from dataclasses import dataclass from datetime import datetime - -from pydrawise.schema import Zone +from typing import Any from homeassistant.components.sensor import ( SensorDeviceClass, @@ -12,7 +13,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import UnitOfTime +from homeassistant.const import UnitOfTime, UnitOfVolume from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import dt as dt_util @@ -21,22 +22,104 @@ from .const import DOMAIN from .coordinator import HydrawiseDataUpdateCoordinator from .entity import HydrawiseEntity -SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key="next_cycle", - translation_key="next_cycle", - device_class=SensorDeviceClass.TIMESTAMP, + +@dataclass(frozen=True, kw_only=True) +class HydrawiseSensorEntityDescription(SensorEntityDescription): + """Describes Hydrawise binary sensor.""" + + 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( - key="watering_time", - translation_key="watering_time", - native_unit_of_measurement=UnitOfTime.MINUTES, + HydrawiseSensorEntityDescription( + 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_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] -TWO_YEAR_SECONDS = 60 * 60 * 24 * 365 * 2 -WATERING_TIME_ICON = "mdi:water-pump" +FLOW_ZONE_SENSORS: tuple[SensorEntityDescription, ...] = ( + HydrawiseSensorEntityDescription( + 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( @@ -48,30 +131,50 @@ async def async_setup_entry( coordinator: HydrawiseDataUpdateCoordinator = hass.data[DOMAIN][ config_entry.entry_id ] - async_add_entities( - HydrawiseSensor(coordinator, description, controller, zone) - for controller in coordinator.data.controllers.values() - for zone in controller.zones - for description in SENSOR_TYPES - ) + entities: list[HydrawiseSensor] = [] + for controller in coordinator.data.controllers.values(): + entities.extend( + HydrawiseSensor(coordinator, description, controller, zone_id=zone.id) + 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): """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: """Update state attributes.""" - if self.entity_description.key == "watering_time": - 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) + self._attr_native_value = self.entity_description.value_fn(self) diff --git a/homeassistant/components/hydrawise/strings.json b/homeassistant/components/hydrawise/strings.json index ee5cc0a541c..1bc5525c9d9 100644 --- a/homeassistant/components/hydrawise/strings.json +++ b/homeassistant/components/hydrawise/strings.json @@ -24,9 +24,21 @@ "binary_sensor": { "watering": { "name": "Watering" + }, + "rain_sensor": { + "name": "Rain 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": { "name": "Next cycle" }, diff --git a/homeassistant/components/hydrawise/switch.py b/homeassistant/components/hydrawise/switch.py index bceaa85eb73..001a8e399ee 100644 --- a/homeassistant/components/hydrawise/switch.py +++ b/homeassistant/components/hydrawise/switch.py @@ -2,10 +2,12 @@ from __future__ import annotations +from collections.abc import Callable, Coroutine +from dataclasses import dataclass from datetime import timedelta from typing import Any -from pydrawise.schema import Zone +from pydrawise import Hydrawise, Zone from homeassistant.components.switch import ( SwitchDeviceClass, @@ -21,16 +23,37 @@ from .const import DEFAULT_WATERING_TIME, DOMAIN from .coordinator import HydrawiseDataUpdateCoordinator 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", translation_key="auto_watering", 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", translation_key="manual_watering", 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 ] async_add_entities( - HydrawiseSwitch(coordinator, description, controller, zone) + HydrawiseSwitch(coordinator, description, controller, zone_id=zone.id) for controller in coordinator.data.controllers.values() for zone in controller.zones for description in SWITCH_TYPES @@ -57,34 +80,21 @@ async def async_setup_entry( class HydrawiseSwitch(HydrawiseEntity, SwitchEntity): """A switch implementation for Hydrawise device.""" + entity_description: HydrawiseSwitchEntityDescription zone: Zone async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" - if self.entity_description.key == "manual_watering": - 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) + await self.entity_description.turn_on_fn(self.coordinator.api, self.zone) self._attr_is_on = True self.async_write_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" - if self.entity_description.key == "manual_watering": - 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) - ) + await self.entity_description.turn_off_fn(self.coordinator.api, self.zone) self._attr_is_on = False self.async_write_ha_state() def _update_attrs(self) -> None: """Update state attributes.""" - if self.entity_description.key == "manual_watering": - 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 + self._attr_is_on = self.entity_description.value_fn(self.zone) diff --git a/tests/components/hydrawise/conftest.py b/tests/components/hydrawise/conftest.py index 11670cb3565..550e944db36 100644 --- a/tests/components/hydrawise/conftest.py +++ b/tests/components/hydrawise/conftest.py @@ -7,8 +7,14 @@ from unittest.mock import AsyncMock, patch from pydrawise.schema import ( Controller, ControllerHardware, + ControllerWaterUseSummary, + CustomSensorTypeEnum, + LocalizedValueType, ScheduledZoneRun, ScheduledZoneRuns, + Sensor, + SensorModel, + SensorStatus, User, Zone, ) @@ -53,12 +59,18 @@ def mock_pydrawise( user: User, controller: Controller, zones: list[Zone], + sensors: list[Sensor], + controller_water_use_summary: ControllerWaterUseSummary, ) -> Generator[AsyncMock, None, None]: """Mock Hydrawise.""" with patch("pydrawise.client.Hydrawise", autospec=True) as mock_pydrawise: user.controllers = [controller] controller.zones = zones + controller.sensors = sensors 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 @@ -86,9 +98,50 @@ def controller() -> Controller: ), last_contact_time=datetime.fromtimestamp(1693292420), 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 def zones() -> list[Zone]: """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 def mock_config_entry_legacy() -> MockConfigEntry: """Mock ConfigEntry.""" diff --git a/tests/components/hydrawise/snapshots/test_binary_sensor.ambr b/tests/components/hydrawise/snapshots/test_binary_sensor.ambr new file mode 100644 index 00000000000..9886345595d --- /dev/null +++ b/tests/components/hydrawise/snapshots/test_binary_sensor.ambr @@ -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': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + 'entity_id': 'binary_sensor.home_controller_connectivity', + 'last_changed': , + 'last_reported': , + 'last_updated': , + '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': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + 'entity_id': 'binary_sensor.home_controller_rain_sensor', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_binary_sensors[binary_sensor.zone_one_watering-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + 'entity_id': 'binary_sensor.zone_one_watering', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_binary_sensors[binary_sensor.zone_two_watering-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + 'entity_id': 'binary_sensor.zone_two_watering', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- diff --git a/tests/components/hydrawise/snapshots/test_sensor.ambr b/tests/components/hydrawise/snapshots/test_sensor.ambr new file mode 100644 index 00000000000..3472de98460 --- /dev/null +++ b/tests/components/hydrawise/snapshots/test_sensor.ambr @@ -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': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + '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': , + }) +# --- +# 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': , + }), + 'context': , + 'entity_id': 'sensor.home_controller_daily_active_water_use', + 'last_changed': , + 'last_reported': , + 'last_updated': , + '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': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + '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': , + }) +# --- +# 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': , + }), + 'context': , + 'entity_id': 'sensor.home_controller_daily_inactive_water_use', + 'last_changed': , + 'last_reported': , + 'last_updated': , + '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': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + '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': , + }) +# --- +# 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': , + }), + 'context': , + 'entity_id': 'sensor.home_controller_daily_total_water_use', + 'last_changed': , + 'last_reported': , + 'last_updated': , + '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': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + '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': , + }) +# --- +# 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': , + }), + 'context': , + 'entity_id': 'sensor.zone_one_daily_active_water_use', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '454.6279552584', + }) +# --- +# name: test_all_sensors[sensor.zone_one_next_cycle-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + 'entity_id': 'sensor.zone_one_next_cycle', + 'last_changed': , + 'last_reported': , + 'last_updated': , + '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': , + 'device_class': None, + 'device_id': , + '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': , + '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': , + }) +# --- +# 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': , + }), + 'context': , + 'entity_id': 'sensor.zone_one_watering_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + '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': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + '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': , + }) +# --- +# 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': , + }), + 'context': , + 'entity_id': 'sensor.zone_two_daily_active_water_use', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_all_sensors[sensor.zone_two_next_cycle-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + 'entity_id': 'sensor.zone_two_next_cycle', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_all_sensors[sensor.zone_two_watering_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + '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': , + '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': , + }) +# --- +# 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': , + }), + 'context': , + 'entity_id': 'sensor.zone_two_watering_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '29', + }) +# --- diff --git a/tests/components/hydrawise/snapshots/test_switch.ambr b/tests/components/hydrawise/snapshots/test_switch.ambr new file mode 100644 index 00000000000..977bd15f004 --- /dev/null +++ b/tests/components/hydrawise/snapshots/test_switch.ambr @@ -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': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + 'entity_id': 'switch.zone_one_automatic_watering', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_all_switches[switch.zone_one_manual_watering-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + 'entity_id': 'switch.zone_one_manual_watering', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_switches[switch.zone_two_automatic_watering-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + 'entity_id': 'switch.zone_two_automatic_watering', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_all_switches[switch.zone_two_manual_watering-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + 'entity_id': 'switch.zone_two_manual_watering', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- diff --git a/tests/components/hydrawise/test_binary_sensor.py b/tests/components/hydrawise/test_binary_sensor.py index f4702758136..6343b345d99 100644 --- a/tests/components/hydrawise/test_binary_sensor.py +++ b/tests/components/hydrawise/test_binary_sensor.py @@ -1,34 +1,34 @@ """Test Hydrawise binary_sensor.""" +from collections.abc import Awaitable, Callable from datetime import timedelta -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, patch from aiohttp import ClientError from freezegun.api import FrozenDateTimeFactory +from syrupy.assertion import SnapshotAssertion from homeassistant.components.hydrawise.const import SCAN_INTERVAL +from homeassistant.const import Platform 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, - mock_added_config_entry: MockConfigEntry, - freezer: FrozenDateTimeFactory, + mock_add_config_entry: Callable[[], Awaitable[MockConfigEntry]], + entity_registry: er.EntityRegistry, + snapshot: SnapshotAssertion, ) -> None: - """Test binary_sensor states.""" - connectivity = hass.states.get("binary_sensor.home_controller_connectivity") - assert connectivity is not None - assert connectivity.state == "on" - - watering1 = hass.states.get("binary_sensor.zone_one_watering") - assert watering1 is not None - assert watering1.state == "off" - - watering2 = hass.states.get("binary_sensor.zone_two_watering") - assert watering2 is not None - assert watering2.state == "on" + """Test that all binary sensors are working.""" + with patch( + "homeassistant.components.hydrawise.PLATFORMS", + [Platform.BINARY_SENSOR], + ): + config_entry = await mock_add_config_entry() + await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id) async def test_update_data_fails( diff --git a/tests/components/hydrawise/test_sensor.py b/tests/components/hydrawise/test_sensor.py index f0edb79b349..fcbc47c41f4 100644 --- a/tests/components/hydrawise/test_sensor.py +++ b/tests/components/hydrawise/test_sensor.py @@ -1,34 +1,33 @@ """Test Hydrawise sensor.""" from collections.abc import Awaitable, Callable +from unittest.mock import patch -from freezegun.api import FrozenDateTimeFactory -from pydrawise.schema import Zone +from pydrawise.schema import Controller, Zone import pytest +from syrupy.assertion import SnapshotAssertion +from homeassistant.const import Platform 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") -async def test_states( +async def test_all_sensors( hass: HomeAssistant, - mock_added_config_entry: MockConfigEntry, - freezer: FrozenDateTimeFactory, + mock_add_config_entry: Callable[[], Awaitable[MockConfigEntry]], + entity_registry: er.EntityRegistry, + snapshot: SnapshotAssertion, ) -> None: - """Test sensor states.""" - watering_time1 = hass.states.get("sensor.zone_one_watering_time") - assert watering_time1 is not None - assert watering_time1.state == "0" - - watering_time2 = hass.states.get("sensor.zone_two_watering_time") - assert watering_time2 is not None - 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" + """Test that all sensors are working.""" + with patch( + "homeassistant.components.hydrawise.PLATFORMS", + [Platform.SENSOR], + ): + config_entry = await mock_add_config_entry() + await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id) @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") 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" diff --git a/tests/components/hydrawise/test_switch.py b/tests/components/hydrawise/test_switch.py index f044d3467cd..ce60011b593 100644 --- a/tests/components/hydrawise/test_switch.py +++ b/tests/components/hydrawise/test_switch.py @@ -1,40 +1,41 @@ """Test Hydrawise switch.""" +from collections.abc import Awaitable, Callable from datetime import timedelta -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, patch from pydrawise.schema import Zone import pytest +from syrupy.assertion import SnapshotAssertion from homeassistant.components.hydrawise.const import DEFAULT_WATERING_TIME 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.helpers import entity_registry as er 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, - mock_added_config_entry: MockConfigEntry, + mock_add_config_entry: Callable[[], Awaitable[MockConfigEntry]], + entity_registry: er.EntityRegistry, + snapshot: SnapshotAssertion, ) -> None: - """Test switch states.""" - watering1 = hass.states.get("switch.zone_one_manual_watering") - assert watering1 is not None - assert watering1.state == "off" - - watering2 = hass.states.get("switch.zone_two_manual_watering") - assert watering2 is not None - 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" + """Test that all switches are working.""" + with patch( + "homeassistant.components.hydrawise.PLATFORMS", + [Platform.SWITCH], + ): + config_entry = await mock_add_config_entry() + await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id) async def test_manual_watering_services(