Skip to content

Commit e54dc25

Browse files
authored
fix: add missing debug command to CLI to control chlorinator (#93)
* fix: add missing debug command to CLI to control chlorinator * ci: have PSR use GITHUB_TOKEN so it doesn't re-trigger workflows
1 parent cd11f1b commit e54dc25

File tree

5 files changed

+128
-8
lines changed

5 files changed

+128
-8
lines changed

.github/workflows/cd-release.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,15 @@ jobs:
9797
# Adjust tag with desired version if applicable.
9898
uses: python-semantic-release/python-semantic-release@v10.4.1
9999
with:
100-
github_token: ${{ secrets.PYTHON_SEMANTIC_RELEASE_TOKEN }}
100+
github_token: ${{ secrets.GITHUB_TOKEN }}
101101
git_committer_name: "github-actions"
102102
git_committer_email: "actions@users.noreply.github.com"
103103

104104
- name: Publish | Upload to GitHub Release Assets
105105
uses: python-semantic-release/publish-action@v10.4.1
106106
if: steps.release.outputs.released == 'true'
107107
with:
108-
github_token: ${{ secrets.PYTHON_SEMANTIC_RELEASE_TOKEN }}
108+
github_token: ${{ secrets.GITHUB_TOKEN }}
109109
tag: ${{ steps.release.outputs.tag }}
110110

111111
- name: Upload | Distribution Artifacts

pyomnilogic_local/api/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ async def async_set_chlorinator_params(
550550
sc_timeout: int,
551551
bow_type: int,
552552
orp_timeout: int,
553-
cfg_state: int = 3,
553+
cfg_state: int = 3, # 3 == on, 2 == off
554554
) -> None:
555555
body_element = ET.Element("Request", {"xmlns": XML_NAMESPACE})
556556

pyomnilogic_local/chlorinator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
if TYPE_CHECKING:
1515
from pyomnilogic_local.models.telemetry import Telemetry
1616
from pyomnilogic_local.omnilogic import OmniLogic
17-
from pyomnilogic_local.omnitypes import ChlorinatorCellType, ChlorinatorOperatingMode
17+
from pyomnilogic_local.omnitypes import ChlorinatorCellType, ChlorinatorDispenserType, ChlorinatorOperatingMode
1818

1919

2020
class Chlorinator(OmniEquipment[MSPChlorinator, TelemetryChlorinator]):
@@ -83,7 +83,7 @@ def orp_timeout(self) -> int:
8383
return self.mspconfig.orp_timeout
8484

8585
@property
86-
def dispenser_type(self) -> str:
86+
def dispenser_type(self) -> ChlorinatorDispenserType:
8787
"""Type of chlorine dispenser (SALT, LIQUID, or TABLET)."""
8888
return self.mspconfig.dispenser_type
8989

@@ -99,7 +99,7 @@ def operating_state(self) -> int:
9999
return self.telemetry.operating_state
100100

101101
@property
102-
def operating_mode(self) -> ChlorinatorOperatingMode | int:
102+
def operating_mode(self) -> ChlorinatorOperatingMode:
103103
"""Current operating mode (DISABLED, TIMED, ORP_AUTO, or ORP_TIMED_RW).
104104
105105
Returns:

pyomnilogic_local/cli/debug/commands.py

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import asyncio
77
from pathlib import Path
8-
from typing import TYPE_CHECKING
8+
from typing import TYPE_CHECKING, cast
99

1010
import click
1111

@@ -15,6 +15,7 @@
1515

1616
if TYPE_CHECKING:
1717
from pyomnilogic_local.api.api import OmniLogicAPI
18+
from pyomnilogic_local.models.telemetry import TelemetryChlorinator
1819

1920

2021
@click.group()
@@ -209,3 +210,122 @@ def set_equipment(ctx: click.Context, bow_id: int, equip_id: int, is_on: str) ->
209210
except Exception as e:
210211
click.echo(f"Error setting equipment: {e}", err=True)
211212
raise click.Abort from e
213+
214+
215+
@debug.command()
216+
@click.argument("bow_id", type=int)
217+
@click.argument("equip_id", type=int)
218+
@click.argument("timed_percent", type=int)
219+
@click.argument("op_mode", type=int)
220+
@click.pass_context
221+
def set_chlor_params(ctx: click.Context, bow_id: int, equip_id: int, timed_percent: int, op_mode: int) -> None:
222+
"""Set chlorinator parameters with explicit control over configuration.
223+
224+
This command sets chlorinator parameters using the current chlorinator's
225+
configuration for cell_type, sc_timeout, bow_type, and orp_timeout, while
226+
allowing you to specify timed_percent and op_mode. The cfg_state is derived
227+
from the chlorinator's current on/off state.
228+
229+
BOW_ID: The Body of Water (pool/spa) system ID
230+
EQUIP_ID: The chlorinator equipment system ID
231+
TIMED_PERCENT: Chlorine generation percentage (0-100)
232+
OP_MODE: Operating mode (0=DISABLED, 1=TIMED, 2=ORP_AUTO, 3=ORP_TIMED_RW)
233+
234+
Examples:
235+
# Set chlorinator to 75% in TIMED mode
236+
omnilogic --host 192.168.1.100 debug set-chlor-params 7 12 75 1
237+
238+
# Disable chlorinator
239+
omnilogic --host 192.168.1.100 debug set-chlor-params 7 12 0 0
240+
241+
# Set to ORP AUTO mode with 50% generation
242+
omnilogic --host 192.168.1.100 debug set-chlor-params 7 12 50 2
243+
244+
"""
245+
ensure_connection(ctx)
246+
omni: OmniLogicAPI = ctx.obj["OMNI"]
247+
248+
# Validate timed_percent
249+
if not 0 <= timed_percent <= 100:
250+
click.echo(f"Error: timed_percent must be between 0-100, got {timed_percent}", err=True)
251+
raise click.Abort
252+
253+
# Validate op_mode
254+
if not 0 <= op_mode <= 2:
255+
click.echo(f"Error: op_mode must be between 0-3, got {op_mode}", err=True)
256+
raise click.Abort
257+
258+
# Get MSPConfig and Telemetry to find the chlorinator
259+
try:
260+
mspconfig_raw = asyncio.run(omni.async_get_mspconfig(raw=False))
261+
telemetry_raw = asyncio.run(omni.async_get_telemetry(raw=False))
262+
except Exception as e:
263+
click.echo(f"Error retrieving configuration: {e}", err=True)
264+
raise click.Abort from e
265+
266+
# Find the BOW
267+
bow = None
268+
if mspconfig_raw.backyard.bow:
269+
for candidate_bow in mspconfig_raw.backyard.bow:
270+
if candidate_bow.system_id == bow_id:
271+
bow = candidate_bow
272+
break
273+
274+
if bow is None:
275+
click.echo(f"Error: Body of Water with ID {bow_id} not found", err=True)
276+
raise click.Abort
277+
278+
# Find the chlorinator
279+
if bow.chlorinator is None or bow.chlorinator.system_id != equip_id:
280+
click.echo(f"Error: Chlorinator with ID {equip_id} not found in BOW {bow_id}", err=True)
281+
raise click.Abort
282+
283+
chlorinator = bow.chlorinator
284+
285+
# Get telemetry for the chlorinator to determine is_on state
286+
chlorinator_telemetry = telemetry_raw.get_telem_by_systemid(equip_id)
287+
if chlorinator_telemetry is None:
288+
click.echo(f"Warning: No telemetry found for chlorinator {equip_id}, defaulting cfg_state to 3 (on)", err=True)
289+
cfg_state = 3
290+
else:
291+
# Cast to TelemetryChlorinator to access enable attribute
292+
chlorinator_telem = cast("TelemetryChlorinator", chlorinator_telemetry)
293+
# Determine cfg_state from enable flag in telemetry
294+
cfg_state = 3 if chlorinator_telem.enable else 2
295+
296+
# Determine bow_type from equipment type (0=pool, 1=spa)
297+
bow_type = 0 if bow.equip_type == "BOW_POOL" else 1
298+
299+
# Get parameters from chlorinator config
300+
cell_type = chlorinator.cell_type.value
301+
sc_timeout = chlorinator.superchlor_timeout
302+
orp_timeout = chlorinator.orp_timeout
303+
304+
# Execute the command
305+
try:
306+
asyncio.run(
307+
omni.async_set_chlorinator_params(
308+
pool_id=bow_id,
309+
equipment_id=equip_id,
310+
timed_percent=timed_percent,
311+
cell_type=cell_type,
312+
op_mode=op_mode,
313+
sc_timeout=sc_timeout,
314+
bow_type=bow_type,
315+
orp_timeout=orp_timeout,
316+
cfg_state=cfg_state,
317+
)
318+
)
319+
click.echo(
320+
f"Sent command to chlorinator {equip_id} in BOW {bow_id}:\n"
321+
f" Timed Percent: {timed_percent}%\n"
322+
f" Operating Mode: {op_mode}\n"
323+
f" Config State: {cfg_state} ({'on' if cfg_state == 3 else 'off'})\n"
324+
f" Cell Type: {cell_type}\n"
325+
f" SC Timeout: {sc_timeout}\n"
326+
f" BOW Type: {bow_type}\n"
327+
f" ORP Timeout: {orp_timeout}"
328+
)
329+
except Exception as e:
330+
click.echo(f"Error setting chlorinator parameters: {e}", err=True)
331+
raise click.Abort from e

pyomnilogic_local/omnitypes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class ChlorinatorOperatingMode(IntEnum, PrettyEnum):
150150
DISABLED = 0
151151
TIMED = 1
152152
ORP_AUTO = 2
153-
ORP_TIMED_RW = 3 # Chlorinator in ORP mode experienced condition that prevents ORP operation
153+
ORP_TIMED_RW = 3 # Chlorinator in ORP mode experienced CSAD condition that prevents ORP operation
154154

155155

156156
class ChlorinatorType(StrEnum, PrettyEnum):

0 commit comments

Comments
 (0)