Ось є час під час домашнього карантину.
Відновив старий 7" планшет Impression ImPAD 1213 на основі процесора Allwinner A13 з Android 4.0.4, перепрошивши прошивку від виробника.
Але що з ним можна зробити ? Він дуже "тугий", але Google Play Market працює.
Тому встановив програму MQTT Dash (IoT, Smart Home), і вирішив реалізувати простішу панель для показу поточних даних погоди.
Хоча простіше можна було б показати готовий віджет від відомих погодних сайтів на головному екрані планшета.
Віджет від відомих погодних сайтів |
Але головна мета розібратися з керування через MQTT пристроями в розумному домі. Тому іду іншим шляхом.
OpenWeather
Для отримання даних вирішив використати ресурс OpenWeather. Для використання API функціоналу потрібно мати персональний appid, котрий можна отримати після реєстрації користувача. Користувач може вибрати різні тарифні плани у тому числі і "Free".За своїм тарифним планом "Free", я вибрав данні: Current weather data ,
Приклад запиту за ID мого міста, можна завантажити і знайти свій ID: city ID.
By city ID: api.openweathermap.org/data/2.5/weather?id={city id}&appid={your api key} Parameters: id City ID Examples of API calls: api.openweathermap.org/data/2.5/weather?id=706483&appid={your api key}&units=metric&lang=uaРезультат є у форматі JSON:
{ "coord": { "lon": 36.25, "lat": 50 }, "weather": [ { "id": 800, "main": "Clear", "description": "чисте небо", "icon": "01n" } ], "base": "stations", "main": { "temp": 5.22, "feels_like": -1.2, "temp_min": 5.22, "temp_max": 5.22, "pressure": 1030, "humidity": 58, "sea_level": 1030, "grnd_level": 1011 }, "wind": { "speed": 5.88, "deg": 339 }, "clouds": { "all": 0 }, "dt": 1586109523, "sys": { "country": "UA", "sunrise": 1586055742, "sunset": 1586103149 }, "timezone": 10800, "id": 706483, "name": "Kharkiv", "cod": 200 }
Візуальний результат у форматі JSON з OpenWeather |
Брокер MQTT - mosquitto.
Пристрої розумного дому обмінються за протоколом MQTT:MQTT (англ. Message Queue Telemetry Transport) — спрощений мережевий протокол, що працює на TCP/IP. Використовується для обміну повідомленнями між пристроями за принципом видавець-підписник.
Для використання MQTT необхідно мати прграмму брокер, вона приймає повідомлення від одного пристрою і розсилає це повідомлення всім іншим підписникам брокеру.
Для розміщення програми я вибираю свій домашній роутер, що працює на основі OpenWrt. Серед готових пакунків для роутера вибираю відому програму mosquitto.
opkg update opkg list | egrep ^mosquitto- opkg install mosquitto-nossl opkg install mosquitto-client-nossl
OpenWrt - mosquitto |
Для передачі данних на mosquitto сервер, використовується mosquitto_pub.
Для розподілення різних данних один від одного на сервері використовуть message-topic.
Для пердачі даних з погодного серверу OpenWeather я використовую OpenWrt, на якому встанвленно і сервер mosquitto з топіком : wrt/temp-inet.
Приклад надсилання простого повідомлення з даними вологості:
mosquitto_pub -h 127.0.0.1 -u userwrt -P password81 -t wrt/temp-inet -m '58%'Приклад надсилання повідомлення з зображенням використовуючи файл:
mosquitto_pub -h 127.0.0.1 -u userwrt -P password81 -t wrt/temp-inet -f 'image.png'Приклад надсилання повідомлення з потоку іншої програми:
echo test123 | mosquitto_pub -h 127.0.0.1 -u userwrt -P password81 -t wrt/temp-inet -s
Для автоматизації процесу, результат з OpenWeather отримую програмою wget , параметри q -O - дозволять видавати результат до потоку іншої програми.
Ось скрипт для автоматизації процесу для надсилання кожні 10 хв.
#!/bin/sh appid=xxxxxxxxxxxxxxxxxxxxxxxxxxxx cityid=706483 topic=wrt/temp-inet user=user1 passw=changeme1 wget -q -O - "https://api.openweathermap.org/data/2.5/weather?id=${cityid}&appid=${appid}&units=metric&lang=ua"| \ mosquitto_pub -h 127.0.0.1 -u ${user} -P ${passw} -t ${topic} -s -q 1 -r
Таким чином кожні 10 хв, данні передаються до MQTT брокера в топік wrt/temp-inet.
Якщо в сервері mosquitto вимкнено опіцію allow_duplicate_messages false, то до інших клієнтів котрі підписанні до сервера будуть надсилатися тільки данні у випадку коли вони змінилися.
MQTT Dash
Для візуалізації на планшеті встановлено програму кліент : MQTT Dash. котра підписується на топік брокера для отримання повідомлень.В програмі MQTT Dash додаються об'єкти різних типів:
Об'єкти MQTT Dash |
Вологість (main.humidity) |
Температура (main.temp) |
Атмосферний тиск (main.pressure) |
Опис (weather[0].description) |
Вітер (wind.speed) |
Блимання при значенні вітру більше 10 м/с |
Зображення іконки з погодою (weather[0].icon) |
Обробка даних при показі даних за допомогою JavaSctipt |
Обробка коду у JavaSctipt onDisplay event |
Для простоти налаштування я робив налаштування в своєму мобільному телефоні, а потім налаштування публікував у топік metrics/exchange засобами програми.
Export / Import налаштувань через metrics/exchange |
В результаті отримав налаштований планшет.
Планшет з погодними даними |
Споживання енергії
Вимкнений екран, 1W |
Ввімкнений екран, 2W |
Данні радіоактивного забруднення
Якщо проаналізувати сайт "Український гідрометеорологічний центр" то можна знайти інформацію у JSON форматі котра передається до карт Google у масиві.
Український гідрометеорологічний центр. Рівень радіоактивного забруднення |
І якщо зробити вибірку з масиву за номером станції stationCode = 34300 різними засобами, то можна побачити поточні данні радіоактивного забруднення в місті Харків:
{ "id": "89", "stationCode": "34300", "stationName": "Харків", "latitude": "49.927978", "longitude": "36.282428", "height": "156", "zievert": "61.11", "roentgen": "7", "measurementDate": "09.4.18", "measurementTime": "9", "iconURL": "/radiations/edose_pic/50_100.png" }А зображення іконки загального стану за адресою "iconURL" з префіксом: https://meteo.gov.ua, тобто: https://meteo.gov.ua/radiations/edose_pic/50_100.png
Для пошуку даних з масиву в OpenWRT можна використати пакунок jq.
opkg update opkg install jq
jq ".[] | select(.stationCode == \"${cityid}\") "Скрипт для передачі вибраних даних до брокера:
#!/bin/sh url=https://meteo.gov.ua/radiations/dataSourceFiles/googleMapDataSource.json cityid=34300 topic=wrt/rad-inet user=userwrt passw=password81 wget -q --no-check-certificate -O - "${url}" | \ jq ".[] | select(.stationCode == \"${cityid}\") " | \ mosquitto_pub -h 127.0.0.1 -u ${user} -P ${passw} -t ${topic} -s -q 1 -r
В результаті тестування іконка загального стану не звантажувалася до програми, тому я замінив її різним кольором тексту в залежності від рівня зараження.
on Display для виділення кольором рівня радіації |
Деякі візуальні доопрацювання
Використовуючи можливості обробки у JavaScript, використовуючи JSON parser цієї мови, я об'єднав параметри для більш ефективного відображення.
Так опис погоди об'єднав з іконкою погоди замінивши заголовок при обробці скриптами on Receive та on Display:
Так опис погоди об'єднав з іконкою погоди замінивши заголовок при обробці скриптами on Receive та on Display:
Обробка даних про іконку погоди та опису |
Для температури об'єднав декілька параметрів:
Отримання та обробка даних температури |
Показ даних температури |
Додав статичне зображення мапи з урахування даних хмарності, завантажуючи її за адресою: https://meteo.ua/var/zip/Sputn-24.jpg, оновлюючи її кожні 3600 секунд.
Інфрачервоний канал - Хмарність |
В результаті маю такий вигляд додатку:
MQTT Dash та погода. |
Максимум / Мінімум
Якщо треба відслідковувати максимальні мінімальні значення, наприклад температури, то зберігаємо їх данні в об'єктах скрипт обробки. О 4:10 щодня, макс. мін. значення фіксуюються поточними значеннями.
on receive:
if (!event.data) { event.data = {}; } obj=JSON.parse(event.payload).main; event.data['fl'] = obj.feels_like; event.data['temp'] =obj.temp; d=new Date(); m=d.getMinutes(); h=d.getHours(); r=(h==4 && m==10); if (r || !event.data['tmax']) event.data['tmax'] = obj.temp; if (r || !event.data['tmin']) event.data['tmin'] = obj.temp; if (event.data['temp']>event.data['tmax'] ) event.data['tmax']=event.data['temp']; if (event.data['temp']<event.data['tmin'] ) event.data['tmin']=event.data['temp'];on display:
if (event.data) { t=event.data['tmax'] +"\n[ "+event.data['temp'] +" ]\n"+event.data['tmin']; event.text=t; }Для скидання значень "max/min" до поточних.
on tap:
if (!event.data) { event.data = {}; } if (event.data['temp']){ event.data['tmax'] = event.data['temp']; event.data['tmin'] = event.data['temp']; }
Дата / час
Для показу поточного часу можна використати JavaSctipt клас Date.
on display:
d=new Date(); event.text=d.toTimeString().split(' ')[0];
Температура зі значеннями Максим./[ Поточ. ]/Мінім. , поточний час |
Усі налаштування
Для використання Export / Import налаштувань через топік: metrics/exchange можна використати мої усі налаштування з очищеними значеннями для lastPayload за допомогою редактору jsoneditoronline.org:
[ { "decimalPrecision": 0, "displayPayloadValue": true, "maxValue": 100, "minValue": 0, "postfix": "%", "prefix": "", "progressColor": -64, "enableIntermediateState": true, "enablePub": false, "enteredIntermediateStateAt": 0, "intermediateStateTimeout": 0, "jsOnReceive": "", "jsonPath": "main.humidity", "lastJsonPathValue": "57", "lastPayload": "", "qos": 1, "retained": false, "topic": "wrt/temp-inet", "topicPub": "", "updateLastPayloadOnPub": true, "id": "c28934a3-37bc-4e2f-9565-7ad1d70d0b04", "jsBlinkExpression": "", "jsOnDisplay": "", "jsOnTap": "", "lastActivity": 1587754200, "longId": 4, "name": " Вологість", "type": 3 }, { "mainTextSize": "SMALL", "postfix": "", "prefix": "", "textColor": -49088, "enableIntermediateState": true, "enablePub": false, "enteredIntermediateStateAt": 0, "intermediateStateTimeout": 0, "jsOnReceive": "if (!event.data) { event.data = {}; } \n \nobj=JSON.parse(event.payload).main;\nevent.data['fl'] = obj.feels_like;\nevent.data['temp'] =obj.temp;\nd=new Date();\nm=d.getMinutes();\nh=d.getHours();\nr=(h==4 && m==10);\n\nif (r || !event.data['tmax']) \n event.data['tmax'] = obj.temp;\nif (r || !event.data['tmin']) \n event.data['tmin'] = obj.temp;\nif (event.data['temp']>event.data['tmax'] )\n event.data['tmax']=event.data['temp'];\nif (event.data['temp']= 11", "jsOnDisplay": "val=99", "jsOnTap": "", "lastActivity": 1587754200, "longId": 7, "name": "Вітер, м/с", "type": 1 }, { "imageUrl": "http://openweathermap.org/img/wn/04n@2x.png", "kind": 2, "openUrl": "", "reloadInterval": 300, "enableIntermediateState": true, "enablePub": false, "enteredIntermediateStateAt": 0, "intermediateStateTimeout": 0, "jsOnReceive": "if (!event.data) { event.data = {}; } \nobj=JSON.parse(event.payload).weather[0];\nif (!obj.icon.startsWith('http')){\n event.data['ico'] = 'http://openweathermap.org/img/wn/'+obj.icon+'@2x.png';\n}\nevent.data['tit'] = obj.description;", "jsonPath": "weather[0].icon", "lastJsonPathValue": "04n", "lastPayload": "", "qos": 0, "retained": false, "topic": "wrt/temp-inet", "topicPub": "", "updateLastPayloadOnPub": true, "id": "66f4abfc-3c59-4932-be4c-22f7a421516f", "jsBlinkExpression": "", "jsOnDisplay": "if (event.data['ico']!==event.url){\n event.url=event.data['ico'];\n}\nevent.name=event.data['tit'];", "jsOnTap": "", "lastActivity": 1587754200, "longId": 3, "name": "Зобр. погоди", "type": 5 }, { "imageUrl": "https://meteo.ua/var/zip/Sputn-24.jpg?u=62501", "kind": 1, "openUrl": "", "reloadInterval": 3600, "enableIntermediateState": true, "enablePub": false, "enteredIntermediateStateAt": 0, "intermediateStateTimeout": 0, "jsOnReceive": "", "jsonPath": "weather[0].icon", "qos": 0, "retained": false, "topic": "wrt/temp-inet", "topicPub": "", "updateLastPayloadOnPub": true, "id": "120b696a-04f5-45a3-82f1-a87991cfefef", "jsBlinkExpression": "", "jsOnDisplay": "", "jsOnTap": "", "lastActivity": 1587754193, "longId": 12, "name": "", "type": 5 }, { "mainTextSize": "MEDIUM", "postfix": "", "prefix": "", "textColor": -4144960, "enableIntermediateState": true, "enablePub": false, "enteredIntermediateStateAt": 0, "intermediateStateTimeout": 0, "jsOnReceive": "if (!event.data) { event.data = {}; } \n \nobj=JSON.parse(event.payload);\nevent.data['r'] = obj.roentgen;\nevent.data['z'] =obj.zievert;", "jsonPath": "roentgen", "lastJsonPathValue": "10", "lastPayload": "", "qos": 1, "retained": false, "topic": "wrt/rad-inet", "topicPub": "", "updateLastPayloadOnPub": true, "id": "e512c0d1-0cf2-47d2-be05-18d835091824", "jsBlinkExpression": "val>11", "jsOnDisplay": "event.text=event.data['z']+\"\\n\"\n+event.data['r'];\n\nc='#ffffff';\nv=event.data['z'];\nif (v > 0 && v < 50 ){\n c='#00D0FC';\n} else if (v >= 50 && v < 100){\n c='#B4FF33';\n} else if (v >= 100 && v < 150){\n c='#BFFF33';\n} else if (v >= 150 && v < 200){\n c='#FF6000';\n} else if (v >= 200 && v < 250){\n c='#FF5E33';\n} else if (v >= 250){\n c='#FF0000';\n}\n\n\n\nevent.textColor = c;", "jsOnTap": "", "lastActivity": 1587754193, "longId": 16, "name": "Радіація, нЗв/год, мРн/год", "type": 1 }, { "mainTextSize": "MEDIUM", "postfix": "", "prefix": "", "textColor": -1, "enableIntermediateState": true, "enablePub": false, "enteredIntermediateStateAt": 0, "intermediateStateTimeout": 0, "jsOnReceive": "", "jsonPath": "", "qos": 0, "retained": false, "topic": "", "topicPub": "", "updateLastPayloadOnPub": true, "id": "81bffc3d-ab44-4772-ae8a-ee99279ec439", "jsBlinkExpression": "", "jsOnDisplay": "d=new Date();\nevent.text=d.toTimeString().split(' ')[0];", "jsOnTap": "", "lastActivity": 0, "longId": 15, "name": "Поточний час", "type": 1 } ]
Використати можна як зберігти ці налаштуваня до файлу, напиклад export.json, і опублікувати до топіку : metrics/exchange.
mosquitto_pub -h <host>-u <user> -P <password> -t metrics/exchange -r -f ./export.json
Немає коментарів:
Дописати коментар