diff --git a/vigilancia/aspiradora.py b/vigilancia/aspiradora.py
index 43f090b..c9172b7 100644
--- a/vigilancia/aspiradora.py
+++ b/vigilancia/aspiradora.py
@@ -1,7 +1,7 @@
"""Aspiradora Dreame D10 Plus (dreame.vacuum.r2205) via python-miio.
-El modelo r2205 habla el spec MIoT de mc1808, asi que se le pasa el
-mapping de mc1808 explicitamente. Lee IP/token del .env del directorio.
+Usa el spec MIoT REAL del r2205 (NO el de mc1808: ahi la bateria esta en
+otro siid y por eso marcaba 2%). Lee IP/token del .env del directorio.
Uso: python aspiradora.py estado|start|stop|home|pause|locate
"""
import os
@@ -11,16 +11,33 @@ BASE = os.path.dirname(os.path.abspath(__file__))
if BASE not in sys.path:
sys.path.insert(0, BASE)
-ESTADOS = {
- 1: "limpiando",
- 2: "inactiva",
- 3: "pausada",
- 4: "error",
- 5: "volviendo a la base",
- 6: "cargando",
- 7: "actualizando",
+# Spec https://home.miot-spec.com/spec/dreame.vacuum.r2205
+MAP_R2205 = {
+ "battery_level": {"siid": 3, "piid": 1},
+ "charging_state": {"siid": 3, "piid": 2},
+ "device_fault": {"siid": 2, "piid": 2},
+ "device_status": {"siid": 2, "piid": 1},
+ "cleaning_mode": {"siid": 4, "piid": 4},
+ "cleaning_time": {"siid": 4, "piid": 2},
+ "cleaning_area": {"siid": 4, "piid": 3},
+ "volume": {"siid": 7, "piid": 1},
+ "start_clean": {"siid": 2, "aiid": 1},
+ "stop_clean": {"siid": 4, "aiid": 2},
+ "pause": {"siid": 2, "aiid": 2},
+ "home": {"siid": 3, "aiid": 1},
+ "locate": {"siid": 7, "aiid": 1},
+ "play_sound": {"siid": 7, "aiid": 2},
}
+ESTADOS = {
+ 1: "limpiando", 2: "inactiva", 3: "pausada", 4: "error",
+ 5: "volviendo a la base", 6: "cargando", 7: "fregando",
+ 11: "creando mapa", 12: "limpieza de zona", 13: "carga completa",
+ 14: "actualizando",
+}
+CARGAS = {1: "cargando", 2: "no cargando", 5: "volviendo a cargar"}
+MODOS = {0: "silencioso", 1: "estándar", 2: "medio", 3: "fuerte"}
+
def _env(nombre, defecto=""):
valor = os.getenv(nombre, defecto).strip()
@@ -48,20 +65,26 @@ def conectar():
token = _env("DREAME_TOKEN")
if not token:
raise RuntimeError("Falta DREAME_TOKEN en el .env")
- return DreameVacuum(ip, token, model="dreame.vacuum.r2205",
- mapping=MIOT_MAPPING["dreame.vacuum.mc1808"])
+ # El mapping se resuelve por modelo: se registra el r2205 (spec real).
+ MIOT_MAPPING.setdefault("dreame.vacuum.r2205", MAP_R2205)
+ return DreameVacuum(ip, token, model="dreame.vacuum.r2205")
def estado():
vac = conectar()
- st = vac.status()
- cod = st.device_status
+ d = vac.status().data
+ cod = d.get("device_status")
texto_estado = ESTADOS.get(cod, f"codigo {cod}")
+ carga = CARGAS.get(d.get("charging_state"),
+ f"codigo {d.get('charging_state')}")
+ modo = MODOS.get(d.get("cleaning_mode"), d.get("cleaning_mode"))
+ fallo = d.get("device_fault")
+ extra = "" if fallo in (0, None) else f"\n⚠️ Fallo: {fallo}"
return (f"🤖 Dreame D10 Plus: {texto_estado}\n"
- f"🔋 Bateria: {st.battery_level}% "
- f"({'cargando' if st.charging_state else 'no cargando'})\n"
- f"🧹 Modo: {st.cleaning_mode} | "
- f"Tiempo: {st.cleaning_time} | Area: {st.cleaning_area}")
+ f"🔋 Bateria: {d.get('battery_level')}% ({carga})\n"
+ f"🧹 Modo: {modo} | "
+ f"Tiempo: {d.get('cleaning_time')} min | "
+ f"Area: {d.get('cleaning_area')} m2{extra}")
def main(argv):
@@ -83,10 +106,10 @@ def main(argv):
vac.home()
print("Volviendo a la base.")
elif cmd == "pause":
- vac.pause()
+ vac.call_action("pause")
print("Pausada.")
elif cmd == "locate":
- vac.play_sound()
+ vac.call_action("play_sound")
print("Emitiendo sonido de localizacion.")
else:
print("Uso: aspiradora.py estado|start|stop|home|pause|locate")
diff --git a/vigilancia/menu_idd.py b/vigilancia/menu_idd.py
index d580d45..b58565f 100644
--- a/vigilancia/menu_idd.py
+++ b/vigilancia/menu_idd.py
@@ -295,6 +295,29 @@ def _fin_accion(chat_id):
_enviar_nuevo(chat_id, "✅ Hecho. Pulsa /menu para volver al menú.", None)
+def _reabrir(chat_id, message_id, categoria, comando):
+ """Para consultas (estado, listar): ejecuta, borra los mensajes viejos
+ dejando solo el ultimo resultado y muestra de nuevo el menu de la
+ categoria (con su tecla de volver operativa)."""
+ import pi_bot
+ pi_bot.procesar(comando, chat_id)
+ try:
+ hist = pi_bot.historial_chat.get(chat_id, []) or []
+ for mid in hist[:-1]:
+ _borrar(chat_id, mid)
+ pi_bot.historial_chat[chat_id] = hist[-1:] if hist else []
+ ent = pi_bot.entrantes_chat.get(chat_id, []) or []
+ for mid in ent:
+ _borrar(chat_id, mid)
+ pi_bot.entrantes_chat[chat_id] = []
+ except Exception:
+ pass
+ _borrar(chat_id, message_id)
+ teclado = _teclado_categoria(chat_id, categoria)
+ if teclado is not None:
+ _enviar_nuevo(chat_id, _header(categoria), teclado)
+
+
# ----------------------------------------------------------------- menu ----
def enviar_menu(chat_id):
@@ -318,8 +341,8 @@ def _header(categoria):
return nombres.get(categoria, "🧭 Menú IDD")
-def _abrir_categoria(chat_id, message_id, categoria):
- teclado = {
+def _teclado_categoria(chat_id, categoria):
+ teclados = {
"videoclub": lambda: teclado_videoclub(chat_id),
"nas": teclado_nas,
"tv": teclado_tv,
@@ -328,10 +351,15 @@ def _abrir_categoria(chat_id, message_id, categoria):
"sistema": teclado_sistema,
"router": teclado_router,
}
- fn = teclado.get(categoria)
- if not fn:
+ fn = teclados.get(categoria)
+ return fn() if fn else None
+
+
+def _abrir_categoria(chat_id, message_id, categoria):
+ teclado = _teclado_categoria(chat_id, categoria)
+ if teclado is None:
return False
- _enviar_edit(chat_id, message_id, _header(categoria), fn())
+ _enviar_edit(chat_id, message_id, _header(categoria), teclado)
return True
@@ -460,18 +488,26 @@ def _ejecutar_accion(chat_id, message_id, partes):
"qos": "qos estado",
"limite": "limite listar",
"seguridad": "seguridad estado",
- "parental_listar": "parental listar",
}
+ if accion == "parental_listar":
+ _reabrir(chat_id, message_id, "parental", "parental listar")
+ return
if accion in directos:
_disp(chat_id, message_id, directos[accion])
return
if accion == "termo":
- _disp(chat_id, message_id, f"termo {sub}")
+ if sub == "estado":
+ _reabrir(chat_id, message_id, "domotica", "termo estado")
+ elif sub in ("on", "off"):
+ _disp(chat_id, message_id, f"termo {sub}")
+ _fin_accion(chat_id)
return
if accion == "aspiradora":
if sub in ("estado", "start", "stop", "home", "pause", "locate"):
- _disp(chat_id, message_id, f"aspiradora {sub}")
- if sub != "estado":
+ if sub == "estado":
+ _reabrir(chat_id, message_id, "domotica", "aspiradora estado")
+ else:
+ _disp(chat_id, message_id, f"aspiradora {sub}")
_fin_accion(chat_id)
return
if accion == "tablets":