Repository: home-assistant/core
Pull request: Make acknowledge requests from LCN modules optional
<!--
  You are amazing! Thanks for contributing to our project!
  Please, DO NOT DELETE ANY TEXT from this template! (unless instructed).
-->
## Proposed change
LCN hardware modules offer the possibility to send acknowledge messages for each command, to ensure the command has been received. As the bandwidth of the LCN bus is limited, these messages can result in telegram collisions and are therefore mainly intended for troubleshooting.

The functionality to request acknowledgment messages is therefore made optional and can be selected during the integration (re-)configuration.

Changes require a dependency upgrade of pypck library:
Release notes: https://github.com/alengwenus/pypck/releases
Code changes: https://github.com/alengwenus/pypck/compare/0.7.22...0.7.23

## Type of change
<!--
  What type of change does your PR introduce to Home Assistant?
  NOTE: Please, check only 1! box!
  If your PR requires multiple boxes to be checked, you'll most likely need to
  split it into multiple PRs. This makes things easier and faster to code review.
-->

- [ ] Dependency upgrade
- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] New integration (thank you!)
- [x] New feature (which adds functionality to an existing integration)
- [ ] Deprecation (breaking change to happen in the future)
- [ ] Breaking change (fix/feature causing existing functionality to break)
- [ ] Code quality improvements to existing code or addition of tests

## Additional information
<!--
  Details are important, and help maintainers processing your PR.
  Please be sure to fill out additional details, if applicable.
-->

- This PR fixes or closes issue: fixes https://github.com/alengwenus/pypck/issues/113
- This PR is related to issue: 
- Link to documentation pull request: 

## Checklist
<!--
  Put an `x` in the boxes that apply. You can also fill these out after
  creating the PR. If you're unsure about any of them, don't hesitate to ask.
  We're here to help! This is simply a reminder of what we are going to look
  for before merging your code.
-->

- [x] The code change is tested and works locally.
- [x] Local tests pass. **Your PR cannot be merged unless tests pass**
- [x] There is no commented out code in this PR.
- [x] I have followed the [development checklist][dev-checklist]
- [x] I have followed the [perfect PR recommendations][perfect-pr]
- [x] The code has been formatted using Ruff (`ruff format homeassistant tests`)
- [x] Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

- [ ] Documentation added/updated for [www.home-assistant.io][docs-repository]

If the code communicates with devices, web services, or third-party tools:

- [x] The [manifest file][manifest-docs] has all fields filled out correctly.  
      Updated and included derived files by running: `python3 -m script.hassfest`.
- [x] New or updated dependencies have been added to `requirements_all.txt`.  
      Updated by running `python3 -m script.gen_requirements_all`.
- [x] For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

<!--
  This project is very active and we have a high turnover of pull requests.

  Unfortunately, the number of incoming pull requests is higher than what our
  reviewers can review and merge so there is a long backlog of pull requests
  waiting for review. You can help here!
  
  By reviewing another pull request, you will help raise the code quality of
  that pull request and the final review will be faster. This way the general
  pace of pull request reviews will go up and your wait time will go down.
  
  When picking a pull request to review, try to choose one that hasn't yet
  been reviewed.

  Thanks for helping out!
-->

To help with the load of incoming pull requests:

- [ ] I have reviewed two other [open pull requests][prs] in this repository.

[prs]: https://github.com/home-assistant/core/pulls?q=is%3Aopen+is%3Apr+-author%3A%40me+-draft%3Atrue+-label%3Awaiting-for-upstream+sort%3Acreated-desc+review%3Anone+-status%3Afailure

<!--
  Thank you for contributing <3

  Below, some useful links you could explore:
-->
[dev-checklist]: https://developers.home-assistant.io/docs/development_checklist/
[manifest-docs]: https://developers.home-assistant.io/docs/creating_integration_manifest/
[quality-scale]: https://developers.home-assistant.io/docs/integration_quality_scale_index/
[docs-repository]: https://github.com/home-assistant/home-assistant.io
[perfect-pr]: https://developers.home-assistant.io/docs/review-process/#creating-the-perfect-pr


Problem statement:
dyn text leads to dimmable leds to spike
Hi there,

I'm using dyn.text service to display outside temperature taken from external service on lcn panels. Whenever 5 parts of the dyn text are reaching the bus then dimmable gu10 bulbs are spiking (those which are connected to UPP together with lcd panel). Any ideas how to prevent this? 

