Latar belakang: sebagai salah satu hobi saya, saya memiliki "Rumah Pintar". Saya ingin perangkat yang indah, tetapi saya juga menginginkan kebebasan dan privasi. Oleh karena itu, saya terlibat dalam persilangan Xiaomi uzhik dengan
Asisten Rumah landak.
Untuk menjaga lingkungan yang nyaman, kita perlu tahu apa yang sedang terjadi di rumah. Singkatnya, sensor diperlukan. Xiaomi memiliki banyak yang berbeda, tetapi yang paling saya sukai adalah termometer persegi pada tinta elektronik. Tapi dia sama sekali tidak pintar dalam arti bahwa dia tidak menyediakan antarmuka sama sekali kecuali yang grafis - tidak WiFi, atau BLE, atau ZigBee. Tapi baterai CR2032 bertahan selama beberapa tahun. Ada juga versi dengan bluetooth, tetapi sedikit kurang elegan - semacam pancake tebal.
Dan pada awal musim semi, sensor suhu / kelembaban baru diumumkan, dengan tinta elektronik, dengan BLE, dan bahkan dengan jam. Saya tidak benar-benar membutuhkan arloji, tetapi semua yang lain segera menekan semua argumen rasional dan termometer dipesan di salah satu toko online populer, berdasarkan pesanan. Ia melaju, melaju, dan akhirnya tiba.

Sensor ditambahkan ke aplikasi MiHome tanpa masalah (saya memiliki antarmuka berbahasa Inggris di mana-mana, dengan MiHome versi Rusia, kata mereka, ada kesulitan terjemahan). Menunjukkan nilai saat ini dan riwayat perubahan dalam bacaan.
Tetapi dengan integrasi di Home Assistant kesulitan telah terjadi. Komponen yang ada untuk sensor suhu sama sekali tidak ingin mengambil data dari perangkat dan mengeluh tentang format data yang salah. Yah, tidak ada yang bisa dilakukan, kami mengeluarkan sekop dan mulai menggali.
Pikiran pertama adalah untuk berkenalan dengan perangkat protokol BLE, tetapi setelah mengevaluasi ukuran dokumentasi, diputuskan untuk beralih ke metode poke populer.
Pendekatan pertama ke shell
Untuk memulai, buka terminal di ubuntu dan jalankan bluetoothctl. Kami melihat yang berikut:
[NEW] Controller 00:1A:7D:DA:71:13 fett [default] [NEW] Device 3F:59:C8:80:70:BE LYWSD02 [NEW] Device 4C:65:A8:DC:0D:AF MJ_HT_V1
MJ_HT_V1 adalah sensor suhu lama, LYWSD02 adalah yang baru. Perbedaan dalam format penamaan model agak mengkhawatirkan.
Maka Anda perlu membaca entah bagaimana, dan data seperti apa yang secara umum dapat diperoleh dari kami. Dia membuka sumber perpustakaan
mitemp , yang digunakan di Asisten Rumah untuk menerima data dari sensor lama. Di sana saya menemukan bahwa perpustakaan blewrap digunakan, yang, pada gilirannya, adalah pembungkus pada dua perpustakaan Python untuk bekerja dengan BLE. Saya tidak membutuhkan banyak lapisan, kami akan menggunakan
warna biru . Ada dokumentasi, tidak banyak dan tidak sedikit, kami membaca dan menulis skrip yang melewati semua bidang data yang ada di perangkat.
from bluepy import btle mac = '3F:59:C8:80:70:BE' p = btle.Peripheral(mac) for s in p.getServices(): print('Service:', s.uuid) for c in s.getCharacteristics(): print('\tCharacteristic:', c.uuid) print('\t\t', c.propertiesToString()) if c.supportsRead(): print('\t\t', c.read())
Secara umum, semuanya sederhana - perangkat BLE menyediakan serangkaian layanan, yang masing-masing terdiri dari serangkaian karakteristik. Setiap karakteristik dapat menjadi salah satu dari 8 jenis, untuk satu karakteristik Anda dapat menentukan beberapa jenis secara bersamaan. Layanan dan fitur diidentifikasi dalam dua cara - alamat dalam bentuk nilai HEX dan UUID. Saya lebih terbiasa bekerja dengan UUID.
Jadi, saya mempertimbangkan semua spesifikasi untuk kedua sensor, melihat mereka dan menyadari bahwa lagi perangkat dari produsen yang sama sekali berbeda dijual di bawah merek Xiaomi. Di antara nilai-nilai sensor lama, "Cleargrass Inc" ditemukan, dan dalam yang baru, "miaomiaoce.com". Struktur layanan dan karakteristik kedua sensor ini juga sangat berbeda, dan daftar karakteristik sensor baru dua kali lebih panjang. Kemudian menjadi jelas bahwa Anda perlu menulis perpustakaan Anda sendiri untuk diintegrasikan dengan sensor (tidak, tentu saja saya googled pada awalnya, mungkin ada sesuatu yang berguna berdasarkan permintaan LYWSD02, tetapi saya tidak memberikan apa pun yang masuk akal google).
Jadi, bagaimana Anda mendapatkan data?
Di antara jenis karakteristik yang tersedia, selain BACA, ada juga TULIS dan PEMBERITAHUAN. MENULIS - untuk mengirim data ke perangkat, dan PEMBERITAHUAN - untuk menerima data. Ada juga MENULIS PEMBERITAHUAN pada saat yang sama - perangkat hanya akan mengirim data setelah berlangganan dengan mengirimkan byte yang diinginkan dengan perintah WRITE.
Upaya untuk melakukan sesuatu dengan tangan saya tidak membuahkan hasil, garis putus asa pertama tercapai, tetapi pada saat itu saya membaca artikel tentang kerajinan tangan berdasarkan chip dari Nordic Semiconductors dan meletakkan program
nRF Connect di ponsel cerdas saya. Dengan bantuannya, saya dapat berlangganan semua layanan yang disediakan perangkat, menyimpan log jawaban dan mulai mencoba memahami apa yang ada di dalamnya.

