Initial commit
This commit is contained in:
commit
e224438cf3
4 changed files with 141 additions and 0 deletions
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
.DS_Store
|
||||
.idea
|
||||
*.log
|
||||
tmp/
|
||||
__pycache__/
|
||||
|
||||
*.py[cod]
|
||||
*.egg
|
||||
build
|
||||
htmlcov
|
||||
poetry.lock
|
0
README.md
Normal file
0
README.md
Normal file
106
ipowerswitch.py
Normal file
106
ipowerswitch.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
import dataclasses
|
||||
import enum
|
||||
import logging
|
||||
import typing
|
||||
|
||||
import requests
|
||||
import requests.auth
|
||||
import typer
|
||||
import yarl
|
||||
|
||||
|
||||
class SwitchStatus(str, enum.Enum):
|
||||
ON = "ON"
|
||||
OFF = "OFF"
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Switch:
|
||||
address: str
|
||||
user: str
|
||||
password: str
|
||||
|
||||
def switch(
|
||||
self,
|
||||
switch_id: int,
|
||||
outlet: int,
|
||||
from_state: SwitchStatus | None = None,
|
||||
) -> None:
|
||||
url = yarl.URL(f"http://{self.address}").joinpath(
|
||||
"cgi-bin",
|
||||
f"iswitch{switch_id:02d}",
|
||||
"irswitch.exe",
|
||||
)
|
||||
|
||||
params = {
|
||||
"CURRENT": f"{switch_id:02d}",
|
||||
f"SW{outlet}.x": 1,
|
||||
f"SW{outlet}.y": 1,
|
||||
}
|
||||
if from_state:
|
||||
params[f"STATUS{outlet}"] = f"{from_state.value:3s}"
|
||||
|
||||
logging.info("Switching outlet: %s", params)
|
||||
response = requests.post(
|
||||
str(url),
|
||||
auth=requests.auth.HTTPBasicAuth(self.user, self.password),
|
||||
data=params,
|
||||
allow_redirects=False,
|
||||
)
|
||||
print(response)
|
||||
|
||||
def switch_on(self, switch_id: int, outlet: int) -> None:
|
||||
return self.switch(switch_id, outlet, from_state=SwitchStatus.OFF)
|
||||
|
||||
def switch_off(self, switch_id: int, outlet: int) -> None:
|
||||
return self.switch(switch_id, outlet, from_state=SwitchStatus.ON)
|
||||
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
SwitchIdArgument = typing.Annotated[int, typer.Argument(min=1, max=16)]
|
||||
OutletArgument = typing.Annotated[int, typer.Argument(min=1, max=8)]
|
||||
|
||||
|
||||
class SwitchAction(str, enum.Enum):
|
||||
on = "on"
|
||||
off = "off"
|
||||
toggle = "toggle"
|
||||
|
||||
|
||||
@app.command()
|
||||
def switch(
|
||||
context: typer.Context,
|
||||
switch_id: SwitchIdArgument,
|
||||
outlet: OutletArgument,
|
||||
action: typing.Annotated[
|
||||
SwitchAction, typer.Argument(case_sensitive=False)
|
||||
] = SwitchAction.toggle,
|
||||
) -> None:
|
||||
switch: Switch = context.obj
|
||||
match action:
|
||||
case SwitchAction.on:
|
||||
switch.switch_on(switch_id, outlet)
|
||||
case SwitchAction.off:
|
||||
switch.switch_off(switch_id, outlet)
|
||||
case SwitchAction.toggle:
|
||||
switch.switch(switch_id, outlet)
|
||||
|
||||
|
||||
@app.callback()
|
||||
def main(
|
||||
context: typer.Context,
|
||||
address: str,
|
||||
user: str = "admin",
|
||||
password: str = "admin",
|
||||
) -> None:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
context.obj = Switch(
|
||||
address=address,
|
||||
user=user,
|
||||
password=password,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
24
pyproject.toml
Normal file
24
pyproject.toml
Normal file
|
@ -0,0 +1,24 @@
|
|||
[tool.poetry]
|
||||
name = "ipowerswitch"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Correl Roush <correl@gmail.com>"]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.12"
|
||||
requests = "^2.32.3"
|
||||
bs4 = "^0.0.2"
|
||||
typer = "^0.13.0"
|
||||
yarl = "^1.17.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^24.10.0"
|
||||
types-requests = "^2.32.0.20241016"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
ips = "ipowerswitch:app"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
Loading…
Reference in a new issue