Skip to content

Commit 67a217e

Browse files
committed
refactor(cli): redesign to equipment-centric command structure
Address reviewer feedback to mirror the library layout in the CLI. Instead of a flat 'set' command group: omnilogic set on 5 omnilogic set heater-temp 4 82 omnilogic set speed 3 75 The CLI now uses equipment-type subcommands with control methods: omnilogic heater 4 (show heater info) omnilogic heater 4 turn_on omnilogic heater 4 set_temperature 82 omnilogic pump 3 set_speed 75 omnilogic filter 3 turn_off omnilogic relay 6 turn_on omnilogic light 5 turn_off Each equipment type is a Click group that: - Takes a system_id argument - Shows all properties when called without a subcommand - Exposes control methods as subcommands matching the library API Equipment commands added: heater, pump, filter, relay, light
1 parent f436a01 commit 67a217e

11 files changed

Lines changed: 342 additions & 197 deletions

File tree

pyomnilogic_local/cli/cli.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77

88
from pyomnilogic_local import OmniLogic
99
from pyomnilogic_local.cli.debug import commands as debug
10+
from pyomnilogic_local.cli.filter_cmd import filter
1011
from pyomnilogic_local.cli.get import commands as get
11-
from pyomnilogic_local.cli.set import commands as set_cmds
12+
from pyomnilogic_local.cli.heater_cmd import heater
13+
from pyomnilogic_local.cli.light_cmd import light
14+
from pyomnilogic_local.cli.pump_cmd import pump
15+
from pyomnilogic_local.cli.relay_cmd import relay
1216

1317

