Skip to content

Commit

Permalink
feat: More functionality (#12)
Browse files Browse the repository at this point in the history
* docs: add comment explaing the purpose of the xml postprocessor

* feat: add support for changing heater modes

* fix: mark supports_cooling as not required for heater equip

* fix: mark solar_set_point as optional for vheaters

* fix: add SENSOR_EXT_INPUT as a Sensor Type

* fix: add active_inactive as a sensor unit
  • Loading branch information
cryptk committed May 27, 2023
1 parent 4dce628 commit 20c4fa1
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 5 deletions.
36 changes: 35 additions & 1 deletion pyomnilogic_local/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
from .models.telemetry import Telemetry
from .models.util import to_pydantic
from .protocol import OmniLogicProtocol
from .types import ColorLogicBrightness, ColorLogicShow, ColorLogicSpeed, MessageType
from .types import (
ColorLogicBrightness,
ColorLogicShow,
ColorLogicSpeed,
HeaterMode,
MessageType,
)

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -165,6 +171,34 @@ async def async_set_solar_heater(self, pool_id: int, equipment_id: int, temperat

return await self.async_send_message(MessageType.SET_SOLAR_SET_POINT_COMMAND, req_body, False)

async def async_set_heater_mode(self, pool_id: int, equipment_id: int, mode: HeaterMode) -> None:
"""async_set_heater_enable handles sending a SetHeaterEnable XML API call to the Hayward Omni pool controller
Args:
pool_id (int): The Pool/BodyOfWater ID that you want to address
equipment_id (int): Which equipment_id within that Pool to address
enabled (bool, optional): Turn the heater on (True) or off (False)
Returns:
_type_: _description_
"""
body_element = ET.Element("Request", {"xmlns": "http://nextgen.hayward.com/api"})

name_element = ET.SubElement(body_element, "Name")
name_element.text = "SetUIHeaterModeCmd"

parameters_element = ET.SubElement(body_element, "Parameters")
parameter = ET.SubElement(parameters_element, "Parameter", name="poolId", dataType="int")
parameter.text = str(pool_id)
parameter = ET.SubElement(parameters_element, "Parameter", name="HeaterID", dataType="int", alias="EquipmentID")
parameter.text = str(equipment_id)
parameter = ET.SubElement(parameters_element, "Parameter", name="Mode", dataType="int", alias="Data")
parameter.text = str(mode.value)

req_body = ET.tostring(body_element, xml_declaration=True, encoding="unicode")

return await self.async_send_message(MessageType.SET_HEATER_MODE_COMMAND, req_body, False)

async def async_set_heater_enable(self, pool_id: int, equipment_id: int, enabled: int | bool) -> None:
"""async_set_heater_enable handles sending a SetHeaterEnable XML API call to the Hayward Omni pool controller
Expand Down
5 changes: 5 additions & 0 deletions pyomnilogic_local/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ async def async_main() -> None:
# Adjust solar heater set point
# await omni.async_set_solar_heater(POOL_ID, HEATER_EQUIPMENT_ID, 90, "F")

# Set the heater to heat/cool/auto
# await omni.async_set_heater_mode(POOL_ID, HEATER_EQUIPMENT_ID, HeaterMode.HEAT)
# await omni.async_set_heater_mode(POOL_ID, HEATER_EQUIPMENT_ID, HeaterMode.COOL)
# await omni.async_set_heater_mode(POOL_ID, HEATER_EQUIPMENT_ID, HeaterMode.AUTO)

# Turn a variable speed pump on to 50%
# await omni.async_set_filter_speed(POOL_ID, PUMP_EQUIPMENT_ID, 50)
# Turn the pump off
Expand Down
4 changes: 2 additions & 2 deletions pyomnilogic_local/models/mspconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class MSPHeaterEquip(OmniBase):
enabled: Literal["yes", "no"] = Field(alias="Enabled")
min_filter_speed: int = Field(alias="Min-Speed-For-Operation")
sensor_id: int = Field(alias="Sensor-System-Id")
supports_cooling: Literal["yes", "no"] = Field(alias="SupportsCooling")
supports_cooling: Literal["yes", "no"] | None = Field(alias="SupportsCooling")


# This is the entry for the VirtualHeater, it does not use OmniBase because it has no name attribute
Expand All @@ -121,7 +121,7 @@ class MSPVirtualHeater(OmniBase):
omni_type: OmniType = OmniType.VIRT_HEATER
enabled: Literal["yes", "no"] = Field(alias="Enabled")
set_point: int = Field(alias="Current-Set-Point")
solar_set_point: int = Field(alias="SolarSetPoint")
solar_set_point: int | None = Field(alias="SolarSetPoint")
max_temp: int = Field(alias="Max-Settable-Water-Temp")
min_temp: int = Field(alias="Min-Settable-Water-Temp")
heater_equipment: list[MSPHeaterEquip] | None
Expand Down
11 changes: 9 additions & 2 deletions pyomnilogic_local/models/telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
FilterState,
FilterValvePosition,
FilterWhyOn,
HeaterMode,
HeaterState,
OmniType,
PumpState,
Expand Down Expand Up @@ -138,7 +139,7 @@ class TelemetryVirtualHeater(BaseModel):
current_set_point: int = Field(alias="@Current-Set-Point")
enabled: bool = Field(alias="@enable")
solar_set_point: int = Field(alias="@SolarSetPoint")
mode: int = Field(alias="@Mode")
mode: HeaterMode = Field(alias="@Mode")
silent_mode: int = Field(alias="@SilentMode")
why_on: int = Field(alias="@whyHeaterIsOn")

Expand Down Expand Up @@ -190,7 +191,13 @@ def xml_postprocessor(path: Any, key: Any, value: Any) -> tuple[Any, Any]:
...

def xml_postprocessor(path: Any, key: Any, value: SupportsInt | Any) -> tuple[Any, SupportsInt | Any]:
"""Post process XML to attempt to convert values to int."""
"""Post process XML to attempt to convert values to int.
Pydantic can coerce values natively, but the Omni API returns values as strings of numbers (I.E. "2", "5", etc) and we need them
coerced into int enums. Pydantic only seems to be able to handle one coercion, so it could coerce an int into an Enum, but it
cannot coerce a string into an int and then into the Enum. We help it out a little bit here by pre-emptively coercing any
string ints into an int, then pydantic handles the int to enum coercion if necessary.
"""
newvalue: SupportsInt | Any

try:
Expand Down
9 changes: 9 additions & 0 deletions pyomnilogic_local/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class MessageType(Enum):
SET_HEATER_COMMAND = 11
REQUEST_LOG_CONFIG = 31
SET_SOLAR_SET_POINT_COMMAND = 40
SET_HEATER_MODE_COMMAND = 42
SET_HEATER_ENABLED = 147
SET_EQUIPMENT = 164
CREATE_SCHEDULE = 230
Expand Down Expand Up @@ -224,6 +225,12 @@ class HeaterType(str, PrettyEnum):
SMART = "HTR_SMART"


class HeaterMode(PrettyEnum):
HEAT = 0
COOL = 1
AUTO = 2


class PumpState(PrettyEnum):
OFF = 0
ON = 1
Expand Down Expand Up @@ -277,6 +284,7 @@ class SensorType(str, PrettyEnum):
SOLAR_TEMP = "SENSOR_SOLAR_TEMP"
WATER_TEMP = "SENSOR_WATER_TEMP"
FLOW = "SENSOR_FLOW"
EXT_INPUT = "SENSOR_EXT_INPUT"


class SensorUnits(str, PrettyEnum):
Expand All @@ -286,6 +294,7 @@ class SensorUnits(str, PrettyEnum):
GRAMS_PER_LITER = "UNITS_GRAMS_PER_LITER"
MILLIVOLTS = "UNITS_MILLIVOLTS"
NO_UNITS = "UNITS_NO_UNITS"
ACTIVE_INACTIVE = "UNITS_ACTIVE_INACTIVE"


class ValveActuatorState(PrettyEnum):
Expand Down

0 comments on commit 20c4fa1

Please sign in to comment.