06-sep: [domotica] Mesillas Meross (comando + submenu)
This commit is contained in:
@@ -118,6 +118,7 @@ def teclado_domotica():
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
_btn("🖥️ Pantalla", PREFIJO + "cat:pantalla"),
|
_btn("🖥️ Pantalla", PREFIJO + "cat:pantalla"),
|
||||||
|
_btn("🛏️ Mesillas", PREFIJO + "cat:mesillas"),
|
||||||
],
|
],
|
||||||
FILA_VOLVER,
|
FILA_VOLVER,
|
||||||
])
|
])
|
||||||
@@ -181,6 +182,21 @@ def teclado_pantalla():
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def teclado_mesillas():
|
||||||
|
return _mk([
|
||||||
|
[_btn("📊 Estado mesillas", PREFIJO + "acc:mesilla:estado")],
|
||||||
|
[
|
||||||
|
_btn("🛏️ Juanjo ON", PREFIJO + "acc:mesilla:juanjo:on"),
|
||||||
|
_btn("🛏️ Juanjo OFF", PREFIJO + "acc:mesilla:juanjo:off"),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
_btn("🛏️ Nahid ON", PREFIJO + "acc:mesilla:nahid:on"),
|
||||||
|
_btn("🛏️ Nahid OFF", PREFIJO + "acc:mesilla:nahid:off"),
|
||||||
|
],
|
||||||
|
[_btn("⬅️ Domótica", PREFIJO + "cat:domotica")],
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
def teclado_tv():
|
def teclado_tv():
|
||||||
return _mk([
|
return _mk([
|
||||||
[
|
[
|
||||||
@@ -424,6 +440,7 @@ def _header(categoria):
|
|||||||
"potencia": "⚡ <b>Potencia aspiradora</b>",
|
"potencia": "⚡ <b>Potencia aspiradora</b>",
|
||||||
"termo": "🔌 <b>Termo</b>",
|
"termo": "🔌 <b>Termo</b>",
|
||||||
"pantalla": "🖥️ <b>Pantalla</b>",
|
"pantalla": "🖥️ <b>Pantalla</b>",
|
||||||
|
"mesillas": "🛏️ <b>Mesillas</b>",
|
||||||
"sistema": "🛠️ <b>Sistema</b>",
|
"sistema": "🛠️ <b>Sistema</b>",
|
||||||
"router": "📡 <b>Router ASUS</b>",
|
"router": "📡 <b>Router ASUS</b>",
|
||||||
}
|
}
|
||||||
@@ -469,6 +486,7 @@ def _teclado_categoria(chat_id, categoria):
|
|||||||
"potencia": teclado_potencia,
|
"potencia": teclado_potencia,
|
||||||
"termo": teclado_termo,
|
"termo": teclado_termo,
|
||||||
"pantalla": teclado_pantalla,
|
"pantalla": teclado_pantalla,
|
||||||
|
"mesillas": teclado_mesillas,
|
||||||
"sistema": teclado_sistema,
|
"sistema": teclado_sistema,
|
||||||
"router": teclado_router,
|
"router": teclado_router,
|
||||||
}
|
}
|
||||||
@@ -685,6 +703,17 @@ def _ejecutar_accion(chat_id, message_id, partes):
|
|||||||
"bloquear/desbloquear:",
|
"bloquear/desbloquear:",
|
||||||
teclado_dispositivos(lista))
|
teclado_dispositivos(lista))
|
||||||
return
|
return
|
||||||
|
if accion == "mesilla":
|
||||||
|
# sub: estado | nombre | nombre:accion
|
||||||
|
rest = ":".join(partes[1:])
|
||||||
|
trozos = rest.split(":")
|
||||||
|
if len(trozos) == 1 and trozos[0] == "estado":
|
||||||
|
_reabrir(chat_id, message_id, "mesillas", "meross estado")
|
||||||
|
elif len(trozos) == 2 and trozos[1] in ("on", "off"):
|
||||||
|
pi_bot.procesar(f"meross {trozos[0]} {trozos[1]}", chat_id)
|
||||||
|
_cerrar_menu(chat_id, message_id)
|
||||||
|
_fin_accion(chat_id, "mesillas")
|
||||||
|
return
|
||||||
if accion == "tablets":
|
if accion == "tablets":
|
||||||
if sub in ("bloquear", "desbloquear"):
|
if sub in ("bloquear", "desbloquear"):
|
||||||
verbo = "desactivar" if sub == "bloquear" else "activar"
|
verbo = "desactivar" if sub == "bloquear" else "activar"
|
||||||
|
|||||||
114
vigilancia/meross_control.py
Normal file
114
vigilancia/meross_control.py
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
"""Enchufes Meross (mesillas) via cuenta Meross en la nube.
|
||||||
|
|
||||||
|
Lee MEROSS_EMAIL/PASSWORD del .env. Solo lectura salvo on/off explicito.
|
||||||
|
Uso: python meross_control.py estado|on|off [nombre]
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
|
|
||||||
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
if BASE not in sys.path:
|
||||||
|
sys.path.insert(0, BASE)
|
||||||
|
|
||||||
|
|
||||||
|
def _env(nombre):
|
||||||
|
valor = os.getenv(nombre, "").strip()
|
||||||
|
if not valor:
|
||||||
|
try:
|
||||||
|
with open(os.path.join(BASE, ".env")) as f:
|
||||||
|
for linea in f:
|
||||||
|
linea = linea.strip()
|
||||||
|
if linea.startswith(nombre + "="):
|
||||||
|
valor = linea.split("=", 1)[1].strip()
|
||||||
|
break
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return valor
|
||||||
|
|
||||||
|
|
||||||
|
async def _dispositivos():
|
||||||
|
from meross_iot.http_api import MerossHttpClient
|
||||||
|
from meross_iot.manager import MerossManager
|
||||||
|
http = await MerossHttpClient.async_from_user_password(
|
||||||
|
api_base_url="https://iotx-eu.meross.com",
|
||||||
|
email=_env("MEROSS_EMAIL"), password=_env("MEROSS_PASSWORD"),
|
||||||
|
ssl_context=ssl.create_default_context())
|
||||||
|
mgr = MerossManager(http_client=http)
|
||||||
|
await mgr.async_init()
|
||||||
|
await mgr.async_device_discovery()
|
||||||
|
return mgr, http
|
||||||
|
|
||||||
|
|
||||||
|
def _estado_dev(d):
|
||||||
|
try:
|
||||||
|
v = getattr(d, "is_on", None)
|
||||||
|
if callable(v):
|
||||||
|
v = v()
|
||||||
|
if isinstance(v, bool):
|
||||||
|
return v
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def _actuar(nombre, accion):
|
||||||
|
mgr, http = await _dispositivos()
|
||||||
|
try:
|
||||||
|
objetivo = (nombre or "").lower()
|
||||||
|
elegidos = [d for d in mgr.find_devices()
|
||||||
|
if not objetivo or objetivo in (d.name or "").lower()]
|
||||||
|
if not elegidos:
|
||||||
|
return [f"❌ No encuentro '{nombre}' (solo mesillas)."]
|
||||||
|
lineas = []
|
||||||
|
for d in elegidos:
|
||||||
|
try:
|
||||||
|
await d.async_update()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for d in elegidos:
|
||||||
|
if accion == "on":
|
||||||
|
try:
|
||||||
|
await d.async_turn_on()
|
||||||
|
except Exception as e:
|
||||||
|
lineas.append(f"❌ {d.name}: {str(e)[:100]}")
|
||||||
|
continue
|
||||||
|
elif accion == "off":
|
||||||
|
try:
|
||||||
|
await d.async_turn_off()
|
||||||
|
except Exception as e:
|
||||||
|
lineas.append(f"❌ {d.name}: {str(e)[:100]}")
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
await d.async_update()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
on = _estado_dev(d)
|
||||||
|
txt = "encendido" if on else ("apagado" if on is False else "?")
|
||||||
|
lineas.append(f"🛏️ {d.name}: <b>{txt}</b>")
|
||||||
|
return lineas
|
||||||
|
finally:
|
||||||
|
mgr.close()
|
||||||
|
await http.async_logout()
|
||||||
|
|
||||||
|
|
||||||
|
def controlar(nombre, accion):
|
||||||
|
"""Devuelve lista de lineas HTML con el resultado."""
|
||||||
|
try:
|
||||||
|
return asyncio.run(_actuar(nombre or "", accion or "estado"))
|
||||||
|
except Exception as e:
|
||||||
|
return [f"❌ Error Meross: {str(e)[:150]}"]
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
if len(argv) < 2 or argv[1] not in ("estado", "on", "off"):
|
||||||
|
print("Uso: meross_control.py estado|on|off [nombre]")
|
||||||
|
return 1
|
||||||
|
for linea in controlar(argv[2] if len(argv) > 2 else "", argv[1]):
|
||||||
|
print(linea.replace("<b>", "").replace("</b>", ""))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv))
|
||||||
@@ -530,6 +530,25 @@ def procesar(texto, chat_id):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f"{comando} error: {e}")
|
log(f"{comando} error: {e}")
|
||||||
enviar(f"❌ Error controlando {comando}: {e}", chat_id)
|
enviar(f"❌ Error controlando {comando}: {e}", chat_id)
|
||||||
|
elif comando == "meross":
|
||||||
|
try:
|
||||||
|
from meross_control import controlar as meross_controlar
|
||||||
|
tok = argumento.lower().strip().split()
|
||||||
|
if not tok:
|
||||||
|
nombre, accion = "", "estado"
|
||||||
|
elif tok[0] in ("estado", "on", "off") and len(tok) == 1:
|
||||||
|
nombre, accion = "", tok[0]
|
||||||
|
else:
|
||||||
|
nombre = tok[0]
|
||||||
|
accion = tok[1] if len(tok) > 1 else "estado"
|
||||||
|
if accion not in ("estado", "on", "off"):
|
||||||
|
enviar("Uso: <code>/meross [juanjo|nahid] estado|on|off</code>.", chat_id)
|
||||||
|
else:
|
||||||
|
for linea in meross_controlar(nombre, accion):
|
||||||
|
enviar(linea, chat_id)
|
||||||
|
except Exception as e:
|
||||||
|
log(f"meross error: {e}")
|
||||||
|
enviar(f"❌ Error Meross: {e}", chat_id)
|
||||||
elif comando == "router":
|
elif comando == "router":
|
||||||
partes_router = argumento.lower().split()
|
partes_router = argumento.lower().split()
|
||||||
accion_router = partes_router[0] if partes_router else "ayuda"
|
accion_router = partes_router[0] if partes_router else "ayuda"
|
||||||
@@ -811,7 +830,9 @@ def procesar(texto, chat_id):
|
|||||||
"<b>10.</b> <code>termo on|off|estado</code> — controla el enchufe Tapo\n"
|
"<b>10.</b> <code>termo on|off|estado</code> — controla el enchufe Tapo\n"
|
||||||
" ✏️ Ej: <code>/termo estado</code>\n"
|
" ✏️ Ej: <code>/termo estado</code>\n"
|
||||||
"<b>10c.</b> <code>pantalla on|off|estado</code> — enchufe de la pantalla\n"
|
"<b>10c.</b> <code>pantalla on|off|estado</code> — enchufe de la pantalla\n"
|
||||||
" ✏️ Ej: <code>/pantalla estado</code>\n\n"
|
" ✏️ Ej: <code>/pantalla estado</code>\n"
|
||||||
|
"<b>10d.</b> <code>meross [juanjo|nahid] estado|on|off</code> — enchufes mesillas\n"
|
||||||
|
" ✏️ Ej: <code>/meross estado</code>\n\n"
|
||||||
"━━━ 🏠 DOMÓTICA ━━━\n"
|
"━━━ 🏠 DOMÓTICA ━━━\n"
|
||||||
"<b>10b.</b> <code>aspiradora ...</code> — Dreame D10 Plus\n"
|
"<b>10b.</b> <code>aspiradora ...</code> — Dreame D10 Plus\n"
|
||||||
" ✏️ Ej: <code>/aspiradora estado</code>, <code>/aspiradora vaciar</code>, <code>/aspiradora stats</code>, <code>/aspiradora potencia 3</code>, <code>/aspiradora agua 2</code>, <code>/aspiradora voz</code>\n\n"
|
" ✏️ Ej: <code>/aspiradora estado</code>, <code>/aspiradora vaciar</code>, <code>/aspiradora stats</code>, <code>/aspiradora potencia 3</code>, <code>/aspiradora agua 2</code>, <code>/aspiradora voz</code>\n\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user