python-omnilogic-local 2.1.1__tar.gz → 2.3.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 (63) hide show
  1. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/PKG-INFO +1 -1
  2. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/chlorinator.py +44 -4
  3. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/debug/commands.py +38 -0
  4. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyproject.toml +1 -1
  5. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/LICENSE +0 -0
  6. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/README.md +0 -0
  7. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/__init__.py +0 -0
  8. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/_base.py +0 -0
  9. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/api/__init__.py +0 -0
  10. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/api/api.py +0 -0
  11. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/api/constants.py +0 -0
  12. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/api/exceptions.py +0 -0
  13. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/api/message.py +0 -0
  14. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/api/mock_api.py +0 -0
  15. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/api/protocol.py +0 -0
  16. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/backyard.py +0 -0
  17. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/bow.py +0 -0
  18. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/chlorinator_equip.py +0 -0
  19. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/__init__.py +0 -0
  20. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/cli.py +0 -0
  21. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/debug/__init__.py +0 -0
  22. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/__init__.py +0 -0
  23. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/backyard.py +0 -0
  24. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/bows.py +0 -0
  25. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/chlorinators.py +0 -0
  26. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/commands.py +0 -0
  27. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/csads.py +0 -0
  28. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/filters.py +0 -0
  29. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/groups.py +0 -0
  30. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/heaters.py +0 -0
  31. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/lights.py +0 -0
  32. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/pumps.py +0 -0
  33. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/relays.py +0 -0
  34. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/schedules.py +0 -0
  35. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/sensors.py +0 -0
  36. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/get/valves.py +0 -0
  37. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/pcap_utils.py +0 -0
  38. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/cli/utils.py +0 -0
  39. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/collections.py +0 -0
  40. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/colorlogiclight.py +0 -0
  41. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/csad.py +0 -0
  42. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/csad_equip.py +0 -0
  43. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/decorators.py +0 -0
  44. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/filter.py +0 -0
  45. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/groups.py +0 -0
  46. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/heater.py +0 -0
  47. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/heater_equip.py +0 -0
  48. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/models/__init__.py +0 -0
  49. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/models/const.py +0 -0
  50. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/models/exceptions.py +0 -0
  51. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/models/filter_diagnostics.py +0 -0
  52. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/models/leadmessage.py +0 -0
  53. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/models/mspconfig.py +0 -0
  54. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/models/telemetry.py +0 -0
  55. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/omnilogic.py +0 -0
  56. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/omnitypes.py +0 -0
  57. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/pump.py +0 -0
  58. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/py.typed +0 -0
  59. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/relay.py +0 -0
  60. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/schedule.py +0 -0
  61. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/sensor.py +0 -0
  62. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.0}/pyomnilogic_local/system.py +0 -0
  63. {python_omnilogic_local-2.1.1 → python_omnilogic_local-2.3.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: 2.1.1
3
+ Version: 2.3.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>
@@ -372,7 +372,7 @@ class Chlorinator(OmniEquipment[MSPChlorinator, TelemetryChlorinator]):
372
372
  Note:
373
373
  This method uses the async_set_chlorinator_params API which requires
374
374
  all chlorinator configuration parameters. The current values from
375
- mspconfig are used for unchanged parameters.
375
+ mspconfig/telemetry are used for unchanged parameters.
376
376
  """
377
377
  if self.bow_id is None or self.system_id is None:
378
378
  msg = "Cannot set timed percent: bow_id or system_id is None"
@@ -388,17 +388,57 @@ class Chlorinator(OmniEquipment[MSPChlorinator, TelemetryChlorinator]):
388
388
  msg = f"Cannot find bow with id {self.bow_id}"
389
389
  raise OmniEquipmentNotInitializedError(msg)
390
390
 
391
- # Map equipment type to numeric bow_type value
392
- # BOW_POOL = 0, BOW_SPA = 1 (based on typical protocol values)
393
391
  bow_type = 0 if bow.equip_type == "BOW_POOL" else 1
394
392
 
395
393
  await self._api.async_set_chlorinator_params(
396
394
  pool_id=self.bow_id,
397
395
  equipment_id=self.system_id,
398
396
  timed_percent=percent,
399
- cell_type=self.mspconfig.cell_type.value, # ChlorinatorCellType is now IntEnum, use .value
397
+ cell_type=self.mspconfig.cell_type.value,
400
398
  op_mode=self.operating_mode.value,
401
399
  sc_timeout=self.superchlor_timeout,
402
400
  bow_type=bow_type,
403
401
  orp_timeout=self.orp_timeout,
404
402
  )
403
+
404
+ @control_method
405
+ async def set_op_mode(self, op_mode: ChlorinatorOperatingMode) -> None:
406
+ """Set the operating mode for chlorine generation.
407
+
408
+ Args:
409
+ op_mode: The operating mode for chlorine generation.
410
+
411
+ Raises:
412
+ OmniEquipmentNotInitializedError: If bow_id or system_id is None.
413
+
414
+ Note:
415
+ This method uses the async_set_chlorinator_params API which requires
416
+ all chlorinator configuration parameters. The current values from
417
+ mspconfig/telemetry are used for unchanged parameters.
418
+ """
419
+ if self.bow_id is None or self.system_id is None:
420
+ msg = "Cannot set operating mode: bow_id or system_id is None"
421
+ raise OmniEquipmentNotInitializedError(msg)
422
+
423
+ # Get the parent Bow to determine bow_type
424
+ # We need to find our bow in the backyard
425
+ if (bow := self._omni.backyard.bow.get(self.bow_id)) is None:
426
+ msg = f"Cannot find bow with id {self.bow_id}"
427
+ raise OmniEquipmentNotInitializedError(msg)
428
+
429
+ if self.timed_percent_telemetry is None:
430
+ msg = "Cannot set operating mode: timed_percent telemetry value is None"
431
+ raise OmniEquipmentNotInitializedError(msg)
432
+
433
+ bow_type = 0 if bow.equip_type == "BOW_POOL" else 1
434
+
435
+ await self._api.async_set_chlorinator_params(
436
+ pool_id=self.bow_id,
437
+ equipment_id=self.system_id,
438
+ timed_percent=self.timed_percent_telemetry,
439
+ cell_type=self.mspconfig.cell_type.value,
440
+ op_mode=op_mode.value,
441
+ sc_timeout=self.superchlor_timeout,
442
+ bow_type=bow_type,
443
+ orp_timeout=self.orp_timeout,
444
+ )
@@ -4,16 +4,21 @@
4
4
  from __future__ import annotations
5
5
 
6
6
  import asyncio
7
+ import xml.etree.ElementTree as ET
8
+ from enum import IntEnum
7
9
  from pathlib import Path
8
10
  from typing import TYPE_CHECKING, cast
9
11
 
10
12
  import click
11
13
 
14
+ from pyomnilogic_local.api.constants import XML_ENCODING, XML_NAMESPACE
12
15
  from pyomnilogic_local.cli.pcap_utils import parse_pcap_file, process_pcap_messages
16
+ from pyomnilogic_local.util import PrettyEnum
13
17
 
14
18
  if TYPE_CHECKING:
15
19
  from pyomnilogic_local import OmniLogic
16
20
  from pyomnilogic_local.models.telemetry import TelemetryChlorinator
21
+ from pyomnilogic_local.omnitypes import MessageType
17
22
 
18
23
 
19
24
  @click.group()
@@ -350,3 +355,36 @@ def set_csad_orp(ctx: click.Context, bow_id: int, csad_id: int, target: int) ->
350
355
  except Exception as e:
351
356
  click.echo(f"Error setting CSAD ORP target: {e}", err=True)
352
357
  raise click.Abort from e
358
+
359
+
360
+ @debug.command()
361
+ @click.argument("bow_id", type=int)
362
+ @click.argument("csad_id", type=int)
363
+ @click.argument("opid", type=int)
364
+ @click.argument("is_on")
365
+ @click.pass_context
366
+ def set_csad_enable(ctx: click.Context, bow_id: int, csad_id: int, opid: int, is_on: bool) -> None:
367
+ """Attempt to enable or disable a CSAD (Chemical Sense and Dispense) device."""
368
+ omnilogic: OmniLogic = ctx.obj["OMNILOGIC"]
369
+
370
+ body_element = ET.Element("Request", {"xmlns": XML_NAMESPACE})
371
+
372
+ name_element = ET.SubElement(body_element, "Name")
373
+ name_element.text = "UISetCSADEnabled"
374
+
375
+ parameters_element = ET.SubElement(body_element, "Parameters")
376
+ parameter = ET.SubElement(parameters_element, "Parameter", name="poolId", dataType="int")
377
+ parameter.text = str(bow_id)
378
+ parameter = ET.SubElement(parameters_element, "Parameter", name="CSADID", dataType="int", alias="EquipmentID")
379
+ parameter.text = str(csad_id)
380
+ parameter = ET.SubElement(parameters_element, "Parameter", name="Enabled", dataType="bool", alias="Data")
381
+ parameter.text = str(int(is_on))
382
+
383
+ req_body = ET.tostring(body_element, xml_declaration=True, encoding=XML_ENCODING)
384
+
385
+ class CustomMessageType(PrettyEnum, IntEnum):
386
+ SET_CSAD_ENABLE = opid
387
+
388
+ click.echo(f"Sending UISetCSADEnabled with op ID: {opid}; body: {req_body}")
389
+
390
+ asyncio.run(omnilogic._api.async_send(cast("MessageType", CustomMessageType.SET_CSAD_ENABLE), req_body))
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-omnilogic-local"
3
- version = "2.1.1"
3
+ version = "2.3.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.14.2"