1418
@click.group()
@@ -61,4 +65,8 @@ def entrypoint(ctx: click.Context, host: str, port: int, timeout: int, debug: bo
6165

6266
entrypoint.add_command(debug.debug)
6367
entrypoint.add_command(get.get)
64-
entrypoint.add_command(set_cmds.set)
68+
entrypoint.add_command(heater)
69+
entrypoint.add_command(pump)
70+
entrypoint.add_command(filter)
71+
entrypoint.add_command(relay)
72+
entrypoint.add_command(light)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# mypy: disable-error-code="misc"
2+
3+
from __future__ import annotations
4+
5+
import asyncio
6+
from typing import TYPE_CHECKING
7+
8+
import click
9+
10+
from pyomnilogic_local.cli.utils import echo_properties
11+
12+
if TYPE_CHECKING:
13+
from pyomnilogic_local import OmniLogic
14+
15+
16+
@click.group(invoke_without_command=True)
17+
@click.argument("system_id", type=int)
18+
@click.pass_context
19+
def filter(ctx: click.Context, system_id: int) -> None:
20+
"""Interact with a filter by system ID.
21+
22+
If no subcommand is given, displays all properties of the filter.
23+
24+
Example:
25+
omnilogic filter 3 turn_on
26+
omnilogic filter 3 set_speed 75
27+
"""
28+
omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
29+
30+
equipment = omnilogic.all_filters.get_by_id(system_id)
31+
if equipment is None:
32+
raise click.ClickException(f"No filter found with system_id {system_id}. Use 'omnilogic get filters' to list available filters.")
33+
34+
ctx.obj["EQUIPMENT"] = equipment
35+
36+
if ctx.invoked_subcommand is None:
37+
echo_properties(equipment)
38+
39+
40+
@filter.command("turn_on")
41+
@click.pass_context
42+
def turn_on(ctx: click.Context) -> None:
43+
"""Turn the filter on."""
44+
equipment = ctx.obj["EQUIPMENT"]
45+
asyncio.run(equipment.turn_on())
46+
click.echo(f"Turned on '{equipment.name}' (system_id={equipment.system_id})")
47+
48+
49+
@filter.command("turn_off")
50+
@click.pass_context
51+
def turn_off(ctx: click.Context) -> None:
52+
"""Turn the filter off."""
53+
equipment = ctx.obj["EQUIPMENT"]
54+
asyncio.run(equipment.turn_off())
55+
click.echo(f"Turned off '{equipment.name}' (system_id={equipment.system_id})")
56+
57+
58+
@filter.command("set_speed")
59+
@click.argument("percent", type=int)
60+
@click.pass_context
61+
def set_speed(ctx: click.Context, percent: int) -> None:
62+
"""Set the filter speed (0-100 percent).
63+
64+
Example:
65+
omnilogic filter 3 set_speed 75
66+
"""
67+
equipment = ctx.obj["EQUIPMENT"]
68+
asyncio.run(equipment.set_speed(percent))
69+
click.echo(f"Set '{equipment.name}' (system_id={equipment.system_id}) to {percent}%")
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# mypy: disable-error-code="misc"
2+
3+
from __future__ import annotations
4+
5+
import asyncio
6+
from typing import TYPE_CHECKING
7+
8+
import click
9+
10+
from pyomnilogic_local.cli.utils import echo_properties
11+
12+
if TYPE_CHECKING:
13+
from pyomnilogic_local import OmniLogic
14+
15+
16+
@click.group(invoke_without_command=True)
17+
@click.argument("system_id", type=int)
18+
@click.pass_context
19+
def heater(ctx: click.Context, system_id: int) -> None:
20+
"""Interact with a heater by system ID.
21+
22+
If no subcommand is given, displays all properties of the heater.
23+
24+
Example:
25+
omnilogic heater 4
26+
omnilogic heater 4 turn_on
27+
omnilogic heater 4 set_temperature 82
28+
"""
29+
omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
30+
31+
equipment = omnilogic.all_heaters.get_by_id(system_id)
32+
if equipment is None:
33+
raise click.ClickException(f"No heater found with system_id {system_id}. Use 'omnilogic get heaters' to list available heaters.")
34+
35+
ctx.obj["EQUIPMENT"] = equipment
36+
37+
if ctx.invoked_subcommand is None:
38+
echo_properties(equipment)
39+
40+
41+
@heater.command("turn_on")
42+
@click.pass_context
43+
def turn_on(ctx: click.Context) -> None:
44+
"""Turn the heater on."""
45+
equipment = ctx.obj["EQUIPMENT"]
46+
asyncio.run(equipment.turn_on())
47+
click.echo(f"Turned on '{equipment.name}' (system_id={equipment.system_id})")
48+
49+
50+
@heater.command("turn_off")
51+
@click.pass_context
52+
def turn_off(ctx: click.Context) -> None:
53+
"""Turn the heater off."""
54+
equipment = ctx.obj["EQUIPMENT"]
55+
asyncio.run(equipment.turn_off())
56+
click.echo(f"Turned off '{equipment.name}' (system_id={equipment.system_id})")
57+
58+
59+
@heater.command("set_temperature")
60+
@click.argument("temperature", type=int)
61+
@click.pass_context
62+
def set_temperature(ctx: click.Context, temperature: int) -> None:
63+
"""Set the heater target temperature (Fahrenheit).
64+
65+
Example:
66+
omnilogic heater 4 set_temperature 82
67+
"""
68+
equipment = ctx.obj["EQUIPMENT"]
69+
asyncio.run(equipment.set_temperature(temperature))
70+
click.echo(f"Set '{equipment.name}' (system_id={equipment.system_id}) to {temperature}°F")
71+
72+
73+
@heater.command("set_solar_temperature")
74+
@click.argument("temperature", type=int)
75+
@click.pass_context
76+
def set_solar_temperature(ctx: click.Context, temperature: int) -> None:
77+
"""Set the solar heater target temperature (Fahrenheit).
78+
79+
Example:
80+
omnilogic heater 4 set_solar_temperature 90
81+
"""
82+
equipment = ctx.obj["EQUIPMENT"]
83+
asyncio.run(equipment.set_solar_temperature(temperature))
84+
click.echo(f"Set solar temperature for '{equipment.name}' (system_id={equipment.system_id}) to {temperature}°F")

pyomnilogic_local/cli/light_cmd.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# mypy: disable-error-code="misc"
2+
3+
from __future__ import annotations
4+
5+
import asyncio
6+
from typing import TYPE_CHECKING
7+
8+
import click
9+
10+
from pyomnilogic_local.cli.utils import echo_properties
11+
12+
if TYPE_CHECKING:
13+
from pyomnilogic_local import OmniLogic
14+
15+
16+
@click.group(invoke_without_command=True)
17+
@click.argument("system_id", type=int)
18+
@click.pass_context
19+
def light(ctx: click.Context, system_id: int) -> None:
20+
"""Interact with a light by system ID.
21+
22+
If no subcommand is given, displays all properties of the light.
23+
24+
Example:
25+
omnilogic light 5 turn_on
26+
omnilogic light 5 turn_off
27+
"""
28+
omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
29+
30+
equipment = omnilogic.all_lights.get_by_id(system_id)
31+
if equipment is None:
32+
raise click.ClickException(f"No light found with system_id {system_id}. Use 'omnilogic get lights' to list available lights.")
33+
34+
ctx.obj["EQUIPMENT"] = equipment
35+
36+
if ctx.invoked_subcommand is None:
37+
echo_properties(equipment)
38+
39+
40+
@light.command("turn_on")
41+
@click.pass_context
42+
def turn_on(ctx: click.Context) -> None:
43+
"""Turn the light on."""
44+
equipment = ctx.obj["EQUIPMENT"]
45+
asyncio.run(equipment.turn_on())
46+
click.echo(f"Turned on '{equipment.name}' (system_id={equipment.system_id})")
47+
48+
49+
@light.command("turn_off")
50+
@click.pass_context
51+
def turn_off(ctx: click.Context) -> None:
52+
"""Turn the light off."""
53+
equipment = ctx.obj["EQUIPMENT"]
54+
asyncio.run(equipment.turn_off())
55+
click.echo(f"Turned off '{equipment.name}' (system_id={equipment.system_id})")

pyomnilogic_local/cli/pump_cmd.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# mypy: disable-error-code="misc"
2+
3+
from __future__ import annotations
4+
5+
import asyncio
6+
from typing import TYPE_CHECKING
7+
8+
import click
9+
10+
from pyomnilogic_local.cli.utils import echo_properties
11+
12+
if TYPE_CHECKING:
13+
from pyomnilogic_local import OmniLogic
14+
15+
16+
@click.group(invoke_without_command=True)
17+
@click.argument("system_id", type=int)
18+
@click.pass_context
19+
def pump(ctx: click.Context, system_id: int) -> None:
20+
"""Interact with a pump by system ID.
21+
22+
If no subcommand is given, displays all properties of the pump.
23+
24+
Example:
25+
omnilogic pump 3 turn_on
26+
omnilogic pump 3 set_speed 75
27+
"""
28+
omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
29+
30+
equipment = omnilogic.all_pumps.get_by_id(system_id)
31+
if equipment is None:
32+
raise click.ClickException(f"No pump found with system_id {system_id}. Use 'omnilogic get pumps' to list available pumps.")
33+
34+
ctx.obj["EQUIPMENT"] = equipment
35+
36+
if ctx.invoked_subcommand is None:
37+
echo_properties(equipment)
38+
39+
40+
@pump.command("turn_on")
41+
@click.pass_context
42+
def turn_on(ctx: click.Context) -> None:
43+
"""Turn the pump on."""
44+
equipment = ctx.obj["EQUIPMENT"]
45+
asyncio.run(equipment.turn_on())
46+
click.echo(f"Turned on '{equipment.name}' (system_id={equipment.system_id})")
47+
48+
49+
@pump.command("turn_off")
50+
@click.pass_context
51+
def turn_off(ctx: click.Context) -> None:
52+
"""Turn the pump off."""
53+
equipment = ctx.obj["EQUIPMENT"]
54+
asyncio.run(equipment.turn_off())
55+
click.echo(f"Turned off '{equipment.name}' (system_id={equipment.system_id})")
56+
57+
58+
@pump.command("set_speed")
59+
@click.argument("percent", type=int)
60+
@click.pass_context
61+
def set_speed(ctx: click.Context, percent: int) -> None:
62+
"""Set the pump speed (0-100 percent).
63+
64+
Example:
65+
omnilogic pump 3 set_speed 75
66+
"""
67+
equipment = ctx.obj["EQUIPMENT"]
68+
asyncio.run(equipment.set_speed(percent))
69+
click.echo(f"Set '{equipment.name}' (system_id={equipment.system_id}) to {percent}%")

pyomnilogic_local/cli/relay_cmd.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# mypy: disable-error-code="misc"
2+
3+
from __future__ import annotations
4+
5+
import asyncio
6+
from typing import TYPE_CHECKING
7+
8+
import click
9+
10+
from pyomnilogic_local.cli.utils import echo_properties
11+
12+
if TYPE_CHECKING:
13+
from pyomnilogic_local import OmniLogic
14+
15+
16+
@click.group(invoke_without_command=True)
17+
@click.argument("system_id", type=int)
18+
@click.pass_context
19+
def relay(ctx: click.Context, system_id: int) -> None:
20+
"""Interact with a relay by system ID.
21+
22+
If no subcommand is given, displays all properties of the relay.
23+
24+
Example:
25+
omnilogic relay 6 turn_on
26+
omnilogic relay 6 turn_off
27+
"""
28+
omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
29+
30+
equipment = omnilogic.all_relays.get_by_id(system_id)
31+
if equipment is None:
32+
raise click.ClickException(f"No relay found with system_id {system_id}. Use 'omnilogic get relays' to list available relays.")
33+
34+
ctx.obj["EQUIPMENT"] = equipment
35+
36+
if ctx.invoked_subcommand is None:
37+
echo_properties(equipment)
38+
39+
40+
@relay.command("turn_on")
41+
@click.pass_context
42+
def turn_on(ctx: click.Context) -> None:
43+
"""Turn the relay on."""
44+
equipment = ctx.obj["EQUIPMENT"]
45+
asyncio.run(equipment.turn_on())
46+
click.echo(f"Turned on '{equipment.name}' (system_id={equipment.system_id})")
47+
48+
49+
@relay.command("turn_off")
50+
@click.pass_context
51+
def turn_off(ctx: click.Context) -> None:
52+
"""Turn the relay off."""
53+
equipment = ctx.obj["EQUIPMENT"]
54+
asyncio.run(equipment.turn_off())
55+
click.echo(f"Turned off '{equipment.name}' (system_id={equipment.system_id})")

pyomnilogic_local/cli/set/__init__.py

Whitespace-only changes.

pyomnilogic_local/cli/set/commands.py

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)