python-omnilogic-local 0.20.12__tar.gz → 0.21.0__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.12 → python_omnilogic_local-0.21.0}/PKG-INFO +1 -1
  2. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/api/api.py +49 -1
  3. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/debug/commands.py +82 -10
  4. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/omnitypes.py +4 -0
  5. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyproject.toml +1 -1
  6. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/LICENSE +0 -0
  7. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/README.md +0 -0
  8. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/__init__.py +0 -0
  9. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/_base.py +0 -0
  10. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/api/__init__.py +0 -0
  11. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/api/constants.py +0 -0
  12. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/api/exceptions.py +0 -0
  13. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/api/protocol.py +0 -0
  14. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/backyard.py +0 -0
  15. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/bow.py +0 -0
  16. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/chlorinator.py +0 -0
  17. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/chlorinator_equip.py +0 -0
  18. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/__init__.py +0 -0
  19. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/cli.py +0 -0
  20. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/debug/__init__.py +0 -0
  21. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/__init__.py +0 -0
  22. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/backyard.py +0 -0
  23. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/bows.py +0 -0
  24. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/chlorinators.py +0 -0
  25. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/commands.py +0 -0
  26. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/csads.py +0 -0
  27. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/filters.py +0 -0
  28. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/groups.py +0 -0
  29. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/heaters.py +0 -0
  30. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/lights.py +0 -0
  31. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/pumps.py +0 -0
  32. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/relays.py +0 -0
  33. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/schedules.py +0 -0
  34. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/sensors.py +0 -0
  35. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/get/valves.py +0 -0
  36. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/pcap_utils.py +0 -0
  37. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/cli/utils.py +0 -0
  38. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/collections.py +0 -0
  39. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/colorlogiclight.py +0 -0
  40. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/csad.py +0 -0
  41. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/csad_equip.py +0 -0
  42. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/decorators.py +0 -0
  43. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/filter.py +0 -0
  44. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/groups.py +0 -0
  45. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/heater.py +0 -0
  46. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/heater_equip.py +0 -0
  47. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/models/__init__.py +0 -0
  48. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/models/const.py +0 -0
  49. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/models/exceptions.py +0 -0
  50. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/models/filter_diagnostics.py +0 -0
  51. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/models/leadmessage.py +0 -0
  52. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/models/mspconfig.py +0 -0
  53. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/models/telemetry.py +0 -0
  54. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/omnilogic.py +0 -0
  55. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/pump.py +0 -0
  56. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/py.typed +0 -0
  57. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/relay.py +0 -0
  58. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/schedule.py +0 -0
  59. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/sensor.py +0 -0
  60. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/pyomnilogic_local/system.py +0 -0
  61. {python_omnilogic_local-0.20.12 → python_omnilogic_local-0.21.0}/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.12
3
+ Version: 0.21.0
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>
@@ -373,7 +373,7 @@ class OmniLogicAPI:
373
373
  self,
374
374
  pool_id: int,
375
375
  equipment_id: int,
376
- is_on: int | bool,
376
+ is_on: int | bool | str,
377
377
  is_countdown_timer: bool = False,
378
378
  start_time_hours: int = 0,
379
379
  start_time_minutes: int = 0,
@@ -540,6 +540,54 @@ class OmniLogicAPI:
540
540
 
541
541
  return await self.async_send_message(MessageType.SET_CHLOR_ENABLED, req_body, False)
542
542
 
