Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add flow and rain sensor support to Hydrawise #116303

Merged
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
62 changes: 43 additions & 19 deletions homeassistant/components/hydrawise/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,27 @@
from .coordinator import HydrawiseDataUpdateCoordinator
from .entity import HydrawiseEntity

BINARY_SENSOR_STATUS = BinarySensorEntityDescription(
key="status",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
CONTROLLER_BINARY_SENSORS: tuple[BinarySensorEntityDescription, ...] = (
BinarySensorEntityDescription(
key="status", device_class=BinarySensorDeviceClass.CONNECTIVITY
),
)

BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = (
RAIN_SENSOR_BINARY_SENSOR: tuple[BinarySensorEntityDescription, ...] = (
BinarySensorEntityDescription(
key="is_watering",
translation_key="watering",
key="rain_sensor",
translation_key="rain_sensor",
device_class=BinarySensorDeviceClass.MOISTURE,
),
)

BINARY_SENSOR_KEYS: list[str] = [
desc.key for desc in (BINARY_SENSOR_STATUS, *BINARY_SENSOR_TYPES)
]
ZONE_BINARY_SENSORS: tuple[BinarySensorEntityDescription, ...] = (
BinarySensorEntityDescription(
key="is_watering",
translation_key="watering",
device_class=BinarySensorDeviceClass.RUNNING,
),
)


async def async_setup_entry(
Expand All @@ -42,15 +47,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,
sensor=sensor,
)
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)
HydrawiseBinarySensor(coordinator, description, controller, zone=zone)
for zone in controller.zones
for description in BINARY_SENSOR_TYPES
for description in ZONE_BINARY_SENSORS
)
async_add_entities(entities)

Expand All @@ -60,8 +77,15 @@ class HydrawiseBinarySensor(HydrawiseEntity, BinarySensorEntity):

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 = getattr(self, f"_get_{self.entity_description.key}")()

def _get_status(self) -> bool:
return self.coordinator.last_update_success

def _get_rain_sensor(self) -> bool | None:
assert self.sensor is not None
return self.sensor.status.active

def _get_is_watering(self) -> bool:
assert self.zone is not None
return self.zone.scheduled_runs.current_run is not None
thomaskistler marked this conversation as resolved.
Show resolved Hide resolved
35 changes: 30 additions & 5 deletions homeassistant/components/hydrawise/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
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

Expand All @@ -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)
Expand All @@ -40,8 +43,30 @@ async def _async_update_data(self) -> 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(
thomaskistler marked this conversation as resolved.
Show resolved Hide resolved
"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,
)
26 changes: 19 additions & 7 deletions homeassistant/components/hydrawise/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,24 +24,38 @@ def __init__(
coordinator: HydrawiseDataUpdateCoordinator,
description: EntityDescription,
controller: Controller,
*,
zone: Zone | None = None,
thomaskistler marked this conversation as resolved.
Show resolved Hide resolved
sensor: Sensor | None = None,
thomaskistler marked this conversation as resolved.
Show resolved Hide resolved
) -> 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 if zone else None
self.sensor_id = sensor.id if sensor else None
self._device_id = str(zone.id) if zone 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=zone.name if zone is not None else controller.name,
model="Zone" if zone is not None else controller.hardware.model.description,
thomaskistler marked this conversation as resolved.
Show resolved Hide resolved
manufacturer=MANUFACTURER,
)
if zone is not None:
if zone is not None or sensor is not None:
self._attr_device_info["via_device"] = (DOMAIN, str(controller.id))
self._update_attrs()

@property
def zone(self) -> Zone | None:
"""Return the entity zone."""
return self.coordinator.data.zones[self.zone_id] if self.zone_id else None
thomaskistler marked this conversation as resolved.
Show resolved Hide resolved

@property
def sensor(self) -> Sensor | None:
"""Return the entity sensor."""
return self.coordinator.data.sensors[self.sensor_id] if self.sensor_id else None

def _update_attrs(self) -> None:
"""Update state attributes."""
return # pragma: no cover
Expand All @@ -50,7 +64,5 @@ def _update_attrs(self) -> None:
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()
23 changes: 22 additions & 1 deletion homeassistant/components/hydrawise/icons.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
Expand Down