Skip to content

Commit 908a5f7

Browse files
committed
tools: auto_activate: added
Add a tool to bulk activate/deactivate devices through the `APPLICATION_ACTIVE` KV entry. Signed-off-by: Jordan Yates <jordan@embeint.com>
1 parent f1b1003 commit 908a5f7

1 file changed

Lines changed: 103 additions & 0 deletions

File tree

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env python3
2+
3+
"""Automatically activate/deactivate observed devices"""
4+
5+
__author__ = "Jordan Yates"
6+
__copyright__ = "Copyright 2026, Embeint Holdings Pty Ltd"
7+
8+
9+
from rich.live import Live
10+
from rich.status import Status
11+
from rich.table import Table
12+
13+
import infuse_iot.definitions.kv as kv
14+
import infuse_iot.definitions.rpc as rpc
15+
from infuse_iot.commands import InfuseCommand
16+
from infuse_iot.epacket.packet import Auth
17+
from infuse_iot.rpc_client import RpcClient
18+
from infuse_iot.socket_comms import (
19+
GatewayRequestConnectionRequest,
20+
LocalClient,
21+
default_multicast_address,
22+
)
23+
from infuse_iot.tdf import TDF
24+
25+
26+
class SubCommand(InfuseCommand):
27+
NAME = "auto_activate"
28+
HELP = "Automatically activate/deactivate observed devices"
29+
DESCRIPTION = "Automatically activate/deactivate observed devices"
30+
31+
def __init__(self, args):
32+
self.app_ids = args.app
33+
self.active = args.active or False
34+
self.client = LocalClient(default_multicast_address(), 1.0)
35+
self.decoder = TDF()
36+
self.state = "Scanning"
37+
self.name = "Active" if args.active else "Inactive"
38+
39+
self.correct: set[int] = set()
40+
self.updated: set[int] = set()
41+
42+
@classmethod
43+
def add_parser(cls, parser):
44+
addr_group = parser.add_mutually_exclusive_group(required=True)
45+
addr_group.add_argument("--app", type=lambda x: int(x, 0), action="append", help="Application ID to control")
46+
mode_group = parser.add_mutually_exclusive_group(required=True)
47+
mode_group.add_argument("--active", action="store_true", help="Move all devices to active state")
48+
mode_group.add_argument("--inactive", action="store_true", help="Move all devices to inactive state")
49+
50+
def progress_table(self):
51+
table = Table()
52+
table.add_column()
53+
table.add_column("Count")
54+
table.add_row(self.name, str(len(self.correct)))
55+
table.add_row("Updated", str(len(self.updated)))
56+
57+
meta = Table(box=None)
58+
meta.add_column()
59+
meta.add_row(table)
60+
meta.add_row(Status(self.state))
61+
62+
return meta
63+
64+
def state_update(self, live: Live, state: str):
65+
self.state = state
66+
live.update(self.progress_table())
67+
68+
def update_active_state(self, live: Live, infuse_id: int, active: bool):
69+
try:
70+
self.state_update(live, f"Connecting to {infuse_id:016X}")
71+
with self.client.connection(infuse_id, GatewayRequestConnectionRequest.DataType.COMMAND) as mtu:
72+
rpc_client = RpcClient(self.client, mtu, infuse_id)
73+
74+
key_val = bytes(kv.slots.application_active(1 if active else 0))
75+
all_vals = (
76+
bytes(rpc.rpc_struct_kv_store_value(kv.slots.application_active.BASE_ID, len(key_val))) + key_val
77+
)
78+
params = bytes(rpc.kv_write.request(1)) + all_vals
79+
80+
hdr, _ = rpc_client.run_standard_cmd(
81+
rpc.kv_write.COMMAND_ID, Auth.DEVICE, params, rpc.kv_write.response.from_buffer_copy
82+
)
83+
if hdr is None:
84+
return
85+
if hdr.return_code == 0:
86+
self.updated.add(infuse_id)
87+
88+
except ConnectionRefusedError:
89+
self.state_update(live, "Scanning")
90+
return
91+
self.state_update(live, "Scanning")
92+
93+
def run(self):
94+
with Live(self.progress_table(), refresh_per_second=4) as live:
95+
for source, announce in self.client.observe_announce():
96+
if announce.application not in self.app_ids:
97+
continue
98+
application_active = not (announce.flags & 0x80)
99+
if self.active == application_active:
100+
self.correct.add(source.infuse_id)
101+
else:
102+
self.update_active_state(live, source.infuse_id, self.active)
103+
live.update(self.progress_table())

0 commit comments

Comments
 (0)