{"id":50640189,"url":"https://github.com/vakovalskii/midea-local-web","last_synced_at":"2026-06-07T07:30:34.521Z","repository":{"id":362565714,"uuid":"1259733784","full_name":"vakovalskii/midea-local-web","owner":"vakovalskii","description":"Локальный веб-пульт Wi-Fi кондиционеров MDV/Midea без облака: FastAPI + React + Docker. Self-hosted web remote, no Home Assistant required.","archived":false,"fork":false,"pushed_at":"2026-06-04T20:46:29.000Z","size":220,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T22:14:31.461Z","etag":null,"topics":["air-conditioner","docker","fastapi","iot","local-control","mdv","midea","react","self-hosted","smart-home"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vakovalskii.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-04T20:01:10.000Z","updated_at":"2026-06-04T21:48:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vakovalskii/midea-local-web","commit_stats":null,"previous_names":["vakovalskii/midea-local-web"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/vakovalskii/midea-local-web","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vakovalskii%2Fmidea-local-web","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vakovalskii%2Fmidea-local-web/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vakovalskii%2Fmidea-local-web/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vakovalskii%2Fmidea-local-web/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vakovalskii","download_url":"https://codeload.github.com/vakovalskii/midea-local-web/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vakovalskii%2Fmidea-local-web/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34013240,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-07T02:00:07.652Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["air-conditioner","docker","fastapi","iot","local-control","mdv","midea","react","self-hosted","smart-home"],"created_at":"2026-06-07T07:30:28.857Z","updated_at":"2026-06-07T07:30:34.514Z","avatar_url":"https://github.com/vakovalskii.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# midea-local-web\n\nВеб-управление Wi-Fi кондиционерами **MDV / Midea** (и совместимыми OEM-брендами)\n**по локальной сети, без облака и без родного приложения**. После однократного\nполучения `token`/`key` устройства серверы Midea больше не нужны.\n\nЭто **самостоятельный веб-пульт** (свой UI + REST API + Docker) — в отличие от\nсуществующих решений, которые почти все являются интеграциями для Home Assistant.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/screenshot.png\" alt=\"Веб-пульт midea-local-web\" width=\"320\"\u003e\n\u003c/p\u003e\n\n## Зачем это и в чём идея\n\nРодное приложение MDV завязано на облако Midea. Когда облако/аккаунт сбоит —\nкондей перестаёт управляться из приложения, хотя сам прекрасно работает в сети.\nЭтот репозиторий **полностью убирает облако из контура**: свой маленький сервер\nв локальной сети говорит с кондеем напрямую по его же протоколу, а красивый\nвеб-пульт открывается с любого телефона/компьютера. Бонусом — несколько\nкондеев в одном интерфейсе и возможность безопасно открыть доступ извне.\n\n## Как это работает (MDV = Midea)\n\nMDV — это OEM-бренд **Midea Group**, и Wi-Fi модуль («SmartKey») использует тот\nже протокол, что и Midea. Он давно разобран сообществом, мы опираемся на\nбиблиотеку [`msmart-ng`](https://github.com/mill1000/midea-msmart):\n\n- **Обнаружение** — UDP-броадкаст на порт `6445` в локальной сети.\n- **Управление** — зашифрованный TCP на порт `6444`.\n- Современные модули (**протокол V3**) требуют пару `token` + `key` на каждое\n  устройство. Её один раз получают через облако NetHome Plus (`msmart-ng` возит\n  с собой публичные сервисные аккаунты, личный логин обычно не нужен), и дальше\n  всё работает **локально без интернета**.\n\n`token`/`key` — это и есть «ключ к протоколу». Скрипт `provision.py` достаёт их\nавтоматически и кладёт в `backend/devices.json`.\n\n```\n[браузер] ──HTTPS+токен──▶ reverse-proxy ──▶ FastAPI (:8000) ──LAN TCP:6444──▶ кондей\n                                              + раздаёт React-UI с того же порта\n```\n\n- **Backend** — FastAPI (`backend/server.py`): REST API + раздача собранного фронта.\n- **Frontend** — React/Vite (`web/`): пульт с выбором устройства.\n- **Docker** — один образ (multi-stage: сборка фронта + рантайм бэкенда).\n\n\u003e ⚠️ **Важно про архитектуру.** Сервер общается с кондеем по локальной сети\n\u003e (TCP 6444 + UDP-discovery). Поэтому он должен стоять **в той же LAN, что и\n\u003e кондиционер**. Несколько точек = по серверу в каждой точке (или VPN-мост).\n\n## Как найти свой кондей (если он MDV)\n\n1. Кондей должен быть подключён к Wi-Fi (через родное приложение или режим\n   точки доступа) и находиться в той же сети, что и сервер.\n2. Запустите обнаружение:\n\n   ```bash\n   # в Docker:\n   docker compose run --rm app python -m msmart.cli discover\n   # или локально из venv:\n   .venv/bin/msmart-ng discover\n   ```\n\n   Вы увидите `ip`, `id`, `version` (обычно V3) и `type` (`0xac` = кондиционер).\n3. Если знаете IP — сразу заводите устройство скриптом (см. ниже), он сам всё\n   обнаружит и получит ключи:\n\n   ```bash\n   docker compose run --rm app python provision.py --ip 192.168.X.Y --name \"Дом\"\n   ```\n\n\u003e Не нашёлся? Проверьте: тот же сегмент сети (не гостевой Wi-Fi), кондей включён\n\u003e в Wi-Fi, на сервере есть исходящий интернет для получения `token`/`key`.\n\n---\n\n## Быстрый старт (Docker, рекомендуется)\n\nЦелевая машина: любой Linux-хост в одной сети с кондеем (мини-ПК, Raspberry Pi, NAS).\n\n```bash\ngit clone \u003crepo\u003e \u0026\u0026 cd lan-condi\n\n# 1. токен доступа к API\ncp .env.example .env\nsed -i \"s/replace-with-long-random-token/$(openssl rand -hex 32)/\" .env\n\n# 2. список устройств: получаем token/key по IP кондея\ncp backend/devices.example.json backend/devices.json   # или пустой []\ndocker compose run --rm app python provision.py --ip 192.168.X.Y --name \"Дом\"\n\n# 3. запуск\ndocker compose up -d --build\n```\n\nОткрыть `http://\u003cip-сервера\u003e:8000`, ввести токен из `.env` — готово.\n\n---\n\n## Добавить ещё один кондиционер (по IP)\n\nНа сервере **той точки**, где стоит кондей (он должен быть в той же сети):\n\n```bash\ndocker compose run --rm app python provision.py --ip 10.0.0.50 --name \"Офис\"\ndocker compose restart app\n```\n\n`provision.py` найдёт устройство, вытащит `token`/`key` из облака NetHome Plus,\nпроверит их реальной LAN-аутентификацией и допишет в `backend/devices.json`.\nВ интерфейсе появится переключатель устройств.\n\n\u003e Если кондей на удалённом статик-IP — запускайте `provision.py` именно на сервере\n\u003e в той сети, а не у себя. Управлять им потом можно через тот же удалённый сервер\n\u003e (см. «Публикация в интернет»).\n\n---\n\n## Публикация в интернет (статический IP)\n\nНикогда не выставляйте голый HTTP наружу — токен утечёт. Минимум — TLS + токен.\n\n**Вариант A. Свой домен + Caddy (авто-HTTPS):**\n1. A-запись `ac.example.com → ваш-статик-IP`.\n2. `cp Caddyfile.example Caddyfile`, поправьте домен.\n3. Запустите Caddy (нативно или контейнером `caddy`), он проксирует на `127.0.0.1:8000`.\n4. На роутере пробросьте 80/443 на сервер. Приложение наружу **не** публикуйте.\n\n**Вариант B. Без проброса портов — Cloudflare Tunnel / Tailscale:**\nтуннель до `127.0.0.1:8000`, статический IP и открытые порты вообще не нужны.\nСамый безопасный путь.\n\nВ обоих случаях `AC_API_TOKEN` обязателен — фронт спросит его при входе.\n\n---\n\n## Локальная разработка (без Docker)\n\n```bash\n# backend\npython3 -m venv .venv \u0026\u0026 .venv/bin/pip install -r backend/requirements.txt\nAC_DEVICES_FILE=backend/devices.json .venv/bin/uvicorn server:app \\\n    --app-dir backend --host 0.0.0.0 --port 8000\n\n# frontend (отдельный терминал) — dev-сервер с проксированием /api на :8000\ncd web \u0026\u0026 npm install \u0026\u0026 npm run dev      # http://localhost:5180\n```\n\n---\n\n## Переменные окружения\n\n| Переменная         | Назначение                                         | По умолчанию          |\n|--------------------|----------------------------------------------------|-----------------------|\n| `AC_API_TOKEN`     | токен для `/api/*`. Пусто = без авторизации        | —                     |\n| `AC_DEVICES_FILE`  | путь к списку устройств                            | `backend/devices.json`|\n| `AC_STATIC_DIR`    | каталог собранного фронта                          | `backend/static`      |\n\n## API\n\n| Метод | Путь                          | Назначение                    |\n|-------|-------------------------------|-------------------------------|\n| GET   | `/api/devices`                | список устройств              |\n| GET   | `/api/devices/{id}/state`     | текущее состояние             |\n| POST  | `/api/devices/{id}/set`       | команда (см. поля ниже)       |\n| GET   | `/api/health`                 | проверка живости (без токена) |\n\nТело `POST /set` (любые поля опциональны):\n`power` (bool), `mode` (COOL/HEAT/AUTO/DRY/FAN_ONLY), `target` (°C),\n`fan` (AUTO/SILENT/LOW/MEDIUM/HIGH/MAX), `turbo` (bool),\n`swing` (OFF/VERTICAL/HORIZONTAL/BOTH), `display` (любое значение = переключить LED).\n\n```bash\ncurl -H \"Authorization: Bearer $AC_API_TOKEN\" \\\n     -H 'content-type: application/json' \\\n     -d '{\"power\":true,\"mode\":\"COOL\",\"target\":22}' \\\n     http://localhost:8000/api/devices/\u003cid\u003e/set\n```\n\n## Безопасность\n\n- `backend/devices.json` и `.env` — **секреты**, в `.gitignore`, не коммитить.\n- Наружу — только через TLS-прокси/туннель, всегда с `AC_API_TOKEN`.\n- Приложение в host-сети слушает порт хоста — закройте 8000 фаерволом, если\n  снаружи ходите только через прокси.\n\n## Заметки по железу\n\n- Управление направлением лопастей **по секторам** (фикс. углы) этот блок\n  анонсирует в таблице возможностей, но команду отвергает (`0x11`). Работает\n  только режим качания (`swing`: OFF/Вверх-вниз/Лево-право/Обе).\n- Состояние LED-подсветки блок не отдаёт — поэтому кнопка «переключить».\n\n---\n\n## Мультисайт: несколько точек за NAT (агент + центр)\n\nКогда кондеи в **разных** сетях (офис, дом) за NAT, один центральный сервер\nне может дотянуться внутрь чужой LAN. Решение — **агент сам открывает\nисходящее WSS-соединение в центр** (как Cloudflare Tunnel / ngrok), проброс\nпортов в точках не нужен.\n\n```\n[браузер] ──HTTPS/Bearer──▶ ЦЕНТР (публичный, TLS)\n                               ▲ WSS (агент держит исходящий сокет)\n                  ┌────────────┴────────────┐\n            [агент-office]             [агент-home]\n            в LAN кондеев              в LAN кондеев\n            └ TCP 6444 → кондеи        └ TCP 6444 → кондеи\n```\n\n- `backend/agent.py` — агент: локальный discovery/контроль + исходящий WSS в центр.\n- `center/server.py` — центр: реестр агентов, маршрутизация команд (RPC по `req_id`),\n  браузерный REST + **realtime по WSS** (`/api/ws`), раздача UI.\n- В пульте кондеи **группируются по точкам** (Офис/Дом), состояние обновляется\n  вживую без поллинга.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/multisite.png\" alt=\"Мультисайт: группировка по точкам\" width=\"300\"\u003e\n\u003c/p\u003e\n\n**Поднять центр** (на публичном хосте с TLS):\n```bash\nAC_API_TOKEN=\u003cбраузерный\u003e AGENT_TOKEN=\u003cсекрет агентов\u003e \\\n  docker compose -f docker-compose.multisite.yml up -d --build\n```\n\n**Добавить точку** — поднять агент в её сети (он сам зарегается в центре):\n```bash\ncp backend/devices.example.json backend/devices.json\ndocker compose -f docker-compose.agent.yml run --rm --entrypoint \"\" agent \\\n  python provision.py --ip \u003cIP_кондея\u003e --name \"\u003cИмя\u003e\"\necho \"AGENT_TOKEN=\u003cсекрет агентов\u003e\" \u003e .env\nAGENT_ID=home CENTER_WS_URL=wss://\u003cдомен-центра\u003e/agent/ws \\\n  docker compose -f docker-compose.agent.yml up -d --build\n```\n\nENV центра: `AC_API_TOKEN` (браузер), `AGENT_TOKEN` (агент↔центр),\n`POLL_INTERVAL` (период realtime-опроса, сек). ENV агента: `CENTER_WS_URL`,\n`AGENT_ID`, `AGENT_TOKEN`.\n\n---\n\n## Альтернативы\n\nБольшинство существующих проектов — это интеграции для Home Assistant или\nбиблиотеки, а не самостоятельный веб-пульт:\n\n- [midea_ac_lan](https://github.com/wuwentao/midea_ac_lan) — интеграция для Home Assistant.\n- [midea-ac-py](https://github.com/mill1000/midea-ac-py) — интеграция для Home Assistant.\n- [midea-beautiful-air](https://github.com/nbogojevic/midea-beautiful-air) — Python-библиотека.\n- [node-mideahvac](https://github.com/reneklootwijk/node-mideahvac) — Node.js + лучшая документация по протоколу.\n\nБерите этот проект, если нужен **готовый веб-интерфейс под Docker без Home Assistant**.\n\n## Благодарности\n\nЛокальный протокол реализован поверх [`msmart-ng`](https://github.com/mill1000/midea-msmart)\n(mill1000) — спасибо сообществу за реверс протокола Midea.\n\n## Лицензия\n\n[MIT](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvakovalskii%2Fmidea-local-web","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvakovalskii%2Fmidea-local-web","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvakovalskii%2Fmidea-local-web/lists"}