Best regards,
K.M.

Hints from issue discussion:
I just took a brief look at the code.
https://github.com/alengwenus/pypck/blob/e0264103d46a49ed1a13117403c0187928252aba/pypck/module.py#L604
any reason for not breaking the loop here if **part** is empty?

```
text = " "
encoded_text = text.encode("utf-8")
parts = [encoded_text[12 * part : 12 * part + 12] for part in range(5)]
```

```
for part_id, part in enumerate(parts):
    print(part_id, part)
0 b' '
1 b''
2 b''
3 b''
4 b''
```
```
for part_id, part in enumerate(parts):
    if part:
        print(part_id, part)
    else:
        break
0 b' '
```

All parts of the displayed message can be addressed individually. If not all parts are sent, former messages get not cleared correctly from the display.
Thx for quick reply. I've played around with your code locally (outside HA).

and seems like I have found a workaround for my problem. If there's no confirmation from the bus then light doesn't blink..

if I switch is_group to False then it doesn't blink and there's no green confirmation in LCN PRO on the bus.
Directly here:
https://github.com/alengwenus/pypck/blob/e0264103d46a49ed1a13117403c0187928252aba/pypck/module.py#L606
..or by calling dyn_text_part separately using send_command myself.

I don't really need confirmation for sending part of something to be displayed on the panel. 

Is my understanding correct that it's similar to UDP/TCP?

```
import asyncio

from pypck.connection import PchkConnectionManager
from pypck.lcn_addr import LcnAddr
from pypck.pck_commands import PckGenerator

async def main():
    async with PchkConnectionManager(
        "**",
        4114,
        username="lcn",
        password="**",
        settings={"SK_NUM_TRIES": 0},
    ) as pck_client:
        module = pck_client.get_address_conn(LcnAddr(0, 32, False))

        text = "123456789012345678901234567890"
        await module.dyn_text(1, text)
        
        await module.send_command(
                False,
                PckGenerator.dyn_text_part(1, 0, text.encode("utf-8")),
            )

        #await asyncio.sleep(3)
        #await module.dim_output(2, 0, 0)

asyncio.run(main())
```
I'm not installer and I'm just working on existing installation in my place. By reading your comments in https://github.com/alengwenus/pypck/blob/dev/pypck/lcn_addr.py makes me thing it's something with my setup which causes your project to consider it multisegment or so? So at the end this makes 'is_group' to be True and forces acknowledges to be requested.

While operating with LCN outside HA, there are no acknowledges send for any operations (I see this on the bus). Only operations made by your project sends acks for any operation. Maybe it would be nice to have an option to disable acks in general or for specific commands?

Without acknolwedges, your HA integration performs a way faster for me and causes no issue like bulbs spikes or so.
Hi @alengwenus,

did you had any chance to consider this? In HAOS I can't simply override existing/official integration. The workaround is changing the code within docker images which are recovered to official version with each HAOS upgrade.

So question, can we have an option to disable acknowledges via configuration.yaml? :) 

