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":