Repository: XKNX/xknx
Pull request #1091 (2022-11-28 19:43:04)

Add `respond_to_read` to Notification
<!--
  You are awesome! Thanks for contributing to our project!
  Please, DO NOT DELETE ANY TEXT from this template!.
-->
## Description
<!--
Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context.
List any dependencies that are required for this change.
-->


Fixes # (issue)

## Type of change
<!--
Please tick the applicable options.
NOTE: Ticking multiple options most likely indicates
that your change is to big and it is suggested to split it into several smaller PRs.
-->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update

## Checklist:

- [ ] The documentation has been adjusted accordingly
- [ ] Tests have been added that prove the fix is effective or that the feature works
- [x] The changes are documented in the changelog (docs/changelog.md)

codecov[bot]: # [Codecov](https://codecov.io/gh/XKNX/xknx/pull/1091?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX) Report
> Merging [#1091](https://codecov.io/gh/XKNX/xknx/pull/1091?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX) (e79cf30) into [main](https://codecov.io/gh/XKNX/xknx/commit/e45c28079d01cad307de42a4d58b9281d50c1910?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX) (e45c280) will **decrease** coverage by `0.02%`.
> The diff coverage is `81.81%`.

[![Impacted file tree graph](https://codecov.io/gh/XKNX/xknx/pull/1091/graphs/tree.svg?width=650&height=150&src=pr&token=irWbIygS84&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX)](https://codecov.io/gh/XKNX/xknx/pull/1091?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX)

```diff
@@            Coverage Diff             @@
##             main    #1091      +/-   ##
==========================================
- Coverage   96.30%   96.28%   -0.03%     
==========================================
  Files         138      138              
  Lines        8637     8639       +2     
==========================================
  Hits         8318     8318              
- Misses        319      321       +2     
```


| [Impacted Files](https://codecov.io/gh/XKNX/xknx/pull/1091?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX) | Coverage Δ | |
|---|---|---|
| [xknx/devices/notification.py](https://codecov.io/gh/XKNX/xknx/pull/1091/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9kZXZpY2VzL25vdGlmaWNhdGlvbi5weQ==) | `92.00% <81.81%> (-8.00%)` | :arrow_down: |


Patch:
diff --git a/docs/changelog.md b/docs/changelog.md
index ce085b57..6ca38d12 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -6,6 +6,13 @@ nav_order: 2
 
 # Changelog
 
+# Unreleased changes
+
+### Devices
+
+- Notification: Add `respond_to_read` option.
+- Notification: Rename `self._message` to `self.remote_value`.
+
 # 2.0.0 Find and Connect 2022-11-25
 
 ### Interface changes
diff --git a/xknx/devices/notification.py b/xknx/devices/notification.py
index e4bf05e1..3704ed43 100644
--- a/xknx/devices/notification.py
+++ b/xknx/devices/notification.py
@@ -1,8 +1,7 @@
-"""Module for managing a notification via KNX."""
+"""Module for managing a text via KNX."""
 from __future__ import annotations
 
 from collections.abc import Iterator
-import logging
 from typing import TYPE_CHECKING
 
 from xknx.remote_value import GroupAddressesType, RemoteValueString
@@ -13,8 +12,6 @@ if TYPE_CHECKING:
     from xknx.telegram import Telegram
     from xknx.xknx import XKNX
 
-logger = logging.getLogger("xknx.log")
-
 
 class Notification(Device):
     """Class for managing a notification."""
@@ -25,42 +22,52 @@ class Notification(Device):
         name: str,
         group_address: GroupAddressesType | None = None,
         group_address_state: GroupAddressesType | None = None,
+        respond_to_read: bool = False,
         sync_state: bool | int | float | str = True,
-        value_type: str | None = None,
+        value_type: int | str | None = None,
         device_updated_cb: DeviceCallbackType[Notification] | None = None,
     ):
         """Initialize notification class."""
         super().__init__(xknx, name, device_updated_cb)
 
-        self._message = RemoteValueString(
+        self.respond_to_read = respond_to_read
+        self.remote_value = RemoteValueString(
             xknx,
             group_address=group_address,
             group_address_state=group_address_state,
             sync_state=sync_state,
             value_type=value_type,
             device_name=name,
-            feature_name="Message",
+            feature_name="Text",
             after_update_cb=self.after_update,
         )
 
     def _iter_remote_values(self) -> Iterator[RemoteValueString]:
         """Iterate the devices RemoteValue classes."""
-        yield self._message
+        yield self.remote_value
 
     @property
     def message(self) -> str | None:
         """Return the current message."""
-        return self._message.value
+        return self.remote_value.value
 
     async def set(self, message: str) -> None:
         """Set message."""
         cropped_message = message[:14]
-        await self._message.set(cropped_message)
+        await self.remote_value.set(cropped_message)
 
-    async def process_group_write(self, telegram: "Telegram") -> None:
+    async def process_group_write(self, telegram: Telegram) -> None:
         """Process incoming and outgoing GROUP WRITE telegram."""
-        await self._message.process(telegram)
+        await self.remote_value.process(telegram)
+
+    async def process_group_read(self, telegram: Telegram) -> None:
+        """Process incoming GroupValueResponse telegrams."""
+        if (
+            self.respond_to_read
+            and telegram.destination_address == self.remote_value.group_address
+        ):
+            await self.remote_value.respond()
 
     def __str__(self) -> str:
         """Return object as readable string."""
-        return f'<Notification name="{self.name}" message={self._message.group_addr_str()} />'
+        return f'<Notification name="{self.name}" message={self.remote_value.group_addr_str()} />'


Test patch:
diff --git a/test/devices_tests/notification_test.py b/test/devices_tests/notification_test.py
index 4f5deb26..8d046f07 100644
--- a/test/devices_tests/notification_test.py
+++ b/test/devices_tests/notification_test.py
@@ -5,7 +5,7 @@ from xknx import XKNX
 from xknx.devices import Notification
 from xknx.dpt import DPTArray, DPTBinary, DPTString
 from xknx.telegram import GroupAddress, Telegram
-from xknx.telegram.apci import GroupValueRead, GroupValueWrite
+from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite
 
 
 class TestNotification:
@@ -95,6 +95,65 @@ class TestNotification:
             log_mock.assert_called_once()
             cb_mock.assert_not_called()
 
+    #
+    # TEST RESPOND
+    #
+    async def test_respond_to_read(self):
+        """Test respond_to_read function."""
+        xknx = XKNX()
+        responding = Notification(
+            xknx,
+            "TestSensor1",
+            group_address="1/1/1",
+            respond_to_read=True,
+            value_type="latin_1",
+        )
+        non_responding = Notification(
+            xknx,
+            "TestSensor2",
+            group_address="1/1/1",
+            respond_to_read=False,
+            value_type="latin_1",
+        )
+        #  set initial payload of Notification
+        responding.remote_value.value = "Halli Hallo"
+        non_responding.remote_value.value = "Halli Hallo"
+
+        read_telegram = Telegram(
+            destination_address=GroupAddress("1/1/1"), payload=GroupValueRead()
+        )
+        # verify no response when respond_to_read is False
+        await non_responding.process(read_telegram)
+        assert xknx.telegrams.qsize() == 0
+
+        # verify response when respond_to_read is True
+        await responding.process(read_telegram)
+        assert xknx.telegrams.qsize() == 1
+        response = xknx.telegrams.get_nowait()
+        assert response == Telegram(
+            destination_address=GroupAddress("1/1/1"),
+            payload=GroupValueResponse(
+                DPTArray(
+                    (
+                        0x48,
+                        0x61,
+                        0x6C,
+                        0x6C,
+                        0x69,
+                        0x20,
+                        0x48,
+                        0x61,
+                        0x6C,
+                        0x6C,
+                        0x6F,
+                        0x0,
+                        0x0,
+                        0x0,
+                    ),
+                ),
+            ),
+        )
+
     #
     # TEST SET MESSAGE
     #