Add device info to Hydrawise (#100828)

* Add device info to Hydrawise

* Apply suggestions from code review

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* Remove _attr_has_entity_name

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
David Knowles 2023-09-26 03:15:20 -04:00 committed by GitHub
parent 7b1b189f3e
commit 8ba6fd7935
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 143 additions and 9 deletions

View file

@ -1,7 +1,7 @@
"""Support for Hydrawise cloud."""
from pydrawise.legacy import LegacyHydrawise
from pydrawise import legacy
from requests.exceptions import ConnectTimeout, HTTPError
import voluptuous as vol
@ -54,7 +54,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
"""Set up Hydrawise from a config entry."""
access_token = config_entry.data[CONF_API_KEY]
try:
hydrawise = await hass.async_add_executor_job(LegacyHydrawise, access_token)
hydrawise = await hass.async_add_executor_job(
legacy.LegacyHydrawise, access_token
)
except (ConnectTimeout, HTTPError) as ex:
LOGGER.error("Unable to connect to Hydrawise cloud service: %s", str(ex))
raise ConfigEntryNotReady(

View file

@ -77,6 +77,7 @@ async def async_setup_entry(
data=hydrawise.current_controller,
coordinator=coordinator,
description=BINARY_SENSOR_STATUS,
device_id_key="controller_id",
)
]

View file

@ -11,6 +11,8 @@ CONF_WATERING_TIME = "watering_minutes"
DOMAIN = "hydrawise"
DEFAULT_WATERING_TIME = 15
MANUFACTURER = "Hydrawise"
SCAN_INTERVAL = timedelta(seconds=120)
SIGNAL_UPDATE_HYDRAWISE = "hydrawise_update"

View file

@ -3,9 +3,11 @@ from __future__ import annotations
from typing import Any
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER
from .coordinator import HydrawiseDataUpdateCoordinator
@ -20,14 +22,16 @@ class HydrawiseEntity(CoordinatorEntity[HydrawiseDataUpdateCoordinator]):
data: dict[str, Any],
coordinator: HydrawiseDataUpdateCoordinator,
description: EntityDescription,
device_id_key: str = "relay_id",
) -> None:
"""Initialize the Hydrawise entity."""
super().__init__(coordinator=coordinator)
self.data = data
self.entity_description = description
self._attr_name = f"{self.data['name']} {description.name}"
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
return {"identifier": self.data.get("relay")}
self._device_id = str(data.get(device_id_key))
self._attr_unique_id = f"{self._device_id}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self._device_id)},
name=data["name"],
manufacturer=MANUFACTURER,
)

View file

@ -1,10 +1,17 @@
"""Common fixtures for the Hydrawise tests."""
from collections.abc import Generator
from unittest.mock import AsyncMock, patch
from typing import Any
from unittest.mock import AsyncMock, Mock, patch
import pytest
from homeassistant.components.hydrawise.const import DOMAIN
from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
@ -13,3 +20,85 @@ def mock_setup_entry() -> Generator[AsyncMock, None, None]:
"homeassistant.components.hydrawise.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
def mock_pydrawise(
mock_controller: dict[str, Any],
mock_zones: list[dict[str, Any]],
) -> Generator[Mock, None, None]:
"""Mock LegacyHydrawise."""
with patch("pydrawise.legacy.LegacyHydrawise", autospec=True) as mock_pydrawise:
mock_pydrawise.return_value.controller_info = {"controllers": [mock_controller]}
mock_pydrawise.return_value.current_controller = mock_controller
mock_pydrawise.return_value.controller_status = {"relays": mock_zones}
mock_pydrawise.return_value.relays = mock_zones
yield mock_pydrawise.return_value
@pytest.fixture
def mock_controller() -> dict[str, Any]:
"""Mock Hydrawise controller."""
return {
"name": "Home Controller",
"last_contact": 1693292420,
"serial_number": "0310b36090",
"controller_id": 52496,
"status": "Unknown",
}
@pytest.fixture
def mock_zones() -> list[dict[str, Any]]:
"""Mock Hydrawise zones."""
return [
{
"name": "Zone One",
"period": 259200,
"relay": 1,
"relay_id": 5965394,
"run": 1800,
"stop": 1,
"time": 330597,
"timestr": "Sat",
"type": 1,
},
{
"name": "Zone Two",
"period": 259200,
"relay": 2,
"relay_id": 5965395,
"run": 1788,
"stop": 1,
"time": 1,
"timestr": "Now",
"type": 106,
},
]
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Mock ConfigEntry."""
return MockConfigEntry(
title="Hydrawise",
domain=DOMAIN,
data={
CONF_API_KEY: "abc123",
},
unique_id="hydrawise-customerid",
)
@pytest.fixture
async def mock_added_config_entry(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_pydrawise: Mock,
) -> MockConfigEntry:
"""Mock ConfigEntry that's been added to HA."""
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert DOMAIN in hass.config_entries.async_domains()
return mock_config_entry

View file

@ -0,0 +1,36 @@
"""Tests for Hydrawise devices."""
from unittest.mock import Mock
from homeassistant.components.hydrawise.const import DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
def test_zones_in_device_registry(
hass: HomeAssistant, mock_added_config_entry: ConfigEntry, mock_pydrawise: Mock
) -> None:
"""Test that devices are added to the device registry."""
device_registry = dr.async_get(hass)
device1 = device_registry.async_get_device(identifiers={(DOMAIN, "5965394")})
assert device1 is not None
assert device1.name == "Zone One"
assert device1.manufacturer == "Hydrawise"
device2 = device_registry.async_get_device(identifiers={(DOMAIN, "5965395")})
assert device2 is not None
assert device2.name == "Zone Two"
assert device2.manufacturer == "Hydrawise"
def test_controller_in_device_registry(
hass: HomeAssistant, mock_added_config_entry: ConfigEntry, mock_pydrawise: Mock
) -> None:
"""Test that devices are added to the device registry."""
device_registry = dr.async_get(hass)
device = device_registry.async_get_device(identifiers={(DOMAIN, "52496")})
assert device is not None
assert device.name == "Home Controller"
assert device.manufacturer == "Hydrawise"