#!/usr/bin/env python3 """ Push state/commands to a WLED device. Accepts a JSON file or simple flags (on, off, bri, preset). Uses only standard library (no pip install). """ import argparse import json import sys from typing import Optional from urllib.request import Request, urlopen from urllib.error import URLError, HTTPError DEFAULT_HOST = "192.168.240.30" def push(host: str, payload: dict, return_state: bool = False) -> Optional[dict]: base = host if host.startswith("http") else f"http://{host}" url = f"{base}/json/state" if return_state: payload["v"] = True body = json.dumps(payload).encode("utf-8") req = Request( url, data=body, method="POST", headers={"Content-Type": "application/json", "Accept": "application/json"}, ) try: with urlopen(req, timeout=10) as r: raw = r.read().decode() if not raw: return None return json.loads(raw) except HTTPError as e: print(f"HTTP error: {e.code} {e.reason}", file=sys.stderr) if e.fp: print(e.fp.read().decode(), file=sys.stderr) sys.exit(1) except URLError as e: print(f"Request failed: {e.reason}", file=sys.stderr) sys.exit(1) except json.JSONDecodeError as e: print(f"Invalid response JSON: {e}", file=sys.stderr) sys.exit(1) def main(): p = argparse.ArgumentParser(description="Push state/commands to WLED") p.add_argument("--host", "-H", default=DEFAULT_HOST, help=f"WLED host (default: {DEFAULT_HOST})") p.add_argument("--file", "-f", metavar="JSON", help="JSON file with state to send") p.add_argument("--on", action="store_true", help="Turn on") p.add_argument("--off", action="store_true", help="Turn off") p.add_argument("--toggle", "-t", action="store_true", dest="toggle", help="Toggle on/off") p.add_argument("--bri", type=int, metavar="0-255", help="Brightness") p.add_argument("--preset", "-p", type=int, metavar="ID", help="Load preset by ID") p.add_argument("--v", action="store_true", help="Return full state in response") args = p.parse_args() if args.file: with open(args.file) as f: payload = json.load(f) else: payload = {} if args.off: payload["on"] = False if args.on: payload["on"] = True if args.toggle: payload["on"] = "t" if args.bri is not None: payload["bri"] = max(0, min(255, args.bri)) if args.preset is not None: payload["ps"] = args.preset if args.v: payload["v"] = True if not payload: print("Nothing to send. Use --file, --on, --off, --toggle, --bri, or --preset.", file=sys.stderr) sys.exit(1) out = push(args.host, payload, return_state=args.v) if out: print(json.dumps(out, indent=2)) if __name__ == "__main__": main()