Четвертый год строю умный дом. Кажется, понял, что в нем самое главное. Не какое решение выбрать в качестве сервера, не какие технологии использовать, не каких производителей девайсы закупать — это все детали.
Главное — вовремя остановиться. И у меня это не получилось, судя по тому, что я потратил прорву времени на это.
Давно хотел сделать такую панель, но нормального гайда не нашел, пришлось придумывать собственный велосипед.
Зачем
Умный дом прекрасен и без тачпанели. Большая часть магии все равно под капотом и обходится без интерфейсов, чисто на автоматизациях. А такие команды как «включить свет» и «сделать теплее», удобно отдавать голосом через умную колонку. Или через телеграм-бот.
В общем, меня все устраивало, пока я не поставил датчики влажности почвы в цветочные горшки. Сообщение в телеграм «Полейте белый цветок на подоконнике!» пару раз ставило в тупик, так как и белых цветков и подоконников в доме немало. Я задумался, и тут все завертелось…
Идея в том, что тачпанель позволяет буквально на ходу, одним взглядом оценить обстановку в доме, запросить дополнительную информацию и отдать нужную команду просто ткнув пальцем. Новый уровень удобства.
Железо
Подробно расписывать, что такое умный дом, и как он устроен конкретно у меня, смысла не вижу, статей об этом полно. Мой основан на Home Assistant, просто потому, что в него можно интегрировать вообще все. У меня, например, 52 Zigbee- и 38 WiFi-устройств, 2 Modbus-шлюза для управления климатической техникой, IP-домофон, и шлюз LoRa для связи с GPS-трекером в машине. Все это от разных производителей, почти у каждого своя экосистема и свои заморочки. Для возведения таких вавилонов Home Assistant вне конкуренции.
Железо для тачпанели выбирал довольно долго. Поначалу рассматривал два очевидных варианта, от которых в итоге отказался: Android-планшет, и промышленный компьютер с тачскрином.
Казалось бы, планшет — идеальная железка для панели, вешай на стену и наслаждайся. Многие так и делают, а через полгода выясняется, что зря. От постоянного подключения к адаптеру питания аккумулятор планшета накрывается (а то и вздувается), и все это приходится нести в ремонт.
Кто-то пытается наколхозить обходное решение: например, ставит умную розетку, которая включает питание только когда аккумулятор разрядится до 10%. Или же вообще извлекает из планшета аккумулятор и подпаивает питание напрямую.
В любом случае этот путь мне не годился, потому что даже если все получится, диагональ панели будет очень маленькой.
У промышленных компов с Aliexpress другие проблемы: либо дорого (40+ тыс. руб.), либо непонятная надежность и никакая производительность. А то и все сразу.
В итоге я приобрел на Авито старый сенсорный монитор iiyama T2252MSC за 16 000 руб. (верный выбор, подсмотрел в одном телеграм-канале про умный дом) и микро-ПК Findarling T5B за 8 000 руб. (ошибка, тормозит, пришлось заменить на более современный).

Следующий вопрос — как и куда вешать эту панель. Когда я 8 лет назад начал строить дом, перерасход бюджета вынудил исключить из проекта не самые необходимые инженерные системы — в частности, под нож пошел проводной умный дом. В итоге никакой инфраструктуры под умный дом не заложено, приходится импровизировать.

Но мне повезло — в холле когда-то планировалось повесить монитор домофона, поэтому туда были выведены витая пара и питание. Оставалось только выдолбить перфоратором нишу для оборудования, и закрепить кронштейн для монитора.

Сначала пытался обойтись WiFi, но в итоге задействовал витую пару — толстая керамическая стена и монитор сильно мешают сигналу, связь иногда терялась, при том, что ближайшая точка доступа висит в 5 метрах.
Софт
На подключенном к тачпанели компе установлен Windows 10 с Chrome в режиме киоска и больше ничего. И все равно производительности древнего Intel Atom не хватило, слишком тяжелый фронтенд я наворотил. Выражается это в периодических фризах картинки с камер и в общей недостаточной отзывчивости интерфейса. В итоге поменял на чуть более громоздкий комп с Intel N97, с некоторым трудом удалось разместить его в той же нише.

Chrome с этого компа в режиме киоска ломится на сервер HA под специальным бесправным пользователем, и показывает доступные ему панели.

Самое трудоемкое — сконфигурировать панель поэтажного плана в Home Assistant. Код этой панели у меня занимает 20 тысяч строк. К счастью, не весь код пишется вручную, основную часть можно сгенерить специальным плагином.
Чтобы нарисовать поэтажный план, я поставил Sweet Home 3D, и наборы моделей к нему. Затем потратил несколько дней, чтобы научиться им пользоваться, и чтобы получилось что-то похожее на реальный дом.
Совет: чем больше на плане деталей, тем он лучше смотрится. Поэтому у меня, например, на плане второго этажа размещена модель стола для пинг-понга, хотя этот стол никак с умным домом не взаимодействует. Но он там есть и хорошо заполняет пустое пространство.

Когда план более-менее готов, ставим плагин home-assistant-floor-plan. Он нужен, чтобы сгенерить код панели Home Assistant. Экономит очень много времени (напоминаю, 20 тысяч строк!).
Теперь размещаем источники света. Те, которые должны быть интерактивными, называем по идентификаторам их сущностей в Home Assistant. Если на плане должно что-то исчезать и появляться — называем этот объект по соответствующему бинарному датчику или переключателю. Например, фигурка человека на унитазе привязана к датчику присутствия binary_sensor.prisutstvie_v_sanuzle_presence. А машина на парковке привязана к вспомогательной сущности input_boolean.car_home, значение которой ставится true в случае, если расстояние машины до дома меньше 20 метров.


Не забываем расставить и неинтерактивные источники света. Например, у меня в прихожей свет не контролируется умным домом. Но если на плане не поставить в прихожую источник света, то на рендере для ночного времени этой комнаты вообще не будет видно.
Совет: выберите и сохраните вид для окна предпросмотра (CTRL+ALT+R). Если вы потом будете вносить изменения в план (а вы будете), малейший сдвиг — и придется перерендерить все варианты плана. Поэтому перед каждым рендерингом восстанавливаем сохраненный вид.

Когда все готово, рендерим тестовую картинку. Только так вы сможете увидеть финальный вид вашего плана, с освещением и тенями. Вам точно придется делать это несколько раз, чтобы пофиксить все косяки и подобрать подходящий размер картинки в пикселях. Я в итоге остановился на 1458x1010, что для FHD-монитора хорошо подошло.

Если все ок, жмем Tools → Home Assistant Floor Plan и настраиваем плагин — конфигуратор панели.

Прокликиваем каждую сущность в списке Other enitities и настраиваем.