543
+ # This is used to set the ORP target value on a CSAD
544
+ async def async_set_csad_orp_target_level(
545
+ self,
546
+ pool_id: int,
547
+ csad_id: int,
548
+ orp_target: int,
549
+ ) -> None:
550
+ body_element = ET.Element("Request", {"xmlns": XML_NAMESPACE})
551
+
552
+ name_element = ET.SubElement(body_element, "Name")
553
+ name_element.text = "SetUICSADORPTargetLevel"
554
+
555
+ parameters_element = ET.SubElement(body_element, "Parameters")
556
+ parameter = ET.SubElement(parameters_element, "Parameter", name="PoolId", dataType="int")
557
+ parameter.text = str(pool_id)
558
+ parameter = ET.SubElement(parameters_element, "Parameter", name="CSADID", dataType="int", alias="EquipmentID")
559
+ parameter.text = str(csad_id)
560
+ parameter = ET.SubElement(parameters_element, "Parameter", name="ORPTargetValue", dataType="byte", alias="Data1")
561
+ parameter.text = str(orp_target)
562
+
563
+ req_body = ET.tostring(body_element, xml_declaration=True, encoding=XML_ENCODING)
564
+
565
+ return await self.async_send_message(MessageType.SET_CSAD_ORP_TARGET, req_body, False)
566
+
567
+ # This is used to set the pH target value on a CSAD
568
+ async def async_set_csad_target_value(
569
+ self,
570
+ pool_id: int,
571
+ csad_id: int,
572
+ ph_target: float,
573
+ ) -> None:
574
+ body_element = ET.Element("Request", {"xmlns": XML_NAMESPACE})
575
+
576
+ name_element = ET.SubElement(body_element, "Name")
577
+ name_element.text = "UISetCSADTargetValue"
578
+
579
+ parameters_element = ET.SubElement(body_element, "Parameters")
580
+ parameter = ET.SubElement(parameters_element, "Parameter", name="PoolId", dataType="int")
581
+ parameter.text = str(pool_id)
582
+ parameter = ET.SubElement(parameters_element, "Parameter", name="CSADID", dataType="int", alias="EquipmentID")
583
+ parameter.text = str(csad_id)
584
+ parameter = ET.SubElement(parameters_element, "Parameter", name="TargetValue", dataType="float", alias="Data1")
585
+ parameter.text = str(ph_target)
586
+
587
+ req_body = ET.tostring(body_element, xml_declaration=True, encoding=XML_ENCODING)
588
+
589
+ return await self.async_send_message(MessageType.SET_CSAD_TARGET_VALUE, req_body, False)
590
+
543
591
  async def async_set_chlorinator_params(
544
592
  self,
545
593
  pool_id: int,
@@ -182,22 +182,15 @@ def set_equipment(ctx: click.Context, bow_id: int, equip_id: int, is_on: str) ->
182
182
  ensure_connection(ctx)
183
183
  omni: OmniLogicAPI = ctx.obj["OMNI"]
184
184
 
185
+ is_on_value: int | bool | str
185
186
  # Parse is_on parameter - can be bool-like string or integer
186
187
  is_on_lower = is_on.lower()
187
188
  if is_on_lower in ("true", "on", "yes", "1"):
188
- is_on_value: int | bool = True
189
+ is_on_value = True
189
190
  elif is_on_lower in ("false", "off", "no", "0"):
190
191
  is_on_value = False
191
192
  else:
192
- # Try to parse as integer for variable speed equipment
193
- try:
194
- is_on_value = int(is_on)
195
- if not 0 <= is_on_value <= 100:
196
- click.echo(f"Error: Integer value must be between 0-100, got {is_on_value}", err=True)
197
- raise click.Abort
198
- except ValueError as exc:
199
- click.echo(f"Error: Invalid value '{is_on}'. Use true/false, on/off, or 0-100 for speed.", err=True)
200
- raise click.Abort from exc
193
+ is_on_value = is_on
201
194
 
202
195
  # Execute the command
203
196
  try:
@@ -326,3 +319,82 @@ def set_chlor_params(ctx: click.Context, bow_id: int, equip_id: int, timed_perce
326
319
  except Exception as e:
327
320
  click.echo(f"Error setting chlorinator parameters: {e}", err=True)
328
321
  raise click.Abort from e
322
+
323
+
324
+ @debug.command()
325
+ @click.argument("bow_id", type=int)
326
+ @click.argument("csad_id", type=int)
327
+ @click.argument("target", type=float)
328
+ @click.pass_context
329
+ def set_csad_ph(ctx: click.Context, bow_id: int, csad_id: int, target: float) -> None:
330
+ """Set the pH target value for a CSAD (Chemical Sense and Dispense).
331
+
332
+ This command sets the target pH level that the CSAD will attempt to maintain.
333
+
334
+ BOW_ID: The Body of Water (pool/spa) system ID
335
+ CSAD_ID: The CSAD equipment system ID
336
+ TARGET: Target pH value (typically 7.0-8.0)
337
+
338
+ Examples:
339
+ # Set pH target to 7.4
340
+ omnilogic --host 192.168.1.100 debug set-csad-ph 7 20 7.4
341
+
342
+ # Set pH target to 7.2
343
+ omnilogic --host 192.168.1.100 debug set-csad-ph 7 20 7.2
344
+
345
+ """
346
+ ensure_connection(ctx)
347
+ omni: OmniLogicAPI = ctx.obj["OMNI"]
348
+
349
+ # Validate target pH (typical range is 6.8-8.2, but allow wider range)
350
+ if not 0.0 <= target <= 14.0:
351
+ click.echo(f"Error: pH target must be between 0.0-14.0, got {target}", err=True)
352
+ raise click.Abort
353
+
354
+ # Execute the command
355
+ try:
356
+ asyncio.run(omni.async_set_csad_target_value(pool_id=bow_id, csad_id=csad_id, ph_target=target))
357
+ click.echo(f"Successfully set CSAD {csad_id} in BOW {bow_id} pH target to {target}")
358
+ except Exception as e:
359
+ click.echo(f"Error setting CSAD pH target: {e}", err=True)
360
+ raise click.Abort from e
361
+
362
+
363
+ @debug.command()
364
+ @click.argument("bow_id", type=int)
365
+ @click.argument("csad_id", type=int)
366
+ @click.argument("target", type=int)
367
+ @click.pass_context
368
+ def set_csad_orp(ctx: click.Context, bow_id: int, csad_id: int, target: int) -> None:
369
+ """Set the ORP target level for a CSAD (Chemical Sense and Dispense).
370
+
371
+ This command sets the target ORP (Oxidation-Reduction Potential) level in
372
+ millivolts that the CSAD will attempt to maintain.
373
+
374
+ BOW_ID: The Body of Water (pool/spa) system ID
375
+ CSAD_ID: The CSAD equipment system ID
376
+ TARGET: Target ORP value in millivolts (typically 600-800 mV)
377
+
378
+ Examples:
379
+ # Set ORP target to 700 mV
380
+ omnilogic --host 192.168.1.100 debug set-csad-orp 7 20 700
381
+
382
+ # Set ORP target to 650 mV
383
+ omnilogic --host 192.168.1.100 debug set-csad-orp 7 20 650
384
+
385
+ """
386
+ ensure_connection(ctx)
387
+ omni: OmniLogicAPI = ctx.obj["OMNI"]
388
+
389
+ # Validate target ORP (typical range is 400-900 mV, but allow 0-1000)
390
+ if not 0 <= target <= 1000:
391
+ click.echo(f"Error: ORP target must be between 0-1000 mV, got {target}", err=True)
392
+ raise click.Abort
393
+
394
+ # Execute the command
395
+ try:
396
+ asyncio.run(omni.async_set_csad_orp_target_level(pool_id=bow_id, csad_id=csad_id, orp_target=target))
397
+ click.echo(f"Successfully set CSAD {csad_id} in BOW {bow_id} ORP target to {target} mV")
398
+ except Exception as e:
399
+ click.echo(f"Error setting CSAD ORP target: {e}", err=True)
400
+ raise click.Abort from e
@@ -21,7 +21,10 @@ class MessageType(IntEnum, PrettyEnum):
21
21
  CREATE_SCHEDULE = 230
22
22
  DELETE_SCHEDULE = 231
23
23
  EDIT_SCHEDULE = 233
24
+ SET_CSAD_TARGET_VALUE = 253
25
+ SET_CSAD_ORP_TARGET = 281
24
26
  GET_TELEMETRY = 300
27
+ GET_ALARM_LIST = 304
25
28
  SET_STANDALONE_LIGHT_SHOW = 308
26
29
  SET_SPILLOVER = 311
27
30
  RUN_GROUP_CMD = 317
@@ -31,6 +34,7 @@ class MessageType(IntEnum, PrettyEnum):
31
34
  ACK = 1002
32
35
  MSP_TELEMETRY_UPDATE = 1004
33
36
  MSP_CONFIGURATIONUPDATE = 1003
37
+ MSP_ALARM_LIST_RESPONSE = 1304
34
38
  MSP_LEADMESSAGE = 1998
35
39
  MSP_BLOCKMESSAGE = 1999
36
40
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-omnilogic-local"
3
- version = "0.20.12"
3
+ version = "0.21.0"
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"