Hermes Shelly-MCP: Plug MG3 .50 + shelly_switch
Neues Geraet plug_50 (192.168.178.50, Gen3) und Tool shelly_switch (on/off/toggle) fuer Gen2/3 Plugs; Status liest switch:0 inkl. Leistung.
This commit is contained in:
parent
b8d1cda7be
commit
3ea427e3f0
1 changed files with 92 additions and 12 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
"""Shelly MCP — Status und Steuerung der Muldenstein-Shellys per LAN-HTTP.
|
"""Shelly MCP — Status und Steuerung der Muldenstein-Shellys per LAN-HTTP.
|
||||||
|
|
||||||
Inventar fest verdrahtet (CT999 projekte/shelly-inventar.md).
|
Inventar fest verdrahtet (CT999 projekte/shelly-inventar.md).
|
||||||
|
Kinds: cover | pulse | sensor | switch (Gen2/3 Plug).
|
||||||
Sicherheit:
|
Sicherheit:
|
||||||
- Rollo Bad unten (.96): Bewegung blockiert (Motor defekt)
|
- Rollo Bad unten (.96): Bewegung blockiert (Motor defekt)
|
||||||
- Garagentor / Haustuer-Impuls: nur mit confirm=true, nie in Cron
|
- Garagentor / Haustuer-Impuls: nur mit confirm=true, nie in Cron
|
||||||
|
|
@ -81,6 +82,13 @@ DEVICES: dict[str, dict] = {
|
||||||
"kind": "sensor",
|
"kind": "sensor",
|
||||||
"aliases": ["garage_decke_b", "decke_b", "garage-b"],
|
"aliases": ["garage_decke_b", "decke_b", "garage-b"],
|
||||||
},
|
},
|
||||||
|
"plug_50": {
|
||||||
|
"ip": "192.168.178.50",
|
||||||
|
"name": "Shelly Plug MG3",
|
||||||
|
"gen": 3,
|
||||||
|
"kind": "switch",
|
||||||
|
"aliases": ["plug_50", "shelly_50", "plug50", "steckdose_50", "mg3"],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
# flatten alias lookup
|
# flatten alias lookup
|
||||||
|
|
@ -168,7 +176,7 @@ def tool_list(_args: dict) -> str:
|
||||||
)
|
)
|
||||||
lines.append(f" Aliase: {', '.join(d.get('aliases', [key]))}")
|
lines.append(f" Aliase: {', '.join(d.get('aliases', [key]))}")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("Tools: shelly_list, shelly_status, shelly_cover, shelly_pulse")
|
lines.append("Tools: shelly_list, shelly_status, shelly_cover, shelly_pulse, shelly_switch")
|
||||||
lines.append(
|
lines.append(
|
||||||
"Cron: Rollos OK; Tor/Tuersummer verboten; Bad unten nie bewegen."
|
"Cron: Rollos OK; Tor/Tuersummer verboten; Bad unten nie bewegen."
|
||||||
)
|
)
|
||||||
|
|
@ -230,6 +238,7 @@ def _status_gen2(d: dict) -> dict:
|
||||||
if not isinstance(st, dict):
|
if not isinstance(st, dict):
|
||||||
out["error"] = str(st)
|
out["error"] = str(st)
|
||||||
return out
|
return out
|
||||||
|
if d["kind"] == "cover":
|
||||||
cover = st.get("cover:0") or {}
|
cover = st.get("cover:0") or {}
|
||||||
out["cover"] = {
|
out["cover"] = {
|
||||||
"state": cover.get("state"),
|
"state": cover.get("state"),
|
||||||
|
|
@ -238,6 +247,17 @@ def _status_gen2(d: dict) -> dict:
|
||||||
"voltage": cover.get("voltage"),
|
"voltage": cover.get("voltage"),
|
||||||
"source": cover.get("source"),
|
"source": cover.get("source"),
|
||||||
}
|
}
|
||||||
|
if d["kind"] == "switch":
|
||||||
|
sw = st.get("switch:0") or {}
|
||||||
|
out["switch"] = {
|
||||||
|
"output": sw.get("output"),
|
||||||
|
"apower": sw.get("apower"),
|
||||||
|
"voltage": sw.get("voltage"),
|
||||||
|
"current": sw.get("current"),
|
||||||
|
"aenergy_total": (sw.get("aenergy") or {}).get("total"),
|
||||||
|
"temperature_c": (sw.get("temperature") or {}).get("tC"),
|
||||||
|
"source": sw.get("source"),
|
||||||
|
}
|
||||||
wifi = st.get("wifi") or {}
|
wifi = st.get("wifi") or {}
|
||||||
out["wifi_rssi"] = wifi.get("rssi")
|
out["wifi_rssi"] = wifi.get("rssi")
|
||||||
if d.get("motor_broken"):
|
if d.get("motor_broken"):
|
||||||
|
|
@ -251,7 +271,7 @@ def tool_status(args: dict) -> str:
|
||||||
return resolved
|
return resolved
|
||||||
key, d = resolved
|
key, d = resolved
|
||||||
try:
|
try:
|
||||||
data = _status_gen2(d) if d["gen"] == 2 else _status_gen1(d)
|
data = _status_gen2(d) if int(d.get("gen", 1)) >= 2 else _status_gen1(d)
|
||||||
data["key"] = key
|
data["key"] = key
|
||||||
return json.dumps(data, ensure_ascii=False, indent=2)
|
return json.dumps(data, ensure_ascii=False, indent=2)
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e: # noqa: BLE001
|
||||||
|
|
@ -327,7 +347,7 @@ def tool_cover(args: dict) -> str:
|
||||||
if not 0 <= pos <= 100:
|
if not 0 <= pos <= 100:
|
||||||
return "position muss 0-100 sein."
|
return "position muss 0-100 sein."
|
||||||
try:
|
try:
|
||||||
if d["gen"] == 2:
|
if int(d.get("gen", 1)) >= 2:
|
||||||
return _cover_gen2(d["ip"], action, pos)
|
return _cover_gen2(d["ip"], action, pos)
|
||||||
return _cover_gen1_roller(d["ip"], action, pos)
|
return _cover_gen1_roller(d["ip"], action, pos)
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e: # noqa: BLE001
|
||||||
|
|
@ -363,6 +383,44 @@ def tool_pulse(args: dict) -> str:
|
||||||
return f"Fehler shelly_pulse {d['name']}: {e}"
|
return f"Fehler shelly_pulse {d['name']}: {e}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def tool_switch(args: dict) -> str:
|
||||||
|
"""Gen2/3 Switch/Plug: on|off|toggle."""
|
||||||
|
resolved = _resolve(args.get("target") or args.get("device") or args.get("name"))
|
||||||
|
if isinstance(resolved, str):
|
||||||
|
return resolved
|
||||||
|
key, d = resolved
|
||||||
|
if d["kind"] != "switch":
|
||||||
|
return (
|
||||||
|
f"{d['name']} ist kein Schalter/Plug (kind={d['kind']}). "
|
||||||
|
"Fuer Rollos: shelly_cover. Fuer Impuls: shelly_pulse."
|
||||||
|
)
|
||||||
|
if int(d.get("gen", 1)) < 2:
|
||||||
|
return f"{d['name']}: Gen1-Switch noch nicht implementiert."
|
||||||
|
action = str(args.get("action") or "").strip().lower()
|
||||||
|
if action in ("an", "ein", "on"):
|
||||||
|
action = "on"
|
||||||
|
elif action in ("aus", "off"):
|
||||||
|
action = "off"
|
||||||
|
elif action in ("toggle", "umschalten", "schalten"):
|
||||||
|
action = "toggle"
|
||||||
|
if action not in ("on", "off", "toggle"):
|
||||||
|
return "action muss on|off|toggle sein."
|
||||||
|
ip = d["ip"]
|
||||||
|
try:
|
||||||
|
method = {"on": "Switch.Set", "off": "Switch.Set", "toggle": "Switch.Toggle"}[action]
|
||||||
|
params = {"id": 0}
|
||||||
|
if action in ("on", "off"):
|
||||||
|
params["on"] = action == "on"
|
||||||
|
res = _http_post_json(f"http://{ip}/rpc", {"id": 1, "method": method, "params": params})
|
||||||
|
return json.dumps(
|
||||||
|
{"ok": True, "device": d["name"], "ip": ip, "action": action, "result": res},
|
||||||
|
ensure_ascii=False,
|
||||||
|
)
|
||||||
|
except Exception as e: # noqa: BLE001
|
||||||
|
return f"Fehler shelly_switch {d['name']}: {e}"
|
||||||
|
|
||||||
|
|
||||||
TOOLS = [
|
TOOLS = [
|
||||||
{
|
{
|
||||||
"name": "shelly_list",
|
"name": "shelly_list",
|
||||||
|
|
@ -377,7 +435,7 @@ TOOLS = [
|
||||||
"description": (
|
"description": (
|
||||||
"Live-Status eines Shellys (Rollo-Position, Relay, Sensor-Temps). "
|
"Live-Status eines Shellys (Rollo-Position, Relay, Sensor-Temps). "
|
||||||
"Ziel per Alias/IP/Name: kueche, computerraum, gaestezimmer, bad_unten, "
|
"Ziel per Alias/IP/Name: kueche, computerraum, gaestezimmer, bad_unten, "
|
||||||
"garagentor, haustuer, garage_decke_a/b."
|
"garagentor, haustuer, garage_decke_a/b, plug_50."
|
||||||
),
|
),
|
||||||
"inputSchema": {
|
"inputSchema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
@ -435,6 +493,27 @@ TOOLS = [
|
||||||
"required": ["target", "confirm"],
|
"required": ["target", "confirm"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "shelly_switch",
|
||||||
|
"description": (
|
||||||
|
"Schaltet einen Shelly-Plug/Switch (on|off|toggle). "
|
||||||
|
"Ziel z.B. plug_50 / 192.168.178.50 / Shelly Plug MG3."
|
||||||
|
),
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"target": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Alias, IP oder Name (z.B. plug_50)",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "on | off | toggle",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["target", "action"],
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
HANDLERS = {
|
HANDLERS = {
|
||||||
|
|
@ -442,6 +521,7 @@ HANDLERS = {
|
||||||
"shelly_status": tool_status,
|
"shelly_status": tool_status,
|
||||||
"shelly_cover": tool_cover,
|
"shelly_cover": tool_cover,
|
||||||
"shelly_pulse": tool_pulse,
|
"shelly_pulse": tool_pulse,
|
||||||
|
"shelly_switch": tool_switch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue