From d228df6d818f663b75a2efd9696d7507789b8a3e Mon Sep 17 00:00:00 2001
From: "Erik J. Olson" <hello@erikjolson.com>
Date: Tue, 28 Mar 2023 07:56:10 -0500
Subject: [PATCH] Fix Notify Group payload data mis-merge (#90253)

Co-authored-by: Erik Montnemery <erik@montnemery.com>
---
 homeassistant/components/group/notify.py | 24 ++++++++++-----------
 tests/components/group/test_notify.py    | 27 +++++++++++++++++++++---
 2 files changed, 35 insertions(+), 16 deletions(-)

diff --git a/homeassistant/components/group/notify.py b/homeassistant/components/group/notify.py
index 7e8ce923649..378a7852343 100644
--- a/homeassistant/components/group/notify.py
+++ b/homeassistant/components/group/notify.py
@@ -32,18 +32,16 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
 )
 
 
-def update(input_dict: dict[str, Any], update_source: dict[str, Any]) -> dict[str, Any]:
-    """Deep update a dictionary.
-
-    Async friendly.
-    """
-    for key, val in update_source.items():
+def add_defaults(
+    input_data: dict[str, Any], default_data: dict[str, Any]
+) -> dict[str, Any]:
+    """Deep update a dictionary with default values."""
+    for key, val in default_data.items():
         if isinstance(val, Mapping):
-            recurse = update(input_dict.get(key, {}), val)  # type: ignore[arg-type]
-            input_dict[key] = recurse
-        else:
-            input_dict[key] = update_source[key]
-    return input_dict
+            input_data[key] = add_defaults(input_data.get(key, {}), val)  # type: ignore[arg-type]
+        elif key not in input_data:
+            input_data[key] = val
+    return input_data
 
 
 async def async_get_service(
@@ -71,8 +69,8 @@ class GroupNotifyPlatform(BaseNotificationService):
         tasks: list[asyncio.Task[bool | None]] = []
         for entity in self.entities:
             sending_payload = deepcopy(payload.copy())
-            if (data := entity.get(ATTR_DATA)) is not None:
-                update(sending_payload, data)
+            if (default_data := entity.get(ATTR_DATA)) is not None:
+                add_defaults(sending_payload, default_data)
             tasks.append(
                 asyncio.create_task(
                     self.hass.services.async_call(
diff --git a/tests/components/group/test_notify.py b/tests/components/group/test_notify.py
index 6e4f9b50393..77569c80f0f 100644
--- a/tests/components/group/test_notify.py
+++ b/tests/components/group/test_notify.py
@@ -54,14 +54,14 @@ async def test_send_message_with_data(hass: HomeAssistant) -> None:
                     "service": "demo2",
                     "data": {
                         "target": "unnamed device",
-                        "data": {"test": "message"},
+                        "data": {"test": "message", "default": "default"},
                     },
                 },
             ]
         },
     )
 
-    """Test sending a message with to a notify group."""
+    """Test sending a message to a notify group."""
     await service.async_send_message(
         "Hello", title="Test notification", data={"hello": "world"}
     )
@@ -77,7 +77,28 @@ async def test_send_message_with_data(hass: HomeAssistant) -> None:
     assert service2.send_message.mock_calls[0][2] == {
         "target": ["unnamed device"],
         "title": "Test notification",
-        "data": {"hello": "world", "test": "message"},
+        "data": {"hello": "world", "test": "message", "default": "default"},
+    }
+
+    """Test sending a message which overrides service defaults to a notify group."""
+    await service.async_send_message(
+        "Hello",
+        title="Test notification",
+        data={"hello": "world", "default": "override"},
+    )
+
+    await hass.async_block_till_done()
+
+    assert service1.send_message.mock_calls[1][1][0] == "Hello"
+    assert service1.send_message.mock_calls[1][2] == {
+        "title": "Test notification",
+        "data": {"hello": "world", "default": "override"},
+    }
+    assert service2.send_message.mock_calls[1][1][0] == "Hello"
+    assert service2.send_message.mock_calls[1][2] == {
+        "target": ["unnamed device"],
+        "title": "Test notification",
+        "data": {"hello": "world", "test": "message", "default": "override"},
     }
 
 
-- 
GitLab