Display type: оставляем Icon
Display condition: условие отображения иконки сущности. Если нам нужна иконка для управления объектом, то Always, если не нужна, то Never. Ну либо When on/When Off, чтобы иконка была видна только когда соответствующая ей сущность была true либо false. Например, для ворот у меня иконка включена, так как через нее я их открываю и закрываю, а для присутствия в санузле — выключена, так как управлять там нечем. Если пофантазировать, можно на эту иконку привязать TTS через Яндекс.Станцию, чтобы она на 10й громкости предлагала освободить туалет, но сортирные шутки быстро надоедают.
Tap action: действие по нажатию. Toggle переключает сущность, More info показывает значение. More info актуально для датчиков и объектов с поп‑апами управления, а вот выключателям света, приводам ворот и т. д. ставим Toggle.
Double tap action и Hold action позволяют настроить реакцию на двойное и продолжительное нажатие. Пока не нашел этому применения.
Display furniture: отображение объекта на плане. Always если всегда, State is.. только когда его сущность в home assistant имеет указанное значение, State isn't — только когда сущность имеет любое другое значение. Например, у меня машина на плане отображается только когда значение input_boolean.car_home равняется true, аналогично с воротами и присутствием в санузле.
Ставим рендерер какой нравится (я разницы между ними не вижу), а качество High. Sensitivity мало на что влияет, я оставляю дефолтную 10.
В Render Times можно добавить для какого времени суток будут рендериться изображения. Дело в том, что плагин позволяет настроить так, чтобы картинка менялась с течением времени. Например, в 10:00 вы увидите на плане свой дом в утреннем освещении, в 15:00 в дневном, а в полночь — в темноте. Звучит круто, по факту же изменения малозаметны. Ну, тени движутся, это да. Но цена этого значительна: на каждое указанное время будет рендериться полный пакет изображений, а это долго.
Чтобы потом в вашей панели рендеры плана переключались по времени, надо создать в HA вспомогательный датчик sensor.time_as_number_utc, с шаблоном {{states("sensor.time_utc").split(":") | join | int}}
. Это позволяет установить смену картинки плана хоть каждые полчаса. Я поигрался этим, и в итоге сделал проще. После восхода у меня дневной вид, после заката — ночной.

Каждый источник освещения и каждый исчезающий объект удвоит вам количество рендеров. Если у вас только одна интерактивная лампа, и вы указали одно время дня, то плагин сделает две картинки: с включенной лампой и выключенной. Если две интерактивные лампы, картинки будет уже четыре, и т.д. У меня 448 рендеров первого этажа и 24 второго. Это при том, что я в итоге остановился всего на двух вариантах плана, на дневном и ночном. Вариант с почасовой сменой плана потребует в 12 раз больше рендеров.
Проблема тут в том, что Sweet Home 3D рендерит эти картинки очень долго. Использовать GPU он не умеет, и на моем i7-14700 эти 448 картинок рендерятся около 15 часов со 100% загрузкой CPU.
Все настроили, проходимся по чеклисту:
Все ли интерактивные источники света указаны в панели Detected Lights?
Есть ли ошибки в именах сущностей источников света?
Все ли исчезающие объекты указаны в панели Other entities?
Все ли исчезающие объекты сконфигурированы правильно?
Установлены ли размеры изображения?
Если все ок, нажимаем Start и идем спать.
У меня Sweet Home 3D при рендеринге иногда крашится, но, к счастью, после перезапуска продолжает с того места, где прервался (если стоит галочка Use existing renders).
Конфигурация панели поэтажного плана
Для корневого макета панели берем Sidebar, чтобы не занимать всю ширину монитора планом дома. На боковую панель выносим самые полезные данные, не имеющие привязки к конкретной локации в доме — в моем случае это напряжения и потребляемая мощность на фазах, управление вентиляцией и кондиционером, почасовой прогноз погоды и т.д.

Основную, левую часть макета занимает карточка Vertical Stack. Так как в доме два этажа, то в этом макете две карточки Conditional, содержимое которых показывается попеременно, в зависимости от значения переменной input_select.floorplan_floor. Внутри каждой Conditional-карточки сидит карточка Picture-Elements, которая, собственно, и содержит план дома и все элементы, которые на него накладываются.

Базовый код Picture-Elements генерит для нас упомянутый плагин. Все картинки с планами всех этажей, которые мы нарендерили в Sweet Home, мы складываем в \\homeassistant\config\www\floorplan
, а полученный код плана этажа из файла floorplan.yaml (который генерит плагин) копипастим в текстовый редактор панелей Home Assistant, в карточку Picture-Elements. Так мы получим интерактивный план этажа дома с управлением светом и исчезающими элементами.

Теперь нужно добавить всякое дополнительное: показания датчиков, управление термостатами, информационные иконки полива цветов и т.д.
Совет: редактируйте код панели в стороннем текстовом редакторе, и затем копипастите в редактор панели Home Assistant. Дело в том, что редактор панели перерисовывает превью панели при изменении любого символа, из-за этого все люто тормозит при попытке редактировать сложные панели.
К сожалению, все дополнительные элементы приходится выравнивать методом приближений — чуть поменяли расположение, сохранили панель, посмотрели, в каком месте теперь элемент, снова подправляем.
Распишу, какие элементы есть в моей панели.
Переключение этажа. Создаем вспомогательную сущность input_select.floorplan_floor с возможными значениями «Первый этаж», «Второй этаж» и т.д., прописываем отображение панели Conditional в зависимости от значения input_select.floorplan_floor.

На каждый этаж добавляем элемент переключения.
Скрытый текст
type: icon
icon: mdi:floor-plan
style:
left: 3%
top: 3%
tap_action:
action: perform-action
perform_action: input_select.select_next
target:
entity_id: input_select.floorplan_floor
data:
cycle: true
Показания датчиков. Самое простое — это элемент типа state-label, привязанный к объекту, чье значение вы хотите показать на плане.
Скрытый текст
type: state-label
entity: sensor.datchik_v_detskoi_temperature
style:
left: 52%
top: 16%
color: black
font-weight: bold
font-size: 16px
background: null

По нажатию можно посмотреть, как показания менялись во времени.

Если датчик питается от батареи, полезно рядом с его показаниями разместить индикатор низкого заряда, это элемент типа conditional, отображаемый при низком значении объекта батареи датчика.
Скрытый текст
type: conditional
conditions:
- condition: numeric_state
entity: sensor.datchik_v_detskoi_battery
below: 5
elements:
- type: icon
icon: mdi:battery-alert-variant-outline
style:
left: 54%
top: 15%
color: "#ff0000"
transform: translate(-50%, -50%)
scale: 60%
entity: sensor.datchik_v_detskoi_battery
Управление шторами. Простой элемент state-icon без изысков, так как его поп-ап достаточно функционален.
Скрытый текст
type: state-icon
entity: cover.shtora_v_kabinete_3
tap_action:
action: more-info
style:
left: 17%
top: 53%
border-radius: 50%
text-align: center
background: "#D8BFD8"
opacity: 80%
Тыкаем в иконку — всплывает поп-ап, где можно управлять шторой.

