eeroctl 1.7.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eeroctl/__init__.py +19 -0
- eeroctl/commands/__init__.py +32 -0
- eeroctl/commands/activity.py +237 -0
- eeroctl/commands/auth.py +471 -0
- eeroctl/commands/completion.py +142 -0
- eeroctl/commands/device.py +492 -0
- eeroctl/commands/eero/__init__.py +12 -0
- eeroctl/commands/eero/base.py +224 -0
- eeroctl/commands/eero/led.py +154 -0
- eeroctl/commands/eero/nightlight.py +235 -0
- eeroctl/commands/eero/updates.py +82 -0
- eeroctl/commands/network/__init__.py +18 -0
- eeroctl/commands/network/advanced.py +191 -0
- eeroctl/commands/network/backup.py +162 -0
- eeroctl/commands/network/base.py +331 -0
- eeroctl/commands/network/dhcp.py +118 -0
- eeroctl/commands/network/dns.py +197 -0
- eeroctl/commands/network/forwards.py +115 -0
- eeroctl/commands/network/guest.py +162 -0
- eeroctl/commands/network/security.py +162 -0
- eeroctl/commands/network/speedtest.py +99 -0
- eeroctl/commands/network/sqm.py +194 -0
- eeroctl/commands/profile.py +671 -0
- eeroctl/commands/troubleshoot.py +317 -0
- eeroctl/context.py +254 -0
- eeroctl/errors.py +156 -0
- eeroctl/exit_codes.py +68 -0
- eeroctl/formatting/__init__.py +90 -0
- eeroctl/formatting/base.py +181 -0
- eeroctl/formatting/device.py +430 -0
- eeroctl/formatting/eero.py +591 -0
- eeroctl/formatting/misc.py +87 -0
- eeroctl/formatting/network.py +659 -0
- eeroctl/formatting/profile.py +443 -0
- eeroctl/main.py +161 -0
- eeroctl/options.py +429 -0
- eeroctl/output.py +739 -0
- eeroctl/safety.py +259 -0
- eeroctl/utils.py +181 -0
- eeroctl-1.7.1.dist-info/METADATA +115 -0
- eeroctl-1.7.1.dist-info/RECORD +45 -0
- eeroctl-1.7.1.dist-info/WHEEL +5 -0
- eeroctl-1.7.1.dist-info/entry_points.txt +3 -0
- eeroctl-1.7.1.dist-info/licenses/LICENSE +21 -0
- eeroctl-1.7.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
"""Device (connected device) commands for the Eero CLI.
|
|
2
|
+
|
|
3
|
+
Commands:
|
|
4
|
+
- eero device list: List all connected devices
|
|
5
|
+
- eero device show: Show device details
|
|
6
|
+
- eero device rename: Rename a device
|
|
7
|
+
- eero device block: Block a device
|
|
8
|
+
- eero device unblock: Unblock a device
|
|
9
|
+
- eero device priority: Device priority management
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import sys
|
|
14
|
+
from typing import Literal, Optional
|
|
15
|
+
|
|
16
|
+
import click
|
|
17
|
+
from eero import EeroClient
|
|
18
|
+
from eero.const import EeroDeviceStatus
|
|
19
|
+
from rich.panel import Panel
|
|
20
|
+
from rich.table import Table
|
|
21
|
+
|
|
22
|
+
from ..context import EeroCliContext, ensure_cli_context
|
|
23
|
+
from ..exit_codes import ExitCode
|
|
24
|
+
from ..options import apply_options, force_option, network_option, output_option
|
|
25
|
+
from ..output import OutputFormat
|
|
26
|
+
from ..safety import OperationRisk, SafetyError, confirm_or_fail
|
|
27
|
+
from ..utils import run_with_client
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@click.group(name="device")
|
|
31
|
+
@click.pass_context
|
|
32
|
+
def device_group(ctx: click.Context) -> None:
|
|
33
|
+
"""Manage connected devices.
|
|
34
|
+
|
|
35
|
+
\b
|
|
36
|
+
Commands:
|
|
37
|
+
list - List all connected devices
|
|
38
|
+
show - Show device details
|
|
39
|
+
rename - Rename a device
|
|
40
|
+
block - Block a device
|
|
41
|
+
unblock - Unblock a device
|
|
42
|
+
priority - Bandwidth priority management
|
|
43
|
+
|
|
44
|
+
\b
|
|
45
|
+
Examples:
|
|
46
|
+
eero device list # List all devices
|
|
47
|
+
eero device show "iPhone" # Show by name
|
|
48
|
+
eero device block AA:BB:CC:DD:EE:FF # Block by MAC
|
|
49
|
+
eero device priority show "iPad" # Show priority
|
|
50
|
+
"""
|
|
51
|
+
ensure_cli_context(ctx)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@device_group.command(name="list")
|
|
55
|
+
@output_option
|
|
56
|
+
@network_option
|
|
57
|
+
@click.pass_context
|
|
58
|
+
def device_list(ctx: click.Context, output: Optional[str], network_id: Optional[str]) -> None:
|
|
59
|
+
"""List all connected devices."""
|
|
60
|
+
cli_ctx = apply_options(ctx, output=output, network_id=network_id)
|
|
61
|
+
console = cli_ctx.console
|
|
62
|
+
|
|
63
|
+
async def run_cmd() -> None:
|
|
64
|
+
async def get_devices(client: EeroClient) -> None:
|
|
65
|
+
with cli_ctx.status("Getting devices..."):
|
|
66
|
+
devices = await client.get_devices(cli_ctx.network_id)
|
|
67
|
+
|
|
68
|
+
if not devices:
|
|
69
|
+
console.print("[yellow]No devices found[/yellow]")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
if cli_ctx.is_structured_output():
|
|
73
|
+
data = [d.model_dump(mode="json") for d in devices]
|
|
74
|
+
cli_ctx.render_structured(data, "eero.device.list/v1")
|
|
75
|
+
elif cli_ctx.output_format == OutputFormat.LIST:
|
|
76
|
+
for d in devices:
|
|
77
|
+
name = d.display_name or d.hostname or d.nickname or "Unknown"
|
|
78
|
+
status = d.status.value if d.status else "unknown"
|
|
79
|
+
device_type = d.device_type or ""
|
|
80
|
+
connection = d.connection_type or ""
|
|
81
|
+
# Use print() with fixed-width columns for alignment
|
|
82
|
+
print(
|
|
83
|
+
f"{d.id or '':<14} {name:<30} {d.ip or d.ipv4 or '':<15} "
|
|
84
|
+
f"{d.mac or '':<17} {status:<12} {device_type:<20} {connection}"
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
table = Table(title="Connected Devices")
|
|
88
|
+
table.add_column("ID", style="dim")
|
|
89
|
+
table.add_column("Name", style="cyan")
|
|
90
|
+
table.add_column("IP", style="green")
|
|
91
|
+
table.add_column("MAC", style="yellow")
|
|
92
|
+
table.add_column("Status")
|
|
93
|
+
table.add_column("Type")
|
|
94
|
+
table.add_column("Connection")
|
|
95
|
+
|
|
96
|
+
for d in devices:
|
|
97
|
+
name = d.display_name or d.hostname or d.nickname or "Unknown"
|
|
98
|
+
|
|
99
|
+
if d.status == EeroDeviceStatus.CONNECTED:
|
|
100
|
+
status = "[green]connected[/green]"
|
|
101
|
+
elif d.status == EeroDeviceStatus.BLOCKED:
|
|
102
|
+
status = "[red]blocked[/red]"
|
|
103
|
+
else:
|
|
104
|
+
status = "[yellow]disconnected[/yellow]"
|
|
105
|
+
|
|
106
|
+
table.add_row(
|
|
107
|
+
d.id or "",
|
|
108
|
+
name,
|
|
109
|
+
d.ip or d.ipv4 or "",
|
|
110
|
+
d.mac or "",
|
|
111
|
+
status,
|
|
112
|
+
d.device_type or "",
|
|
113
|
+
d.connection_type or "",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
console.print(table)
|
|
117
|
+
|
|
118
|
+
await run_with_client(get_devices)
|
|
119
|
+
|
|
120
|
+
asyncio.run(run_cmd())
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@device_group.command(name="show")
|
|
124
|
+
@click.argument("device_id")
|
|
125
|
+
@output_option
|
|
126
|
+
@network_option
|
|
127
|
+
@click.pass_context
|
|
128
|
+
def device_show(
|
|
129
|
+
ctx: click.Context, device_id: str, output: Optional[str], network_id: Optional[str]
|
|
130
|
+
) -> None:
|
|
131
|
+
"""Show details of a specific device.
|
|
132
|
+
|
|
133
|
+
\b
|
|
134
|
+
Arguments:
|
|
135
|
+
DEVICE_ID Device ID, MAC address, or name
|
|
136
|
+
"""
|
|
137
|
+
cli_ctx = apply_options(ctx, output=output, network_id=network_id)
|
|
138
|
+
console = cli_ctx.console
|
|
139
|
+
|
|
140
|
+
async def run_cmd() -> None:
|
|
141
|
+
async def get_device(client: EeroClient) -> None:
|
|
142
|
+
with cli_ctx.status("Getting devices..."):
|
|
143
|
+
devices = await client.get_devices(cli_ctx.network_id)
|
|
144
|
+
|
|
145
|
+
# Find device by ID, MAC, or name
|
|
146
|
+
target = None
|
|
147
|
+
for d in devices:
|
|
148
|
+
if (
|
|
149
|
+
d.id == device_id
|
|
150
|
+
or d.mac == device_id
|
|
151
|
+
or d.display_name == device_id
|
|
152
|
+
or d.nickname == device_id
|
|
153
|
+
or d.hostname == device_id
|
|
154
|
+
):
|
|
155
|
+
target = d
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
if not target or not target.id:
|
|
159
|
+
console.print(f"[red]Device '{device_id}' not found[/red]")
|
|
160
|
+
sys.exit(ExitCode.NOT_FOUND)
|
|
161
|
+
|
|
162
|
+
# Get full details
|
|
163
|
+
with cli_ctx.status("Getting device details..."):
|
|
164
|
+
device = await client.get_device(target.id, cli_ctx.network_id)
|
|
165
|
+
|
|
166
|
+
if cli_ctx.is_structured_output():
|
|
167
|
+
cli_ctx.render_structured(device.model_dump(mode="json"), "eero.device.show/v1")
|
|
168
|
+
else:
|
|
169
|
+
from ..formatting import print_device_details
|
|
170
|
+
|
|
171
|
+
detail: Literal["brief", "full"] = (
|
|
172
|
+
"full" if cli_ctx.detail_level == "full" else "brief"
|
|
173
|
+
)
|
|
174
|
+
print_device_details(device, detail_level=detail)
|
|
175
|
+
|
|
176
|
+
await run_with_client(get_device)
|
|
177
|
+
|
|
178
|
+
asyncio.run(run_cmd())
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@device_group.command(name="rename")
|
|
182
|
+
@click.argument("device_id")
|
|
183
|
+
@click.option("--name", required=True, help="New nickname for the device")
|
|
184
|
+
@network_option
|
|
185
|
+
@click.pass_context
|
|
186
|
+
def device_rename(ctx: click.Context, device_id: str, name: str, network_id: Optional[str]) -> None:
|
|
187
|
+
"""Rename a device.
|
|
188
|
+
|
|
189
|
+
\b
|
|
190
|
+
Arguments:
|
|
191
|
+
DEVICE_ID Device ID, MAC address, or name
|
|
192
|
+
|
|
193
|
+
\b
|
|
194
|
+
Options:
|
|
195
|
+
--name TEXT New nickname (required)
|
|
196
|
+
"""
|
|
197
|
+
cli_ctx = apply_options(ctx, network_id=network_id)
|
|
198
|
+
console = cli_ctx.console
|
|
199
|
+
|
|
200
|
+
async def run_cmd() -> None:
|
|
201
|
+
async def rename_device(client: EeroClient) -> None:
|
|
202
|
+
# Find device first
|
|
203
|
+
with cli_ctx.status("Finding device..."):
|
|
204
|
+
devices = await client.get_devices(cli_ctx.network_id)
|
|
205
|
+
|
|
206
|
+
target = None
|
|
207
|
+
for d in devices:
|
|
208
|
+
if (
|
|
209
|
+
d.id == device_id
|
|
210
|
+
or d.mac == device_id
|
|
211
|
+
or d.display_name == device_id
|
|
212
|
+
or d.nickname == device_id
|
|
213
|
+
):
|
|
214
|
+
target = d
|
|
215
|
+
break
|
|
216
|
+
|
|
217
|
+
if not target or not target.id:
|
|
218
|
+
console.print(f"[red]Device '{device_id}' not found[/red]")
|
|
219
|
+
sys.exit(ExitCode.NOT_FOUND)
|
|
220
|
+
|
|
221
|
+
with cli_ctx.status(f"Renaming device to '{name}'..."):
|
|
222
|
+
result = await client.set_device_nickname(target.id, name, cli_ctx.network_id)
|
|
223
|
+
|
|
224
|
+
if result:
|
|
225
|
+
console.print(f"[bold green]Device renamed to '{name}'[/bold green]")
|
|
226
|
+
else:
|
|
227
|
+
console.print("[red]Failed to rename device[/red]")
|
|
228
|
+
sys.exit(ExitCode.GENERIC_ERROR)
|
|
229
|
+
|
|
230
|
+
await run_with_client(rename_device)
|
|
231
|
+
|
|
232
|
+
asyncio.run(run_cmd())
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@device_group.command(name="block")
|
|
236
|
+
@click.argument("device_id")
|
|
237
|
+
@force_option
|
|
238
|
+
@network_option
|
|
239
|
+
@click.pass_context
|
|
240
|
+
def device_block(
|
|
241
|
+
ctx: click.Context, device_id: str, force: Optional[bool], network_id: Optional[str]
|
|
242
|
+
) -> None:
|
|
243
|
+
"""Block a device from the network.
|
|
244
|
+
|
|
245
|
+
\b
|
|
246
|
+
Arguments:
|
|
247
|
+
DEVICE_ID Device ID, MAC address, or name
|
|
248
|
+
"""
|
|
249
|
+
cli_ctx = apply_options(ctx, network_id=network_id, force=force)
|
|
250
|
+
_set_device_blocked(cli_ctx, device_id, True)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@device_group.command(name="unblock")
|
|
254
|
+
@click.argument("device_id")
|
|
255
|
+
@force_option
|
|
256
|
+
@network_option
|
|
257
|
+
@click.pass_context
|
|
258
|
+
def device_unblock(
|
|
259
|
+
ctx: click.Context, device_id: str, force: Optional[bool], network_id: Optional[str]
|
|
260
|
+
) -> None:
|
|
261
|
+
"""Unblock a device.
|
|
262
|
+
|
|
263
|
+
\b
|
|
264
|
+
Arguments:
|
|
265
|
+
DEVICE_ID Device ID, MAC address, or name
|
|
266
|
+
"""
|
|
267
|
+
cli_ctx = apply_options(ctx, network_id=network_id, force=force)
|
|
268
|
+
_set_device_blocked(cli_ctx, device_id, False)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _set_device_blocked(cli_ctx: EeroCliContext, device_id: str, blocked: bool) -> None:
|
|
272
|
+
"""Block or unblock a device."""
|
|
273
|
+
console = cli_ctx.console
|
|
274
|
+
action = "block" if blocked else "unblock"
|
|
275
|
+
|
|
276
|
+
async def run_cmd() -> None:
|
|
277
|
+
async def toggle_block(client: EeroClient) -> None:
|
|
278
|
+
# Find device first
|
|
279
|
+
with cli_ctx.status("Finding device..."):
|
|
280
|
+
devices = await client.get_devices(cli_ctx.network_id)
|
|
281
|
+
|
|
282
|
+
target = None
|
|
283
|
+
for d in devices:
|
|
284
|
+
if (
|
|
285
|
+
d.id == device_id
|
|
286
|
+
or d.mac == device_id
|
|
287
|
+
or d.display_name == device_id
|
|
288
|
+
or d.nickname == device_id
|
|
289
|
+
):
|
|
290
|
+
target = d
|
|
291
|
+
break
|
|
292
|
+
|
|
293
|
+
if not target or not target.id:
|
|
294
|
+
console.print(f"[red]Device '{device_id}' not found[/red]")
|
|
295
|
+
sys.exit(ExitCode.NOT_FOUND)
|
|
296
|
+
|
|
297
|
+
device_name = target.display_name or target.nickname or target.hostname or device_id
|
|
298
|
+
|
|
299
|
+
try:
|
|
300
|
+
confirm_or_fail(
|
|
301
|
+
action=action,
|
|
302
|
+
target=device_name,
|
|
303
|
+
risk=OperationRisk.MEDIUM,
|
|
304
|
+
force=cli_ctx.force,
|
|
305
|
+
non_interactive=cli_ctx.non_interactive,
|
|
306
|
+
dry_run=cli_ctx.dry_run,
|
|
307
|
+
console=cli_ctx.console,
|
|
308
|
+
)
|
|
309
|
+
except SafetyError as e:
|
|
310
|
+
cli_ctx.renderer.render_error(e.message)
|
|
311
|
+
sys.exit(e.exit_code)
|
|
312
|
+
|
|
313
|
+
with cli_ctx.status(f"{action.capitalize()}ing {device_name}..."):
|
|
314
|
+
result = await client.block_device(target.id, blocked, cli_ctx.network_id)
|
|
315
|
+
|
|
316
|
+
if result:
|
|
317
|
+
console.print(f"[bold green]Device {action}ed[/bold green]")
|
|
318
|
+
else:
|
|
319
|
+
console.print(f"[red]Failed to {action} device[/red]")
|
|
320
|
+
sys.exit(ExitCode.GENERIC_ERROR)
|
|
321
|
+
|
|
322
|
+
await run_with_client(toggle_block)
|
|
323
|
+
|
|
324
|
+
asyncio.run(run_cmd())
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
# ==================== Priority Subcommand Group ====================
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@device_group.group(name="priority")
|
|
331
|
+
@click.pass_context
|
|
332
|
+
def priority_group(ctx: click.Context) -> None:
|
|
333
|
+
"""Manage device bandwidth priority.
|
|
334
|
+
|
|
335
|
+
\b
|
|
336
|
+
Commands:
|
|
337
|
+
show - Show priority status
|
|
338
|
+
on - Enable priority
|
|
339
|
+
off - Disable priority
|
|
340
|
+
"""
|
|
341
|
+
pass
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@priority_group.command(name="show")
|
|
345
|
+
@click.argument("device_id")
|
|
346
|
+
@output_option
|
|
347
|
+
@network_option
|
|
348
|
+
@click.pass_context
|
|
349
|
+
def priority_show(
|
|
350
|
+
ctx: click.Context, device_id: str, output: Optional[str], network_id: Optional[str]
|
|
351
|
+
) -> None:
|
|
352
|
+
"""Show priority status for a device."""
|
|
353
|
+
cli_ctx = apply_options(ctx, output=output, network_id=network_id)
|
|
354
|
+
console = cli_ctx.console
|
|
355
|
+
renderer = cli_ctx.renderer
|
|
356
|
+
|
|
357
|
+
async def run_cmd() -> None:
|
|
358
|
+
async def get_priority(client: EeroClient) -> None:
|
|
359
|
+
# Find device first
|
|
360
|
+
with cli_ctx.status("Finding device..."):
|
|
361
|
+
devices = await client.get_devices(cli_ctx.network_id)
|
|
362
|
+
|
|
363
|
+
target = None
|
|
364
|
+
for d in devices:
|
|
365
|
+
if (
|
|
366
|
+
d.id == device_id
|
|
367
|
+
or d.mac == device_id
|
|
368
|
+
or d.display_name == device_id
|
|
369
|
+
or d.nickname == device_id
|
|
370
|
+
):
|
|
371
|
+
target = d
|
|
372
|
+
break
|
|
373
|
+
|
|
374
|
+
if not target or not target.id:
|
|
375
|
+
console.print(f"[red]Device '{device_id}' not found[/red]")
|
|
376
|
+
sys.exit(ExitCode.NOT_FOUND)
|
|
377
|
+
|
|
378
|
+
with cli_ctx.status("Getting priority status..."):
|
|
379
|
+
priority_data = await client.get_device_priority(target.id, cli_ctx.network_id)
|
|
380
|
+
|
|
381
|
+
if cli_ctx.is_json_output():
|
|
382
|
+
renderer.render_json(priority_data, "eero.device.priority.show/v1")
|
|
383
|
+
else:
|
|
384
|
+
prioritized = priority_data.get("prioritized", False)
|
|
385
|
+
duration = priority_data.get("duration", 0)
|
|
386
|
+
|
|
387
|
+
content = f"[bold]Prioritized:[/bold] {'[green]Yes[/green]' if prioritized else '[dim]No[/dim]'}"
|
|
388
|
+
if prioritized and duration > 0:
|
|
389
|
+
content += f"\n[bold]Duration:[/bold] {duration} minutes"
|
|
390
|
+
|
|
391
|
+
console.print(Panel(content, title="Priority Status", border_style="blue"))
|
|
392
|
+
|
|
393
|
+
await run_with_client(get_priority)
|
|
394
|
+
|
|
395
|
+
asyncio.run(run_cmd())
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
@priority_group.command(name="on")
|
|
399
|
+
@click.argument("device_id")
|
|
400
|
+
@click.option("--minutes", "-m", type=int, default=0, help="Duration in minutes (0=indefinite)")
|
|
401
|
+
@network_option
|
|
402
|
+
@click.pass_context
|
|
403
|
+
def priority_on(
|
|
404
|
+
ctx: click.Context, device_id: str, minutes: int, network_id: Optional[str]
|
|
405
|
+
) -> None:
|
|
406
|
+
"""Enable priority for a device.
|
|
407
|
+
|
|
408
|
+
\b
|
|
409
|
+
Options:
|
|
410
|
+
--minutes, -m Duration (0=indefinite)
|
|
411
|
+
"""
|
|
412
|
+
cli_ctx = apply_options(ctx, network_id=network_id)
|
|
413
|
+
console = cli_ctx.console
|
|
414
|
+
|
|
415
|
+
async def run_cmd() -> None:
|
|
416
|
+
async def set_priority(client: EeroClient) -> None:
|
|
417
|
+
# Find device first
|
|
418
|
+
with cli_ctx.status("Finding device..."):
|
|
419
|
+
devices = await client.get_devices(cli_ctx.network_id)
|
|
420
|
+
|
|
421
|
+
target = None
|
|
422
|
+
for d in devices:
|
|
423
|
+
if (
|
|
424
|
+
d.id == device_id
|
|
425
|
+
or d.mac == device_id
|
|
426
|
+
or d.display_name == device_id
|
|
427
|
+
or d.nickname == device_id
|
|
428
|
+
):
|
|
429
|
+
target = d
|
|
430
|
+
break
|
|
431
|
+
|
|
432
|
+
if not target or not target.id:
|
|
433
|
+
console.print(f"[red]Device '{device_id}' not found[/red]")
|
|
434
|
+
sys.exit(ExitCode.NOT_FOUND)
|
|
435
|
+
|
|
436
|
+
duration_str = f" for {minutes} minutes" if minutes > 0 else " (indefinite)"
|
|
437
|
+
with cli_ctx.status(f"Prioritizing device{duration_str}..."):
|
|
438
|
+
result = await client.prioritize_device(target.id, minutes, cli_ctx.network_id)
|
|
439
|
+
|
|
440
|
+
if result:
|
|
441
|
+
console.print(f"[bold green]Device prioritized{duration_str}[/bold green]")
|
|
442
|
+
else:
|
|
443
|
+
console.print("[red]Failed to prioritize device[/red]")
|
|
444
|
+
sys.exit(ExitCode.GENERIC_ERROR)
|
|
445
|
+
|
|
446
|
+
await run_with_client(set_priority)
|
|
447
|
+
|
|
448
|
+
asyncio.run(run_cmd())
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
@priority_group.command(name="off")
|
|
452
|
+
@click.argument("device_id")
|
|
453
|
+
@network_option
|
|
454
|
+
@click.pass_context
|
|
455
|
+
def priority_off(ctx: click.Context, device_id: str, network_id: Optional[str]) -> None:
|
|
456
|
+
"""Remove priority from a device."""
|
|
457
|
+
cli_ctx = apply_options(ctx, network_id=network_id)
|
|
458
|
+
console = cli_ctx.console
|
|
459
|
+
|
|
460
|
+
async def run_cmd() -> None:
|
|
461
|
+
async def remove_priority(client: EeroClient) -> None:
|
|
462
|
+
# Find device first
|
|
463
|
+
with cli_ctx.status("Finding device..."):
|
|
464
|
+
devices = await client.get_devices(cli_ctx.network_id)
|
|
465
|
+
|
|
466
|
+
target = None
|
|
467
|
+
for d in devices:
|
|
468
|
+
if (
|
|
469
|
+
d.id == device_id
|
|
470
|
+
or d.mac == device_id
|
|
471
|
+
or d.display_name == device_id
|
|
472
|
+
or d.nickname == device_id
|
|
473
|
+
):
|
|
474
|
+
target = d
|
|
475
|
+
break
|
|
476
|
+
|
|
477
|
+
if not target or not target.id:
|
|
478
|
+
console.print(f"[red]Device '{device_id}' not found[/red]")
|
|
479
|
+
sys.exit(ExitCode.NOT_FOUND)
|
|
480
|
+
|
|
481
|
+
with cli_ctx.status("Removing priority..."):
|
|
482
|
+
result = await client.deprioritize_device(target.id, cli_ctx.network_id)
|
|
483
|
+
|
|
484
|
+
if result:
|
|
485
|
+
console.print("[bold green]Priority removed[/bold green]")
|
|
486
|
+
else:
|
|
487
|
+
console.print("[red]Failed to remove priority[/red]")
|
|
488
|
+
sys.exit(ExitCode.GENERIC_ERROR)
|
|
489
|
+
|
|
490
|
+
await run_with_client(remove_priority)
|
|
491
|
+
|
|
492
|
+
asyncio.run(run_cmd())
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Eero commands package for the Eero CLI.
|
|
2
|
+
|
|
3
|
+
This package contains all Eero mesh node-related commands, split into logical submodules:
|
|
4
|
+
- base: Core commands (list, show, reboot)
|
|
5
|
+
- led: LED management commands
|
|
6
|
+
- nightlight: Nightlight commands (Beacon only)
|
|
7
|
+
- updates: Update management commands
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .base import eero_group
|
|
11
|
+
|
|
12
|
+
__all__ = ["eero_group"]
|