"""
title: Home Assistant
author: Andrew Tait Gehrhardt
author_url: https://github.com/atgehrhardt
funding_url: https://github.com/open-webui
version: 0.3.1
"""
import requests
from typing import Literal, Dict, Any, List
from datetime import datetime
import pytz
from difflib import get_close_matches
from pydantic import BaseModel, Field
class Tools:
class Valves(BaseModel):
priority: int = Field(
default=0, description="Priority level for the filter operations."
)
BASE_URL: str = Field(
default="",
)
AUTH_TOKEN: str = Field(
default="",
)
def __init__(self):
self.valves = self.Valves()
def get_all_lights(self) -> Dict[str, str]:
"""
Retrieve a dictionary of lights in the house.
:return: A dictionary of light entity names and their IDs.
"""
if not self.valves.BASE_URL or not self.valves.AUTH_TOKEN:
return {
"error": "Home Assistant URL or token not set, ask the user to set it up."
}
else:
url = f"{self.valves.BASE_URL}/api/states"
headers = {
"Authorization": f"Bearer {self.valves.AUTH_TOKEN}",
"Content-Type": "application/json",
}
response = requests.get(url, headers=headers)
response.raise_for_status() # Raises an HTTPError for bad responses
data = response.json()
lights = {
entity["attributes"]["friendly_name"]: entity["entity_id"]
for entity in data
if entity["entity_id"].startswith("light.")
}
return lights
def list_lights(self) -> str:
"""
List all lights in a human-readable format.
:return: A string listing all the lights.
"""
lights = self.get_all_lights()
if isinstance(lights, dict) and "error" in lights:
return lights["error"]
if not lights:
return "No lights found in your home."
light_list = "\n".join(f"- {name}" for name in lights.keys())
return f"Here are the lights currently in your home:\n{light_list}"
def control_lights(self, names: List[str], state: Literal["on", "off"]) -> str:
"""
Turn multiple lights on or off based on their names.
:param names: A list of light names.
:param state: The desired state ('on' or 'off').
:return: The result of the operation.
"""
if not self.valves.BASE_URL or not self.valves.AUTH_TOKEN:
return "Home Assistant URL or token not set, ask the user to set it up."
lights = self.get_all_lights()
if isinstance(lights, dict) and "error" in lights:
return lights["error"]
light_ids = []
not_found = []
for name in names:
# Normalize the light name
normalized_name = " ".join(name.lower().split())
# Find the closest matching light name
closest_matches = get_close_matches(
normalized_name, list(lights.keys()), n=1, cutoff=0.6
)
if closest_matches:
best_match = closest_matches[0]
light_ids.append(lights[best_match])
else:
not_found.append(name)
if not light_ids:
return f"None of the specified lights were found: {', '.join(names)}"
url = f"{self.valves.BASE_URL}/api/services/light/turn_{state}"
headers = {
"Authorization": f"Bearer {self.valves.AUTH_TOKEN}",
"Content-Type": "application/json",
}
payload = {"entity_id": light_ids}
response = requests.post(url, headers=headers, json=payload)
result = []
if response.status_code == 200:
result.append(
f"Successfully turned {state} the following lights: {', '.join(names)}"
)
else:
result.append(
f"Failed to turn {state} the lights. Status code: {response.status_code}"
)
if not_found:
result.append(
f"The following lights were not found: {', '.join(not_found)}"
)
return " ".join(result)