From cab9f821a1dfc3c80096ad3c60aab5b9f08ee2ab Mon Sep 17 00:00:00 2001
From: "J. Nick Koston" <nick@koston.org>
Date: Thu, 11 Nov 2021 00:31:08 -0600
Subject: [PATCH] Fix zeroconf with sonos v1 firmware (#59460)

---
 homeassistant/components/sonos/config_flow.py |  2 +-
 homeassistant/components/sonos/helpers.py     |  7 ++-
 tests/components/sonos/test_config_flow.py    | 50 +++++++++++++++++++
 tests/components/sonos/test_helpers.py        |  6 +++
 4 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/homeassistant/components/sonos/config_flow.py b/homeassistant/components/sonos/config_flow.py
index 745d3db3890..98e1194ebd0 100644
--- a/homeassistant/components/sonos/config_flow.py
+++ b/homeassistant/components/sonos/config_flow.py
@@ -30,7 +30,7 @@ class SonosDiscoveryFlowHandler(DiscoveryFlowHandler):
     ) -> FlowResult:
         """Handle a flow initialized by zeroconf."""
         hostname = discovery_info["hostname"]
-        if hostname is None or not hostname.startswith("Sonos-"):
+        if hostname is None or not hostname.lower().startswith("sonos"):
             return self.async_abort(reason="not_sonos_device")
         await self.async_set_unique_id(self._domain, raise_on_progress=False)
         host = discovery_info[CONF_HOST]
diff --git a/homeassistant/components/sonos/helpers.py b/homeassistant/components/sonos/helpers.py
index 01a75eb7747..490bcdefba5 100644
--- a/homeassistant/components/sonos/helpers.py
+++ b/homeassistant/components/sonos/helpers.py
@@ -44,5 +44,10 @@ def soco_error(errorcodes: list[str] | None = None) -> Callable:
 
 def hostname_to_uid(hostname: str) -> str:
     """Convert a Sonos hostname to a uid."""
-    baseuid = hostname.split("-")[1].replace(".local.", "")
+    if hostname.startswith("Sonos-"):
+        baseuid = hostname.split("-")[1].replace(".local.", "")
+    elif hostname.startswith("sonos"):
+        baseuid = hostname[5:].replace(".local.", "")
+    else:
+        raise ValueError(f"{hostname} is not a sonos device.")
     return f"{UID_PREFIX}{baseuid}{UID_POSTFIX}"
diff --git a/tests/components/sonos/test_config_flow.py b/tests/components/sonos/test_config_flow.py
index 39f3966e2ce..7d6fd02f51d 100644
--- a/tests/components/sonos/test_config_flow.py
+++ b/tests/components/sonos/test_config_flow.py
@@ -75,6 +75,56 @@ async def test_zeroconf_form(hass: core.HomeAssistant):
     assert len(mock_manager.mock_calls) == 2
 
 
+async def test_zeroconf_sonos_v1(hass: core.HomeAssistant):
+    """Test we pass sonos devices to the discovery manager with v1 firmware devices."""
+
+    mock_manager = hass.data[DATA_SONOS_DISCOVERY_MANAGER] = MagicMock()
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN,
+        context={"source": config_entries.SOURCE_ZEROCONF},
+        data={
+            "host": "192.168.1.107",
+            "port": 1443,
+            "hostname": "sonos5CAAFDE47AC8.local.",
+            "type": "_sonos._tcp.local.",
+            "name": "Sonos-5CAAFDE47AC8._sonos._tcp.local.",
+            "properties": {
+                "_raw": {
+                    "info": b"/api/v1/players/RINCON_5CAAFDE47AC801400/info",
+                    "vers": b"1",
+                    "protovers": b"1.18.9",
+                },
+                "info": "/api/v1/players/RINCON_5CAAFDE47AC801400/info",
+                "vers": "1",
+                "protovers": "1.18.9",
+            },
+        },
+    )
+    assert result["type"] == "form"
+    assert result["errors"] is None
+
+    with patch(
+        "homeassistant.components.sonos.async_setup",
+        return_value=True,
+    ) as mock_setup, patch(
+        "homeassistant.components.sonos.async_setup_entry",
+        return_value=True,
+    ) as mock_setup_entry:
+        result2 = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            {},
+        )
+        await hass.async_block_till_done()
+
+    assert result2["type"] == "create_entry"
+    assert result2["title"] == "Sonos"
+    assert result2["data"] == {}
+
+    assert len(mock_setup.mock_calls) == 1
+    assert len(mock_setup_entry.mock_calls) == 1
+    assert len(mock_manager.mock_calls) == 2
+
+
 async def test_zeroconf_form_not_sonos(hass: core.HomeAssistant):
     """Test we abort on non-sonos devices."""
     mock_manager = hass.data[DATA_SONOS_DISCOVERY_MANAGER] = MagicMock()
diff --git a/tests/components/sonos/test_helpers.py b/tests/components/sonos/test_helpers.py
index a52337f9455..be32d3a190b 100644
--- a/tests/components/sonos/test_helpers.py
+++ b/tests/components/sonos/test_helpers.py
@@ -1,9 +1,15 @@
 """Test the sonos config flow."""
 from __future__ import annotations
 
+import pytest
+
 from homeassistant.components.sonos.helpers import hostname_to_uid
 
 
 async def test_uid_to_hostname():
     """Test we can convert a hostname to a uid."""
     assert hostname_to_uid("Sonos-347E5C0CF1E3.local.") == "RINCON_347E5C0CF1E301400"
+    assert hostname_to_uid("sonos5CAAFDE47AC8.local.") == "RINCON_5CAAFDE47AC801400"
+
+    with pytest.raises(ValueError):
+        assert hostname_to_uid("notsonos5CAAFDE47AC8.local.")
-- 
GitLab