Skip to content

Commit 956009f

Browse files
committed
feat(cli): add set command group for equipment control
Add a new 'set' command group to the CLI that exposes equipment control operations that were previously only available through the Python API: - set heater-temp <system_id> <temperature> — Set heater target temp (°F) - set solar-temp <system_id> <temperature> — Set solar heater target (°F) - set speed <system_id> <percent> — Set pump/filter speed (0-100%) - set on <system_id> — Turn equipment on - set off <system_id> — Turn equipment off This allows scripting and automation of pool control without writing Python code, complementing the existing read-only 'get' and 'debug' command groups. Resolves #145
1 parent 113fadb commit 956009f

6 files changed

Lines changed: 197 additions & 0 deletions

File tree

pyomnilogic_local/cli/cli.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pyomnilogic_local import OmniLogic
88
from pyomnilogic_local.cli.debug import commands as debug
99
from pyomnilogic_local.cli.get import commands as get
10+
from pyomnilogic_local.cli.set import commands as set_cmds
1011

1112

1213
@click.group()
@@ -55,3 +56,4 @@ def entrypoint(ctx: click.Context, host: str, port: int, timeout: int) -> None:
5556

5657
entrypoint.add_command(debug.debug)
5758
entrypoint.add_command(get.get)
59+
entrypoint.add_command(set_cmds.set)

pyomnilogic_local/cli/set/__init__.py

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Need to figure out how to resolve the 'Untyped decorator makes function "..." untyped' errors in mypy when using click decorators
2+
# mypy: disable-error-code="misc"
3+
4+
from __future__ import annotations
5+
6+
import click
7+
8+
from pyomnilogic_local.cli.set.equipment import equipment_off, equipment_on
9+
from pyomnilogic_local.cli.set.heater_temp import heater_temp, solar_temp
10+
from pyomnilogic_local.cli.set.speed import speed
11+
12+
13+
@click.group()
14+
@click.pass_context
15+
def set(ctx: click.Context) -> None:
16+
"""Control pool equipment (turn on/off, set temperature, set speed).
17+
18+
These commands send control signals to pool equipment. They require
19+
equipment system IDs which can be found using the 'get' commands.
20+
21+
Use with caution — these commands directly control physical equipment.
22+
"""
23+
ctx.ensure_object(dict)
24+
25+
26+
# Register subcommands
27+
set.add_command(equipment_on)
28+
set.add_command(equipment_off)
29+
set.add_command(heater_temp)
30+
set.add_command(solar_temp)
31+
set.add_command(speed)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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+
if TYPE_CHECKING:
11+
from pyomnilogic_local import OmniLogic
12+
13+
14+
@click.command("on")
15+
@click.argument("system_id", type=int)
16+
@click.pass_context
17+
def equipment_on(ctx: click.Context, system_id: int) -> None:
18+
"""Turn equipment on.
19+
20+
SYSTEM_ID is the equipment's system ID. Works with heaters, pumps, filters,
21+
lights, and relays. Use the appropriate 'get' command to find system IDs.
22+
23+
Example:
24+
omnilogic set on 5
25+
"""
26+
omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
27+
28+
equipment = omnilogic.get_equipment_by_id(system_id)
29+
if equipment is None:
30+
raise click.ClickException(f"No equipment found with system_id {system_id}.")
31+
32+
if not hasattr(equipment, "turn_on"):
33+
raise click.ClickException(f"Equipment '{equipment.name}' (system_id={system_id}) does not support turn_on.")
34+
35+
asyncio.run(equipment.turn_on())
36+
click.echo(f"Turned on '{equipment.name}' (system_id={system_id})")
37+
38+
39+
@click.command("off")
40+
@click.argument("system_id", type=int)
41+
@click.pass_context
42+
def equipment_off(ctx: click.Context, system_id: int) -> None:
43+
"""Turn equipment off.
44+
45+
SYSTEM_ID is the equipment's system ID. Works with heaters, pumps, filters,
46+
lights, and relays. Use the appropriate 'get' command to find system IDs.
47+
48+
Example:
49+
omnilogic set off 5
50+
"""
51+
omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
52+
53+
equipment = omnilogic.get_equipment_by_id(system_id)
54+
if equipment is None:
55+
raise click.ClickException(f"No equipment found with system_id {system_id}.")
56+
57+
if not hasattr(equipment, "turn_off"):
58+
raise click.ClickException(f"Equipment '{equipment.name}' (system_id={system_id}) does not support turn_off.")
59+
60+
asyncio.run(equipment.turn_off())
61+
click.echo(f"Turned off '{equipment.name}' (system_id={system_id})")
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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+
if TYPE_CHECKING:
11+
from pyomnilogic_local import OmniLogic
12+
13+
14+
@click.command("heater-temp")
15+
@click.argument("system_id", type=int)
16+
@click.argument("temperature", type=int)
17+
@click.pass_context
18+
def heater_temp(ctx: click.Context, system_id: int, temperature: int) -> None:
19+
"""Set heater target temperature (Fahrenheit).
20+
21+
SYSTEM_ID is the virtual heater's system ID (use 'get heaters' to find it).
22+
TEMPERATURE is the target temperature in Fahrenheit.
23+
24+
Example:
25+
omnilogic set heater-temp 4 82
26+
"""
27+
omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
28+
29+
heater = omnilogic.all_heaters.get_by_id(system_id)
30+
if heater is None:
31+
raise click.ClickException(f"No heater found with system_id {system_id}. Use 'omnilogic get heaters' to list available heaters.")
32+
33+
asyncio.run(heater.set_temperature(temperature))
34+
click.echo(f"Set heater '{heater.name}' (system_id={system_id}) to {temperature}°F")
35+
36+
37+
@click.command("solar-temp")
38+
@click.argument("system_id", type=int)
39+
@click.argument("temperature", type=int)
40+
@click.pass_context
41+
def solar_temp(ctx: click.Context, system_id: int, temperature: int) -> None:
42+
"""Set solar heater target temperature (Fahrenheit).
43+
44+
SYSTEM_ID is the virtual heater's system ID (use 'get heaters' to find it).
45+
TEMPERATURE is the target solar temperature in Fahrenheit.
46+
47+
Example:
48+
omnilogic set solar-temp 4 90
49+
"""
50+
omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
51+
52+
heater = omnilogic.all_heaters.get_by_id(system_id)
53+
if heater is None:
54+
raise click.ClickException(f"No heater found with system_id {system_id}. Use 'omnilogic get heaters' to list available heaters.")
55+
56+
asyncio.run(heater.set_solar_temperature(temperature))
57+
click.echo(f"Set solar temperature for '{heater.name}' (system_id={system_id}) to {temperature}°F")

