From 1d6a3124405f239ed993f5455e193390349d1b1f Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Thu, 30 Jul 2026 16:33:26 +1000 Subject: [PATCH 1/4] epacket: packet: notify key ids Include the key IDs in the information reported to the database when peeling through packets. This fixes the database rarely not knowing the devices network ID after a connection has been established, if both gateway and device are on the same network. Signed-off-by: Jordan Yates --- src/infuse_iot/epacket/packet.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/infuse_iot/epacket/packet.py b/src/infuse_iot/epacket/packet.py index 1a6ba04..27bc53e 100644 --- a/src/infuse_iot/epacket/packet.py +++ b/src/infuse_iot/epacket/packet.py @@ -189,8 +189,11 @@ def from_serial(cls, database: DeviceDatabase, serial_frame: bytes) -> list[Self decr_header = CtypePacketReceived.DecryptedHeader.from_buffer_copy(packet_bytes) del packet_bytes[: ctypes.sizeof(decr_header)] - # Notify database of BT Addr -> Infuse ID mapping - database.observe_device(decr_header.device_id, bt_addr=addr.val) + # Notify database of BT Addr -> Infuse ID mapping and key metadata + if decr_header.flags & Flags.ENCR_DEVICE: + database.observe_device(decr_header.device_id, device_key_id=decr_header.key_id, bt_addr=addr.val) + else: + database.observe_device(decr_header.device_id, network_id=decr_header.key_id, bt_addr=addr.val) bt_hop = HopReceived( decr_header.device_id, From 425ac035aaff858d5a9bfd84b4f8d9597bbb4515 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Thu, 30 Jul 2026 15:53:36 +1000 Subject: [PATCH 2/4] tools: gateway: internal consistency fix Use the response bytes object directly in a callback, instead of manually reconstructing it from a different argument. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/gateway.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/infuse_iot/tools/gateway.py b/src/infuse_iot/tools/gateway.py index 5600f30..309aa75 100644 --- a/src/infuse_iot/tools/gateway.py +++ b/src/infuse_iot/tools/gateway.py @@ -21,7 +21,7 @@ import infuse_iot.definitions.rpc as defs import infuse_iot.definitions.tdf as tdf_defs import infuse_iot.epacket.interface as interface -from infuse_iot import rpc, tdf +from infuse_iot import tdf from infuse_iot.commands import InfuseCommand from infuse_iot.common import InfuseID, InfuseType from infuse_iot.database import ( @@ -295,8 +295,8 @@ def _pub_keys_cb(self, pkt: PacketReceived, rc: int, response: bytes, _): # Notify connection success self._connected_notification(infuse_id) - def _bt_connect_cb(self, pkt: PacketReceived, rc: int, response: bytes, _): - resp = defs.bt_connect_infuse.response.from_buffer_copy(pkt.payload[ctypes.sizeof(rpc.ResponseHeader) :]) + def _bt_connect_cb(self, _pkt: PacketReceived, rc: int, response: bytes, _): + resp = defs.bt_connect_infuse.response.from_buffer_copy(response) if_addr = interface.Address.BluetoothLeAddr.from_rpc_struct(resp.peer) infuse_id = self._common.ddb.infuse_id_from_bluetooth(if_addr) From f9612bc1c012ddae5c30850b726a38a52957d683 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Thu, 30 Jul 2026 15:56:15 +1000 Subject: [PATCH 3/4] tools: ota_upgrade: set times on connections Ensure that the local gateways and remote devices have synchronised times before starting the upgrade process. This ensures time isn't wasted by regenerating interface keys on each packet. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/ota_upgrade.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/infuse_iot/tools/ota_upgrade.py b/src/infuse_iot/tools/ota_upgrade.py index ebed674..bd8cf46 100644 --- a/src/infuse_iot/tools/ota_upgrade.py +++ b/src/infuse_iot/tools/ota_upgrade.py @@ -21,7 +21,7 @@ from infuse_iot.commands import InfuseCommand from infuse_iot.common import InfuseID -from infuse_iot.definitions.rpc import bt_file_copy_basic, file_write_basic, rpc_enum_file_action +from infuse_iot.definitions.rpc import bt_file_copy_basic, file_write_basic, rpc_enum_file_action, time_set from infuse_iot.epacket.packet import Auth, HopReceived from infuse_iot.generated.tdf_definitions import readings from infuse_iot.rpc_client import RpcClient @@ -29,6 +29,7 @@ GatewayRequestConnectionRequest, LocalClient, ) +from infuse_iot.time import InfuseTime from infuse_iot.util.argparse import InfuseDeviceId, ValidFile, ValidRelease, add_server_port_parser from infuse_iot.util.crc import crc16_ccitt from infuse_iot.zephyr.errno import errno @@ -163,6 +164,20 @@ def gateway_diff_load(self): sys.exit(f"Failed to save diff file to gateway (({errno.strerror(-return_code)}))") print(f"'{self._single_diff}' written to gateway") + def set_device_time(self, mtu: int, infuse_id: int) -> bool: + rpc_client = RpcClient(self._client, mtu, infuse_id) + rpc_client.set_timeout(2.0) + + params = time_set.request(InfuseTime.epoch_time_from_unix(time.time())) + hdr, _rsp = rpc_client.run_standard_cmd( + time_set.COMMAND_ID, + Auth.DEVICE, + bytes(params), + time_set.response.from_buffer_copy, + ) + # Command completed and succeeded + return hdr is not None and hdr.return_code == 0 + def run_file_upload(self, live: Live, mtu: int, source: HopReceived): self.state_update(live, f"Uploading patch file to {source.infuse_id:016X}") rpc_client = RpcClient(self._client, mtu, source.infuse_id) @@ -215,6 +230,11 @@ def run(self): if self._single_diff: self.gateway_diff_load() + # Set the gateways time at script startup + with self._client.connection(InfuseID.GATEWAY, GatewayRequestConnectionRequest.DataType.COMMAND, 10) as mtu: + if not self.set_device_time(mtu, InfuseID.GATEWAY): + sys.exit("Failed to set time on local gateway") + with Live(self.progress_table(), refresh_per_second=4) as live: for source, announce in self._client.observe_announce(): self.state_update(live, "Scanning") @@ -302,6 +322,9 @@ def run(self): with self._client.connection( source.infuse_id, GatewayRequestConnectionRequest.DataType.COMMAND, self._conn_timeout ) as mtu: + # Set time on the remote device to keep keys in sync + if not self.set_device_time(mtu, source.infuse_id): + break if self._single_diff: self.run_file_copy(live, mtu, source) else: From b6a9eef0e2e022a5a9ed94b8193cbd5110119b87 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Thu, 30 Jul 2026 15:57:31 +1000 Subject: [PATCH 4/4] tools: ota_upgrade: increase copy timeout Unlike `run_file_upload`, the client timeout associated with `run_file_copy` needs to be large enough to contain the entire copy process, as the local gateway manages the copy itself. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/ota_upgrade.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/infuse_iot/tools/ota_upgrade.py b/src/infuse_iot/tools/ota_upgrade.py index bd8cf46..02d7108 100644 --- a/src/infuse_iot/tools/ota_upgrade.py +++ b/src/infuse_iot/tools/ota_upgrade.py @@ -201,6 +201,7 @@ def run_file_upload(self, live: Live, mtu: int, source: HopReceived): def run_file_copy(self, live: Live, mtu: int, source: HopReceived): self.state_update(live, f"Copying patch file to {source.infuse_id:016X}") rpc_client = RpcClient(self._client, mtu, InfuseID.GATEWAY) + rpc_client.set_timeout(60.0) params = bt_file_copy_basic.request( source.interface_address.val.to_rpc_struct(),