From d2cc25cee68b4185d172dc28c98bcedb886082cc Mon Sep 17 00:00:00 2001
From: Shay Levy <levyshay1@gmail.com>
Date: Thu, 18 Jul 2024 23:27:03 +0300
Subject: [PATCH] Prevent connecting to a Shelly device that is already
 connected (#122105)

---
 .../components/shelly/coordinator.py          |  3 +++
 tests/components/shelly/conftest.py           |  1 +
 tests/components/shelly/test_binary_sensor.py |  1 +
 tests/components/shelly/test_config_flow.py   |  1 +
 tests/components/shelly/test_coordinator.py   | 20 +++++++++++++++++++
 tests/components/shelly/test_init.py          |  2 ++
 tests/components/shelly/test_sensor.py        |  2 ++
 tests/components/shelly/test_update.py        |  1 +
 8 files changed, 31 insertions(+)

diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py
index a4b848bb7f2..50140e1890d 100644
--- a/homeassistant/components/shelly/coordinator.py
+++ b/homeassistant/components/shelly/coordinator.py
@@ -669,6 +669,9 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]):
         """Handle device update."""
         LOGGER.debug("Shelly %s handle update, type: %s", self.name, update_type)
         if update_type is RpcUpdateType.ONLINE:
+            if self.device.connected:
+                LOGGER.debug("Device %s already connected", self.name)
+                return
             self.entry.async_create_background_task(
                 self.hass,
                 self._async_device_connect_task(),
diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py
index e9662b55cf5..a2629d21362 100644
--- a/tests/components/shelly/conftest.py
+++ b/tests/components/shelly/conftest.py
@@ -353,6 +353,7 @@ def _mock_rpc_device(version: str | None = None):
         status=MOCK_STATUS_RPC,
         firmware_version="some fw string",
         initialized=True,
+        connected=True,
     )
     type(device).name = PropertyMock(return_value="Test name")
     return device
diff --git a/tests/components/shelly/test_binary_sensor.py b/tests/components/shelly/test_binary_sensor.py
index b90d89b8e48..18f65deb907 100644
--- a/tests/components/shelly/test_binary_sensor.py
+++ b/tests/components/shelly/test_binary_sensor.py
@@ -265,6 +265,7 @@ async def test_rpc_sleeping_binary_sensor(
 ) -> None:
     """Test RPC online sleeping binary sensor."""
     entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_cloud"
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
     config_entry = await init_integration(hass, 2, sleep_period=1000)
 
diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py
index a26c6eac405..a3040fc2eb8 100644
--- a/tests/components/shelly/test_config_flow.py
+++ b/tests/components/shelly/test_config_flow.py
@@ -1114,6 +1114,7 @@ async def test_zeroconf_sleeping_device_not_triggers_refresh(
     caplog: pytest.LogCaptureFixture,
 ) -> None:
     """Test zeroconf discovery does not triggers refresh for sleeping device."""
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
     entry = MockConfigEntry(
         domain="shelly",
diff --git a/tests/components/shelly/test_coordinator.py b/tests/components/shelly/test_coordinator.py
index 35123a2db91..d3494c094f9 100644
--- a/tests/components/shelly/test_coordinator.py
+++ b/tests/components/shelly/test_coordinator.py
@@ -545,6 +545,7 @@ async def test_rpc_update_entry_sleep_period(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     """Test RPC update entry sleep period."""
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 600)
     entry = await init_integration(hass, 2, sleep_period=600)
     register_entity(
@@ -578,6 +579,7 @@ async def test_rpc_sleeping_device_no_periodic_updates(
 ) -> None:
     """Test RPC sleeping device no periodic updates."""
     entity_id = f"{SENSOR_DOMAIN}.test_name_temperature"
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
     entry = await init_integration(hass, 2, sleep_period=1000)
     register_entity(
@@ -609,6 +611,7 @@ async def test_rpc_sleeping_device_firmware_unsupported(
     issue_registry: ir.IssueRegistry,
 ) -> None:
     """Test RPC sleeping device firmware not supported."""
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     monkeypatch.setattr(mock_rpc_device, "firmware_supported", False)
     entry = await init_integration(hass, 2, sleep_period=3600)
 
@@ -912,6 +915,7 @@ async def test_rpc_sleeping_device_connection_error(
         hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud-cloud", entry
     )
     mock_restore_cache(hass, [State(entity_id, STATE_ON)])
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     monkeypatch.setattr(mock_rpc_device, "initialized", False)
     await hass.config_entries.async_setup(entry.entry_id)
     await hass.async_block_till_done()
@@ -939,3 +943,19 @@ async def test_rpc_sleeping_device_connection_error(
 
     assert "Sleeping device did not update" in caplog.text
     assert get_entity_state(hass, entity_id) == STATE_UNAVAILABLE
+
+
+async def test_rpc_already_connected(
+    hass: HomeAssistant,
+    freezer: FrozenDateTimeFactory,
+    mock_rpc_device: Mock,
+    caplog: pytest.LogCaptureFixture,
+) -> None:
+    """Test RPC ignore connect event if already connected."""
+    await init_integration(hass, 2)
+
+    mock_rpc_device.mock_online()
+    await hass.async_block_till_done(wait_background_tasks=True)
+
+    assert "already connected" in caplog.text
+    mock_rpc_device.initialize.assert_called_once()
diff --git a/tests/components/shelly/test_init.py b/tests/components/shelly/test_init.py
index 998d56fc6cc..46698c23c0a 100644
--- a/tests/components/shelly/test_init.py
+++ b/tests/components/shelly/test_init.py
@@ -279,6 +279,7 @@ async def test_sleeping_rpc_device_online(
     caplog: pytest.LogCaptureFixture,
 ) -> None:
     """Test sleeping RPC device online."""
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", device_sleep)
     entry = await init_integration(hass, 2, sleep_period=entry_sleep)
     assert "will resume when device is online" in caplog.text
@@ -297,6 +298,7 @@ async def test_sleeping_rpc_device_online_new_firmware(
     caplog: pytest.LogCaptureFixture,
 ) -> None:
     """Test sleeping device Gen2 with firmware 1.0.0 or later."""
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     entry = await init_integration(hass, 2, sleep_period=None)
     assert "will resume when device is online" in caplog.text
 
diff --git a/tests/components/shelly/test_sensor.py b/tests/components/shelly/test_sensor.py
index 0148ba67208..a39123a6722 100644
--- a/tests/components/shelly/test_sensor.py
+++ b/tests/components/shelly/test_sensor.py
@@ -450,6 +450,7 @@ async def test_rpc_sleeping_sensor(
 ) -> None:
     """Test RPC online sleeping sensor."""
     entity_id = f"{SENSOR_DOMAIN}.test_name_temperature"
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
     entry = await init_integration(hass, 2, sleep_period=1000)
 
@@ -601,6 +602,7 @@ async def test_rpc_sleeping_update_entity_service(
     await async_setup_component(hass, "homeassistant", {})
 
     entity_id = f"{SENSOR_DOMAIN}.test_name_temperature"
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
     await init_integration(hass, 2, sleep_period=1000)
 
diff --git a/tests/components/shelly/test_update.py b/tests/components/shelly/test_update.py
index 8448c116815..721e86559a3 100644
--- a/tests/components/shelly/test_update.py
+++ b/tests/components/shelly/test_update.py
@@ -334,6 +334,7 @@ async def test_rpc_sleeping_update(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     """Test RPC sleeping device update entity."""
+    monkeypatch.setattr(mock_rpc_device, "connected", False)
     monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
     monkeypatch.setitem(mock_rpc_device.shelly, "ver", "1")
     monkeypatch.setitem(
-- 
GitLab