Новый датчик температуры/влажности с часами на электронных чернилах с BLE от Xiaomi появился в продаже относительно недавно. На вид это скорее часы, нежели датчик температуры и влажности – большую часть экрана занимают показания часов, а показания температуры и влажности пристроились скромно внизу вместе с индикатором комфорта, стилизованного под рожицу. Обзоров этих часов в инете хватает,
здесь, например.
Часы довольно симпатичные, мне понравились. К Mihome подключились без проблем. Стало интересно, можно ли передавать показания датчиков в систему «умного дома» Domoticz.
Поиск вывел на
сайт, где автор подробно расписал, как получить данные с этих часов.
У меня Domoticz установлен на raspberry pi 3 и первое, что попробовал — связаться с часами по блютузу, для этого наколхозил скрипт на python3:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from bluepy import btle
import struct
mac_addr_LYWSD02 = '3F:59:C8:61:10:CF'
class MyDelegate(btle.DefaultDelegate):
def handleNotification(self, cHandle, data):
global HUM
global TEMP
HUM = data[2]
TEMP = struct.unpack('h', data[:2])[0] / 100
p = btle.Peripheral(mac_addr_LYWSD02)
p.setDelegate(MyDelegate())
uuid = 'EBE0CCC1-7A0A-4B0C-8A1A-6FF2997DA3A6'
ch = p.getCharacteristics(uuid=uuid)[0]
desc = ch.getDescriptors(forUUID=0x2902)[0]
desc.write(0x01.to_bytes(2, byteorder="little"), withResponse=True)
print ("LYWSD02")
i = 0
while True:
HUM = 0
TEMP = 0
p.waitForNotifications(1)
i += 1
print(i,"T =",TEMP,"*C, H =", HUM,"%")
if i >= 20 :
break
p.disconnect()
В строке mac_addr_LYWSD02 = 'xx:xx:xx:xx:xx:xx' необходимо прописать MAC адрес своих часов, его можно посмотреть в приложении Mihome:
В результате (не сразу, конечно :) увидел следующие:
root@raspberrypi:/home/pi/domoticz/devices/lywsd02mmc# python3 test.py
LYWSD02
1 T = 0 *C, H = 0 %
2 T = 0 *C, H = 0 %
3 T = 26.11 *C, H = 44 %
4 T = 0 *C, H = 0 %
5 T = 0 *C, H = 0 %
6 T = 0 *C, H = 0 %
7 T = 0 *C, H = 0 %
8 T = 0 *C, H = 0 %
9 T = 26.08 *C, H = 44 %
10 T = 0 *C, H = 0 %
11 T = 0 *C, H = 0 %
12 T = 0 *C, H = 0 %
13 T = 0 *C, H = 0 %
14 T = 0 *C, H = 0 %
15 T = 26.08 *C, H = 44 %
16 T = 0 *C, H = 0 %
17 T = 0 *C, H = 0 %
18 T = 0 *C, H = 0 %
19 T = 0 *C, H = 0 %
20 T = 0 *C, H = 0 %
root@raspberrypi:/home/pi/domoticz/devices/lywsd02mmc#
Нулевые значения получились, потому что 1 сек мало для времени ожидания ответа в waitForNotifications(timeout). Подробнее можно посмотреть
здесь.Теперь можно было подумать о связке с Domoticz. В оборудовании добавил Dummy (Does nothing, use for virtual switches only):
Затем создал виртуальный датчик температуры/влажности:
В устройствах надо посмотреть Idx датчика, в моем случае 27:
Теперь попробуем новому датчику передать данные. В адресной строке браузера пишем:
http://192.168.8.10:8080/json.htm?type=command¶m=udevice&idx=27&nvalue=0&svalue=25;38;0
192.168.8.10:8080 – адрес Domoticz; idx=27 – индекс датчика. Подробнее можно почитать
здесь. В результате должны получить:
А в датчике появятся переданные значения температуры и влажности:
Все функционирует, осталось собрать воедино. Опять же наколхозил скрипт:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
domoticzserver = '127.0.0.1:8080'
domoticzusername = 'Admin'
domoticzpassword = 'admin'
IDX = '27'
mac_addr_LYWSD02 = '3F:59:C8:61:10:CF'
from bluepy import btle
import struct
import urllib.request
import base64
class MyDelegate(btle.DefaultDelegate):
def handleNotification(self, cHandle, data):
global HUM
global TEMP
HUM = data[2]
TEMP = struct.unpack('h', data[:2])[0] / 100
def _encodeBase64(consumer_key, consumer_secret):
dummy_param_name = 'bla'
key_url_encoded = urllib.parse.urlencode({dummy_param_name: consumer_key})[len(dummy_param_name) + 1:]
secret_url_encoded = urllib.parse.urlencode({dummy_param_name: consumer_secret})[len(dummy_param_name) + 1:]
credentials = '{}:{}'.format(key_url_encoded, secret_url_encoded)
bytes_base64_encoded_credentials = base64.encodebytes(credentials.encode('utf-8'))
return bytes_base64_encoded_credentials.decode('utf-8').replace('\n', '')
def domoticzrequest (url):
request = urllib.request.Request(url)
request.add_header("Authorization", "Basic %s" % base64string)
response = urllib.request.urlopen(request)
respond = response.read()
response.close()
return respond
p = btle.Peripheral(mac_addr_LYWSD02)
p.setDelegate(MyDelegate())
uuid = 'EBE0CCC1-7A0A-4B0C-8A1A-6FF2997DA3A6'
ch = p.getCharacteristics(uuid=uuid)[0]
desc = ch.getDescriptors(forUUID=0x2902)[0]
desc.write(0x01.to_bytes(2, byteorder="little"), withResponse=True)
print ("LYWSD02")
i = 0
HUM = 0
TEMP = 0
while not p.waitForNotifications(5):
i += 1
if i >= 20 :
break
p.disconnect()
print(i,"T =",TEMP,"*C, H =", HUM,"%")
base64string = _encodeBase64(domoticzusername, domoticzpassword)
HUM_STAT = "0" # Норма
if HUM > 85:
HUM_STAT = "3" # Влажно
elif HUM < 20:
HUM_STAT = "2" # Сухо
elif HUM <= 60 and HUM >= 40:
HUM_STAT = "1" # Комфортно
print(domoticzrequest("http://" + domoticzserver + "/json.htm?type=command¶m=udevice&idx=" + IDX + "&nvalue=0&svalue=" + str(TEMP)[:4] + ";" + str(HUM) + ";" + HUM_STAT))
В начале скрипта необходимо прописать свои данные:
Скрипт я назвал lywsd02.py и поместил в :/home/pi/domoticz/devices/lywsd02mmc. При запуске, если все хорошо, в датчике отобразятся данные с часов, а в консоли увидим:
root@raspberrypi:/home/pi/domoticz/devices/lywsd02mmc# python3 lywsd02.py
LYWSD02
0 T = 26.12 *C, H = 44 %
b'{\n "status" : "OK",\n "title" : "Update Device"\n}\n'
root@raspberrypi:/home/pi/domoticz/devices/lywsd02mmc#
# Здесь во второй строчке данные полученные от часов, в третьей строчке ответ от Domoticz
Ну и осталась самая малость — прописать запуск скрипта каждый час в cron:
# Даем права на исполение:
pi@raspberrypi:~/domoticz/devices/lywsd02mmc $ sudo chmod 755 lywsd02.py
# Теперь пропишем запуск скрипта в cron:
pi@raspberrypi:~/domoticz/devices/lywsd02mmc $ sudo crontab -e
# В nano правим: в начале вставляем строчку
MAILTO=""
# в конце дописывам:
0 * * * * /home/pi/domoticz/devices/lywsd02mmc/lywsd02.py > /dev/null 2>&1
# Ctrl X сохраняем, получаем:
crontab: installing new crontab
# Перегружаемся:
pi@raspberrypi:~/domoticz/devices/lywsd02mmc $ sudo reboot
Ну вот, как-то так.
И да, это не готовое решение, а скорее размышление и эксперимент.
В дополнение добавлю еще несколько строк:
Значение уровня батареи можно узнать по handle: 0x0052. При чтении получим 1 byte от 0 до 0x64 (0..100%)
С учетом этого скрипт для Domoticz Будет выглядеть:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from bluepy import btle
import struct
import urllib.request
import base64
domoticzserver = '127.0.0.1:8080'
domoticzusername = 'Admin'
domoticzpassword = 'admin'
IDX = '27'
mac_addr_LYWSD02 = '3F:59:C8:61:10:CF'
class MyDelegate(btle.DefaultDelegate):
def handleNotification(self, cHandle, data):
global HUM
global TEMP
HUM = data[2]
TEMP = struct.unpack('h', data[:2])[0] / 100
def _encodeBase64(consumer_key, consumer_secret):
dummy_param_name = 'bla'
key_url_encoded = urllib.parse.urlencode({dummy_param_name: consumer_key})[len(dummy_param_name) + 1:]
secret_url_encoded = urllib.parse.urlencode({dummy_param_name: consumer_secret})[len(dummy_param_name) + 1:]
credentials = '{}:{}'.format(key_url_encoded, secret_url_encoded)
bytes_base64_encoded_credentials = base64.encodebytes(credentials.encode('utf-8'))
return bytes_base64_encoded_credentials.decode('utf-8').replace('\n', '')
def domoticzrequest (url):
request = urllib.request.Request(url)
request.add_header("Authorization", "Basic %s" % base64string)
response = urllib.request.urlopen(request)
respond = response.read()
response.close()
return respond
p = btle.Peripheral(mac_addr_LYWSD02)
p.setDelegate(MyDelegate())
uuid = 'EBE0CCC1-7A0A-4B0C-8A1A-6FF2997DA3A6'
ch = p.getCharacteristics(uuid=uuid)[0]
desc = ch.getDescriptors(forUUID=0x2902)[0]
desc.write(0x01.to_bytes(2, byteorder="little"), withResponse=True)
print ("LYWSD02")
i = 0
HUM = 0
TEMP = 0
while not p.waitForNotifications(5):
i += 1
if i >= 20 :
break
batt = p.readCharacteristic(0x0052)
battlev = struct.unpack('B', batt)[0]
p.disconnect()
print(i,"T =",TEMP,"*C, H =", HUM,"% BATT = ", battlev, "%" )
base64string = _encodeBase64(domoticzusername, domoticzpassword)
HUM_STAT = "0" # Норма
if HUM > 85:
HUM_STAT = "3" # Влажно
elif HUM < 20:
HUM_STAT = "2" # Сухо
elif HUM <= 60 and HUM >= 40:
HUM_STAT = "1" # Комфортно
print(domoticzrequest("http://" + domoticzserver + "/json.htm?type=command¶m=udevice&idx=" + IDX + "&nvalue=0&svalue=" + str(TEMP)[:4] + ";" + str(HUM) + ";" + HUM_STAT + "&battery=" + str(battlev)))
Отобразить значение температуры на экране часов в градусах Фарингейта можно записав по handle: 0x0049 значение 0x01 (0xFF — отображение в градусах Цельсия).
Ну и собственно, значение текущего времени можно прочитать по handle: 0x003e. Получим 5 bytes, первые четыре — время в секундах от 00:00:00 01.01.1970, последний байт — временная зона.
Вот пример скрипта для синхронизации часов с от RTC малинки
#!/usr/bin/env python3
import time
from bluepy import btle
import struct
tzlocal = 3
print("LYWSD02 time update")
mac = '3F:59:C8:61:10:CF'
p = btle.Peripheral(mac)
data_r = p.readCharacteristic(0x003e)
print("Read(0x003e) = ", data_r)
print ("LYWSD02 time: ", time.ctime(struct.unpack('I', data_r[:4])[0]))
print("Sistem time:", time.ctime())
sis_time = int(time.time())
data_w = struct.pack("<I", sis_time) + bytes([tzlocal])
print ("Write(0x003e) = ", data_w)
p.writeCharacteristic(0x003e,data_w,False)
time.sleep(5)
data_r = p.readCharacteristic(0x003e)
print("Read(0x003e) = ", data_r)
print ("LYWSD02 time: ", time.ctime(struct.unpack('I', data_r[:4])[0]))
p.disconnect()
Когда, где и кем?
с ней все впорядке, смысла в ней особого нет.
>>Когда, где и кем?
С момента появления 2го ядра… прирост 1-2%, что в общей работе системы вообще никак не сказывается.
Хочется копатся с ней, пожалуйста, я сказал, что потраченое на генту время не стоит того…
upd: А, по вашей ссылке как раз о прикручивании к нему и речь.
Маловаты по мне, увеличить бы их раза в полтора. Но и эти видно хорошо. в пару комнат уже поставил.
Как данные с часов предаются, я понял. Но там еще и синхронизация часов по времени интернета происходит. Извините старого связиста, но мне то, где это прописано в скриптах, не очевидно.
Ссылка на мой пост на 4PDA
Рад, что у других всё повеселее, чем у меня) Спасибо за статью)
В смысле прикрутить к кондею через шлюз, чтобы по температуре включал и отключал кондей?
Был какой то круглый, но работал так себе.
Молотит все время. Некомфортно.
Вопрос не в этом, а в том какой из всех датчиков сяоми наиболее точен/адекватен и не тормозит в системе умный дом со шлюзом? Был круглый, какой то через чур задумчивый оказался.
И да, всё уже придумано ---> github.com/h4/lywsd02
Было бы классно использовать эти часы, как дополнительный терминал умного дома.
Я уже думал в этом ключе. Пока только по взломанному протоколу передаю на Oregon значения дополнительных датчиков.
Тут очевидно часы = датчик, вряд ли может он принимать
ebay.com/itm/113839074614
uuid = 'EBE0CCC1-7A0A-4B0C-8A1A-6FF2997DA3A6'
И вот эту?
desc = ch.getDescriptors(forUUID=0x2902)[0]
Реверс инженеринг или есть открытое API?
В начале считывается время с часов, затем записывается время с системных часов и через 5с считывается уже обновленное время:
Можно добавить в cron и запускать раз месяц, например.
1576878932000000000 45 0.01
1576878998000000000 45 0.01
1576879052000000000 45 655.34
1576879118000000000 45 655.3
1576879172000000000 45 655.35
температура — правая колонка. это с помощью этой библиотеки получено github.com/h4/lywsd02
С вашим скриптом тоже такие цифры
3 T = 654.38 *C, H = 41 %