Иконка полива цветка. Три иконки (элемента) на самом деле — красная, желтая и зеленая, отображаются на одном и том же месте в зависимости от показаний датчика влажности почвы.
Скрытый текст
type: conditional
conditions:
- condition: numeric_state
entity: sensor.vlazhnost_tsvetka_na_bufete_soil_moisture
above: 49
elements:
- type: icon
icon: mdi:flower
entity: sensor.vlazhnost_tsvetka_na_bufete_soil_moisture
style:
top: 38%
left: 78%
color: green
background: "#D8BFD8"
border-radius: 5px
title: цветок на буфете полит
type: conditional
conditions:
- condition: numeric_state
entity: sensor.vlazhnost_tsvetka_na_bufete_soil_moisture
below: 50
- condition: numeric_state
entity: sensor.vlazhnost_tsvetka_na_bufete_soil_moisture
above: 16
elements:
- type: icon
icon: mdi:flower
entity: sensor.vlazhnost_tsvetka_na_bufete_soil_moisture
style:
top: 38%
left: 78%
color: yellow
background: "#D8BFD8"
border-radius: 5px
title: цветок на буфете надо полить
type: conditional
conditions:
- condition: numeric_state
entity: sensor.vlazhnost_tsvetka_na_bufete_soil_moisture
below: 16
elements:
- type: icon
icon: mdi:flower
entity: sensor.vlazhnost_tsvetka_na_bufete_soil_moisture
style:
top: 38%
left: 78%
color: red
background: "#D8BFD8"
border-radius: 5px
title: цветок на буфете сохнет
Термостаты. Вот тут можно поизвращаться. Дело в том, что у панели типа Picture-Elements набор разрешенных элементов очень убог, а обычную интерфейсную карточку для Home Assistant разместить на этой панели нельзя. При этом можно разместить любую кастомную карточку для Home Assistant — вроде тех, что устанавливаются через HACS. Идиотизм, но приходится действовать так: находим более-менее подходящую кастомную карточку, устанавливаем через HACS, и пожалуйста, добавляем через элемент типа custom. Например, мне пришлось устанавливать карточку Better Thermostat, несмотря на то что меня вполне устроила бы и стандартная карточка термостата.
Тут нужно два элемента: иконка для вызова карточки термостата (чтобы не загромождать план дома, если термостатов несколько, а показывать по нажатию), и сама карточка термостата типа custom, вложенная в conditional.
Скрытый текст
type: state-icon
style:
left: 22%
top: 63%
border-radius: 50%
border: 2px solid red
text-align: center
background-color: rgba(255, 255, 255, 0.3)
opacity: 100%
entity: input_boolean.koshkin_dom
state_color: true
tap_action:
action: toggle
Создаем вспомогательную сущность input_boolean.koshkin_dom, и если она true, то показываем элемент с карточкой термостата.
Скрытый текст
type: conditional
conditions:
- condition: state
entity: input_boolean.koshkin_dom
state: "on"
elements:
- type: custom:better-thermostat-ui-card
entity: climate.koshkin_dom
disable_window: false
disable_off: false
name: Кошкин дом
style:
left: 30%
top: 40%
width: 300px
border-radius: 10px
border: 2px solid black
text-align: center
opacity: 95%

Но можно так не извращаться и обойтись довольно убогим информационным поп-апом термостата.
Скрытый текст
type: state-icon
icon: mdi:cat
style:
left: 22%
top: 63%
border-radius: 50%
border: 2px solid red
text-align: center
background-color: rgba(255, 255, 255, 0.3)
opacity: 100%
entity: climate.koshkin_dom
state_color: true
tap_action:
action: more-info

В итоге я перешел к второму варианту, в качестве бонуса получил изменение цвета иконки на оранжевый, когда термостат греет.
Внимание! Обогревать кошек — это важно. Не пренебрегайте этой обязанностью.

Картинка с камеры. Просто элемент типа image.
Скрытый текст
type: image
camera_view: live
camera_image: camera.camera2
style:
left: 62%
top: 87%
width: 300px
border: 2px solid black
border-radius: 10px
text-align: center
background-color: rgba(255, 255, 255, 0.3)
opacity: 100%
Не всегда работает стабильно, картинка иногда фризится, почему — пока не разобрался. Но в целом удобно.

Тапнув по картике, получим поп-ап с картинкой побольше.

Картинка с отслеживаемым объектом. В моем случае это последнее появление кота на камере с отметкой, когда он был зафиксирован. Мне это нужно, чтобы понимать, откуда — с крыльца или веранды — звать котов, когда они изволят вернуться с гулянки.