Best regards,
K.M.
Hi @krismarc ,
implementing a `configuration.yaml` option to switch the achknowledge commands on/off would be quite easy.
Unfortunately there has been an [architectural decision](https://github.com/home-assistant/architecture/blob/master/adr/0010-integration-configuration.md) by the HA core developers to not accept new configuration options for integrations which still rely on a `configuration.yaml`. This has been decided in favour of UI configurations using config entries (that means the configuration is stored in the internal HA database).
In a nutshell this means, that the LCN integration has to be adapted, so it can be configured using the UI solely. I'm working on this for quite some time but as LCN has not the highest priority of all integrations, the review process is lengthy. However, you can monitor the progress [here](https://github.com/home-assistant/core/pull/108664).

As soon as the mentioned PR is merged, I would have the possibility to add an additional configuration option to switch the acknowledgement commands on/off.

@alengwenus thanks for sharing all those details. You're doing great job. Highly appreciated. The panel looks promising. Is it just a sort of frontend for existing LCN configuration and the panel will became available out of actual configuration? or is it something to be configured over again?

Regarding my request. No worries then. It's not super urgent, I'm rather  looking for long term solution. I'm happy to hear you're willing to support such feature 👍 

I'm looking further to become your tester 😄 
Once the panel got merged, the configuration from the configuration.yaml is imported and stored in the HA database on startup. You will then get a notification that you can safely delete the LCN configuration from the configuration.yaml.
The imported configuration can then be viewed/modified from the new UI panel.
Besides the import functionality you can of course start from scratch and set up a new configuration solely from the UI.

Unfortunately the PCK protocol does not allow to read-out the configs from the LCN modules. If that would be the case, I guess there would not be the need for the new panel because everything could be discovered on the fly. But well, that's the way it is with such a "closed" system like LCN.
Hi @alengwenus 
first of all, how did they dare to not mention this in HA changelog? 😢 
https://www.home-assistant.io/blog/2024/09/04/release-20249/#now-available-to-set-up-from-the-ui

I've just removed yaml LCN configuration and seems like UI way of configuration worked out of the box without any surprises. I just don't know yet how to put it into the side bar like in your screenshot attached in the PR
![image](https://github.com/user-attachments/assets/1dba49ae-2d56-4366-bc2b-c1fa2d95f8be)

. btw, I'll try using UI eg. adding new things etc. in the next couple of days.

First thing which came to my mind was that it would be nice to have an option to 'try' modules' entities from configuration view directly. Like switching outputs/relays states so while configuring you can test right away. Anyway, really great job!

I'm still looking for an option to disable acknowledgement commands, so my lights won't blink anymore 😃

Thank you very much for you time spent on this project!

Best regards,
K.M.

Hi @krismarc,
the panel link in the sidebar was just a workaround. Nowadays, it is not possible to add the panel to the sidebar anymore. The HA core developers offered a way to open panels like this via the integrations page using the `Configure` button.

I also started making the acknowledge commands optional, but unfortunately this has some side effects on other parts of the code which have to be changed first (e.g. https://github.com/home-assistant/core/pull/124411 and https://github.com/home-assistant/core/pull/124466). I'm on it.

Commit message: Add data_description to strings.json

Patch to review:
diff --git a/homeassistant/components/lcn/__init__.py b/homeassistant/components/lcn/__init__.py
index 96ffaddfb9356..a8d75fe56352a 100644
--- a/homeassistant/components/lcn/__init__.py
+++ b/homeassistant/components/lcn/__init__.py
@@ -22,6 +22,7 @@
 
 from .const import (
     ADD_ENTITIES_CALLBACKS,
+    CONF_ACKNOWLEDGE,
     CONF_DIM_MODE,
     CONF_SK_NUM_TRIES,
     CONNECTION,
@@ -73,6 +74,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
     settings = {
         "SK_NUM_TRIES": config_entry.data[CONF_SK_NUM_TRIES],
         "DIM_MODE": pypck.lcn_defs.OutputPortDimMode[config_entry.data[CONF_DIM_MODE]],
+        "ACKNOWLEDGE": config_entry.data[CONF_ACKNOWLEDGE],
     }
 
     # connect to PCHK
@@ -137,6 +139,32 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
     return True
 
 
+async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
+    """Migrate old entry."""
+    _LOGGER.debug(
+        "Migrating configuration from version %s.%s",
+        config_entry.version,
+        config_entry.minor_version,
+    )
+
+    if config_entry.version == 1:
+        new_data = {**config_entry.data}
+
+        if config_entry.minor_version < 2:
+            new_data[CONF_ACKNOWLEDGE] = False
+
+        hass.config_entries.async_update_entry(
+            config_entry, data=new_data, minor_version=2, version=1
+        )
+
+    _LOGGER.debug(
+        "Migration to configuration version %s.%s successful",
+        config_entry.version,
+        config_entry.minor_version,
+    )
+    return True
+
+
 async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
     """Close connection to PCHK host represented by config_entry."""
     # forward unloading to platforms
diff --git a/homeassistant/components/lcn/config_flow.py b/homeassistant/components/lcn/config_flow.py
index e3979effc0722..a1a98a39db364 100644
--- a/homeassistant/components/lcn/config_flow.py
+++ b/homeassistant/components/lcn/config_flow.py
@@ -26,7 +26,7 @@
 from homeassistant.helpers.typing import ConfigType
 
 from . import PchkConnectionManager
-from .const import CONF_DIM_MODE, CONF_SK_NUM_TRIES, DIM_MODES, DOMAIN
+from .const import CONF_ACKNOWLEDGE, CONF_DIM_MODE, CONF_SK_NUM_TRIES, DIM_MODES, DOMAIN
 from .helpers import purge_device_registry, purge_entity_registry
 
 _LOGGER = logging.getLogger(__name__)
@@ -38,6 +38,7 @@
     vol.Required(CONF_PASSWORD, default=""): str,
     vol.Required(CONF_SK_NUM_TRIES, default=0): cv.positive_int,
     vol.Required(CONF_DIM_MODE, default="STEPS200"): vol.In(DIM_MODES),
+    vol.Required(CONF_ACKNOWLEDGE, default=False): cv.boolean,
 }
 
 USER_DATA = {vol.Required(CONF_HOST, default="pchk"): str, **CONFIG_DATA}
@@ -71,10 +72,12 @@ async def validate_connection(data: ConfigType) -> str | None:
     password = data[CONF_PASSWORD]
     sk_num_tries = data[CONF_SK_NUM_TRIES]
     dim_mode = data[CONF_DIM_MODE]
+    acknowledge = data[CONF_ACKNOWLEDGE]
 
     settings = {
         "SK_NUM_TRIES": sk_num_tries,
         "DIM_MODE": pypck.lcn_defs.OutputPortDimMode[dim_mode],
+        "ACKNOWLEDGE": acknowledge,
     }
 
     _LOGGER.debug("Validating connection parameters to PCHK host '%s'", host_name)
@@ -108,6 +111,7 @@ class LcnFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
     """Handle a LCN config flow."""
 
     VERSION = 1
+    MINOR_VERSION = 2
 
     async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
         """Import existing configuration from LCN."""
diff --git a/homeassistant/components/lcn/const.py b/homeassistant/components/lcn/const.py
index 24d2e68495ca3..707d0f29ba367 100644
--- a/homeassistant/components/lcn/const.py
+++ b/homeassistant/components/lcn/const.py
@@ -25,6 +25,7 @@
 CONF_HARDWARE_TYPE = "hardware_type"
 CONF_DOMAIN_DATA = "domain_data"
 
+CONF_ACKNOWLEDGE = "acknowledge"
 CONF_CONNECTIONS = "connections"
 CONF_SK_NUM_TRIES = "sk_num_tries"
 CONF_OUTPUT = "output"
diff --git a/homeassistant/components/lcn/helpers.py b/homeassistant/components/lcn/helpers.py
index 70034e9020a6d..7da047682ac27 100644
--- a/homeassistant/components/lcn/helpers.py
+++ b/homeassistant/components/lcn/helpers.py
@@ -37,6 +37,7 @@
 
 from .const import (
     BINSENSOR_PORTS,
+    CONF_ACKNOWLEDGE,
     CONF_CLIMATES,
     CONF_CONNECTIONS,
     CONF_DIM_MODE,
@@ -158,6 +159,7 @@ def import_lcn_config(lcn_config: ConfigType) -> list[ConfigType]:
         "password": "lcn,
         "sk_num_tries: 0,
         "dim_mode: "STEPS200",
+        "acknowledge": False,
         "devices": [
             {
                 "address": (0, 7, False)
@@ -192,6 +194,7 @@ def import_lcn_config(lcn_config: ConfigType) -> list[ConfigType]:
             CONF_PASSWORD: connection[CONF_PASSWORD],
             CONF_SK_NUM_TRIES: connection[CONF_SK_NUM_TRIES],
             CONF_DIM_MODE: connection[CONF_DIM_MODE],
+            CONF_ACKNOWLEDGE: False,
             CONF_DEVICES: [],
             CONF_ENTITIES: [],
         }
diff --git a/homeassistant/components/lcn/manifest.json b/homeassistant/components/lcn/manifest.json
index 9023941277f9b..43a342911383e 100644
--- a/homeassistant/components/lcn/manifest.json
+++ b/homeassistant/components/lcn/manifest.json
@@ -8,5 +8,5 @@
   "documentation": "https://www.home-assistant.io/integrations/lcn",
   "iot_class": "local_push",
   "loggers": ["pypck"],
-  "requirements": ["pypck==0.7.22", "lcn-frontend==0.1.6"]
+  "requirements": ["pypck==0.7.23", "lcn-frontend==0.1.6"]
 }
diff --git a/homeassistant/components/lcn/strings.json b/homeassistant/components/lcn/strings.json
index a5f303c639275..9b5ce8c9cc0b7 100644
--- a/homeassistant/components/lcn/strings.json
+++ b/homeassistant/components/lcn/strings.json
@@ -26,7 +26,12 @@
           "username": "[%key:common::config_flow::data::username%]",
           "password": "[%key:common::config_flow::data::password%]",
           "sk_num_tries": "Segment coupler scan attempts",
-          "dim_mode": "Dimming mode"
+          "dim_mode": "Dimming mode",
+          "acknowledge": "Request acknowledgement from modules"
+        },
+        "data_description": {
+          "dim_mode": "The number of steps used for dimming outputs.",
+          "acknowledge": "Retry sendig commands if no response is received (increases bus traffic)."
         }
       },
       "reconfigure": {
@@ -37,8 +42,13 @@
           "port": "[%key:common::config_flow::data::port%]",
           "username": "[%key:common::config_flow::data::username%]",
           "password": "[%key:common::config_flow::data::password%]",
-          "sk_num_tries": "Segment coupler scan attempts",
-          "dim_mode": "Dimming mode"
+          "sk_num_tries": "[%key:component::lcn::config::step::user::data::sk_num_tries%]",
+          "dim_mode": "[%key:component::lcn::config::step::user::data::dim_mode%]",
+          "acknowledge": "[%key:component::lcn::config::step::user::data::acknowledge%]"
+        },
+        "data_description": {
+          "dim_mode": "[%key:component::lcn::config::step::user::data_description::dim_mode%]",
+          "acknowledge": "[%key:component::lcn::config::step::user::data_description::acknowledge%]"
         }
       }
     },
