#!/usr/bin/env python3 """ List all available Home Assistant services This script queries Home Assistant and displays all available services organized by domain. Usage: # List all services python3 list_services.py --url http://homeassistant.local:8123 --token YOUR_TOKEN # List services for specific domain python3 list_services.py --url http://homeassistant.local:8123 --token YOUR_TOKEN --domain light # Output as JSON python3 list_services.py --url http://homeassistant.local:8123 --token YOUR_TOKEN --format json Environment variables: HA_URL: Home Assistant URL HA_TOKEN: Long-lived access token """ import argparse import json import os import sys import urllib.request def get_services(url: str, token: str): """Get all services from Home Assistant.""" api_url = f"{url.rstrip('/')}/api/services" headers = { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } req = urllib.request.Request(api_url, headers=headers) try: with urllib.request.urlopen(req) as response: return json.loads(response.read().decode('utf-8')) except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) def main(): parser = argparse.ArgumentParser(description='List Home Assistant services') parser.add_argument('--url', help='Home Assistant URL', default=os.getenv('HA_URL')) parser.add_argument('--token', help='Long-lived access token', default=os.getenv('HA_TOKEN')) parser.add_argument('--domain', help='Filter by domain (e.g., light, switch)') parser.add_argument('--format', choices=['tree', 'json', 'list'], default='tree', help='Output format') args = parser.parse_args() if not args.url or not args.token: print("Error: URL and token are required", file=sys.stderr) sys.exit(1) services = get_services(args.url, args.token) # Filter by domain if specified if args.domain: services = {args.domain: services.get(args.domain, {})} if args.format == 'json': print(json.dumps(services, indent=2)) elif args.format == 'list': for domain, domain_services in sorted(services.items()): for service in sorted(domain_services.keys()): print(f"{domain}.{service}") else: # tree format for domain, domain_services in sorted(services.items()): print(f"\n{domain}:") for service, details in sorted(domain_services.items()): description = details.get('description', 'No description') print(f" • {service}") if description and description != 'No description': print(f" {description}") # Show fields if available if 'fields' in details and details['fields']: print(f" Fields:") for field_name, field_info in details['fields'].items(): field_desc = field_info.get('description', '') print(f" - {field_name}: {field_desc}") print(f"\nTotal: {sum(len(s) for s in services.values())} services across {len(services)} domains") if __name__ == '__main__': main()