Определением объектов в кадре занимается сервер видеонаблюдения Frigate, с ним интегрирован Home Assistant. Интеграция Frigate создает в HA сущности с изображениями последнего обнаруженного объекта каждого опознаваемого типа, меня интересует объект типа cat.
Скрытый текст
type: image
entity: image.camera2_cat
camera_view: auto
style:
left: 75%
top: 85%
width: 120px
border-radius: 50%
border: 2px solid black
text-align: center
background-color: rgba(255, 255, 255, 0.3)
opacity: 100%
Сгенерить подпись с количеством времени, прошедшего с момента появления объекта в кадре, оказалось не очень просто. Пришлось в configuration.yaml прописать специальные датчики (тут прибегнул к вайбкодингу, собственных знаний не хватило).
Скрытый текст
time_since_camera2_cat_change:
friendly_name: 'Время с последнего изменения camera2_cat'
value_template: >-
{% set snapshot_time_str = states('image.camera2_cat') %}
{% if snapshot_time_str is none or snapshot_time_str in ['unavailable', 'unknown', ''] %}
Неизвестно
{% else %}
{% set snapshot_ts = as_timestamp(snapshot_time_str, 0) %}
{% set current_ts = now().timestamp() %}
{% if snapshot_ts == 0 %}
Ошибка формата
{% else %}
{% set diff_secs = current_ts - snapshot_ts %}
{% if diff_secs < 0 %}
Ошибка времени
{% elif diff_secs >= 86400 %}
Давно
{% else %}
{% set hours = (diff_secs // 3600) | int %}
{% set minutes = ((diff_secs % 3600) // 60) | int %}
{% if hours >= 1 %}
{{ hours }} ч {{ minutes }} мин назад
{% elif minutes >= 1 %}
{{ minutes }} мин назад
{% else %}
Меньше минуты назад
{% endif %}
{% endif %}
{% endif %}
{% endif %}
icon_template: mdi:clock
Ну и сама подпись, элемент типа state-label.
Скрытый текст
type: state-label
entity: sensor.time_since_camera2_cat_change
style:
left: 74.8%
top: 92%
color: black
font-weight: bold
font-size: 12px
background: white
border-radius: 10px
opacity: 40%
Управление роботом-пылесосом. Это то, на что я потратил больше всего времени. Сначала вешаем иконку в место расположения док-станции.
Скрытый текст
type: state-icon
entity: vacuum.roborock_m1s_6ec6_robot_cleaner
title: null
style:
top: 53.32%
left: 44.64%
border-radius: 50%
text-align: center
background-color: rgba(255, 255, 255, 0.3)
opacity: 100%
tap_action:
action: more-info
Получаем по клику простенькую панель управления, где нет возможности указать, где убираться.

После этого, конечно, хочется большего, и мы переходим к отображению по клику кастомной карточки с картой и возможностью выбора комнаты, области, или точки, где убираться.
Для этого сначала отдельно настраиваем кастомную карточку пылесоса. У меня пылесос Dreame, к нему подошла интеграция Dreame vacuum от Tasshack.


После того, как интеграция сгенерировала конфиг карточки, а мы его поправили как нам надо, можно переносить этот код на наш план дома в элемент типа conditional, внутри которого сидит элемент типа custom с нашей карточкой. Код получается огромный — зато можно все настроить.
Скрытый текст
type: conditional
conditions:
- condition: state
entity: input_boolean.vacuum_map_show
state: "on"
elements:
- type: custom:xiaomi-vacuum-map-card
map_source:
camera: camera.x40_ultra_complete_map
calibration_source:
camera: true
entity: vacuum.x40_ultra_complete
vacuum_platform: Tasshack/dreame-vacuum
icons:
- icon: mdi:play
conditions:
- entity: vacuum.x40_ultra_complete
value_not: cleaning
- entity: vacuum.x40_ultra_complete
value_not: error
- entity: vacuum.x40_ultra_complete
value_not: returning
tooltip: Start
tap_action:
action: call-service
service: vacuum.start
service_data:
entity_id: vacuum.x40_ultra_complete
- icon: mdi:pause
conditions:
- entity: vacuum.x40_ultra_complete
value_not: docked
- entity: vacuum.x40_ultra_complete
value_not: idle
- entity: vacuum.x40_ultra_complete
value_not: error
- entity: vacuum.x40_ultra_complete
value_not: paused
tooltip: Pause
tap_action:
action: call-service
service: vacuum.pause
service_data:
entity_id: vacuum.x40_ultra_complete
- icon: mdi:stop
conditions:
- entity: vacuum.x40_ultra_complete
value_not: docked
- entity: vacuum.x40_ultra_complete
value_not: idle
- entity: vacuum.x40_ultra_complete
value_not: error
- entity: vacuum.x40_ultra_complete
value_not: paused
tooltip: Stop
tap_action:
action: call-service
service: vacuum.stop
service_data:
entity_id: vacuum.x40_ultra_complete
- icon: mdi:home-map-marker
conditions:
- entity: vacuum.x40_ultra_complete
value_not: docked
- entity: vacuum.x40_ultra_complete
value_not: returning
tooltip: Return to base
tap_action:
action: call-service
service: vacuum.return_to_base
service_data:
entity_id: vacuum.x40_ultra_complete
- icon: mdi:map-marker
tooltip: Locate
tap_action:
action: call-service
service: vacuum.locate
service_data:
entity_id: vacuum.x40_ultra_complete
- menu_id: cleaning_mode
icon: mdi:broom
tooltip: Cleaning mode
label: Сухая
conditions:
- entity: vacuum.x40_ultra_complete
attribute: cleaning_mode
value: Sweeping
entity: select.x40_ultra_complete_cleaning_mode
available_values_attribute: options
icon_mapping:
sweeping: mdi:broom
mopping: mdi:water-opacity
sweeping_and_mopping: mdi:hydro-power
mopping_after_sweeping: mdi:water-polo
tap_action:
action: call-service
service: select.select_option
service_data:
option: sweeping
entity_id: select.x40_ultra_complete_cleaning_mode
- menu_id: cleaning_mode
icon: mdi:water-opacity
tooltip: Cleaning mode
label: Влажная
conditions:
- entity: vacuum.x40_ultra_complete
attribute: cleaning_mode
value: Mopping
entity: select.x40_ultra_complete_cleaning_mode
available_values_attribute: options
icon_mapping:
sweeping: mdi:broom
mopping: mdi:water-opacity
sweeping_and_mopping: mdi:hydro-power
mopping_after_sweeping: mdi:water-polo
tap_action:
action: call-service
service: select.select_option
service_data:
option: mopping
entity_id: select.x40_ultra_complete_cleaning_mode
- menu_id: cleaning_mode
icon: mdi:hydro-power
tooltip: Cleaning mode
label: Сухая и влажная
conditions:
- entity: vacuum.x40_ultra_complete
attribute: cleaning_mode
value: Sweeping and mopping
entity: select.x40_ultra_complete_cleaning_mode
available_values_attribute: options
icon_mapping:
sweeping: mdi:broom
mopping: mdi:water-opacity
sweeping_and_mopping: mdi:hydro-power
mopping_after_sweeping: mdi:water-polo
tap_action:
action: call-service
service: select.select_option
service_data:
option: sweeping_and_mopping
entity_id: select.x40_ultra_complete_cleaning_mode
- menu_id: cleaning_mode
icon: mdi:water-polo
tooltip: Cleaning mode
label: Влажная после сухой
conditions:
- entity: vacuum.x40_ultra_complete
attribute: cleaning_mode
value: Mopping after sweeping
entity: select.x40_ultra_complete_cleaning_mode
available_values_attribute: options
icon_mapping:
sweeping: mdi:broom
mopping: mdi:water-opacity
sweeping_and_mopping: mdi:hydro-power
mopping_after_sweeping: mdi:water-polo
tap_action:
action: call-service
service: select.select_option
service_data:
option: mopping_after_sweeping
entity_id: select.x40_ultra_complete_cleaning_mode
- menu_id: fan_speed
icon: mdi:fan-remove
label: Тихая
conditions:
- entity: vacuum.x40_ultra_complete
attribute: fan_speed
value: Silent
tooltip: Change fan speed
tap_action:
action: call-service
service: vacuum.set_fan_speed
service_data:
entity_id: vacuum.x40_ultra_complete
fan_speed: Silent
- menu_id: fan_speed
icon: mdi:fan-speed-1
label: Обычная
conditions:
- entity: vacuum.x40_ultra_complete
attribute: fan_speed
value: Standard
tooltip: Change fan speed
tap_action:
action: call-service
service: vacuum.set_fan_speed
service_data:
entity_id: vacuum.x40_ultra_complete
fan_speed: Standard
- menu_id: fan_speed
icon: mdi:fan-speed-2
label: Мощная
conditions:
- entity: vacuum.x40_ultra_complete
attribute: fan_speed
value: Strong
tooltip: Change fan speed
tap_action:
action: call-service
service: vacuum.set_fan_speed
service_data:
entity_id: vacuum.x40_ultra_complete
fan_speed: Strong
- menu_id: fan_speed
icon: mdi:fan-speed-3
label: Турбо
conditions:
- entity: vacuum.x40_ultra_complete
attribute: fan_speed
value: Turbo
tooltip: Change fan speed
tap_action:
action: call-service
service: vacuum.set_fan_speed
service_data:
entity_id: vacuum.x40_ultra_complete
fan_speed: Turbo
- icon: mdi:fan-alert
conditions:
- entity: vacuum.x40_ultra_complete
attribute: fan_speed
value_not: Silent
- entity: vacuum.x40_ultra_complete
attribute: fan_speed
value_not: Standard
- entity: vacuum.x40_ultra_complete
attribute: fan_speed
value_not: Strong
- entity: vacuum.x40_ultra_complete
attribute: fan_speed
value_not: Turbo
tooltip: Change fan speed
tap_action:
action: call-service
service: vacuum.set_fan_speed
service_data:
entity_id: vacuum.x40_ultra_complete
fan_speed: Silent
tiles:
- tile_id: status
entity: vacuum.x40_ultra_complete
label: Чем занят
attribute: status
icon: mdi:robot-vacuum
translations:
sleeping: Спит
starting: Поехал
charger disconnected: Нет питания
idle: Ждет
remote control active: Удаленное управление
cleaning: Убирается
returning home: Возвращается
manual mode: Ручной режим
charging: Заряжается
charging problem: Проблема с зарядкой
paused: На паузе
spot cleaning: Чистит точку
error: Ошибка
shutting down: Выключается
updating: Обновляет прошивку
docking: Заходит в базу
going to target: Идет на цель
zoned cleaning: Зональная уборка
segment cleaning: Уборка сегмента
emptying the bin: Выгружает пыль
charging complete: Зарядка завершена
device offline: Не в сети
- tile_id: battery_level
entity: vacuum.x40_ultra_complete
label: Заряд
attribute: battery_level
icon_source: vacuum.x40_ultra_complete.attributes.battery_icon
unit: "%"
- tile_id: fan_speed
entity: vacuum.x40_ultra_complete
label: Мощность
attribute: fan_speed
icon: mdi:fan
translations:
silent: Тихая
standard: Обычная
medium: Средняя
strong: Мощная
turbo: Турбо
auto: Auto
gentle: Нежная
- tile_id: mop_pad_humidity
attribute: mop_pad_humidity
label: Швабры
icon: mdi:water-percent
entity: vacuum.x40_ultra_complete
precision: 0
translations:
wet: Мокрые
dry: Сухие
map_modes:
- name: Зональная уборка
icon: mdi:select-drag
run_immediately: false
coordinates_rounding: true
coordinates_to_meters_divider: 1000
selection_type: MANUAL_RECTANGLE
max_selections: 20
repeats_type: EXTERNAL
max_repeats: 3
service_call_schema:
service: dreame_vacuum.vacuum_clean_zone
service_data:
zone: "[[selection]]"
repeats: "[[repeats]]"
entity_id: "[[entity_id]]"
predefined_selections: []
variables: {}
- name: Уборка на точке
icon: mdi:map-marker-plus
run_immediately: false
coordinates_rounding: true
coordinates_to_meters_divider: 1000
selection_type: MANUAL_POINT
max_selections: 999
repeats_type: EXTERNAL
max_repeats: 3
service_call_schema:
service: dreame_vacuum.vacuum_clean_spot
service_data:
points: "[[selection]]"
repeats: "[[repeats]]"
entity_id: "[[entity_id]]"
predefined_selections: []
variables: {}
- name: Передвинуть робота
icon: mdi:map-marker-radius
run_immediately: false
coordinates_rounding: true
coordinates_to_meters_divider: 1000
selection_type: MANUAL_POINT
max_selections: 1
repeats_type: NONE
max_repeats: 1
service_call_schema:
service: dreame_vacuum.vacuum_goto
service_data:
x: "[[point_x]]"
"y": "[[point_y]]"
entity_id: "[[entity_id]]"
predefined_selections: []
variables: {}
- template: vacuum_clean_segment
predefined_selections:
- id: "1"
icon:
name: mdi:bookshelf
x: -150
"y": 8350
label:
text: Кабинет
x: -150
"y": 8350
offset_y: 35
outline:
- - -1750
- 6850
- - 1750
- 6850
- - 1750
- 9850
- - -1750
- 9850
- id: "2"
icon:
name: mdi:bed-king-outline
x: 3150
"y": 4600
label:
text: Спальня
x: 3150
"y": 4600
offset_y: 35
outline:
- - 1700
- 2400
- - 4700
- 2400
- - 4700
- 6600
- - 1700
- 6600
- id: "3"
icon:
name: mdi:toilet
x: 2950
"y": 1750
label:
text: Малый санузел
x: 2950
"y": 1750
offset_y: 35
outline:
- - 1750
- 1100
- - 4650
- 1100
- - 4650
- 2400
- - 1750
- 2400
- id: "4"
icon:
name: mdi:foot-print
x: -150
"y": 4400
label:
text: Холл
x: -150
"y": 4400
offset_y: 35
outline:
- - -2900
- 1050
- - 1700
- 1050
- - 1700
- 6850
- - -2900
- 6850
- id: "5"
icon:
name: mdi:foot-print
x: -3950
"y": 2150
label:
text: Прихожая
x: -3950
"y": 2150
offset_y: 35
outline:
- - -4750
- 150
- - -2900
- 150
- - -2900
- 4050
- - -4750
- 4050
- id: "6"
icon:
name: mdi:sofa-outline
x: -1350
"y": -1700
label:
text: Гостиная
x: -1350
"y": -1700
offset_y: 35
outline:
- - -2700
- -4200
- - 900
- -4200
- - 900
- 1100
- - -2700
- 1100
- id: "7"
icon:
name: mdi:toilet
x: -1500
"y": 5200
label:
text: Санузел
x: -1500
"y": 5200
offset_y: 35
outline:
- - -2750
- 3800
- - -600
- 3800
- - -600
- 6550
- - -2750
- 6550
- id: "8"
icon:
name: mdi:archive-outline
x: 950
"y": 2900
label:
text: Кладовка
x: 950
"y": 2900
offset_y: 35
outline:
- - 600
- 1700
- - 1350
- 1700
- - 1350
- 4150
- - 600
- 4150
- id: "9"
icon:
name: mdi:chef-hat
x: 2750
"y": -1900
label:
text: Кухня
x: 2750
"y": -1900
offset_y: 35
outline:
- - 900
- -4600
- - 4750
- -4600
- - 4750
- 750
- - 900
- 750
additional_presets: []
style:
left: 36%
top: 48%
width: 450px
border-radius: 10px
border: 2px solid black
text-align: center
opacity: 95%
Теперь следующий этап: хочется, чтобы иконка робота не просто сидела на месте док-станции, а перемещалась в соответствии с местоположением самого робота.
Иконку с переменными координатами панель Picture-Elements сделать так просто мне не дала, пришлось снова обратиться к нейросети, которая посоветовала использовать card-mod. Это кастомная UI-интеграция, которая позволяет использовать CSS-стили внутри карточек Home Assistant.
Скрытый текст
type: state-icon
entity: input_boolean.vacuum_map_show
tap_action:
action: toggle
icon: mdi:robot-vacuum
card_mod:
style: >
{% set vac = state_attr('camera.x40_ultra_complete_map','vacuum_position')
%}
{% set raw_angle = vac.a | float(0) %}
{% set offset = 180 %}
{% set angle = ((offset - raw_angle) % 360) %}
:host {
position: absolute;
left: {{ states('sensor.vacuum_left_px_new') }}px;
top: {{ states('sensor.vacuum_top_px_new') }}px;
width: 25px;
height: 25px;
padding: 3px;
box-sizing: border-box;
background-color: rgba(255,255,255,0.9);
border: 1px solid #e0e0e0;
border-radius: 50%;
box-shadow: 0 1px 4px rgba(0,0,0,0.25);
display: flex;
align-items: center;
justify-content: center;
transform: rotate({{ angle }}deg) !important;
transform-origin: center center !important;
z-index: initial !important;
}
ha-icon {
color: #000;
width: 20px;
height: 20px;
margin: 0;
transform: rotate({{ angle }}deg) !important;
transform-origin: center center !important;
Основная сложность в преобразовании координат робота на плоскости его карты в координаты на плане дома, который не плоский, а имеет перспективу. Эту задачу я пока решил не до конца — в некоторых комнатах иконка пока еще заметно смещается от реального местоположения робота. Я разбил карту на 25 квадратов, вручную погонял робота по этажу, и составил таблицу из 40 калибровочных точек, где записал соответствие координат робота на его карте и координат на моем плане. Затем скормил эту таблицу Gemini 2.5, и попросил просчитать коэффициенты формул для билинейной интерполяции координат.
Скрытый текст
gemini-2.5-pro-exp
17.04.2025 at 21:52
Здравствуйте! Понимаю вашу задачу: нужно отобразить положение робота-пылесоса (vacuum_position из camera.x40_ultra_complete_map) на плане этажа в Home Assistant, учитывая разницу в системах координат, ориентации и перспективное искажение плана. У вас есть набор калибровочных точек, связывающих координаты робота и пиксельные координаты на плане.
Ваша идея с разделением плана на сетку 5x5 и расчетом локальных коэффициентов для каждого квадрата — это правильный подход для учета нелинейных искажений, таких как перспектива. Однако реализация такого сложного алгоритма интерполяции (например, билинейной или полиномиальной для каждого квадрата сетки) непосредственно в шаблоне сенсора Jinja2 (configuration.yaml) будет чрезвычайно сложной или даже невозможной.
Сложно, очень сложно. Пришлось поуговаривать, чтобы получить код для датчиков, преобразующих координаты (вставляется в configuration.yaml).
Скрытый текст
Не, ну должно же как-то проще решаться!
template:
- sensor:
- name: "Vacuum Left Px New"
unit_of_measurement: "px"
state: >
{% set vac = state_attr('camera.x40_ultra_complete_map','vacuum_position') %}
{% if vac and vac.x is number and vac.y is number %}
{% set x = vac.x %}
{% set y = vac.y %}
{# Параметры сетки #}
{% set x_min = -4579.0 %}
{% set y_min = -3992.0 %}
{% set dx = 1809.2 %}
{% set dy = 2721.0 %}
{# Коэффициенты преобразования для каждой ячейки (i,j) #}
{% set T = {
'0,0': {'a11': -0.009983, 'a12': -0.066817, 'b1': 832.657804, 'a21': -0.078867, 'a22': -0.007117, 'b2': 412.183573},
'0,1': {'a11': 0.019068, 'a12': -0.140582, 'b1': 990.284377, 'a21': -0.060169, 'a22': -0.054593, 'b2': 513.633816},
'0,2': {'a11': -0.002175, 'a12': -0.065921, 'b1': 874.062874, 'a21': -0.073109, 'a22': 0.006211, 'b2': 413.315245},
'0,3': {'a11': 0.002615, 'a12': -0.060233, 'b1': 870.560191, 'a21': -0.052611, 'a22': -0.000162, 'b2': 500.394952},
'0,4': {'a11': 0.012255, 'a12': -0.059423, 'b1': 884.735305, 'a21': -0.073910, 'a22': -0.001304, 'b2': 464.928882},
'1,0': {'a11': -0.003491, 'a12': -0.056227, 'b1': 890.937886, 'a21': -0.050846, 'a22': -0.000977, 'b2': 505.763572},
'1,1': {'a11': 0.015411, 'a12': -0.088180, 'b1': 952.763568, 'a21': -0.048956, 'a22': -0.005007, 'b2': 512.407395},
'1,2': {'a11': 0.000446, 'a12': -0.057291, 'b1': 854.350294, 'a21': -0.046248, 'a22': -0.002631, 'b2': 522.396612},
'1,3': {'a11': -0.009347, 'a12': -0.061078, 'b1': 851.945874, 'a21': -0.081280, 'a22': -0.002187, 'b2': 455.781485},
'1,4': {'a11': 0.012255, 'a12': -0.059423, 'b1': 884.735305, 'a21': -0.073910, 'a22': -0.001304, 'b2': 464.928882},
'2,0': {'a11': -0.005939, 'a12': -0.061828, 'b1': 871.041469, 'a21': -0.054300, 'a22': 0.005926, 'b2': 536.218771},
'2,1': {'a11': 0.006405, 'a12': -0.013656, 'b1': 865.722704, 'a21': -0.038146, 'a22': 0.004478, 'b2': 523.389948},
'2,2': {'a11': -0.027127, 'a12': -0.049086, 'b1': 798.194359, 'a21': -0.071610, 'a22': 0.001844, 'b2': 479.471436},
'2,3': {'a11': 0.000527, 'a12': -0.051492, 'b1': 820.432526, 'a21': -0.078333, 'a22': -0.003569, 'b2': 510.680259},
'2,4': {'a11': 0.017637, 'a12': -0.063504, 'b1': 894.109592, 'a21': -0.049961, 'a22': -0.003430, 'b2': 495.854427},
'3,0': {'a11': -0.002444, 'a12': -0.069982, 'b1': 835.602179, 'a21': -0.065399, 'a22': 0.031824, 'b2': 648.769444},
'3,1': {'a11': -0.010379, 'a12': -0.038164, 'b1': 876.685108, 'a21': -0.063546, 'a22': -0.032611, 'b2': 539.979253},
'3,2': {'a11': -0.028113, 'a12': -0.117443, 'b1': 1088.703, 'a21': -0.043728, 'a22': -0.018741, 'b2': 429.021},
'3,3': {'a11': 0.008957, 'a12': -0.031518, 'b1': 680.888, 'a21': -0.064406, 'a22': 0.005359, 'b2': 457.586},
'3,4': {'a11': -0.038366, 'a12': -0.073320, 'b1':1032.926937,'a21': -0.078527,'a22': -0.008437,'b2':566.662052},
'4,0': {'a11': 0.000319, 'a12': -0.060475, 'b1': 860.744277, 'a21': -0.074903, 'a22': -0.000886,'b2':562.266867},
'4,1': {'a11': -0.000857, 'a12': -0.049575,'b1':865.376888,'a21': -0.044549,'a22': -0.003059,'b2':442.610229},
'4,2': {'a11': 0.016497, 'a12': -0.004794, 'b1': 1008.000, 'a21': -0.068834, 'a22': 0.020000, 'b2': 617.500},
'4,3': {'a11': 0.004780, 'a12': -0.032272, 'b1': 687.222, 'a21': -0.051716, 'a22': 0.017072, 'b2': 351.838},
'4,4': {'a11': 0.004780, 'a12': -0.032272,'b1':687.222253,'a21': -0.051716,'a22': 0.017072,'b2':351.838141}
} %}
{# Вычисляем индексы ячеек и веса #}
{% set i0 = ((x - x_min) / dx) | int %}
{% if i0 < 0 %}{% set i0 = 0 %}{% endif %}
{% if i0 > 4 %}{% set i0 = 4 %}{% endif %}
{% set i1 = i0 + 1 if i0 < 4 else 4 %}
{% set j0 = ((y - y_min) / dy) | int %}
{% if j0 < 0 %}{% set j0 = 0 %}{% endif %}
{% if j0 > 4 %}{% set j0 = 4 %}{% endif %}
{% set j1 = j0 + 1 if j0 < 4 else 4 %}
{% set x0 = x_min + dx * i0 %}
{% set x1 = x_min + dx * i1 %}
{% set y0 = y_min + dy * j0 %}
{% set y1 = y_min + dy * j1 %}
{% set alpha = 0 if x1 == x0 else ((x - x0) / (x1 - x0)) %}
{% set beta = 0 if y1 == y0 else ((y - y0) / (y1 - y0)) %}
{# Применяем 4 аффинных преобразования #}
{% set k00 = i0 ~ ',' ~ j0 %}
{% set k10 = i1 ~ ',' ~ j0 %}
{% set k01 = i0 ~ ',' ~ j1 %}
{% set k11 = i1 ~ ',' ~ j1 %}
{% set t00 = T[k00] %}{% set t10 = T[k10] %}
{% set t01 = T[k01] %}{% set t11 = T[k11] %}
{% set px00 = t00.a11 * x + t00.a12 * y + t00.b1 %}
{% set px10 = t10.a11 * x + t10.a12 * y + t10.b1 %}
{% set px01 = t01.a11 * x + t01.a12 * y + t01.b1 %}
{% set px11 = t11.a11 * x + t11.a12 * y + t11.b1 %}
{# Билинейная интерполяция #}
{% set pred_x = (1 - alpha) * (1 - beta) * px00 + alpha * (1 - beta) * px10 + (1 - alpha) * beta * px01 + alpha * beta * px11 %}
{{ pred_x | round(1) }}
{% else %}
0
{% endif %}
- sensor:
- name: "Vacuum Top Px New"
unit_of_measurement: "px"
state: >
{% set vac = state_attr('camera.x40_ultra_complete_map','vacuum_position') %}
{% if vac and vac.x is number and vac.y is number %}
{% set x = vac.x %}
{% set y = vac.y %}
{% set x_min = -4579.0 %}
{% set y_min = -3992.0 %}
{% set dx = 1809.2 %}
{% set dy = 2721.0 %}
{% set T = {
'0,0': {'a11': -0.009983, 'a12': -0.066817, 'b1': 832.657804, 'a21': -0.078867, 'a22': -0.007117, 'b2': 412.183573},
'0,1': {'a11': 0.019068, 'a12': -0.140582, 'b1': 990.284377, 'a21': -0.060169, 'a22': -0.054593, 'b2': 513.633816},
'0,2': {'a11': -0.002175, 'a12': -0.065921, 'b1': 874.062874, 'a21': -0.073109, 'a22': 0.006211, 'b2': 413.315245},
'0,3': {'a11': 0.002615, 'a12': -0.060233, 'b1': 870.560191, 'a21': -0.052611, 'a22': -0.000162, 'b2': 500.394952},
'0,4': {'a11': 0.012255, 'a12': -0.059423, 'b1': 884.735305, 'a21': -0.073910, 'a22': -0.001304, 'b2': 464.928882},
'1,0': {'a11': -0.003491, 'a12': -0.056227, 'b1': 890.937886, 'a21': -0.050846, 'a22': -0.000977, 'b2': 505.763572},
'1,1': {'a11': 0.015411, 'a12': -0.088180, 'b1': 952.763568, 'a21': -0.048956, 'a22': -0.005007, 'b2': 512.407395},
'1,2': {'a11': 0.000446, 'a12': -0.057291, 'b1': 854.350294, 'a21': -0.046248, 'a22': -0.002631, 'b2': 522.396612},
'1,3': {'a11': -0.009347, 'a12': -0.061078, 'b1': 851.945874, 'a21': -0.081280, 'a22': -0.002187, 'b2': 455.781485},
'1,4': {'a11': 0.012255, 'a12': -0.059423, 'b1': 884.735305, 'a21': -0.073910, 'a22': -0.001304, 'b2': 464.928882},
'2,0': {'a11': -0.005939, 'a12': -0.061828, 'b1': 871.041469, 'a21': -0.054300, 'a22': 0.005926, 'b2': 536.218771},
'2,1': {'a11': 0.006405, 'a12': -0.013656, 'b1': 865.722704, 'a21': -0.038146, 'a22': 0.004478, 'b2': 523.389948},
'2,2': {'a11': -0.027127, 'a12': -0.049086, 'b1': 798.194359, 'a21': -0.071610, 'a22': 0.001844, 'b2': 479.471436},
'2,3': {'a11': 0.000527, 'a12': -0.051492, 'b1': 820.432526, 'a21': -0.078333, 'a22': -0.003569, 'b2': 510.680259},
'2,4': {'a11': 0.017637, 'a12': -0.063504, 'b1': 894.109592, 'a21': -0.049961, 'a22': -0.003430, 'b2': 495.854427},
'3,0': {'a11': -0.002444, 'a12': -0.069982, 'b1': 835.602179, 'a21': -0.065399, 'a22': 0.031824, 'b2': 648.769444},
'3,1': {'a11': -0.010379, 'a12': -0.038164, 'b1': 876.685108, 'a21': -0.063546, 'a22': -0.032611, 'b2': 539.979253},
'3,2': {'a11': -0.028113, 'a12': -0.117443, 'b1': 1088.703, 'a21': -0.043728, 'a22': -0.018741, 'b2': 429.021},
'3,3': {'a11': 0.008957, 'a12': -0.031518, 'b1': 680.888, 'a21': -0.064406, 'a22': 0.005359, 'b2': 457.586},
'3,4': {'a11': -0.038366, 'a12': -0.073320, 'b1':1032.926937,'a21': -0.078527,'a22': -0.008437,'b2':566.662052},
'4,0': {'a11': 0.000319, 'a12': -0.060475, 'b1': 860.744277, 'a21': -0.074903, 'a22': -0.000886,'b2':562.266867},
'4,1': {'a11': -0.000857, 'a12': -0.049575,'b1':865.376888,'a21': -0.044549,'a22': -0.003059,'b2':442.610229},
'4,2': {'a11': 0.016497, 'a12': -0.004794, 'b1': 1008.000, 'a21': -0.068834, 'a22': 0.020000, 'b2': 617.500},
'4,3': {'a11': 0.004780, 'a12': -0.032272, 'b1': 687.222, 'a21': -0.051716, 'a22': 0.017072, 'b2': 351.838},
'4,4': {'a11': 0.004780, 'a12': -0.032272,'b1':687.222253,'a21': -0.051716,'a22': 0.017072,'b2':351.838141}
} %}
{# Расчёт индексов #}
{% set raw_i = (x - x_min) / dx %}
{% set i0 = 0 if raw_i < 0 else (4 if raw_i > 4 else raw_i|int) %}
{% set raw_j = (y - y_min) / dy %}
{% set j0 = 0 if raw_j < 0 else (4 if raw_j > 4 else raw_j|int) %}
{% set i1 = i0 + 1 if i0 < 4 else 4 %}
{% set j1 = j0 + 1 if j0 < 4 else 4 %}
{% set alpha = (x - (x_min + i0*dx)) / dx %}
{% set beta = (y - (y_min + j0*dy)) / dy %}
{% set k00 = i0|string + ',' + j0|string %}
{% set k10 = i1|string + ',' + j0|string %}
{% set k01 = i0|string + ',' + j1|string %}
{% set k11 = i1|string + ',' + j1|string %}
{% set P00y = (T[k00]['a21'] * x) + (T[k00]['a22'] * y) + T[k00]['b2'] %}
{% set P10y = (T[k10]['a21'] * x) + (T[k10]['a22'] * y) + T[k10]['b2'] %}
{% set P01y = (T[k01]['a21'] * x) + (T[k01]['a22'] * y) + T[k01]['b2'] %}
{% set P11y = (T[k11]['a21'] * x) + (T[k11]['a22'] * y) + T[k11]['b2'] %}
{% set pred_y = (1 - alpha)*(1 - beta)*P00y + alpha*(1 - beta)*P10y + (1 - alpha)*beta*P01y + alpha*beta*P11y %}
{{ pred_y | round(1) }}
{% else %}
0
{% endif %}
Отклонение получилось в целом терпимое, но я не теряю надежды, что когда-нибудь все же найду более точный подход. Возможно, это позволит отказаться от накладной карточки с картой пылесоса, и указывать зону чистки прямо на плане этажа.
Итог
Ну, работает. Удобно. Каждый день используем. Ваять все это было интересно, хотя меня не отпускало чувство, что я изобретаю велосипед, и где-то есть подробные гайды, по которым все это можно сделать быстро и просто. Ткните меня в нее носом, если есть. А если не было, то теперь есть мой.
Очень рассчитываю на советы, идеи, а также на пинки за костыльные и неоптимальные решения.
Комментарии (11)
vbifkol
18.06.2025 12:02А что за лора шлюз в гпс-трекер?
Lynx-eyed Автор
18.06.2025 12:02Мне нужно было, чтобы ворота открывались заранее, когда я приезжаю домой, и закрывались, когда уезжаю. При этом не хотелось платить опсосу за связь. Решение - в машине GPS-трекер с модемом LoRa, а в доме на чердаке стоит шлюз LoRa-WiFi. Связь на 800 МГц, бьет где-то до километра, более чем хватает. Дом раз в 30 секунд получает MQTT-сообщение с координатами, рассчитывает расстояние до машины и решает, открывать ворота, закрывать, или ничего не делать.
Zara6502
я сейчас на свидетелей умного дома смотрю так же как на меня в 1995 году родственники смотрели сидящего днями и ночами за ПК, самые популярные слова "главное что не наркоманит" и "главное что по улице не шляется".
в этом во всем есть один момент - когда у тебя этого нет, то невозможно понять зачем оно всё нужно, а когда оно у тебя есть, ты не понимаешь как ты мог раньше жить без этого.
я почитав самые верхушки про умный дом понял одно - я не хочу тратить на это время, мне не сложно выключить свет руками или полить цветок - это же и есть жизнь, а чтобы не вставать с кровати выключатель можно прикрутить у изголовья.
Lynx-eyed Автор
Как хобби не хуже любого другого, я считаю.
Zara6502
а в ряде случаев полезнее для окружающих
xSVPx
Зачем в коридоре монитор действительно понятно не очень. В целом умный дом должен работать "сам". Т.е. не требовать бдить и управлять, тогда это удобно.
Даже обычное включение и выключение света в прихожей по датчику ощутимо так качество жизни улучшает. Да, можно не выключать аесь день, но у меня свет оттуда попадает в спальню, значит на ночь выключать надо. И если вдруг "пошел пописать" то делаешь это во тьме :). Или он тебе включит, а потом и сам выключит. Или он тебе включит когда ты заходишь с улицы и руки заняты. Итд итп.
Т.е. сценарии, где прям можно удобнее сделать есть. Но да - требует некоторых усилий.
Lynx-eyed Автор
Дом и работает сам, панель управления лишь дополняет автоматизации и другие каналы управления (голос, телеграм, кнопки).
vbifkol
В статье же есть об этом - возможность получить визуально быстро нужную инфу. На примере цветка. Мне вот еще может быть важно посмотреть текущее электропотребление и где включен свет перед выходом.
xSVPx
Зачем на свет смотреть ?
Ну т.е. у себя я планирую когда-нибудь сделать сценарную кнопку "выход" по которой будет проводиться проверка всего что надо, выключаться свет итд итп. Но вешать монитор даже в голову не приходило. В телефон приложение поставить еще ладно...
Zara6502
я на озоне купил светильник за 150 руб с датчиком движения, подключил к нему старый повербанк который лежил без дела и теперь у меня в коридоре умнейший дом )))) когда я иду ночью в туалет он мне подсвечивает и обратно. Чтобы не вставать к светильнику общему я у изголовья сделал лампу и выключатель = 300 руб. Просто я читал много про умные дома и имхо я бы таким деньгам нашел лучшее применение. Просто люди которые никогда в жизни не испытывали проблем со здоровьем наивно полагают что у них всё будет круто, стабильно и доходно еще очень много лет, но как правило звиздец подкрадывается незаметно, рушатся семьи, страдают дети. Я сгущаю конечно, но не думать о таком наверное тоже преступно в каком-то смысле.
xSVPx
Причем тут здоровье то ?
На дистанции умный дом выгоднее кучи автономных светильников. И сложную логику можно сделать только на нём. А кое-где эта логика в целом не лишняя. К примеру, у меня автомат включает на 2 минуты свет, а кнопка на 10 минут, чтобы руками не махать если что-то надо в коридоре сделать. Сможете так с своей лампой :)? А еще у меня есть кнопка включающая свет в коридоре из спальни, чтобы в темеоте не красться...
Закроются ли ваши краны холодной и горячей воды, если открыть воду на полчасика ? Получите ли вы уведомление хотя бы об этом ? А если намочить датчики протечки (они ведь у вас есть)?
У меня в ванной осушитель, и в автоматическом режиме он работает не слишком хорошо, потому как датчик должен быть не в осушителе :). Какую такую "лампу" мне купить для решения этой проблемы :)? Итд итп.
Да даже свет. Вы врубаете ночью "на полную"? Зачем ? Можно ведь в щависимости от времени суток по ночам включать на чуть-чуть, чтобы он не мешал остальным домашним.
Итд итп.
Но в одном вы правы - это всё, как и любое удобство в целом лакшери. Т.е. не необходимо.