В первой части статьи была рассмотрена работа устройства, касающаяся ESP8266. В этой части рассмотрим связку устройства с фреймворком умного дома MajorDoMo. Выглядит это так
Состоять панель управления будет из двух страниц. Первая — кнопки управления и текущая температура, вторая — графики за выбранный период времени.
Идем в панель управления, в меню «Объекты» выбираем «Объекты»
Видим целую кучу классов, предустановленных в MajorDoMo. Но нам нужен свой. Нажимаем кнопку «Добавить новый класс»
Общий класс для всех устройств ESP8266 создан. Теперь можно наполнить его общими для всех устройств свойствами. Как это делать — можно глянуть в предыдущих статьях, повторять не вижу смысла.
У меня было создано 4 свойства, но используется только два — это свободная память и уровень сигнала WiFi.
Теперь можно поделить по направлениям. Создаем новый класс для курятника (называем Chicken), в качестве родительского класса выбираем выше созданный класс «ESP8266»
Зачем все эти телодвижения с классами? Во-первых объектно-ориентированное программирование — это модно, стильно молодежно! Во-вторых у меня уже строится второй домик для более породистых «лицензионных» кур, чтобы не держать их вместе с обычными «пиратскими» 🙂 Т.е. устройств будет уже несколько, и это только для курятника. А есть еще теплица, водоснабжение, канализация, видеодомофон (это все планы на будущее). И когда все лежит по-полочкам — это аккуратно и вполне себе удобно.
Значит теперь у нас появился класс «Chicken». Можно наполнить его свойствами, присущими только это классу — это температура, освещение, обогреватель и др.
Ну и конечно у него есть родительские свойства уровня сигнала и свободной памяти. И здесь уже можно добавить наш первый метод — парсинга температуры. Дело в том, что по HTTP-запросу температура передается в целых числах, без плавающей запятой. А мы хотим ее видеть не как 2545, а как 25,45 °С. Поэтому полученное от устройства значение мы сразу же поделим на 100. Переходим на вкладку «Методы» класса «Chicken», и создаем новый метод, называем «ParsingTemperature»
Сам код перевода температуры в человекопонятное значение я нарисовал на Blockly. Конечно более профессионально было сделать на PHP, тем более там несколько строк кода. Но сам PHP я не знаю, изучать его ньюансы не хотелось. И вместо того, чтобы потратить 1 день, я сразу долетел за 5 минут
Создаем переменную, которой присваиваем значение нашей температуры, поделенной на 100. И тут же присваиваем его свойству «temperaturaParsing».
Теперь нам надо запускать этот метод каждый раз, когда нам прилетает новая температура с нашего устройства. Можно повесить его на изменение значения свойства «temperatura» — это то самое свойство, куда записывается температура с устройства. Переходим в список свойств класса «Chicken», выбираем свойство «temperatura», редактируем его
В поле «Запускать метод при изменении (не обязательно)» выбираем наш созданный метод. Получается, что при получении запроса с новой температурой он обновляет значение свойства, и сам запускает метод, который записывает в альтернативную переменную уже значение в градусах, которую мы потом и будем выводить на экране. Кому надо управление обогревателем не от ESP8266, а от MajorDoMo, может использовать необработанное значение (с целыми числами всегда приятнее работать). Но у меня сам курятник расположен на небольшом отдалении от дома, и сигнал WiFi там не очень. 95% времени он в сети, но иногда сигнал теряет. Сам конечно подключается потом, но все равно неудобно, если сигнал на управление обогревателем к нему будет подаваться тогда, когда само устройство недоступно. Поэтому само управление я сделал именно в нем, а MDM служит для красивой индикации и ручного управления.
Так, теперь наши классы готовы. Осталось создать сам объект, т.е. воплощение физического устройства. Создаем в начальном меню объект так же, как создавали класс. В качестве родительского класса выбираем «Chicken»
В принципе все 🙂 У нас уже есть объект, в который записываются данные, которые мы пока, правда, не видим. Но они есть! 🙂 При условии, что наше на устройство подано питание, и оно зарегистрировалось в домашней сети. Чтобы убедиться в правильности работы, можно перейти на вкладку «Свойства» объекта «ChickenObject». Именно объекта, а не классов выше.
В MajorDoMo есть очень удачная функция — отображение операции, которая изменила то или иное свойство. Например мы видим, что «ChickenObject.temperatura» имеет значение 3718, а изменило его API-функция, или точнгее сказать следующий http-запрос:
3718 (src: /objects/?op=set&object=ChickenObject&p=temperatura&v=3718)
Именно этот запрос мы вбивали в код LUA нашего устройства.
Один ньюанс — наверное было бы правильно поменять части статьи местами. Т.е. это первая, а прошивка ESP8266 — вторая. Просто мы только сейчас создаем классы с названиями и свойствами, которые уже используются в коде NodeMCU. Т.е. не читая этой статьи немного непонятно, откуда взялись «ChickenObject» и «temperatura» там, в первой статье. Но этим уже заниматься не буду, лень.
Теперь займемся визуализацией. По умолчанию главной страницей в MajorDoMo идет своя менюшка, показывающая возможности и пример создания. Но она нам в принципе не нужна. Нам надо своя страница с нашими данными. переходим на главную страницу панели управления, и выбираем «Объекты — Сцены».
Тут видим уже созданную по-умолчанию цену «Scene 1«. Создаем новую сцену, называем «KitchenScene». Да, тут великий и могучий дал сбой, не помню как это было сделано, давно уже, но я перепутал курицу и кухню 🙂 Ну, вещи в принципе взаимосвязаны, и переделывать я не буду. Кухня так кухня.
В качестве фонового изображения нарисовал 3D-модель своего курятника. Сохраняемся, перемещаем стрелкой «вверх» наше сцену выше дефолтной (ее кстати можно вообще удалить).
Сцена готова, теперь повесим на нее температуру, приходящую с ESP8266. Хранится она в свойстве «ChickenObject.temperaturaParsing» — то самое свойство, куда МДМ записывает температуру методом «ParsingTemperature». Заходим в свойства созданной сцены, переходим на вкладку «Элементы», создаем новый элемент.
Тут много настроек, но нам нужно буквально несколько. Называем элемент банально — температура. Сцена — «KitchenScene». Тип — HTML — температуру мы будем размещать обычным текстом, приправленным капелькой CSS для более приятной глазу визуализации.
Некоторые пункты становятся видны только после сохранения, поэтому сохраняемся, и снова переходим в редактирование.
Отступы слева и справа — тут после сохранения удобно перейти на вкладку «Простомтр/Редактировать», и вместо того, чтобы вручную высчитывть координаты, уже мышкой взять нашу температуру за шкирку, и переместить в нужное положение. Так кстати можно сделать и со всеми элементами, что я и делал дальше.
Далее переходим в самый низ страницы, и добавляем новое состояние, которое будет всегда отображаться. И добавляем туда код вывода нашей температуры. Тут можно добавлять что угодно, любой код HTML, но главное, чтобы вывести какое-то свойство, оно должно быть заключено между двумя процентами:
<font style="font-size: 30pt; text-shadow: 4px 3px 0px #fff, 9px 8px 0px rgba(0,0,0,0.15); color: #278a00;">%ChickenObject.temperaturaParsing% °C</font>
Т.е. тут банально вывод значения свойства «%ChickenObject.temperaturaParsing%» заключенного в тэг FONT. Ну и добавлен CSS для отображения тени.
Сохраняем, нажимаем на кнопку просмотр, любуемся.
Теперь добавляем кнопки. Покажу на примере одной, т.к. все они делаются однотипно. Возьмем кнопку включения/отключения света.
К сожалению не могу на сайте показать как она выглядит вживую, кроме как картинкой. Попробую описать словами. Тут все не совсем просто, но очень интересно 🙂 Сама кнопка — простейший код HTML:
<button class="buttonOn">Свет включен</button> <button class="buttonOff">Свет отключен</button>
Но в MajorDoMo она имеет два состояния — включена и выключена, и соответственно два класса «buttonOn» и «buttonOff». Когда свет отключен — кнопка принадлежит классу «buttonOff», и отображается красной. При наведении на нее мышкой — градиент отражается вертикально — т.е. был вверху светлый, внизу темный, а при наведении становится наоборот, как на картинке вверху. Это придает динамики кнопке. При нажатии на кнопку она утапливается, что дает видимость нажатия, при отпускании кнопки она возвращается в исходное положение, но меняет цвет на зеленый (при условии что свет включился).
Это что касается HTML и CSS кода для кнопки, теперь про изменение состояния. Само нажатие кнопки не должно влечь за собой изменение цвета. Только подтверждение включения света на самом ESP8266 должно изменять цвет кнопки. Это было моей ошибкой при настройке МДМ. Я в коде отработал нажатие кнопки — и вручную менял состояния свойства. И в 95% случаев все работало как надо. Но бывало что связи с модулем нет, а состояние отображения кнопки менялось. Я радостно смотрел на зеленую кнопку, и грустно смотрел на отсутствие света в курятнике.
Поэтому теперь у меня все работает по обратной связи. Я нажимаю кнопку включения света — МДМ подает команду ESP8266 — тот в свою очередь включает свет, и отправляет команду МДМ на изменение свойства состояния света — и МДМ уже в свою очередь меняет нужное свойство, и меняет цвет кнопки..
Приступим… Переходим в редактирование сцены «KitchenScene» на вкладку «Элементы», добавляем новый элемент, называем «Свет», тип выбираем «HTML». Сохраняем, заходим в редактирование нашего элемента.
Привел всю страницу настроек в 1 файле, для увеличения нажмите на картинку.
Начнем с внешнего вида кнопок. Размеры и положение выбираете сами (положение опять же удобно делать вручную на вкладке «Просмотр/Редактировать»). Самое главное тут — «Дополнительный код CSS» — устанавливаем его в активное состояние. Сам код такой:
a.buttonOn { width: 140px; height: 70px; text-align: center; vertical-align: middle; display: inline-block; font-weight: bold; color: #fff; text-decoration: none; text-shadow: 0 -1px rgba(0,0,0,.5); user-select: none; padding: .7em 1.5em; border: 1px solid rgb(20,50,0); border-radius: 5px; outline: none; background: rgb(42,105,0) linear-gradient(rgb(64,161,0), rgb(42,105,0) 80%); box-shadow: 0 6px rgb(29,71,0), 0 3px 15px rgba(0,0,0,.4), inset 0 1px rgba(255,255,255,.3), inset 0 0 3px rgba(255,255,255,.5); transition: .2s; } .buttonOn:hover { background: rgb(64,161,0) linear-gradient(rgb(64,120,0), rgb(64,161,0) 80%); } .buttonOn:active { background: rgb(64,120,0) linear-gradient(rgb(64,120,0) 20%, rgb(64,120,0)); box-shadow: 0 2px rgb(29,71,0), 0 1px 6px rgba(0,0,0,.4), inset 0 1px rgba(255,255,255,.3), inset 0 0 3px rgba(255,255,255,.5); -webkit-transform: translate(0, 4px); transform: translate(0, 4px); } a.buttonOff { width: 140px; height: 70px; text-align: center; vertical-align: middle; display: inline-block; font-weight: bold; color: #fff; text-decoration: none; text-shadow: 0 -1px rgba(0,0,0,.5); user-select: none; padding: .7em 1.5em; border: 1px solid rgb(20,50,0); border-radius: 5px; outline: none; background: rgb(158,0,0) linear-gradient(rgb(194,0,0), rgb(158,0,0) 80%); box-shadow: 0 6px rgb(110,0,0), 0 3px 15px rgba(0,0,0,.4), inset 0 1px rgba(255,255,255,.3), inset 0 0 3px rgba(255,255,255,.5); transition: .2s; } .buttonOff:hover { background: rgb(194,0,0) linear-gradient(rgb(158,0,0), rgb(194,0,0) 80%); } .buttonOff:active { background: rgb(194,0,0) linear-gradient(rgb(194,0,0) 20%, rgb(194,0,0)); box-shadow: 0 2px rgb(110,0,0), 0 1px 6px rgba(0,0,0,.4), inset 0 1px rgba(255,255,255,.3), inset 0 0 3px rgba(255,255,255,.5); -webkit-transform: translate(0, 4px); transform: translate(0, 4px); }
Что за что отвечает — расписывать не буду, рассмотрение CSS в эту статью не входит. В двух словах — есть у кнопки два класса — и ее отображение зависит от активного состояния. Создадим их — переходим вниз страницы, нажимаем кнопку «Добавить новое состояние».
Называем состояние «ON», в поле «Код» вписываем классический код создания кнопки на HTML:
<button class="buttonOn">Свет включен</button>
Т.е. создаем кнопку, принадлежащую классу «buttonOn» с текстом «Свет включен». При нажатии на кнопку она что-то должна делать. В данном случае она будет отправлять запрос ESP8266 на отключение света. Выбираем в меню «Выполнить при клике» пункт «Код», и пишем код:
getURL('http://192.168.1.102/?light=0');
При нажатии на кнопку будет отправлен GET-запрос с командой отключения света.
Теперь сделаем так, чтобы данная кнопка отображалась только тогда, когда свет включен. В пункте «Условие отображения» ставим значение «Простое». И привязываем ее к значению поля «lihgt» объекта «ChickenObject». Получается, что данная кнопка отображается только тогда, когда свойство «lihgt» = 1, т.е. когда свет включен. Сохраняемся, создаем в этом же элементе новое состояние «OFF».
Тут все то же самое, отличия только в том, что кнопка принадлежит уже классу «buttonOff», при нажатии генерируется GET-запрос включения света «light=1», и кнопка отображается только если поле «light» = 0.
После сохранения можно загрузить главную страницу MajorDoMo, где мы увидим текущую температуру и нашу кнопку на фоне загруженной картинки.Нажимая на нее можем управрять освещением и лицезреть на спецэффекты :).
По этому же примеру создаем новые кнопки «Обогреватель» и «Автоматическое управление обогревателем».
Первая страница автоматизированного курятника готова. Графики я разместил на второй странице, т.к. контроль и управление происходит через мобильный телефон, и так мне удобнее.
Сперва создадим сами графики, потом добавим их на главную. Заходим в «Панель управления — Объекты — Графики». Нажимаем кнопку «Добавить».
Даем название, выбираем нужный период отображения — у меня 24 часа. Сам график появится после окончания настройки и сохрарения. У меня давно все создано, поэтому он виден. переходим на вкладку «Данные».
Главное тут — привязка к нужному свойству нужного объекта — «ChickenObject.temperaturaParsing», и тип графика. Мне больше нравится «Area».
После сохранения нам дадут ссылку на вставку графика в страницы, которую мы и будем использовать в дальнейшем.
Создаем остальные 3 графика — состояние освещения, уровень сигнала и свободную память, запоминаем ссылки на графики.
Теперь идем в «Панель управления — Настройки — Домашние страницы». Тут я возможно немного введу в заблуждение, т.к. не помню, что тут было в самом начале — как-никак 8 месяцев прошло. Но я покажу, как должно быть. Первый пунктом у нас стоит страница «Управление курятником». Создаем ее с такими настройками:
Название будет отображаться в виде кнопки/ссылки. Тип должен быть «Application», и в качестве приложения «scenes» — причем последнее пишется вручную (могу ошибаться).
Второй страницей создаем графики:
И записываем код для добавления графиков в нужном порядке.
В принципе все. Сохраняемся, загружаем главную страницу — и любуемся готовым устройством.
Слишком длинная статья вышла, поэтому добавление автоматического управления по планировщику, управление по Telegram, и получение доступа к веб-морде МДМ из интернета добавлю в следующей статье.
Ну эпилог. Почему не хотел писать статью про конкретную реализацию — из-за размещения кода HTML-страницы в оперативной памяти и возможной утечки памяти. Все-таки прошивка NodeMCU накладывает свои ограничения.
Исправлять косяки в прошивке я не хотел сознательно, т.к. во-первых есть костыль в виде МДМ, который полностью решает проблему, хотя конечно не устраняет ее на самом устройстве. Во-вторых уже через месяц пользования готовым устройством я понял, что его функций мне стало мало, и по-любому будет его следующая версия.
Захотелось сделать плавное управление яркостью света, чтобы имитировать закат. Замечено, что если свет гаснет резко — куры остаются там, где и были. Сидели в тарелке с едой — там и уснули. При плавном отключении света в течение 5-10 минут куры же возвращаются на свои жердочки и спят где покладено. А также захотелось получать видеоизображение из самого курятника, и состыковать его с сервером MajorDoMo. В мыслях еще было сделать открытие/закрытие двери нажатием на кнопку, чтобы не бегать каждое утро и каждый вечер самому, но это потребует дополнительной доработки двери, так что я еще подумаю. Хотя сложного ничего не вижу — недорогие актуаторы продаются на алиэкспресс. Встроить их в двери и вуаля 🙂
Но это все в планах. Железо уже все куплено, даже протестировано видеоизображение по WiFi благодаря модулю ESP32-Cam, а времени как всегда нет.