Tool
v0.3.1
Home Assistant Light Control
A tool that lists and controls my lights. Uses devices names, not entity ids
Tool ID
home_assistant_light_control
Creator
@atgehrhardt
Downloads
226+

Tool Content
python
"""
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)