mirror of
https://github.com/RichieCahill/dotfiles.git
synced 2026-04-17 04:58:19 -04:00
Add Signal location command backed by Home Assistant
This commit is contained in:
74
python/signal_bot/commands/location.py
Normal file
74
python/signal_bot/commands/location.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""Location command for the Signal bot."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import requests
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from python.signal_bot.models import SignalMessage
|
||||
from python.signal_bot.signal_client import SignalClient
|
||||
|
||||
|
||||
def _get_location_payload(ha_url: str, ha_token: str, entity_id: str) -> dict[str, Any]:
|
||||
"""Fetch location entity state from Home Assistant."""
|
||||
response = requests.get(
|
||||
f"{ha_url}/api/states/{entity_id}",
|
||||
headers={"Authorization": f"Bearer {ha_token}"},
|
||||
timeout=30,
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
def _format_location(payload: dict[str, Any]) -> str:
|
||||
"""Render a friendly location response."""
|
||||
attributes = payload.get("attributes", {})
|
||||
latitude = attributes.get("latitude")
|
||||
longitude = attributes.get("longitude")
|
||||
|
||||
if latitude is None or longitude is None:
|
||||
state = payload.get("state", "unknown")
|
||||
if "," not in state:
|
||||
return "Van location is unavailable in Home Assistant right now."
|
||||
latitude_text, longitude_text = [part.strip() for part in state.split(",", maxsplit=1)]
|
||||
else:
|
||||
latitude_text = str(latitude)
|
||||
longitude_text = str(longitude)
|
||||
|
||||
lines = [
|
||||
f"Van location: {latitude_text}, {longitude_text}",
|
||||
f"https://maps.google.com/?q={latitude_text},{longitude_text}",
|
||||
]
|
||||
|
||||
speed = attributes.get("speed")
|
||||
if speed not in (None, "", "unknown", "unavailable"):
|
||||
lines.append(f"Speed: {speed}")
|
||||
|
||||
last_updated = attributes.get("last_updated")
|
||||
if last_updated:
|
||||
lines.append(f"Updated: {last_updated}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def handle_location_request(
|
||||
message: SignalMessage,
|
||||
signal: SignalClient,
|
||||
ha_url: str | None,
|
||||
ha_token: str | None,
|
||||
ha_location_entity: str,
|
||||
) -> None:
|
||||
"""Reply with van location from Home Assistant."""
|
||||
if ha_url is None or ha_token is None:
|
||||
signal.reply(message, "Location command is not configured (missing HA_URL or HA_TOKEN).")
|
||||
return
|
||||
|
||||
try:
|
||||
payload = _get_location_payload(ha_url, ha_token, ha_location_entity)
|
||||
except requests.RequestException:
|
||||
signal.reply(message, "Couldn't fetch van location from Home Assistant right now.")
|
||||
return
|
||||
|
||||
signal.reply(message, _format_location(payload))
|
||||
@@ -14,6 +14,7 @@ from python.common import configure_logger, utcnow
|
||||
from python.orm.common import get_postgres_engine
|
||||
from python.orm.richie.dead_letter_message import DeadLetterMessage
|
||||
from python.signal_bot.commands.inventory import handle_inventory_update
|
||||
from python.signal_bot.commands.location import handle_location_request
|
||||
from python.signal_bot.device_registry import DeviceRegistry
|
||||
from python.signal_bot.llm_client import LLMClient
|
||||
from python.signal_bot.models import BotConfig, MessageStatus, SignalMessage
|
||||
@@ -27,6 +28,7 @@ HELP_TEXT = (
|
||||
" inventory <text list> — update van inventory from a text list\n"
|
||||
" inventory (+ photo) — update van inventory from a receipt photo\n"
|
||||
" status — show bot status\n"
|
||||
" location — get current van location\n"
|
||||
" help — show this help message\n"
|
||||
"Send a receipt photo with the message 'inventory' to scan it.\n"
|
||||
)
|
||||
@@ -86,6 +88,18 @@ def inventory_action(
|
||||
handle_inventory_update(message, signal, llm, config.inventory_api_url)
|
||||
|
||||
|
||||
def location_action(
|
||||
signal: SignalClient,
|
||||
message: SignalMessage,
|
||||
_llm: LLMClient,
|
||||
_registry: DeviceRegistry,
|
||||
config: BotConfig,
|
||||
_cmd: str,
|
||||
) -> None:
|
||||
"""Reply with current van location."""
|
||||
handle_location_request(message, signal, config.ha_url, config.ha_token, config.ha_location_entity)
|
||||
|
||||
|
||||
def dispatch(
|
||||
message: SignalMessage,
|
||||
signal: SignalClient,
|
||||
@@ -112,6 +126,7 @@ def dispatch(
|
||||
"help": help_action,
|
||||
"status": status_action,
|
||||
"inventory": inventory_action,
|
||||
"location": location_action,
|
||||
}
|
||||
logger.info(f"f{source=} running {cmd=} with {message=}")
|
||||
action = commands.get(cmd)
|
||||
@@ -209,6 +224,9 @@ def main(
|
||||
signal_api_url=signal_api_url,
|
||||
phone_number=phone_number,
|
||||
inventory_api_url=inventory_api_url,
|
||||
ha_url=getenv("HA_URL"),
|
||||
ha_token=getenv("HA_TOKEN"),
|
||||
ha_location_entity=getenv("HA_LOCATION_ENTITY", "sensor.gps_location"),
|
||||
engine=engine,
|
||||
)
|
||||
|
||||
|
||||
@@ -79,6 +79,9 @@ class BotConfig(BaseModel):
|
||||
signal_api_url: str
|
||||
phone_number: str
|
||||
inventory_api_url: str
|
||||
ha_url: str | None = None
|
||||
ha_token: str | None = None
|
||||
ha_location_entity: str = "sensor.gps_location"
|
||||
engine: Engine
|
||||
reconnect_delay: int = 5
|
||||
max_reconnect_delay: int = 300
|
||||
|
||||
Reference in New Issue
Block a user