Coverage for core / src / sensorkit / cli / agent.py: 0%
108 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-02 00:03 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-02 00:03 +0000
1# SPDX-License-Identifier: Apache-2.0
2import asyncclick as click
4from sensorkit.auto.agent import AgentControllerInfo, AgentState
5from sensorkit.cli.utils import entity_option, with_kit
8@click.group("agent")
9async def agent_group():
10 """Agent management commands."""
11 pass
14@agent_group.command("include")
15@click.argument("program_name")
16@entity_option(default="agent", help="Agent entity name")
17@with_kit
18async def include(kit, program_name: str, entity: str):
19 """Include a program in the agent's management."""
20 from sensorkit.auto.agent import AgentConfigureRequest, agent_configure_request
22 await kit.entity(entity).call(
23 agent_configure_request,
24 AgentConfigureRequest(remove_program_exclusions={program_name}),
25 )
26 click.echo(f"Removed scheduling exclusion for '{program_name}'")
29@agent_group.command("exclude")
30@click.argument("program_name")
31@entity_option(default="agent", help="Agent entity name")
32@with_kit
33async def exclude(kit, program_name: str, entity: str):
34 """Exclude a program from the agent's management."""
35 from sensorkit.auto.agent import AgentConfigureRequest, agent_configure_request
37 await kit.entity(entity).call(
38 agent_configure_request,
39 AgentConfigureRequest(add_program_exclusions={program_name}),
40 )
41 click.echo(f"Added scheduling exclusion for '{program_name}'")
44@agent_group.command("global-control")
45@click.argument("state", type=click.Choice(["on", "off"], case_sensitive=False))
46@entity_option(default="agent", help="Agent entity name")
47@with_kit
48async def global_control(kit, state: str, entity: str):
49 """Enable or disable global control."""
50 from sensorkit.auto.agent import AgentConfigureRequest, agent_configure_request
52 enabled = state.lower() == "on"
53 await kit.entity(entity).call(
54 agent_configure_request,
55 AgentConfigureRequest(global_control_enabled=enabled),
56 )
57 click.echo(f"Global control was set to {state}")
60@agent_group.command("control")
61@click.argument("controller")
62@click.argument("state", type=click.Choice(["on", "off"], case_sensitive=False))
63@entity_option(default="agent", help="Agent entity name")
64@with_kit
65async def controller_control(kit, controller: str, state: str, entity: str):
66 """Enable or disable control for a specific controller."""
67 from sensorkit.auto.agent import AgentConfigureRequest, agent_configure_request
69 enabled = state.lower() == "on"
70 await kit.entity(entity).call(
71 agent_configure_request,
72 AgentConfigureRequest(controller_control_enabled={controller: enabled}),
73 )
74 click.echo(f"Control for '{controller}' was set to {state}")
77@agent_group.command("override")
78@click.argument("controller")
79@click.argument("state", type=click.Choice(["up", "down", "none"], case_sensitive=False))
80@entity_option(default="agent", help="Agent entity name")
81@with_kit
82async def controller_override(kit, controller: str, state: str, entity: str):
83 """Set or clear demand override for a specific controller."""
84 from sensorkit.auto.agent import AgentConfigureRequest, agent_configure_request
86 state = state.lower()
87 override = True if state == "up" else False if state == "down" else None
89 await kit.entity(entity).call(
90 agent_configure_request,
91 AgentConfigureRequest(controller_demand_override={controller: override}),
92 )
93 click.echo(f"Demand override for '{controller}' was set to {state.upper()}")
96@agent_group.command("scheduling")
97@click.argument("state", type=click.Choice(["on", "off"], case_sensitive=False))
98@entity_option(default="agent", help="Agent entity name")
99@with_kit
100async def scheduling(kit, state: str, entity: str):
101 """Enable or disable scheduling."""
102 from sensorkit.auto.agent import AgentConfigureRequest, agent_configure_request
104 enabled = state.lower() == "on"
105 await kit.entity(entity).call(
106 agent_configure_request, AgentConfigureRequest(scheduling_enabled=enabled)
107 )
108 click.echo(f"Scheduling was set to {state}")
111@agent_group.command("status")
112@entity_option(default="agent", help="Agent entity name")
113@with_kit
114async def status(kit, entity: str):
115 """View the agent's current status and configuration."""
116 from sensorkit.auto.agent import AgentState
118 try:
119 state = await kit.entity(entity).kv_get_model(AgentState)
120 except Exception as e:
121 click.echo(f"Error: Could not recover agent state: {e}", err=True)
122 return
124 click.echo(f"{'Agent:':<18} {entity}")
126 # Global settings
127 click.echo(
128 f"{'Global Control:':<18} {'ON' if state.operating_state.global_control_enabled else 'OFF'}"
129 )
130 click.echo(
131 f"{'Scheduling:':<18} {'ON' if state.scheduler_state.scheduling_enabled else 'OFF'}\n"
132 )
134 if state.operating_state.controllers:
135 for i, (name, info) in enumerate(state.operating_state.controllers.items(), 1):
136 _print_controller_info(i, name, info, state)
137 click.echo()
138 else:
139 click.echo("No controllers registered in agent.\n")
141 # Excluded programs
142 if state.scheduler_state.excluded_programs:
143 click.echo("Excluded Programs:")
145 for program in sorted(state.scheduler_state.excluded_programs):
146 click.echo(f" - {program}")
147 else:
148 click.echo("No excluded programs.")
151def _print_controller_info(i: int, name: str, info: AgentControllerInfo, state: AgentState):
152 control = "ON" if info.control_enabled else "OFF"
154 if not state.operating_state.global_control_enabled:
155 control += "*"
157 override = "NONE"
159 if info.demand_override is True:
160 override = "UP"
161 elif info.demand_override is False:
162 override = "DOWN"
164 elected = "NONE"
166 if info.elected_state is True:
167 elected = "UP"
168 elif info.elected_state is False:
169 elected = "DOWN"
171 click.echo(f"{f'Controller {i}:':<18} {name}\n")
172 click.echo(f" Control: {control}")
173 click.echo(f" State override: {override}")
174 click.echo(f" Elected state: {elected}")
175 click.echo()
177 if schedule := state.scheduler_state.schedule.get(name):
178 from intervaltree import IntervalTree
180 from sensorkit.auto.scheduler import debug_print_schedule
182 debug_print_schedule(
183 IntervalTree.from_tuples(schedule),
184 lookahead_mins=5,
185 print_func=lambda x: click.echo(f" {x}"),
186 )
187 else:
188 click.echo(" No schedule present.")