From d6c27809dc0e11f60269aa4114d59004925247da Mon Sep 17 00:00:00 2001 From: Clifford Roche Date: Fri, 3 Dec 2021 14:18:53 -0500 Subject: [PATCH] Gree update device ips when changed (#57876) --- homeassistant/components/gree/bridge.py | 7 +++ tests/components/gree/common.py | 32 ++++++++++-- tests/components/gree/test_bridge.py | 67 +++++++++++++++++++++++++ tests/components/gree/test_climate.py | 18 ++----- 4 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 tests/components/gree/test_bridge.py diff --git a/homeassistant/components/gree/bridge.py b/homeassistant/components/gree/bridge.py index 9a927d13d29..41ba4bd9842 100644 --- a/homeassistant/components/gree/bridge.py +++ b/homeassistant/components/gree/bridge.py @@ -103,3 +103,10 @@ class DiscoveryService(Listener): await coordo.async_refresh() async_dispatcher_send(self.hass, DISPATCH_DEVICE_DISCOVERED, coordo) + + async def device_update(self, device_info: DeviceInfo) -> None: + """Handle updates in device information, update if ip has changed.""" + for coordinator in self.hass.data[DOMAIN][COORDINATORS]: + if coordinator.device.device_info.mac == device_info.mac: + coordinator.device.device_info.ip = device_info.ip + await coordinator.async_refresh() diff --git a/tests/components/gree/common.py b/tests/components/gree/common.py index 40403377957..c7db03b118f 100644 --- a/tests/components/gree/common.py +++ b/tests/components/gree/common.py @@ -5,7 +5,10 @@ from unittest.mock import AsyncMock, Mock from greeclimate.discovery import Listener -from homeassistant.components.gree.const import DISCOVERY_TIMEOUT +from homeassistant.components.gree.const import DISCOVERY_TIMEOUT, DOMAIN as GREE_DOMAIN +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry _LOGGER = logging.getLogger(__name__) @@ -16,6 +19,7 @@ class FakeDiscovery: def __init__(self, timeout: int = DISCOVERY_TIMEOUT) -> None: """Initialize the class.""" self.mock_devices = [build_device_mock()] + self.last_mock_infos = [] self.timeout = timeout self._listeners = [] self.scan_count = 0 @@ -29,14 +33,27 @@ class FakeDiscovery: self.scan_count += 1 _LOGGER.info("CALLED SCAN %d TIMES", self.scan_count) - infos = [x.device_info for x in self.mock_devices] + mock_infos = [x.device_info for x in self.mock_devices] + + new_infos = [] + updated_infos = [] + for info in mock_infos: + if not [i for i in self.last_mock_infos if info.mac == i.mac]: + new_infos.append(info) + else: + last_info = next(i for i in self.last_mock_infos if info.mac == i.mac) + if info.ip != last_info.ip: + updated_infos.append(info) + + self.last_mock_infos = mock_infos for listener in self._listeners: - [await listener.device_found(x) for x in infos] + [await listener.device_found(x) for x in new_infos] + [await listener.device_update(x) for x in updated_infos] if wait_for: await asyncio.sleep(wait_for) - return infos + return new_infos def build_device_info_mock( @@ -71,3 +88,10 @@ def build_device_mock(name="fake-device-1", ipAddress="1.1.1.1", mac="aabbcc1122 steady_heat=False, ) return mock + + +async def async_setup_gree(hass): + """Set up the gree platform.""" + MockConfigEntry(domain=GREE_DOMAIN).add_to_hass(hass) + await async_setup_component(hass, GREE_DOMAIN, {GREE_DOMAIN: {"climate": {}}}) + await hass.async_block_till_done() diff --git a/tests/components/gree/test_bridge.py b/tests/components/gree/test_bridge.py new file mode 100644 index 00000000000..13522b1216b --- /dev/null +++ b/tests/components/gree/test_bridge.py @@ -0,0 +1,67 @@ +"""Tests for gree component.""" +from datetime import timedelta +from unittest.mock import patch + +import pytest + +from homeassistant.components.climate.const import DOMAIN +from homeassistant.components.gree.const import COORDINATORS, DOMAIN as GREE +import homeassistant.util.dt as dt_util + +from .common import async_setup_gree, build_device_mock + +from tests.common import async_fire_time_changed + +ENTITY_ID_1 = f"{DOMAIN}.fake_device_1" +ENTITY_ID_2 = f"{DOMAIN}.fake_device_2" + + +@pytest.fixture +def mock_now(): + """Fixture for dtutil.now.""" + return dt_util.utcnow() + + +async def test_discovery_after_setup(hass, discovery, device, mock_now): + """Test gree devices don't change after multiple discoveries.""" + mock_device_1 = build_device_mock( + name="fake-device-1", ipAddress="1.1.1.1", mac="aabbcc112233" + ) + mock_device_2 = build_device_mock( + name="fake-device-2", ipAddress="2.2.2.2", mac="bbccdd223344" + ) + + discovery.return_value.mock_devices = [mock_device_1, mock_device_2] + device.side_effect = [mock_device_1, mock_device_2] + + await async_setup_gree(hass) + await hass.async_block_till_done() + + assert discovery.return_value.scan_count == 1 + assert len(hass.states.async_all(DOMAIN)) == 2 + + device_infos = [x.device.device_info for x in hass.data[GREE][COORDINATORS]] + assert device_infos[0].ip == "1.1.1.1" + assert device_infos[1].ip == "2.2.2.2" + + # rediscover the same devices with new ip addresses should update + mock_device_1 = build_device_mock( + name="fake-device-1", ipAddress="1.1.1.2", mac="aabbcc112233" + ) + mock_device_2 = build_device_mock( + name="fake-device-2", ipAddress="2.2.2.1", mac="bbccdd223344" + ) + discovery.return_value.mock_devices = [mock_device_1, mock_device_2] + device.side_effect = [mock_device_1, mock_device_2] + + next_update = mock_now + timedelta(minutes=6) + with patch("homeassistant.util.dt.utcnow", return_value=next_update): + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + + assert discovery.return_value.scan_count == 2 + assert len(hass.states.async_all(DOMAIN)) == 2 + + device_infos = [x.device.device_info for x in hass.data[GREE][COORDINATORS]] + assert device_infos[0].ip == "1.1.1.2" + assert device_infos[1].ip == "2.2.2.1" diff --git a/tests/components/gree/test_climate.py b/tests/components/gree/test_climate.py index d88f6a6fbf0..ce1d8f3c705 100644 --- a/tests/components/gree/test_climate.py +++ b/tests/components/gree/test_climate.py @@ -43,11 +43,7 @@ from homeassistant.components.gree.climate import ( HVAC_MODES_REVERSE, SUPPORTED_FEATURES, ) -from homeassistant.components.gree.const import ( - DOMAIN as GREE_DOMAIN, - FAN_MEDIUM_HIGH, - FAN_MEDIUM_LOW, -) +from homeassistant.components.gree.const import FAN_MEDIUM_HIGH, FAN_MEDIUM_LOW from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, @@ -59,12 +55,11 @@ from homeassistant.const import ( TEMP_CELSIUS, TEMP_FAHRENHEIT, ) -from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from .common import build_device_mock +from .common import async_setup_gree, build_device_mock -from tests.common import MockConfigEntry, async_fire_time_changed +from tests.common import async_fire_time_changed ENTITY_ID = f"{DOMAIN}.fake_device_1" @@ -75,13 +70,6 @@ def mock_now(): return dt_util.utcnow() -async def async_setup_gree(hass): - """Set up the gree platform.""" - MockConfigEntry(domain=GREE_DOMAIN).add_to_hass(hass) - await async_setup_component(hass, GREE_DOMAIN, {GREE_DOMAIN: {"climate": {}}}) - await hass.async_block_till_done() - - async def test_discovery_called_once(hass, discovery, device): """Test discovery is only ever called once.""" await async_setup_gree(hass)