From 77f077854c5e59581c84b61dc9c9627c0227c360 Mon Sep 17 00:00:00 2001 From: juanjo Date: Sun, 6 Sep 2026 13:28:15 +0200 Subject: [PATCH] 06-sep: [aspiradora] modulo Dreame D10 Plus (r2205 map mc1808) + comando /aspiradora en bot IDD --- vigilancia/aspiradora.py | 101 +++++++++++++++++++++++++++++++++++++++ vigilancia/pi_bot.py | 41 ++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 vigilancia/aspiradora.py diff --git a/vigilancia/aspiradora.py b/vigilancia/aspiradora.py new file mode 100644 index 0000000..43f090b --- /dev/null +++ b/vigilancia/aspiradora.py @@ -0,0 +1,101 @@ +"""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. +Uso: python aspiradora.py estado|start|stop|home|pause|locate +""" +import os +import sys + +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", +} + + +def _env(nombre, defecto=""): + valor = os.getenv(nombre, defecto).strip() + if not valor: + try: + with open(os.path.join(BASE, ".env")) as f: + for linea in f: + linea = linea.strip() + if linea and not linea.startswith("#") and "=" in linea: + k, v = linea.split("=", 1) + if k.strip() == nombre: + valor = v.strip() + break + except OSError: + pass + return valor + + +def conectar(): + from miio.integrations.vacuum.dreame.dreamevacuum_miot import ( + MIOT_MAPPING, + DreameVacuum, + ) + ip = _env("DREAME_IP", "192.168.50.122") + 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"]) + + +def estado(): + vac = conectar() + st = vac.status() + cod = st.device_status + texto_estado = ESTADOS.get(cod, f"codigo {cod}") + 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}") + + +def main(argv): + if len(argv) < 2: + print("Uso: aspiradora.py estado|start|stop|home|pause|locate") + return 1 + cmd = argv[1].lower() + try: + vac = conectar() + if cmd == "estado": + print(estado().replace("", "").replace("", "")) + elif cmd == "start": + vac.start() + print("Limpieza iniciada.") + elif cmd == "stop": + vac.stop() + print("Limpieza detenida.") + elif cmd == "home": + vac.home() + print("Volviendo a la base.") + elif cmd == "pause": + vac.pause() + print("Pausada.") + elif cmd == "locate": + vac.play_sound() + print("Emitiendo sonido de localizacion.") + else: + print("Uso: aspiradora.py estado|start|stop|home|pause|locate") + return 1 + except Exception as e: + print(f"ERROR: {e}") + return 2 + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/vigilancia/pi_bot.py b/vigilancia/pi_bot.py index e33cd3e..39ee162 100644 --- a/vigilancia/pi_bot.py +++ b/vigilancia/pi_bot.py @@ -754,6 +754,8 @@ def procesar(texto, chat_id): elif t.startswith("bajar ") or t.startswith("/bajar "): n = _int_arg(texto) procesar_bajar(n, chat_id) + elif comando == "aspiradora": + procesar_aspiradora(argumento, chat_id) elif comando in ("menu", "menú", "inicio", "start") and not argumento: try: from menu_idd import enviar_menu @@ -1003,6 +1005,45 @@ def procesar_novedades(chat_id): log(f"NOVEDADES: {len(items)} items") +def procesar_aspiradora(argumento, chat_id): + """Comandos de la Dreame D10 Plus: estado|start|stop|home|pause|locate.""" + accion = (argumento or "estado").lower().strip() + if accion not in ("estado", "start", "stop", "home", "pause", "locate"): + enviar("Uso: /aspiradora estado|start|stop|home|pause|locate.", + chat_id) + return + try: + import aspiradora + except Exception as e: + log(f"aspiradora import error: {e}") + enviar("❌ No se pudo cargar el modulo aspiradora", chat_id) + return + try: + if accion == "estado": + enviar(aspiradora.estado(), chat_id) + else: + vac = aspiradora.conectar() + if accion == "start": + vac.start() + enviar("🧹 Limpieza iniciada.", chat_id) + elif accion == "stop": + vac.stop() + enviar("⏹️ Limpieza detenida.", chat_id) + elif accion == "home": + vac.home() + enviar("🏠 Volviendo a la base.", chat_id) + elif accion == "pause": + vac.pause() + enviar("⏸️ Pausada.", chat_id) + elif accion == "locate": + vac.play_sound() + enviar("🔊 Emitiendo sonido de localización.", chat_id) + log(f"ASPIRADORA {accion} ok") + except Exception as e: + log(f"aspiradora error: {e}") + enviar(f"❌ Error con la aspiradora: {e}", chat_id) + + def _int_arg(texto): """Extrae el numero de un comando tipo '/bajar 3'.""" partes = texto.split()