From f6da32489716582b50b022267f71861f28e7d73f Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 20 Mar 2026 14:39:42 +1000 Subject: [PATCH 1/5] time: add new time source Add the new time source recently added to the embedded repository. Signed-off-by: Jordan Yates --- src/infuse_iot/time.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/infuse_iot/time.py b/src/infuse_iot/time.py index c170caf2..ad249e36 100644 --- a/src/infuse_iot/time.py +++ b/src/infuse_iot/time.py @@ -10,7 +10,8 @@ class base(enum.IntEnum): GNSS = 1 NTP = 2 RPC = 3 - INVALID = 4 + EPACKET = 4 + INVALID = 5 def __init__(self, value: int): self.recovered: bool = value & 0x80 != 0 From 0429bfc016bc23f9ba24d3fef9cbde4bf9114679 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 20 Mar 2026 14:40:24 +1000 Subject: [PATCH 2/5] generated: tdf_definitions: regenerate Include the latest TDF definitions. Signed-off-by: Jordan Yates --- src/infuse_iot/generated/tdf_definitions.py | 22 ++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/infuse_iot/generated/tdf_definitions.py b/src/infuse_iot/generated/tdf_definitions.py index 86165c94..33cebbbd 100644 --- a/src/infuse_iot/generated/tdf_definitions.py +++ b/src/infuse_iot/generated/tdf_definitions.py @@ -4,7 +4,7 @@ import ctypes -from infuse_iot.generated.tdf_base import TdfReadingBase, TdfStructBase +from infuse_iot.generated.tdf_base import TdfReadingBase, TdfStructBase # noqa F401 class structs: @@ -1694,6 +1694,25 @@ class pcm_16bit_chan_dual(TdfReadingBase): "right": "{}", } + class kvs_value_changed(TdfReadingBase): + """Record of key value store data updates""" + + ID = 61 + NAME = "KVS_VALUE_CHANGED" + _fields_ = [ + ("key", ctypes.c_uint16), + ("value", 0 * ctypes.c_uint8), + ] + _pack_ = 1 + _postfix_ = { + "key": "", + "value": "", + } + _display_fmt_ = { + "key": "{}", + "value": "{}", + } + id_type_mapping: dict[int, type[TdfReadingBase]] = { readings.announce.ID: readings.announce, @@ -1755,6 +1774,7 @@ class pcm_16bit_chan_dual(TdfReadingBase): readings.pcm_16bit_chan_left.ID: readings.pcm_16bit_chan_left, readings.pcm_16bit_chan_right.ID: readings.pcm_16bit_chan_right, readings.pcm_16bit_chan_dual.ID: readings.pcm_16bit_chan_dual, + readings.kvs_value_changed.ID: readings.kvs_value_changed, } __all__ = [ From 6e3b5b73f546458c28cd7af3244b35d0fdd27d4f Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Thu, 12 Mar 2026 11:23:59 +1000 Subject: [PATCH 3/5] tools: cloud: output IP address for `device info` If the last route is via UDP, display the IP address. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/cloud.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/infuse_iot/tools/cloud.py b/src/infuse_iot/tools/cloud.py index d4660730..a0f98bb6 100644 --- a/src/infuse_iot/tools/cloud.py +++ b/src/infuse_iot/tools/cloud.py @@ -222,6 +222,8 @@ def info(self, client: Client): ] if route.bt_adv: table += [("BT Address", f"{route.bt_adv.address} ({route.bt_adv.type_})")] + if route.udp: + table += [("IP Address", route.udp.address)] if isinstance(logger_states, list) and len(logger_states) > 0: logger_names = { From 0c847577a0f45d977e93b5877ec4366efaa43497 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Thu, 12 Mar 2026 11:24:34 +1000 Subject: [PATCH 4/5] tools: cloud: handle `Unset` values for logger sync state Detect unset values and replace them with "N/A" in the output. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/cloud.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/infuse_iot/tools/cloud.py b/src/infuse_iot/tools/cloud.py index a0f98bb6..7eac5c99 100644 --- a/src/infuse_iot/tools/cloud.py +++ b/src/infuse_iot/tools/cloud.py @@ -231,14 +231,20 @@ def info(self, client: Client): 1: "Removable", } + def val_or_na(value) -> str: + if isinstance(value, Unset): + return "N/A" + return str(value) + for logger in logger_states: name = logger_names.get(logger.index, str(logger.index)) table += [ (f"~~~{name} Logger Sync~~~", ""), - ("Last Report Time", logger.last_reported_time), - ("Last Downloaded Time", logger.last_downloaded_time), - ("Reported Block", logger.last_reported_block), - ("Downloaded Block", logger.last_downloaded_block), + ("Enabled", logger.download_enabled), + ("Last Report Time", val_or_na(logger.last_reported_time)), + ("Last Downloaded Time", val_or_na(logger.last_downloaded_time)), + ("Reported Block", val_or_na(logger.last_reported_block)), + ("Downloaded Block", val_or_na(logger.last_downloaded_block)), ] if isinstance(logger.last_reported_block, int) and isinstance(logger.last_downloaded_block, int): table += [("Block Lag", logger.last_reported_block - logger.last_downloaded_block)] From a68bec4f9201e04f9188de8d1cb394f9b3634b3f Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 20 Mar 2026 14:38:00 +1000 Subject: [PATCH 5/5] tools: gateway: print RTT device name suggestions If the RTT device name is not found on startup, display a list of possible options. Searching is limited to case insensitivity and missing postfixes. Typos are not detected. Signed-off-by: Jordan Yates --- src/infuse_iot/serial_comms.py | 23 ++++++++++++++++++++++- src/infuse_iot/tools/gateway.py | 9 +++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/infuse_iot/serial_comms.py b/src/infuse_iot/serial_comms.py index fe9b1fe0..1f513205 100644 --- a/src/infuse_iot/serial_comms.py +++ b/src/infuse_iot/serial_comms.py @@ -10,6 +10,12 @@ from pyocd.debug.rtt import GenericRTTControlBlock +class SerialBadNameException(Exception): + def __init__(self, requested: str, options: list[str]): + self.requested = requested + self.options = options + + class SerialFrame: """Serial frame reconstructor""" @@ -119,7 +125,22 @@ def __init__(self, rtt_device: str, serial_number: str | None = None): def open(self, timeout: float | None = None): self._jlink.open(serial_no=self._serial_number) self._jlink.set_tif(pylink.enums.JLinkInterfaces.SWD) - self._jlink.connect(self._name, 4000) + try: + self._jlink.connect(self._name, 4000) + except pylink.errors.JLinkException as e: + if e.message != "Unsupported device selected.": + # Not a device name error + raise e + + # Find valid options + name_lower = self._name.lower() + options = [] + for i in range(self._jlink.num_supported_devices()): + info = self._jlink.supported_device(i) + if info.name.lower().startswith(name_lower): + options.append(info.name) + raise SerialBadNameException(self._name, options) from e + self._jlink.rtt_start() end_time = time.time() + timeout if timeout else None diff --git a/src/infuse_iot/tools/gateway.py b/src/infuse_iot/tools/gateway.py index be27f4e8..51f7c05b 100644 --- a/src/infuse_iot/tools/gateway.py +++ b/src/infuse_iot/tools/gateway.py @@ -36,7 +36,7 @@ PacketOutputRouted, PacketReceived, ) -from infuse_iot.serial_comms import PyOcdPort, RttPort, SerialFrame, SerialLike, SerialPort +from infuse_iot.serial_comms import PyOcdPort, RttPort, SerialBadNameException, SerialFrame, SerialLike, SerialPort from infuse_iot.socket_comms import ( ClientNotification, ClientNotificationCommsCheck, @@ -552,7 +552,12 @@ def __init__(self, args: argparse.Namespace): def run(self): # Open the serial port - self.port.open() + try: + self.port.open() + except SerialBadNameException as e: + tabbed_options = "\n".join(f"\t{o}" for o in e.options) + Console.log_error(f"Unknown name '{e.requested}', possible options:\n{tabbed_options}") + return Console.log_info(f"Port '{str(self.port)}' opened") # Ping the port to get the local device ID self.port.ping()