mirror of
https://github.com/Nighthawk42/fulloch.git
synced 2026-08-30 08:02:28 +00:00
config tool enable/disable with comment
This commit is contained in:
@@ -56,6 +56,7 @@ data/credentials.json
|
||||
data/token.json
|
||||
*.pem
|
||||
*.key
|
||||
.python_hue
|
||||
|
||||
# =============================================================================
|
||||
# Local Data and Models
|
||||
|
||||
@@ -11,9 +11,12 @@ Usage:
|
||||
"""
|
||||
|
||||
import os
|
||||
import warnings
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
|
||||
warnings.filterwarnings("ignore", message="Setting `pad_token_id` to `eos_token_id`")
|
||||
|
||||
# Load configuration
|
||||
with open("./data/config.yml", "r") as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
+58
-54
@@ -2,6 +2,13 @@
|
||||
# Copy this file to config.yml and update with your values.
|
||||
# See README.md for detailed setup instructions for each integration.
|
||||
|
||||
# =============================================================================
|
||||
# Tool Activation
|
||||
# =============================================================================
|
||||
# Each section below corresponds to an integration/tool. To disable a tool,
|
||||
# comment out its entire section. Only configured (uncommented) tools are
|
||||
# loaded on launch.
|
||||
|
||||
# =============================================================================
|
||||
# General Settings
|
||||
# =============================================================================
|
||||
@@ -23,22 +30,22 @@ general:
|
||||
# =============================================================================
|
||||
# Register your app at https://developer.spotify.com/dashboard
|
||||
# Required scopes: user-read-playback-state, user-modify-playback-state, user-read-currently-playing
|
||||
spotify:
|
||||
client_id: "your_spotify_client_id"
|
||||
client_secret: "your_spotify_client_secret"
|
||||
redirect_uri: "http://localhost:8888/callback"
|
||||
# Device name as shown in Spotify Connect
|
||||
device_id: "Your Speaker Name"
|
||||
# Enable Pioneer AVR integration for audio output switching
|
||||
# Set to false if not using an AVR
|
||||
use_avr: false
|
||||
# spotify:
|
||||
# client_id: "your_spotify_client_id"
|
||||
# client_secret: "your_spotify_client_secret"
|
||||
# redirect_uri: "http://localhost:8888/callback"
|
||||
# # Device name as shown in Spotify Connect
|
||||
# device_id: "Your Speaker Name"
|
||||
# # Enable Pioneer AVR integration for audio output switching
|
||||
# # Set to false if not using an AVR
|
||||
# use_avr: false
|
||||
|
||||
# =============================================================================
|
||||
# Philips Hue Lighting
|
||||
# =============================================================================
|
||||
# Press the button on your Hue Bridge before first connection
|
||||
philips:
|
||||
hue_hub_ip: "192.168.1.100"
|
||||
# philips:
|
||||
# hue_hub_ip: "192.168.1.100"
|
||||
|
||||
# =============================================================================
|
||||
# Home Assistant Integration
|
||||
@@ -47,29 +54,26 @@ philips:
|
||||
# 1. Go to your HA profile: http://your-ha:8123/profile
|
||||
# 2. Scroll to "Long-Lived Access Tokens" and create a token
|
||||
#
|
||||
# IMPORTANT: This integration must be explicitly enabled. When enabled, it
|
||||
# registers generic tool names like "turn_on" and "turn_off" which may conflict
|
||||
# with other integrations (e.g., Philips Hue lighting). Only enable if you want
|
||||
# Home Assistant to be your primary home automation controller.
|
||||
home_assistant:
|
||||
# Set to true to enable Home Assistant tools (disabled by default)
|
||||
enabled: false
|
||||
# URL to your Home Assistant instance
|
||||
url: "http://192.168.1.50:8123"
|
||||
# Long-lived access token from your HA profile
|
||||
token: "your_long_lived_access_token"
|
||||
# Request timeout in seconds
|
||||
timeout: 10
|
||||
# Map friendly names to entity IDs for easier voice control
|
||||
# Keys are lowercase, values are full entity IDs
|
||||
entity_aliases:
|
||||
living room lights: "light.living_room"
|
||||
bedroom lights: "light.bedroom"
|
||||
front door: "lock.front_door"
|
||||
garage: "cover.garage_door"
|
||||
thermostat: "climate.main"
|
||||
movie time: "scene.movie_time"
|
||||
bedtime: "script.bedtime"
|
||||
# NOTE: This registers generic tool names like "turn_on" and "turn_off" which
|
||||
# may conflict with other integrations (e.g., Philips Hue lighting). Comment
|
||||
# out this entire section if you don't use Home Assistant.
|
||||
# home_assistant:
|
||||
# # URL to your Home Assistant instance
|
||||
# url: "http://192.168.1.50:8123"
|
||||
# # Long-lived access token from your HA profile
|
||||
# token: "your_long_lived_access_token"
|
||||
# # Request timeout in seconds
|
||||
# timeout: 10
|
||||
# # Map friendly names to entity IDs for easier voice control
|
||||
# # Keys are lowercase, values are full entity IDs
|
||||
# entity_aliases:
|
||||
# living room lights: "light.living_room"
|
||||
# bedroom lights: "light.bedroom"
|
||||
# front door: "lock.front_door"
|
||||
# garage: "cover.garage_door"
|
||||
# thermostat: "climate.main"
|
||||
# movie time: "scene.movie_time"
|
||||
# bedtime: "script.bedtime"
|
||||
|
||||
# =============================================================================
|
||||
# Weather - Bureau of Meteorology (Australia)
|
||||
@@ -88,20 +92,20 @@ bom:
|
||||
# 1. Create credentials at https://console.cloud.google.com/
|
||||
# 2. Enable Google Calendar API
|
||||
# 3. Download OAuth 2.0 credentials JSON file
|
||||
google:
|
||||
# Path to OAuth client credentials file
|
||||
cred_file: "./data/credentials.json"
|
||||
# Path to store OAuth token (auto-generated on first auth)
|
||||
token_file: "./data/token.json"
|
||||
# google:
|
||||
# # Path to OAuth client credentials file
|
||||
# cred_file: "./data/credentials.json"
|
||||
# # Path to store OAuth token (auto-generated on first auth)
|
||||
# token_file: "./data/token.json"
|
||||
|
||||
# =============================================================================
|
||||
# LG ThinQ (Smart Appliances)
|
||||
# =============================================================================
|
||||
# Get credentials from LG ThinQ developer portal
|
||||
thinq:
|
||||
access_token: "your_thinq_access_token"
|
||||
country_code: "AU"
|
||||
client_id: "your_thinq_client_id"
|
||||
# thinq:
|
||||
# access_token: "your_thinq_access_token"
|
||||
# country_code: "AU"
|
||||
# client_id: "your_thinq_client_id"
|
||||
|
||||
# =============================================================================
|
||||
# Web Search - SearXNG
|
||||
@@ -115,26 +119,26 @@ search:
|
||||
# =============================================================================
|
||||
# Find TV IP in TV settings: Network > Wi-Fi Connection > Advanced
|
||||
# Find MAC address in TV settings: Network > Wi-Fi Connection > Advanced
|
||||
webos:
|
||||
ip_address: "192.168.1.101"
|
||||
mac_address: "AA:BB:CC:DD:EE:FF"
|
||||
# webos:
|
||||
# ip_address: "192.168.1.101"
|
||||
# mac_address: "AA:BB:CC:DD:EE:FF"
|
||||
|
||||
# =============================================================================
|
||||
# Pioneer/Onkyo AVR (Audio Receiver)
|
||||
# =============================================================================
|
||||
# Uses eISCP protocol over network
|
||||
# Default port is 60128 for most Pioneer/Onkyo receivers
|
||||
pioneer:
|
||||
avr_host: "192.168.1.102"
|
||||
avr_port: 60128
|
||||
# pioneer:
|
||||
# avr_host: "192.168.1.102"
|
||||
# avr_port: 60128
|
||||
|
||||
# =============================================================================
|
||||
# Airtouch HVAC Control
|
||||
# =============================================================================
|
||||
# Map zone names to zone IDs (0-indexed)
|
||||
# Zone IDs correspond to the order in your Airtouch system
|
||||
airtouch:
|
||||
living room: 0
|
||||
bedroom: 1
|
||||
office: 2
|
||||
upstairs: 3
|
||||
# airtouch:
|
||||
# living room: 0
|
||||
# bedroom: 1
|
||||
# office: 2
|
||||
# upstairs: 3
|
||||
|
||||
+49
-27
@@ -2,36 +2,58 @@
|
||||
Tools package for the voice assistant.
|
||||
|
||||
This package contains all the tools that can be used by the voice assistant.
|
||||
All tools are automatically registered with the tool registry when this
|
||||
package is imported.
|
||||
Tools are conditionally loaded based on which integrations are configured
|
||||
in data/config.yml. Commenting out or removing a config section disables
|
||||
that tool entirely.
|
||||
"""
|
||||
|
||||
# Import all tools to ensure they are registered with the tool registry
|
||||
from . import spotify
|
||||
from . import lighting
|
||||
from . import weather_time
|
||||
from . import google_calendar
|
||||
from . import airtouch
|
||||
from . import thinq
|
||||
from . import webos
|
||||
from . import search_web
|
||||
from . import pioneer_avr
|
||||
from . import home_assistant
|
||||
import yaml
|
||||
import logging
|
||||
import importlib
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Load configuration to determine which tools to activate
|
||||
try:
|
||||
with open("./data/config.yml", "r") as f:
|
||||
_config = yaml.safe_load(f) or {}
|
||||
except FileNotFoundError:
|
||||
_config = {}
|
||||
|
||||
# Mapping of tool module names to their required config key.
|
||||
# A tool is only loaded if its config key is present in config.yml.
|
||||
_TOOL_CONFIG_MAP = {
|
||||
'spotify': 'spotify',
|
||||
'lighting': 'philips',
|
||||
'google_calendar': 'google',
|
||||
'airtouch': 'airtouch',
|
||||
'thinq': 'thinq',
|
||||
'webos': 'webos',
|
||||
'search_web': 'search',
|
||||
'pioneer_avr': 'pioneer',
|
||||
'home_assistant': 'home_assistant',
|
||||
}
|
||||
|
||||
# Always load these tools (no config dependency)
|
||||
_ALWAYS_LOAD = ['weather_time']
|
||||
|
||||
for _module_name in _ALWAYS_LOAD:
|
||||
try:
|
||||
importlib.import_module(f'.{_module_name}', package=__name__)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load tool {_module_name}: {e}")
|
||||
|
||||
for _module_name, _config_key in _TOOL_CONFIG_MAP.items():
|
||||
if _config_key in _config:
|
||||
try:
|
||||
importlib.import_module(f'.{_module_name}', package=__name__)
|
||||
logger.info(f"Loaded tool: {_module_name}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load tool {_module_name}: {e}")
|
||||
else:
|
||||
logger.info(f"Skipping tool {_module_name} ('{_config_key}' not in config)")
|
||||
|
||||
# Import the tool registry
|
||||
from .tool_registry import tool_registry, tool
|
||||
|
||||
__all__ = [
|
||||
'tool_registry',
|
||||
'tool',
|
||||
'spotify',
|
||||
'lighting',
|
||||
'weather_time',
|
||||
'google_calendar',
|
||||
'airtouch',
|
||||
'thinq',
|
||||
'webos',
|
||||
'search_web',
|
||||
'pioneer_avr',
|
||||
'home_assistant'
|
||||
]
|
||||
__all__ = ['tool_registry', 'tool']
|
||||
|
||||
+20
-36
@@ -3,8 +3,9 @@
|
||||
Connects to a Home Assistant instance via REST API for home automation control.
|
||||
Requires a long-lived access token configured in data/config.yml.
|
||||
|
||||
This integration must be explicitly enabled in config.yml to prevent conflicts
|
||||
with other home automation tools (e.g., Philips Hue lighting).
|
||||
This module is only loaded when 'home_assistant' is present in config.yml.
|
||||
Note: This registers generic tool names like "turn_on" and "turn_off" which
|
||||
may conflict with other integrations (e.g., Philips Hue lighting).
|
||||
"""
|
||||
|
||||
import requests
|
||||
@@ -14,15 +15,9 @@ from typing import Optional
|
||||
from .tool_registry import tool
|
||||
|
||||
# Load configuration
|
||||
try:
|
||||
with open("./data/config.yml", "r") as f:
|
||||
config = yaml.safe_load(f)
|
||||
HA_CONFIG = config.get('home_assistant', {})
|
||||
except FileNotFoundError:
|
||||
HA_CONFIG = {}
|
||||
|
||||
# Check if Home Assistant integration is enabled
|
||||
HA_ENABLED = HA_CONFIG.get('enabled', False)
|
||||
with open("./data/config.yml", "r") as f:
|
||||
config = yaml.safe_load(f)
|
||||
HA_CONFIG = config.get('home_assistant', {})
|
||||
|
||||
# Home Assistant connection settings
|
||||
HA_URL = HA_CONFIG.get('url', 'http://localhost:8123')
|
||||
@@ -30,17 +25,6 @@ HA_TOKEN = HA_CONFIG.get('token', '')
|
||||
TIMEOUT = HA_CONFIG.get('timeout', 10)
|
||||
|
||||
|
||||
def _noop_decorator(name=None, description=None, aliases=None):
|
||||
"""No-op decorator when Home Assistant integration is disabled."""
|
||||
def decorator(func):
|
||||
return func
|
||||
return decorator
|
||||
|
||||
|
||||
# Use real tool decorator only when enabled
|
||||
ha_tool = tool if HA_ENABLED else _noop_decorator
|
||||
|
||||
|
||||
def _get_headers() -> dict:
|
||||
"""Return authorization headers for Home Assistant API."""
|
||||
return {
|
||||
@@ -112,7 +96,7 @@ def _resolve_entity(name: str, domain: str = None) -> str:
|
||||
return name
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="turn_on",
|
||||
description="Turn on a device, light, switch, or other Home Assistant entity",
|
||||
aliases=["ha_turn_on", "switch_on", "turn_on_device"]
|
||||
@@ -137,7 +121,7 @@ def turn_on(entity: str, brightness: Optional[int] = None) -> str:
|
||||
return _call_service(domain, "turn_on", entity_id, data if data else None)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="turn_off",
|
||||
description="Turn off a device, light, switch, or other Home Assistant entity",
|
||||
aliases=["ha_turn_off", "switch_off", "turn_off_device"]
|
||||
@@ -154,7 +138,7 @@ def turn_off(entity: str) -> str:
|
||||
return _call_service(domain, "turn_off", entity_id)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="toggle",
|
||||
description="Toggle a Home Assistant entity on or off",
|
||||
aliases=["ha_toggle", "toggle_device"]
|
||||
@@ -171,7 +155,7 @@ def toggle(entity: str) -> str:
|
||||
return _call_service(domain, "toggle", entity_id)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="ha_set_brightness",
|
||||
description="Set the brightness of a light in Home Assistant",
|
||||
aliases=["ha_brightness", "ha_dim_light"]
|
||||
@@ -192,7 +176,7 @@ def set_ha_brightness(entity: str, brightness: int) -> str:
|
||||
return _call_service("light", "turn_on", entity_id, {"brightness": brightness_255})
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="ha_set_color",
|
||||
description="Set the color of a light in Home Assistant using color name or RGB",
|
||||
aliases=["ha_color", "change_light_color"]
|
||||
@@ -240,7 +224,7 @@ def set_color(entity: str, color: str) -> str:
|
||||
return _call_service("light", "turn_on", entity_id, {"rgb_color": rgb})
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="get_entity_state",
|
||||
description="Get the current state of a Home Assistant entity",
|
||||
aliases=["ha_state", "check_state", "is_on"]
|
||||
@@ -275,7 +259,7 @@ def get_entity_state(entity: str) -> str:
|
||||
return ", ".join(details)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="ha_service",
|
||||
description="Call any Home Assistant service with custom data",
|
||||
aliases=["call_service", "ha_call"]
|
||||
@@ -303,7 +287,7 @@ def call_ha_service(domain: str, service: str, entity: str, data: Optional[str]
|
||||
return _call_service(domain, service, entity_id, extra_data)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="ha_set_climate",
|
||||
description="Set the temperature of a climate/thermostat entity in Home Assistant",
|
||||
aliases=["ha_climate", "ha_thermostat"]
|
||||
@@ -325,7 +309,7 @@ def set_climate(entity: str, temperature: float, hvac_mode: Optional[str] = None
|
||||
return _call_service("climate", "set_temperature", entity_id, data)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="ha_lock",
|
||||
description="Lock a lock entity in Home Assistant",
|
||||
aliases=["lock_door"]
|
||||
@@ -340,7 +324,7 @@ def lock(entity: str) -> str:
|
||||
return _call_service("lock", "lock", entity_id)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="ha_unlock",
|
||||
description="Unlock a lock entity in Home Assistant",
|
||||
aliases=["unlock_door"]
|
||||
@@ -355,7 +339,7 @@ def unlock(entity: str) -> str:
|
||||
return _call_service("lock", "unlock", entity_id)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="ha_open_cover",
|
||||
description="Open a cover/blind/garage in Home Assistant",
|
||||
aliases=["ha_open", "open_blind", "open_garage"]
|
||||
@@ -370,7 +354,7 @@ def open_cover(entity: str) -> str:
|
||||
return _call_service("cover", "open_cover", entity_id)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="ha_close_cover",
|
||||
description="Close a cover/blind/garage in Home Assistant",
|
||||
aliases=["ha_close", "close_blind", "close_garage"]
|
||||
@@ -385,7 +369,7 @@ def close_cover(entity: str) -> str:
|
||||
return _call_service("cover", "close_cover", entity_id)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="ha_run_script",
|
||||
description="Run a Home Assistant script or automation",
|
||||
aliases=["ha_script", "run_automation"]
|
||||
@@ -400,7 +384,7 @@ def run_script(script_name: str) -> str:
|
||||
return _call_service("script", "turn_on", entity_id)
|
||||
|
||||
|
||||
@ha_tool(
|
||||
@tool(
|
||||
name="ha_activate_scene",
|
||||
description="Activate a Home Assistant scene",
|
||||
aliases=["ha_scene", "set_scene"]
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ from phue import Bridge
|
||||
|
||||
from .tool_registry import tool, tool_registry
|
||||
|
||||
b = Bridge(config['philips']['hue_hub_ip'])
|
||||
b = Bridge(config['philips']['hue_hub_ip'], config_file_path="./data/.python_hue")
|
||||
|
||||
|
||||
@tool(
|
||||
|
||||
+14
-13
@@ -3,8 +3,11 @@ Weather and time information tool using the centralized tool registry.
|
||||
"""
|
||||
import yaml
|
||||
|
||||
with open("./data/config.yml", "r") as f:
|
||||
config = yaml.safe_load(f)
|
||||
try:
|
||||
with open("./data/config.yml", "r") as f:
|
||||
config = yaml.safe_load(f) or {}
|
||||
except FileNotFoundError:
|
||||
config = {}
|
||||
|
||||
from datetime import datetime
|
||||
import re
|
||||
@@ -75,14 +78,10 @@ def summarize_today_tomorrow(forecast_data, location):
|
||||
|
||||
|
||||
def load_weather_config():
|
||||
try:
|
||||
return config['bom']
|
||||
except Exception as e:
|
||||
# Fallback to default values if config file is not available
|
||||
return {
|
||||
"host": "ftp.bom.gov.au",
|
||||
"path": "/anon/gen/fwo/IDN11060.xml"
|
||||
}
|
||||
return config.get('bom', {
|
||||
"host": "ftp.bom.gov.au",
|
||||
"path": "/anon/gen/fwo/IDN11060.xml"
|
||||
})
|
||||
|
||||
|
||||
@tool(
|
||||
@@ -90,18 +89,20 @@ def load_weather_config():
|
||||
description="Get weather forecast for a location",
|
||||
aliases=["weather", "forecast", "get_weather"]
|
||||
)
|
||||
def get_weather_forecast(location: str = config['bom']['default']) -> str:
|
||||
def get_weather_forecast(location: str = None) -> str:
|
||||
"""
|
||||
Get weather forecast for the specified location.
|
||||
|
||||
|
||||
Args:
|
||||
location: The location for weather forecast
|
||||
|
||||
|
||||
Returns:
|
||||
Weather forecast summary
|
||||
"""
|
||||
# Load FTP configuration
|
||||
ftp_config = load_weather_config()
|
||||
if location is None:
|
||||
location = ftp_config.get('default', 'Sydney')
|
||||
|
||||
# Connect to FTP and retrieve file into memory
|
||||
ftp = FTP(ftp_config['host'])
|
||||
|
||||
Reference in New Issue
Block a user