2026-08-23 08:16:15 +02:00
|
|
|
"""Send controller events to the cloud event logger."""
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
from datetime import datetime, timezone
|
2026-08-26 18:17:03 +02:00
|
|
|
from pprint import pprint
|
2026-08-23 08:16:15 +02:00
|
|
|
import requests
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG = logging.getLogger("home_control.cloud_logger")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _filter_values(config: dict, mode: str, field: str) -> set[str]:
|
|
|
|
|
value = config.get("filter", {}).get(mode, {}).get(field, [])
|
|
|
|
|
if not isinstance(value, list) or any(not isinstance(item, str) for item in value):
|
|
|
|
|
raise ValueError(
|
|
|
|
|
f"cloud_logger.filter.{mode}.{field} must be a list of strings"
|
|
|
|
|
)
|
|
|
|
|
return set(value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def accepts_event(config: dict, message: dict) -> bool:
|
|
|
|
|
"""Return whether an event passes configured event and ID filters."""
|
|
|
|
|
accepted_events = _filter_values(config, "accept", "events")
|
|
|
|
|
accepted_ids = _filter_values(config, "accept", "ids")
|
|
|
|
|
ignored_events = _filter_values(config, "ignore", "events")
|
|
|
|
|
ignored_ids = _filter_values(config, "ignore", "ids")
|
|
|
|
|
|
|
|
|
|
if message["event"] in ignored_events or message["id"] in ignored_ids:
|
|
|
|
|
return False
|
|
|
|
|
if accepted_events and message["event"] not in accepted_events:
|
|
|
|
|
return False
|
|
|
|
|
if accepted_ids and message["id"] not in accepted_ids:
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def send_event(message: dict, event_url: str, timeout: float = 10) -> None:
|
|
|
|
|
"""POST an event dictionary as JSON to the configured logger endpoint."""
|
|
|
|
|
if not isinstance(message, dict):
|
|
|
|
|
raise TypeError("message must be a dictionary")
|
|
|
|
|
if not event_url:
|
|
|
|
|
raise ValueError("event_url is required")
|
|
|
|
|
|
|
|
|
|
response = requests.post(event_url, json=message, timeout=timeout)
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def on_event(config: dict, message: dict) -> None:
|
|
|
|
|
"""Controller action that forwards event data to the cloud logger."""
|
|
|
|
|
if not config.get("enabled", True):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if not accepts_event(config, message):
|
|
|
|
|
LOG.debug(
|
|
|
|
|
"cloud event ignored by filter: event=%s id=%s",
|
|
|
|
|
message["event"],
|
|
|
|
|
message["id"],
|
|
|
|
|
)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
timestamp = datetime.now(timezone.utc).isoformat()
|
|
|
|
|
values = {**message, "datetime": timestamp}
|
|
|
|
|
messages = config.get("messages", {})
|
2026-08-26 18:17:03 +02:00
|
|
|
template = config.get(
|
2026-08-23 08:16:15 +02:00
|
|
|
message["event"],
|
|
|
|
|
config.get("message", "{datetime} {sender}: {event} ({id})"),
|
|
|
|
|
)
|
|
|
|
|
payload = {
|
|
|
|
|
**message,
|
|
|
|
|
"datetime": timestamp,
|
|
|
|
|
"text": message.get("text") or str(template).format(**values),
|
|
|
|
|
}
|
2026-08-26 18:17:03 +02:00
|
|
|
pprint(payload)
|
2026-08-23 08:16:15 +02:00
|
|
|
send_event(
|
|
|
|
|
payload,
|
|
|
|
|
event_url=str(config.get("event_url", "")),
|
|
|
|
|
timeout=float(config.get("timeout", 10)),
|
|
|
|
|
)
|
|
|
|
|
except (TypeError, ValueError, requests.RequestException) as error:
|
|
|
|
|
LOG.error("cloud event logging failed: %s", error)
|