python-omnilogic-local 0.20.9__tar.gz → 0.20.10__tar.gz

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.
Files changed (61) hide show
  1. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/PKG-INFO +1 -1
  2. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/api/api.py +1 -1
  3. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/chlorinator.py +3 -3
  4. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/debug/commands.py +121 -1
  5. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/omnitypes.py +1 -1
  6. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyproject.toml +1 -1
  7. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/LICENSE +0 -0
  8. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/README.md +0 -0
  9. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/__init__.py +0 -0
  10. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/_base.py +0 -0
  11. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/api/__init__.py +0 -0
  12. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/api/constants.py +0 -0
  13. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/api/exceptions.py +0 -0
  14. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/api/protocol.py +0 -0
  15. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/backyard.py +0 -0
  16. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/bow.py +0 -0
  17. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/chlorinator_equip.py +0 -0
  18. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/__init__.py +0 -0
  19. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/cli.py +0 -0
  20. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/debug/__init__.py +0 -0
  21. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/__init__.py +0 -0
  22. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/backyard.py +0 -0
  23. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/bows.py +0 -0
  24. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/chlorinators.py +0 -0
  25. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/commands.py +0 -0
  26. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/csads.py +0 -0
  27. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/filters.py +0 -0
  28. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/groups.py +0 -0
  29. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/heaters.py +0 -0
  30. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/lights.py +0 -0
  31. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/pumps.py +0 -0
  32. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/relays.py +0 -0
  33. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/schedules.py +0 -0
  34. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/sensors.py +0 -0
  35. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/get/valves.py +0 -0
  36. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/pcap_utils.py +0 -0
  37. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/cli/utils.py +0 -0
  38. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/collections.py +0 -0
  39. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/colorlogiclight.py +0 -0
  40. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/csad.py +0 -0
  41. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/csad_equip.py +0 -0
  42. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/decorators.py +0 -0
  43. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/filter.py +0 -0
  44. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/groups.py +0 -0
  45. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/heater.py +0 -0
  46. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/heater_equip.py +0 -0
  47. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/models/__init__.py +0 -0
  48. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/models/const.py +0 -0
  49. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/models/exceptions.py +0 -0
  50. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/models/filter_diagnostics.py +0 -0
  51. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/models/leadmessage.py +0 -0
  52. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/models/mspconfig.py +0 -0
  53. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/models/telemetry.py +0 -0
  54. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/omnilogic.py +0 -0
  55. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/pump.py +0 -0
  56. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/py.typed +0 -0
  57. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/relay.py +0 -0
  58. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/schedule.py +0 -0
  59. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/sensor.py +0 -0
  60. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/system.py +0 -0
  61. {python_omnilogic_local-0.20.9 → python_omnilogic_local-0.20.10}/pyomnilogic_local/util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-omnilogic-local
3
- Version: 0.20.9
3
+ Version: 0.20.10
4
4
  Summary: A library for local control of Hayward OmniHub/OmniLogic pool controllers using their local API
5
5
  Author: Chris Jowett, djtimca, garionphx
6
6
  Author-email: Chris Jowett <421501+cryptk@users.noreply.github.com>
@@ -550,7 +550,7 @@ class OmniLogicAPI:
550
550
  sc_timeout: int,
551
551
  bow_type: int,
552
552
  orp_timeout: int,
553
- cfg_state: int = 3,
553
+ cfg_state: int = 3, # 3 == on, 2 == off
554
554
  ) -> None:
555
555
  body_element = ET.Element("Request", {"xmlns": XML_NAMESPACE})
556
556
 
@@ -14,7 +14,7 @@ from pyomnilogic_local.util import OmniEquipmentNotInitializedError
14
14
  if TYPE_CHECKING:
15
15
  from pyomnilogic_local.models.telemetry import Telemetry
16
16
  from pyomnilogic_local.omnilogic import OmniLogic
17
- from pyomnilogic_local.omnitypes import ChlorinatorCellType, ChlorinatorOperatingMode
17
+ from pyomnilogic_local.omnitypes import ChlorinatorCellType, ChlorinatorDispenserType, ChlorinatorOperatingMode
18
18
 
19
19
 
20
20
  class Chlorinator(OmniEquipment[MSPChlorinator, TelemetryChlorinator]):
@@ -83,7 +83,7 @@ class Chlorinator(OmniEquipment[MSPChlorinator, TelemetryChlorinator]):
83
83
  return self.mspconfig.orp_timeout
84
84
 
85
85
  @property
86
- def dispenser_type(self) -> str:
86
+ def dispenser_type(self) -> ChlorinatorDispenserType:
87
87
  """Type of chlorine dispenser (SALT, LIQUID, or TABLET)."""
88
88
  return self.mspconfig.dispenser_type
89
89
 
@@ -99,7 +99,7 @@ class Chlorinator(OmniEquipment[MSPChlorinator, TelemetryChlorinator]):
99
99
  return self.telemetry.operating_state
100
100
 
101
101
  @property
102
- def operating_mode(self) -> ChlorinatorOperatingMode | int:
102
+ def operating_mode(self) -> ChlorinatorOperatingMode:
103
103
  """Current operating mode (DISABLED, TIMED, ORP_AUTO, or ORP_TIMED_RW).
104
104
 
105
105
  Returns:
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
 
6
6
  import asyncio
7
7
  from pathlib import Path
8
- from typing import TYPE_CHECKING
8
+ from typing import TYPE_CHECKING, cast
9
9
 
10
10
  import click
11
11
 
@@ -15,6 +15,7 @@ from pyomnilogic_local.cli.utils import async_get_filter_diagnostics
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  from pyomnilogic_local.api.api import OmniLogicAPI
18
+ from pyomnilogic_local.models.telemetry import TelemetryChlorinator
18
19
 
19
20
 
20
21
  @click.group()
@@ -209,3 +210,122 @@ def set_equipment(ctx: click.Context, bow_id: int, equip_id: int, is_on: str) ->
209
210
  except Exception as e:
210
211
  click.echo(f"Error setting equipment: {e}", err=True)
211
212
  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
@@ -150,7 +150,7 @@ class ChlorinatorOperatingMode(IntEnum, PrettyEnum):
150
150
  DISABLED = 0
151
151
  TIMED = 1
152
152
  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
154
154
 
155
155
 
156
156
  class ChlorinatorType(StrEnum, PrettyEnum):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-omnilogic-local"
3
- version = "0.20.9"
3
+ version = "0.20.10"
4
4
  description = "A library for local control of Hayward OmniHub/OmniLogic pool controllers using their local API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13,<4.0.0"