pyomnilogic_local/cli/set/speed.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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.filter import Filter
11+
from pyomnilogic_local.pump import Pump
12+
13+
if TYPE_CHECKING:
14+
from pyomnilogic_local import OmniLogic
15+
16+
17+
@click.command("speed")
18+
@click.argument("system_id", type=int)
19+
@click.argument("percent", type=int)
20+
@click.pass_context
21+
def speed(ctx: click.Context, system_id: int, percent: int) -> None:
22+
"""Set pump or filter speed (0-100 percent).
23+
24+
SYSTEM_ID is the pump or filter's system ID (use 'get pumps' or 'get filters' to find it).
25+
PERCENT is the speed percentage (0 will turn the pump off).
26+
27+
Example:
28+
omnilogic set speed 3 75
29+
"""
30+
omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
31+
32+
equipment = omnilogic.all_pumps.get_by_id(system_id)
33+
if equipment is None:
34+
equipment = omnilogic.all_filters.get_by_id(system_id)
35+
36+
if equipment is None:
37+
raise click.ClickException(
38+
f"No pump or filter found with system_id {system_id}. "
39+
"Use 'omnilogic get pumps' or 'omnilogic get filters' to list available equipment."
40+
)
41+
42+
if not isinstance(equipment, (Pump, Filter)):
43+
raise click.ClickException(f"Equipment with system_id {system_id} is not a pump or filter.")
44+
45+
asyncio.run(equipment.set_speed(percent))
46+
click.echo(f"Set '{equipment.name}' (system_id={system_id}) to {percent}%")

0 commit comments

Comments
 (0)