Skip to content

Coordinator backup #161

@Shulyaka

Description

@Shulyaka

I'd like to start a discussion on how to back up the network key on XBee coordinators.
The issue is that on XBee devices the network key is write-only, so we have to remember it from the moment we set it. Luckily, we have a backup mechanism that we can use. Currently, however, we only use the last backup to restore the state if the device is not already configured, and optionally for verification.
Current flow:

  1. For a new network, we generate a new random key and write it to the device
  2. The key is kept in memory until the next restart, and will be saved in the backup once it is created.
  3. After the restart, we set the key to the default value (unknown), and because we can't get the key from the device, it remains unknown.

Suggestions:

  1. Enhance zigpy ControllerApplication.initialize() function to use last_backup to set the initial network_info (instead of default) before trying to load it from the device. Something like:
     async def initialize(self, *, auto_form: bool = False) -> None:
         """Starts the network on a connected radio, optionally forming one with random
         settings if necessary.
         """

         last_backup = self.backups.most_recent_backup()

+        if last_backup:
+            self.state.network_info = zigpy.state.NetworkInfo.from_dict(last_backup.network_info.as_dict())
+
         try:
             await self.load_network_info(load_devices=False)
         except zigpy.exceptions.NetworkNotFormed:

So the radio library would only update what it can, and the rest will remain from the backup.

  1. Pass last_backup as a new optional parameter to ControllerApplication.load_network_info and let the radio library (zigpy-xbee`) handle it as it wishes.
         try:
-            await self.load_network_info(load_devices=False)
+            await self.load_network_info(load_devices=False, last_backup=last_backup)
         except zigpy.exceptions.NetworkNotFormed:
  1. To avoid changes to zigpy, overload ControllerApplication.initialize() function in zigpy_xbee.zigbee.ControllerApplication, load last backup inside it and restore the network key in memory there:
    async def initialize(self, *, auto_form: bool = False) -> None:
        """Overloaded initialize() to restore unreadable info from backup."""

        last_backup = self.backups.most_recent_backup()

        if last_backup:
            self.state.network_info = zigpy.state.NetworkInfo.from_dict(last_backup.network_info.as_dict())

        await super().initialize(auto_form=auto_form)
  1. Do not try to read the key from backup, but perform implicit key rotation when making a new backup and the network key is not known. We can simply generate a new network key and write it to the device (and the backup), and it will be distributed to all devices in the network. Might be a bit tricky to implement it to do it only when the backup is created, and I also don't really like the idea of doing implicit actions like rotating the key when a user might not expect it.

  2. There is also a native backup functionality in newer firmwares (the 'BK' AT command), it requires an additional investigation and is not available for legacy modules.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions