From 62918394a4cbe4d6626fee77352be562084dd8a1 Mon Sep 17 00:00:00 2001 From: edel-macias-cubix Date: Fri, 22 Aug 2025 13:19:03 -0600 Subject: [PATCH 1/7] Add `node_id` field to Device model --- src/tailscale/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tailscale/models.py b/src/tailscale/models.py index b23a1a75..9a0b4fb5 100644 --- a/src/tailscale/models.py +++ b/src/tailscale/models.py @@ -59,6 +59,7 @@ class Device(DataClassORJSONMixin): last_seen: datetime | None = field(metadata=field_options(alias="lastSeen")) machine_key: str = field(metadata=field_options(alias="machineKey")) name: str + node_id: str | None = field(default=None, metadata=field_options(alias="nodeId")) node_key: str = field(metadata=field_options(alias="nodeKey")) os: str update_available: bool = field(metadata=field_options(alias="updateAvailable")) From 9cc32bac8d1fe49f961fcc4c065e15c638e51ec4 Mon Sep 17 00:00:00 2001 From: edel-macias-cubix Date: Thu, 28 Aug 2025 09:21:01 -0600 Subject: [PATCH 2/7] Moved node_id param with default value to the end of params list --- src/tailscale/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tailscale/models.py b/src/tailscale/models.py index 9a0b4fb5..7d7cab4d 100644 --- a/src/tailscale/models.py +++ b/src/tailscale/models.py @@ -59,7 +59,6 @@ class Device(DataClassORJSONMixin): last_seen: datetime | None = field(metadata=field_options(alias="lastSeen")) machine_key: str = field(metadata=field_options(alias="machineKey")) name: str - node_id: str | None = field(default=None, metadata=field_options(alias="nodeId")) node_key: str = field(metadata=field_options(alias="nodeKey")) os: str update_available: bool = field(metadata=field_options(alias="updateAvailable")) @@ -71,6 +70,7 @@ class Device(DataClassORJSONMixin): default_factory=list, metadata=field_options(alias="enabledRoutes") ) tags: list[str] = field(default_factory=list) + node_id: str | None = field(default=None, metadata=field_options(alias="nodeId")) @classmethod def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]: From e1ba41eb1336912a662a59f0914f21014769cd60 Mon Sep 17 00:00:00 2001 From: Laszlo Magyar Date: Wed, 12 Nov 2025 16:23:23 +0100 Subject: [PATCH 3/7] node_id is not optional "The preferred identifier for a device" --- src/tailscale/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tailscale/models.py b/src/tailscale/models.py index 7d7cab4d..9f3a50c1 100644 --- a/src/tailscale/models.py +++ b/src/tailscale/models.py @@ -60,6 +60,7 @@ class Device(DataClassORJSONMixin): machine_key: str = field(metadata=field_options(alias="machineKey")) name: str node_key: str = field(metadata=field_options(alias="nodeKey")) + node_id: str = field(metadata=field_options(alias="nodeId")) os: str update_available: bool = field(metadata=field_options(alias="updateAvailable")) user: str @@ -70,7 +71,6 @@ class Device(DataClassORJSONMixin): default_factory=list, metadata=field_options(alias="enabledRoutes") ) tags: list[str] = field(default_factory=list) - node_id: str | None = field(default=None, metadata=field_options(alias="nodeId")) @classmethod def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]: From fc70ab871bffa956d908b6c9d86f11b2d201f454 Mon Sep 17 00:00:00 2001 From: Laszlo Magyar Date: Wed, 12 Nov 2025 16:56:19 +0100 Subject: [PATCH 4/7] add other 6 missing fields --- src/tailscale/models.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/tailscale/models.py b/src/tailscale/models.py index 9f3a50c1..6202ec30 100644 --- a/src/tailscale/models.py +++ b/src/tailscale/models.py @@ -50,6 +50,7 @@ class Device(DataClassORJSONMixin): metadata=field_options(alias="clientConnectivity") ) client_version: str = field(metadata=field_options(alias="clientVersion")) + connected_to_control: bool = field(metadata=field_options(alias="connectedToControl")) created: datetime | None device_id: str = field(metadata=field_options(alias="id")) expires: datetime | None @@ -62,6 +63,7 @@ class Device(DataClassORJSONMixin): node_key: str = field(metadata=field_options(alias="nodeKey")) node_id: str = field(metadata=field_options(alias="nodeId")) os: str + tailnet_lock_key: str = field(metadata=field_options(alias="tailnetLockKey")) update_available: bool = field(metadata=field_options(alias="updateAvailable")) user: str advertised_routes: list[str] = field( @@ -70,7 +72,23 @@ class Device(DataClassORJSONMixin): enabled_routes: list[str] = field( default_factory=list, metadata=field_options(alias="enabledRoutes") ) + isEphemeral: bool | None = field( + default=None, + metadata=field_options(alias="isEphemeral"), + ) + multiple_connections: bool | None = field( + default=None, + metadata=field_options(alias="multipleConnections"), + ) + ssh_enabled: bool | None = field( + default=None, + metadata=field_options(alias="sshEnabled"), + ) tags: list[str] = field(default_factory=list) + tailnet_lock_error: str | None = field( + default=None, + metadata=field_options(alias="tailnetLockError"), + ) @classmethod def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]: @@ -88,6 +106,8 @@ def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]: # Convert an empty string to None. if not d.get("created"): d["created"] = None + if not d.get("tailnetLockError"): + d["tailnetLockError"] = None return d From 173efdb3d34c8528481dc453c5ffd5891881106a Mon Sep 17 00:00:00 2001 From: Laszlo Magyar Date: Wed, 12 Nov 2025 17:41:20 +0100 Subject: [PATCH 5/7] add latency also --- src/tailscale/models.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tailscale/models.py b/src/tailscale/models.py index 6202ec30..8a325563 100644 --- a/src/tailscale/models.py +++ b/src/tailscale/models.py @@ -22,6 +22,14 @@ class ClientSupports(DataClassORJSONMixin): upnp: bool | None +@dataclass +class Latency(DataClassORJSONMixin): + """Object holding Tailscale device information.""" + + latency_ms: float = field(metadata=field_options(alias="latencyMs")) + preferred: bool | None = None + + @dataclass class ClientConnectivity(DataClassORJSONMixin): """Object holding Tailscale device information.""" @@ -30,6 +38,7 @@ class ClientConnectivity(DataClassORJSONMixin): metadata=field_options(alias="clientSupports") ) endpoints: list[str] = field(default_factory=list) + latency: dict[str, Latency] = field(default_factory=dict) mapping_varies_by_dest_ip: bool | None = field( default=None, metadata=field_options(alias="mappingVariesByDestIP"), From 778889249a8fa4617164496d31f7b82616c5abbf Mon Sep 17 00:00:00 2001 From: Laszlo Magyar Date: Wed, 12 Nov 2025 22:06:02 +0100 Subject: [PATCH 6/7] fix is_ephemeral --- src/tailscale/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tailscale/models.py b/src/tailscale/models.py index 8a325563..9bfbefa5 100644 --- a/src/tailscale/models.py +++ b/src/tailscale/models.py @@ -81,7 +81,7 @@ class Device(DataClassORJSONMixin): enabled_routes: list[str] = field( default_factory=list, metadata=field_options(alias="enabledRoutes") ) - isEphemeral: bool | None = field( + is_ephemeral: bool | None = field( default=None, metadata=field_options(alias="isEphemeral"), ) From 6b43d4db7181b7f0e47eed5fff19571eec14a88e Mon Sep 17 00:00:00 2001 From: Laszlo Magyar Date: Wed, 12 Nov 2025 22:08:33 +0100 Subject: [PATCH 7/7] fix long line --- src/tailscale/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tailscale/models.py b/src/tailscale/models.py index 9bfbefa5..033efce5 100644 --- a/src/tailscale/models.py +++ b/src/tailscale/models.py @@ -59,7 +59,9 @@ class Device(DataClassORJSONMixin): metadata=field_options(alias="clientConnectivity") ) client_version: str = field(metadata=field_options(alias="clientVersion")) - connected_to_control: bool = field(metadata=field_options(alias="connectedToControl")) + connected_to_control: bool = field( + metadata=field_options(alias="connectedToControl") + ) created: datetime | None device_id: str = field(metadata=field_options(alias="id")) expires: datetime | None