diff --git a/requirements_all.txt b/requirements_all.txt
index 22dff112debc5..0d5d0cf0661ba 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -2130,7 +2130,7 @@ pyownet==0.10.0.post1
 pypca==0.0.7
 
 # homeassistant.components.lcn
-pypck==0.7.22
+pypck==0.7.23
 
 # homeassistant.components.pjlink
 pypjlink2==1.2.1
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index f476d4d48178b..95bc6d9c3f8b8 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -1711,7 +1711,7 @@ pyoverkiz==1.13.14
 pyownet==0.10.0.post1
 
 # homeassistant.components.lcn
-pypck==0.7.22
+pypck==0.7.23
 
 # homeassistant.components.pjlink
 pypjlink2==1.2.1
diff --git a/tests/components/lcn/conftest.py b/tests/components/lcn/conftest.py
index 16797f6065dd1..2c57db0a07bd4 100644
--- a/tests/components/lcn/conftest.py
+++ b/tests/components/lcn/conftest.py
@@ -98,6 +98,12 @@ def create_config_entry_pchk() -> MockConfigEntry:
     return create_config_entry("pchk")
 
 
+@pytest.fixture(name="entry_v1_1")
+def create_config_entry_pchk_v1_1() -> MockConfigEntry:
+    """Return one specific config entry with v1.1."""
+    return create_config_entry("pchk_v1_1")
+
+
 @pytest.fixture(name="entry2")
 def create_config_entry_myhome() -> MockConfigEntry:
     """Return one specific config entry."""
diff --git a/tests/components/lcn/fixtures/config_entry_myhome.json b/tests/components/lcn/fixtures/config_entry_myhome.json
index a0f8e7d3e10c1..5abc9749b464e 100644
--- a/tests/components/lcn/fixtures/config_entry_myhome.json
+++ b/tests/components/lcn/fixtures/config_entry_myhome.json
@@ -6,6 +6,7 @@
   "password": "lcn",
   "sk_num_tries": 0,
   "dim_mode": "STEPS200",
+  "acknowledge": false,
   "devices": [],
   "entities": [
     {
diff --git a/tests/components/lcn/fixtures/config_entry_pchk.json b/tests/components/lcn/fixtures/config_entry_pchk.json
index 9a8095ff16d07..d8eef6d1eb31b 100644
--- a/tests/components/lcn/fixtures/config_entry_pchk.json
+++ b/tests/components/lcn/fixtures/config_entry_pchk.json
@@ -6,6 +6,7 @@
   "password": "lcn",
   "sk_num_tries": 0,
   "dim_mode": "STEPS200",
+  "acknowledge": false,
   "devices": [
     {
       "address": [0, 7, false],
diff --git a/tests/components/lcn/fixtures/config_entry_pchk_v1_1.json b/tests/components/lcn/fixtures/config_entry_pchk_v1_1.json
new file mode 100644
index 0000000000000..9a8095ff16d07
--- /dev/null
+++ b/tests/components/lcn/fixtures/config_entry_pchk_v1_1.json
@@ -0,0 +1,230 @@
+{
+  "host": "pchk",
+  "ip_address": "192.168.2.41",
+  "port": 4114,
+  "username": "lcn",
+  "password": "lcn",
+  "sk_num_tries": 0,
+  "dim_mode": "STEPS200",
+  "devices": [
+    {
+      "address": [0, 7, false],
+      "name": "TestModule",
+      "hardware_serial": -1,
+      "software_serial": -1,
+      "hardware_type": -1
+    },
+    {
+      "address": [0, 5, true],
+      "name": "TestGroup",
+      "hardware_serial": -1,
+      "software_serial": -1,
+      "hardware_type": -1
+    }
+  ],
+  "entities": [
+    {
+      "address": [0, 7, false],
+      "name": "Light_Output1",
+      "resource": "output1",
+      "domain": "light",
+      "domain_data": {
+        "output": "OUTPUT1",
+        "dimmable": true,
+        "transition": 5000.0
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Light_Output2",
+      "resource": "output2",
+      "domain": "light",
+      "domain_data": {
+        "output": "OUTPUT2",
+        "dimmable": false,
+        "transition": 0
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Light_Relay1",
+      "resource": "relay1",
+      "domain": "light",
+      "domain_data": {
+        "output": "RELAY1",
+        "dimmable": false,
+        "transition": 0.0
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Switch_Output1",
+      "resource": "output1",
+      "domain": "switch",
+      "domain_data": {
+        "output": "OUTPUT1"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Switch_Output2",
+      "resource": "output2",
+      "domain": "switch",
+      "domain_data": {
+        "output": "OUTPUT2"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Switch_Relay1",
+      "resource": "relay1",
+      "domain": "switch",
+      "domain_data": {
+        "output": "RELAY1"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Switch_Relay2",
+      "resource": "relay2",
+      "domain": "switch",
+      "domain_data": {
+        "output": "RELAY2"
+      }
+    },
+    {
+      "address": [0, 5, true],
+      "name": "Switch_Group5",
+      "resource": "relay1",
+      "domain": "switch",
+      "domain_data": {
+        "output": "RELAY1"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Cover_Outputs",
+      "resource": "outputs",
+      "domain": "cover",
+      "domain_data": {
+        "motor": "OUTPUTS",
+        "reverse_time": "RT1200"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Cover_Relays",
+      "resource": "motor1",
+      "domain": "cover",
+      "domain_data": {
+        "motor": "MOTOR1",
+        "reverse_time": "RT1200"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Climate1",
+      "resource": "var1.r1varsetpoint",
+      "domain": "climate",
+      "domain_data": {
+        "source": "VAR1",
+        "setpoint": "R1VARSETPOINT",
+        "lockable": true,
+        "min_temp": 0.0,
+        "max_temp": 40.0,
+        "unit_of_measurement": "°C"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Romantic",
+      "resource": "0.0",
+      "domain": "scene",
+      "domain_data": {
+        "register": 0,
+        "scene": 0,
+        "outputs": ["OUTPUT1", "OUTPUT2", "RELAY1"],
+        "transition": null
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Romantic Transition",
+      "resource": "0.1",
+      "domain": "scene",
+      "domain_data": {
+        "register": 0,
+        "scene": 1,
+        "outputs": ["OUTPUT1", "OUTPUT2", "RELAY1"],
+        "transition": 10
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Sensor_LockRegulator1",
+      "resource": "r1varsetpoint",
+      "domain": "binary_sensor",
+      "domain_data": {
+        "source": "R1VARSETPOINT"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Binary_Sensor1",
+      "resource": "binsensor1",
+      "domain": "binary_sensor",
+      "domain_data": {
+        "source": "BINSENSOR1"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Sensor_KeyLock",
+      "resource": "a5",
+      "domain": "binary_sensor",
+      "domain_data": {
+        "source": "A5"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Sensor_Var1",
+      "resource": "var1",
+      "domain": "sensor",
+      "domain_data": {
+        "source": "VAR1",
+        "unit_of_measurement": "°C"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Sensor_Setpoint1",
+      "resource": "r1varsetpoint",
+      "domain": "sensor",
+      "domain_data": {
+        "source": "R1VARSETPOINT",
+        "unit_of_measurement": "°C"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Sensor_Led6",
+      "resource": "led6",
+      "domain": "sensor",
+      "domain_data": {
+        "source": "LED6",
+        "unit_of_measurement": "NATIVE"
+      }
+    },
+    {
+      "address": [0, 7, false],
+      "name": "Sensor_LogicOp1",
+      "resource": "logicop1",
+      "domain": "sensor",
+      "domain_data": {
+        "source": "LOGICOP1",
+        "unit_of_measurement": "NATIVE"
+      }
+    }
+  ]
+}
diff --git a/tests/components/lcn/test_config_flow.py b/tests/components/lcn/test_config_flow.py
index 9f46202ac8a0c..a34592a4f873c 100644
--- a/tests/components/lcn/test_config_flow.py
+++ b/tests/components/lcn/test_config_flow.py
@@ -7,7 +7,12 @@
 
 from homeassistant import config_entries, data_entry_flow
 from homeassistant.components.lcn.config_flow import LcnFlowHandler, validate_connection
-from homeassistant.components.lcn.const import CONF_DIM_MODE, CONF_SK_NUM_TRIES, DOMAIN
+from homeassistant.components.lcn.const import (
+    CONF_ACKNOWLEDGE,
+    CONF_DIM_MODE,
+    CONF_SK_NUM_TRIES,
+    DOMAIN,
+)
 from homeassistant.const import (
     CONF_BASE,
     CONF_DEVICES,
@@ -31,6 +36,7 @@
     CONF_PASSWORD: "lcn",
     CONF_SK_NUM_TRIES: 0,
     CONF_DIM_MODE: "STEPS200",
+    CONF_ACKNOWLEDGE: False,
 }
 
 CONNECTION_DATA = {CONF_HOST: "pchk", **CONFIG_DATA}
diff --git a/tests/components/lcn/test_init.py b/tests/components/lcn/test_init.py
index ece0e95e501fc..32ced98997bed 100644
--- a/tests/components/lcn/test_init.py
+++ b/tests/components/lcn/test_init.py
@@ -125,3 +125,24 @@ async def test_async_setup_from_configuration_yaml(hass: HomeAssistant) -> None:
         await setup_component(hass)
 
         assert async_setup_entry.await_count == 2
+
+
+@patch("homeassistant.components.lcn.PchkConnectionManager", MockPchkConnectionManager)
+async def test_migrate_1_1(hass: HomeAssistant, entry, entry_v1_1) -> None:
+    """Test migration config entry."""
+    entry_v1_1.add_to_hass(hass)
+
+    hass.config_entries.async_update_entry(
+        entry_v1_1,
+        version=1,
+        minor_version=1,
+    )
+
+    await hass.config_entries.async_setup(entry_v1_1.entry_id)
+    await hass.async_block_till_done()
+    entry_migrated = hass.config_entries.async_get_entry(entry_v1_1.entry_id)
+
+    assert entry_migrated.state is ConfigEntryState.LOADED
+    assert entry_migrated.version == 1
+    assert entry_migrated.minor_version == 2
+    assert entry_migrated.data == entry.data


Code review comments:
diff_hunk: @@ -125,3 +125,24 @@ async def test_async_setup_from_configuration_yaml(hass: HomeAssistant) -> None:
         await setup_component(hass)
 
         assert async_setup_entry.await_count == 2
+
+
+@patch("homeassistant.components.lcn.PchkConnectionManager", MockPchkConnectionManager)
+async def test_migrate_1_1(hass: HomeAssistant, entry, entry_v1_1) -> None:
+    """Test migration config entry."""
+    entry_v1_1.add_to_hass(hass)
+
+    hass.config_entries.async_update_entry(
+        entry_v1_1,
+        version=1,
+        minor_version=1,
+    )
path: tests/components/lcn/test_init.py
text: @user1:
We only use v1_1 here right? Should we just create it here instead of using a fixture? I find it kinda strange to use a fixture for a test and directly run async update entry to modify it

@author:
I removed the fixture and added a parameter to `create_config_entry` to set the version.