Tiga panah ini mengaktifkan langganan.
Fitur dari sensor lama adalah bahwa data tentang suhu dan kelembaban datang dalam bentuk string UTF, sedangkan yang baru mengembalikan semuanya dalam bentuk biner.
Berlangganan Notifikasi
Untuk menerima data dari sensor, Anda harus mengirim permintaan berlangganan. Di perpustakaan mitemp, dua byte untuk karakteristik dikirim untuk ini, tetapi tidak jelas di mana mendapatkannya. Di sini saya melihat struktur data untuk sensor lama di nRF Connect dan saya perhatikan bahwa alamat yang diinginkan ditentukan untuk karakteristik dengan data, seperti deskriptor. Kemudian saya mulai membaca dokumentasi untuk bluepy lagi dan menyadari bahwa alamat deskriptor dapat dengan mudah diperoleh dari objek karakteristik. Tinggal menulis kelas dengan metode panggilan balik, yang akan menerima data dari notifikasi.
class MyDelegate(btle.DefaultDelegate): def handleNotification(self, cHandle, data): print(data) mac_addr = '3F:59:C8:80:70:BE' p = btle.Peripheral(mac_addr) p.setDelegate(MyDelegate()) uuid = 'EBE0CCC1-7A0A-4B0C-8A1A-6FF2997DA3A6'
Kami memisahkan biji-bijian dari sekam
Untungnya, hanya tiga karakteristik yang ditandai sebagai WRITE NOTIFY, sementara datanya datang dengan frekuensi yang berbeda dan, um ..., fitur visual.
Permintaan pertama segera mengirim catatan besar data, dan kemudian macet. Dalam hal ini, byte pertama adalah jumlah yang meningkat secara monoton. Ini tampaknya merupakan akumulasi sejarah rata-rata.
Yang kedua dan ketiga dikirim secara berkala, tetapi melihat dari dekat, saya melihat bahwa salah satunya tidak berubah, dan dalam data yang kedua hanya satu byte yang diubah. Nah, maka ini adalah waktu sekarang (saya ingatkan Anda bahwa termometer ini memiliki jam. Seharusnya ada jam di perangkat yang menghargai diri sendiri untuk rumah pintar).
Misalkan karakteristik ketiga adalah data yang berguna tentang suhu dan kelembaban. Untuk mengkonfirmasi hipotesis, percobaan fisik dilakukan - dia pergi ke sensor dan menghela nafas. Nilai data meningkat tajam pada tampilan, dan byte berubah di terminal. Hore, data ada di suatu tempat di dekatnya.
Penguraian data
Saya biasanya bekerja dengan data teks (dapatkan data HTTP dalam bentuk JSON / xml, taruh dalam file atau dalam database), jadi saya tidak benar-benar mengerti cara mendekati tugas. Oleh karena itu, saya mulai mencoba mengubah data dengan berbagai cara yang dapat dibuat dari python. Saya menulis di sini fungsi konversi dan mulai mengamati bagaimana ini berkorelasi dengan data pada layar sensor.
def parse(v): print([x for x in v]) print('{0:#x}'.format(int.from_bytes(data, byteorder='big'))) print('{0:#x}'.format(int.from_bytes(data, byteorder='little')))
Garis-garis berbagai tingkat ketidakjelasan mengalir ke konsol, tetapi byte ketiga selalu berupa angka, dan angka ini bertepatan dengan nilai kelembaban. Demi kesetiaan, saya sekali lagi menghirup sensor - dan nilai kelembaban pada layar dan di byte ketiga berubah sama!
Kemudian saya menyarankan agar suhu disimpan dalam dua byte pertama. Agar data berubah, saya memindahkan sensor ke gantungan handuk yang dipanaskan di kamar mandi. Tetapi tidak peduli bagaimana saya mencoba mengubah hasilnya, angka-angka yang diperlukan tidak berhasil.
Di jalan menuju kesuksesan
Pada saat itu, saya melihat lagi pada
deskripsi sensor dan melihat ada sensor dari Swiss Sensirion di dalamnya. Mungkin layak dimulai dengan ini, tetapi ini bukan metode kami. Sekelompok sensor ditemukan di situs web Sensirion Swiss, dan lembar data untuk mereka. Dalam lembar data, antara lain, rumus ditemukan untuk mengkonversi byte yang dikirimkan melalui bus I2C ke nomor.
Tapi ... Ternyata nilainya sangat aneh. Sesuatu seperti -34.66, tapi aku jelas lebih hangat. Dari kesedihan dan kesedihan, saya bahkan membuka sensor dan memeriksa apakah sensor dari Swiss Sensirion benar di sana. Ternyata itu benar, tetapi dengan indeks SHTC3, dan itu membutuhkan formula yang sedikit berbeda.
Namun, semua sama, data setelah konversi bahkan tidak terlihat mendekati nyata. Di sini saya bahkan lebih sedih, membuka kode sumber perpustakaan untuk SHTC3 Adfruit dan mulai mencoba mengadaptasi kode transformasi dari C ++ ke python. Saya membawa semuanya ke tablet - data mentah, struktur dan hasil yang dikonversi.
def handleNotification(self, cHandle, data): temp = data[:2] humid = data[2] unpacked = struct.unpack('H', temp)[0] print(data, unpacked, -45 + 175 * unpacked / 2 ** 19, sep='\t')
Mendapat sesuatu seperti ini:
b',\n2' 2604 -44.130821228027344 b'-\n2' 2605 -44.1304874420166 b'+\n2' 2603 -44.131155014038086 b',\n2' 2604 -44.130821228027344
Ya ... agak dingin ... Tapi, tunggu, tunggu, apa itu 2604? Ini dia, 26.0 derajat di layar! Untuk mengkonfirmasi hipotesis, ia kembali mengambil sensor ke baterai, memeriksa apakah nilainya sesuai.
Hasilnya, kami mendapatkan kode konversi data berikut:
def handleNotification(self, cHandle, data): humid_bytes = data[2] temp_bytes = data[:2] humidity = humid_bytes temperature = struct.unpack('H', temp_bytes)[0] / 100 print(temperature, humidity)
Epilog
Operasi untuk terhubung ke sensor dan mencari algoritma transformasi yang benar membutuhkan beberapa malam. Beberapa kali saya ingin meninggalkan segalanya, tetapi pada saat yang sama muncul ide-ide baru dan saya terus mencoba.
Sekarang data ditransfer ke Home Assistant, maka Anda harus menyelesaikan kode integrasi dan, mungkin, menulis ulang dari bluepy menjadi suram, karena suram menggunakan async / menunggu dan lebih cocok untuk Home Assistant yang ditulis oleh aiohttp.

Referensi: