Skip to content
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
9 changes: 6 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@ repos:
- id: ruff
args:
- --fix
- repo: https://github.com/pylint-dev/pylint
rev: v3.3.3
- repo: local
hooks:
- id: pylint
additional_dependencies: [ "pydantic>=1.10.17", "xmltodict" ]
name: pylint
entry: poetry run pylint
language: system
types: [python]
require_serial: true
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.14.1
hooks:
Expand Down
21 changes: 18 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
25 changes: 25 additions & 0 deletions pyomnilogic_local/cli/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import asyncio

import click

from pyomnilogic_local.api import OmniLogicAPI
from pyomnilogic_local.cli.debug import commands as debug
from pyomnilogic_local.cli.get import commands as get


async def get_omni(host: str) -> OmniLogicAPI:
return OmniLogicAPI(host, 10444, 5.0)


@click.group()
@click.pass_context
@click.option("--host", default="127.0.0.1", help="Hostname or IP address of omnilogic system")
def entrypoint(ctx: click.Context, host: str) -> None:
ctx.ensure_object(dict)
omni = asyncio.run(get_omni(host))

ctx.obj["OMNI"] = omni


entrypoint.add_command(debug.debug)
entrypoint.add_command(get.get)
Empty file.
95 changes: 95 additions & 0 deletions pyomnilogic_local/cli/debug/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Need to figure out how to resolve the 'Untyped decorator makes function "..." untyped' errors in mypy when using click decorators
# mypy: disable-error-code="misc"
import asyncio
from typing import Literal, overload

import click

from pyomnilogic_local.api import OmniLogicAPI
from pyomnilogic_local.cli.utils import async_get_mspconfig, async_get_telemetry
from pyomnilogic_local.models.filter_diagnostics import FilterDiagnostics


@click.group()
@click.option("--raw/--no-raw", default=False, help="Output the raw XML from the OmniLogic, do not parse the response")
@click.pass_context
def debug(ctx: click.Context, raw: bool) -> None:
# Container for all get commands

ctx.ensure_object(dict)
ctx.obj["RAW"] = raw


@debug.command()
@click.pass_context
def get_mspconfig(ctx: click.Context) -> None:
mspconfig = asyncio.run(async_get_mspconfig(ctx.obj["OMNI"], ctx.obj["RAW"]))
click.echo(mspconfig)


@debug.command()
@click.pass_context
def get_telemetry(ctx: click.Context) -> None:
telemetry = asyncio.run(async_get_telemetry(ctx.obj["OMNI"], ctx.obj["RAW"]))
click.echo(telemetry)


@debug.command()
@click.pass_context
def get_alarm_list(ctx: click.Context) -> None:
alarm_list = asyncio.run(async_get_alarm_list(ctx.obj["OMNI"]))
click.echo(alarm_list)


async def async_get_alarm_list(omni: OmniLogicAPI) -> str:
alarm_list = await omni.async_get_alarm_list()
return alarm_list


@debug.command()
@click.option("--pool-id", help="System ID of the Body Of Water the filter is associated with")
@click.option("--filter-id", help="System ID of the filter to request diagnostics for")
@click.pass_context
def get_filter_diagnostics(ctx: click.Context, pool_id: int, filter_id: int) -> None:
filter_diags = asyncio.run(async_get_filter_diagnostics(ctx.obj["OMNI"], pool_id, filter_id, ctx.obj["RAW"]))
if ctx.obj["RAW"]:
click.echo(filter_diags)
else:
drv1 = chr(filter_diags.get_param_by_name("DriveFWRevisionB1"))
drv2 = chr(filter_diags.get_param_by_name("DriveFWRevisionB2"))
drv3 = chr(filter_diags.get_param_by_name("DriveFWRevisionB3"))
drv4 = chr(filter_diags.get_param_by_name("DriveFWRevisionB4"))
dfw1 = chr(filter_diags.get_param_by_name("DisplayFWRevisionB1"))
dfw2 = chr(filter_diags.get_param_by_name("DisplayFWRevisionB2"))
dfw3 = chr(filter_diags.get_param_by_name("DisplayFWRevisionB3"))
dfw4 = chr(filter_diags.get_param_by_name("DisplayFWRevisionB4"))
pow1 = filter_diags.get_param_by_name("PowerMSB")
pow2 = filter_diags.get_param_by_name("PowerLSB")
errs = filter_diags.get_param_by_name("ErrorStatus")
click.echo(
f"DRIVE FW REV: {drv1}{drv2}.{drv3}{drv4}\n"
f"DISPLAY FW REV: {dfw1}{dfw2}.{dfw3}.{dfw4}\n"
f"POWER: {pow1:x}{pow2:x}W\n"
f"ERROR STATUS: {errs}"
)


