Refactor main entry point and command handling in HassSystemClient and HassUserClient
This commit is contained in:
parent
9b3df6416e
commit
fad234ad00
4 changed files with 104 additions and 85 deletions
|
@ -0,0 +1,68 @@
|
|||
import logging
|
||||
import signal
|
||||
import tomllib
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
|
||||
from hasspy.mqtt import HassClient, HassSystemClient, HassUserClient
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
log.info("Starting HassPy")
|
||||
parser = ArgumentParser(
|
||||
prog="HassPy",
|
||||
description="Home Assistant MQTT client",
|
||||
)
|
||||
parser.add_argument("-c", "--config", help="Path to configuration file")
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", help="Enable verbose logging", action="count", default=0
|
||||
)
|
||||
parser.add_argument("-u", "--user", help="User mode client", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.config:
|
||||
config_file = Path(args.config)
|
||||
else:
|
||||
config_file = next(
|
||||
(
|
||||
x
|
||||
for x in (Path("/etc/hasspy.toml"), Path("/etc/hasspy/config.toml"))
|
||||
if x.exists()
|
||||
),
|
||||
Path("config.toml"),
|
||||
)
|
||||
|
||||
if not config_file or not config_file.exists():
|
||||
raise FileNotFoundError("No configuration file found")
|
||||
|
||||
with open(config_file, "rb") as file:
|
||||
config = tomllib.load(file)
|
||||
|
||||
if isinstance(config.get("log_level"), str):
|
||||
config["log_level"] = getattr(logging, config["log_level"])
|
||||
logging.basicConfig(
|
||||
level=config.get("log_level", logging.INFO) - (args.verbose * 10)
|
||||
)
|
||||
|
||||
ha: HassClient
|
||||
if not args.user:
|
||||
ha = HassSystemClient(
|
||||
"orchomenos",
|
||||
config=config,
|
||||
)
|
||||
else:
|
||||
ha = HassUserClient(
|
||||
"orchomenos",
|
||||
config=config,
|
||||
)
|
||||
|
||||
ha.loop_start()
|
||||
|
||||
signal.sigwait([signal.SIGHUP, signal.SIGINT, signal.SIGTERM])
|
||||
|
||||
log.info("Shutting down")
|
||||
|
||||
ha.loop_stop()
|
||||
return 0
|
|
@ -1,61 +1,3 @@
|
|||
import logging
|
||||
import tomllib
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
from hasspy import main
|
||||
|
||||
from hasspy.mqtt import HassClient, HassSystemClient, HassUserClient
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = ArgumentParser(
|
||||
prog="HassPy",
|
||||
description="Home Assistant MQTT client",
|
||||
)
|
||||
parser.add_argument("-c", "--config", help="Path to configuration file")
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", help="Enable verbose logging", action="count", default=0
|
||||
)
|
||||
parser.add_argument("-u", "--user", help="User mode client", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.config:
|
||||
config_file = Path(args.config)
|
||||
else:
|
||||
config_file = next(
|
||||
(
|
||||
x
|
||||
for x in (Path("/etc/hasspy.toml"), Path("/etc/hasspy/config.toml"))
|
||||
if x.exists()
|
||||
),
|
||||
Path("config.toml"),
|
||||
)
|
||||
|
||||
if not config_file or not config_file.exists():
|
||||
raise FileNotFoundError("No configuration file found")
|
||||
|
||||
with open(config_file, "rb") as file:
|
||||
config = tomllib.load(file)
|
||||
|
||||
if isinstance(config.get("log_level"), str):
|
||||
config["log_level"] = getattr(logging, config["log_level"])
|
||||
logging.basicConfig(
|
||||
level=config.get("log_level", logging.INFO) - (args.verbose * 10)
|
||||
)
|
||||
|
||||
ha: HassClient
|
||||
if not args.user:
|
||||
ha = HassSystemClient(
|
||||
"orchomenos",
|
||||
config=config,
|
||||
)
|
||||
else:
|
||||
ha = HassUserClient(
|
||||
"orchomenos",
|
||||
config=config,
|
||||
)
|
||||
|
||||
ha.loop_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import getpass
|
||||
import json
|
||||
import logging
|
||||
from subprocess import run
|
||||
|
@ -122,29 +121,26 @@ class HassClient(Client):
|
|||
|
||||
|
||||
class HassSystemClient(HassClient):
|
||||
commands = {
|
||||
"POWER_ON": ["systemctl", "poweroff", "--when=cancel"],
|
||||
"POWER_OFF": ["systemctl", "poweroff", "--when=+1m"],
|
||||
"LOCK": ["loginctl", "lock-sessions"],
|
||||
}
|
||||
|
||||
def do_command(self, payload: str) -> None:
|
||||
if payload not in self.commands:
|
||||
return
|
||||
|
||||
super().do_command(payload)
|
||||
|
||||
match payload:
|
||||
case "POWER_ON":
|
||||
if not self.power_on:
|
||||
log.info("Cancelling shutdown…")
|
||||
self.power_on = True
|
||||
proc = run(self.commands[payload])
|
||||
if proc.returncode != 0:
|
||||
log.error(f"Failed to execute command: {payload}")
|
||||
|
||||
proc = run(["systemctl", "poweroff", "--when=cancel"])
|
||||
if proc.returncode != 0:
|
||||
log.error("Failed to cancel shutdown")
|
||||
case "POWER_OFF":
|
||||
if self.power_on:
|
||||
log.info("Powering off…")
|
||||
self.power_on = False
|
||||
|
||||
proc = run(["systemctl", "poweroff", "--when=+1m"])
|
||||
if proc.returncode != 0:
|
||||
log.error("Failed to schedule shutdown")
|
||||
case "LOCK":
|
||||
log.info("Locking screen…")
|
||||
run(["loginctl", "lock-sessions"])
|
||||
if payload == "POWER_ON":
|
||||
self.power_on = True
|
||||
elif payload == "POWER_OFF":
|
||||
self.power_on = False
|
||||
|
||||
@property
|
||||
def components(self) -> dict[str, dict[str, str]]:
|
||||
|
@ -173,16 +169,26 @@ class HassSystemClient(HassClient):
|
|||
|
||||
|
||||
class HassUserClient(HassClient):
|
||||
commands = {
|
||||
"PLAY_PAUSE": ["playerctl", "play-pause"],
|
||||
}
|
||||
|
||||
def __init__(self, node_id: str, config: Mapping[str, Any]) -> None:
|
||||
super().__init__(f"{node_id}_{getpass.getuser()}", config)
|
||||
super().__init__(f"{node_id}", config)
|
||||
|
||||
def do_command(self, payload: str) -> None:
|
||||
if payload not in self.commands:
|
||||
return
|
||||
|
||||
super().do_command(payload)
|
||||
|
||||
match payload:
|
||||
case "PLAY_PAUSE":
|
||||
log.info("Toggling play/pause…")
|
||||
run(["playerctl", "play-pause"])
|
||||
proc = run(self.commands[payload])
|
||||
if proc.returncode != 0:
|
||||
log.error(f"Failed to execute command: {payload}")
|
||||
|
||||
@property
|
||||
def availability_topic(self) -> str:
|
||||
return f"{self.node_id}/user/availability"
|
||||
|
||||
@property
|
||||
def components(self) -> dict[str, dict[str, str]]:
|
||||
|
|
|
@ -13,3 +13,6 @@ dev = [
|
|||
"mypy>=1.15.0",
|
||||
"ruff>=0.9.10",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
hasspy = "hasspy:main"
|
||||
|
|
Loading…
Add table
Reference in a new issue