Skip to content

More functionality #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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