@overload
async def async_get_filter_diagnostics(omni: OmniLogicAPI, pool_id: int, filter_id: int, raw: Literal[True]) -> str: ...
@overload
async def async_get_filter_diagnostics(omni: OmniLogicAPI, pool_id: int, filter_id: int, raw: Literal[False]) -> FilterDiagnostics: ...
async def async_get_filter_diagnostics(omni: OmniLogicAPI, pool_id: int, filter_id: int, raw: bool) -> FilterDiagnostics | str:
filter_diags = await omni.async_get_filter_diagnostics(pool_id, filter_id, raw=raw)
return filter_diags


@debug.command()
@click.pass_context
def get_log_config(ctx: click.Context) -> None:
log_config = asyncio.run(async_get_log_config(ctx.obj["OMNI"]))
click.echo(log_config)


async def async_get_log_config(omni: OmniLogicAPI) -> str:
log_config = await omni.async_get_log_config()
return log_config
Empty file.
33 changes: 33 additions & 0 deletions pyomnilogic_local/cli/get/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Need to figure out how to resolve the 'Untyped decorator makes function "..." untyped' errors in mypy when using click decorators
# mypy: disable-error-code="misc"
import asyncio

import click

from pyomnilogic_local.cli.utils import async_get_mspconfig


@click.group()
@click.pass_context
def get(ctx: click.Context) -> None:
# Container for all get commands

ctx.ensure_object(dict)


@get.command()
@click.pass_context
def lights(ctx: click.Context) -> None:
mspconfig = asyncio.run(async_get_mspconfig(ctx.obj["OMNI"]))
# Return data about lights in the backyard
if mspconfig.backyard.colorlogic_light:
for light in mspconfig.backyard.colorlogic_light:
click.echo(light)

# Return data about lights in the Body of Water
if mspconfig.backyard.bow:
for bow in mspconfig.backyard.bow:
if bow.colorlogic_light:
for cl_light in bow.colorlogic_light:
for k, v in cl_light:
click.echo(f"{k:15}\t{str(v)}")
15 changes: 15 additions & 0 deletions pyomnilogic_local/cli/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from pyomnilogic_local.api import OmniLogicAPI
from pyomnilogic_local.models.mspconfig import MSPConfig
from pyomnilogic_local.models.telemetry import Telemetry


async def async_get_mspconfig(omni: OmniLogicAPI, raw: bool = False) -> MSPConfig:
mspconfig: MSPConfig
mspconfig = await omni.async_get_config(raw=raw)
return mspconfig


async def async_get_telemetry(omni: OmniLogicAPI, raw: bool = False) -> Telemetry:
telemetry: Telemetry
telemetry = await omni.async_get_telemetry(raw=raw)
return telemetry
File renamed without changes.
9 changes: 9 additions & 0 deletions pyomnilogic_local/omnitypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class OmniType(str, Enum):
VALVE_ACTUATOR = "ValveActuator"
VIRT_HEATER = "VirtualHeater"

def __str__(self) -> str:
return OmniType[self.name].value


# Backyard/BoW
class BackyardState(PrettyEnum):
Expand Down Expand Up @@ -173,6 +176,9 @@ class ColorLogicShow(PrettyEnum):
WARM_WHITE = 25
BRIGHT_YELLOW = 26

def __str__(self) -> str:
return self.name


class ColorLogicPowerState(PrettyEnum):
OFF = 0
Expand All @@ -188,6 +194,9 @@ class ColorLogicLightType(str, PrettyEnum):
FOUR_ZERO = "COLOR_LOGIC_4_0"
TWO_FIVE = "COLOR_LOGIC_2_5"

def __str__(self) -> str:
return ColorLogicLightType[self.name].value


class CSADType(str, PrettyEnum):
ACID = "ACID"
Expand Down
12 changes: 9 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ readme = "README.md"
requires-python = ">=3.10,<3.14"
dependencies = [
"pydantic (>=1.10.17)",
"xmltodict (>=0.13.0,<0.14.0)"
"xmltodict (>=0.13.0,<0.14.0)",
"click (>=8.0.0,<8.1.0)",
]

[project.scripts]
omnilogic = "pyomnilogic_local.cli:main"
omnilogic = "pyomnilogic_local.cli.cli:entrypoint"

[tool.poetry]
packages = [{include = "pyomnilogic_local"}]
Expand All @@ -41,7 +42,9 @@ profile = "black"

[tool.mypy]
python_version = "3.13"
plugins = "pydantic.mypy"
plugins = [
"pydantic.mypy",
]
follow_imports = "silent"
strict = true
ignore_missing_imports = true
Expand All @@ -56,6 +59,9 @@ warn_untyped_fields = true

[tool.pylint.MAIN]
py-version = "3.13"
extension-pkg-allow-list = [
"pydantic",
]
ignore = [
"tests",
]
Expand Down
Loading