Kami membongkar protokol ketel Redmond G200S dan menghubungkannya ke HomeAssistant

Entri


Sudah ada artikel tentang Gicktime yang ditujukan untuk mengurai protokol ketel Redmond SkyKettle. Namun, di sana mereka berbicara tentang model RK-M171S, di sini kita akan berbicara tentang G200S yang lebih fungsional. Dalam model ini, protokol interaksi telah berubah, karena itu pendekatan penulis artikel sebelumnya tidak lagi berfungsi, dan fungsi tambahan lampu malam dan indikasi suhu saat ini dalam warna telah muncul.

Pada artikel ini, saya akan mempresentasikan hasil analisis protokol dengan contoh kode python (jika ada yang ingin mengembangkan modul / aplikasi mereka untuk mengendalikan teko teh). Juga di akhir artikel adalah tautan ke modul yang sudah jadi untuk menghubungkan teko ke HomeAssistant (ini adalah pengalaman pertama saya dalam menulis dengan python setelah mengambil kursus online, sehingga modul ini dapat dan bahkan perlu ditingkatkan).

Semua orang yang tertarik, selamat datang di kucing.

Masalah dan Tugas


Teko ini memiliki satu minus besar (kecuali yang ditunjukkan oleh penulis artikel pertama): segera setelah teko dihapus dari dudukan, waktu saat ini diatur ulang dan, akibatnya, jadwal tidak dapat digunakan untuk merebus teko. Menurut ide-ide penulis penciptaan ini, setiap kali setelah mengembalikan ketel ke dudukan, Anda harus meluncurkan aplikasi miliknya dan menyinkronkan ketel dengan smartphone. Jadi alih-alih memfasilitasi tugas rutin, teknologi "pintar" melatih kita untuk melakukan tindakan tambahan. Tapi itu semua berubah ketika HomeAssistant muncul di rumah. Kemudian saya memutuskan untuk memahami protokolnya.

Alat-alatnya


Jujur saya mencoba mendekompilasi dan mengurai aplikasi asli, tetapi gagal. Alat yang saya gunakan tidak memungkinkan saya untuk memahami logika ketel. Semua prosedur dan fungsi diperoleh dengan "kurva", tanpa nama (berdasarkan tipe a, b, c, dll.). Mungkin saya tidak memiliki pengalaman dan keterampilan yang cukup. Pada akhirnya, saya pergi dengan cara yang sama seperti penulis artikel sebelumnya. Satu-satunya perbedaan yang signifikan adalah saya menggunakan mode interaktif utilitas gatttool. Keuntungannya adalah mode ini menghilangkan semua jenis "ras", yang ditulis oleh penulis artikel pertama.

Karena HomeAssistant ditulis dengan python, kami akan menulis semua perintah lebih lanjut. Untuk menggunakan mode operasi gatttool interaktif dalam python, pexpect library akan membantu kami, yang memungkinkan Anda untuk menelurkan esensi dari aplikasi pihak ketiga dan memantau hasilnya (terkenal bengkok).

Berlatih


Saya akan kembali mengirim artikel pertama tentang deskripsi umum protokol pertukaran kepada penulis artikel, jadi tanpa basa-basi lagi, kami akan melanjutkan ke perintah kontrol.

  1. Instalasi dan pemutusan

    Membangun koneksi:

    child = pexpect.spawn("gatttool -I -t random -b " + mac, ignore_sighup=False) child.expect(r'\[LE\]>', timeout=3) child.sendline("connect") child.expect(r'Connection successful.*\[LE\]>', timeout=3) 

    Di sini mac adalah alamat poppy dari poci teh.

    Kami memutuskan koneksi:

     child.sendline("exit") 
  2. Berlangganan Notifikasi

    Setelah membuat koneksi, pertama-tama, kita perlu berlangganan untuk menerima notifikasi dari ketel. Tanpa ini, teko akan memahami perintah, tetapi tidak akan dapat menjawab apa pun kecuali teks "Berhasil".

     child.sendline("char-write-cmd 0x000c 0100") child.expect(r'\[LE\]>') 
  3. Login

     child.sendline("char-write-req 0x000e 55" + iter + "ff" + key + "aa") child.expect("value: ") child.expect("\r\n") connectedStr = child.before[0:].decode("utf-8") answer = connectedStr.split()[3] # parse: 00 - no 01 - yes child.expect(r'\[LE\]>') 

    Selanjutnya, iter adalah variabel hex integer iteratif dari 0 hingga 64 (dari 0 hingga 100 dalam sistem desimal). Setelah setiap perintah (berhasil dan tidak berhasil), variabel ini harus ditingkatkan sebesar 1; ketika mencapai 64, kembali diatur ke 0; key - hex 8 bytes kunci otorisasi (misalnya: ffffffffffffffff).

    Contoh jawaban:
    nilai: 55 00 ff 01 aa
    Byte keempat (01) berarti ketel telah mengizinkan Anda, jika tidak maka jawabannya adalah 00.
  4. Beberapa sihir jalanan
    Setelah otorisasi, permintaan "ajaib" selalu dikirimkan, yang intinya tidak jelas bagi saya. Ada teori yang diperlukan untuk "menahan" keadaan yang terhubung. Diduga, jika Anda tidak mengirimkannya, maka pemutusan terjadi dalam satu detik, dan Anda harus memulai dari awal lagi. Jika Anda mengirimnya, maka batas waktu meningkat secara signifikan, mencapai sekitar selusin detik. Andal mengkonfirmasi ini, saya tidak bisa.

     child.sendline("char-write-req 0x000e 55" + iter + "01aa") child.expect("value: ") child.expect("\r\n") child.expect(r'\[LE\]>') 

    Contoh jawaban:
    nilai: 55 01 01 02 1d aa

    Dalam semua percobaan saya, jawabannya selalu seperti ini.

    UPD: dalam komentar mereka menyarankan bahwa itu sama sekali bukan sihir, tetapi hanya meminta versi perangkat lunak, sehingga versi ini terkandung dalam respons. Dengan demikian, permintaan ini secara umum dapat dihapus sebagai tidak perlu.
  5. Sinkronkan
    Perintah yang menyinkronkan waktu dalam teko dengan jam server. Dia memiliki satu efek lagi. Dalam ketel, dimungkinkan untuk menunjukkan suhu saat ini dalam mode siaga dengan mem-flash LED warna tertentu. Fungsi ini hanya berfungsi setelah sinkronisasi. Untuk deskripsi fungsi itu sendiri, lihat paragraf 11.

     child.sendline("char-write-req 0x000e 55" + iter + "6e" + timeNow + tmz + "0000aa") child.expect("value: ") child.expect("\r\n") child.expect(r'\[LE\]>') 

    Di sini tmz adalah zona waktu dalam format hex terbalik (misalnya, menerjemahkan zona waktu +3 ke dalam detik, kemudian ke dalam format hex dan dapatkan hex (3 * 60 * 60) = 2a30, dibagi berpasangan dan output 302a dalam urutan terbalik). Saya tidak tahu apa yang harus dilakukan dengan zona waktu negatif, saya belum mengujinya, tetapi ada kecurigaan bahwa byte tmz berikutnya bertanggung jawab untuk ini. Di sini timeNow adalah waktu unixtime saat ini dalam format hex terbalik. Algoritmanya sama: kita mendapatkan waktu saat ini dalam detik, menerjemahkannya ke HEX, membaginya menjadi berpasangan dan mengeluarkannya dalam satu baris dalam urutan terbalik.

    Contoh jawaban:
    nilai: 55 02 6e 00 aa
    Dalam semua percobaan saya, jawabannya selalu seperti ini.
  6. Statistik
    Ketel memiliki meteran listrik yang dikonsumsi, total waktu operasi dan jumlah start. Jika seseorang tidak membutuhkan data ini, Anda dapat melewati item ini dengan aman.

     child.sendline("char-write-req 0x000e 55" + iter + "4700aa") child.expect("value: ") child.expect("\r\n") statusStr = child.before[0:].decode("utf-8") Watts = hexToDec(str(statusStr.split()[11] + statusStr.split()[10] + statusStr.split()[9])) alltime = round(self._Watts/2200, 1) child.expect(r'\[LE\]>') child.sendline("char-write-req 0x000e 55" + iter + "5000aa") child.expect("value: ") child.expect("\r\n") statusStr = child.before[0:].decode("utf-8") times = hexToDec(str(statusStr.split()[7] + statusStr.split()[6])) child.expect(r'\[LE\]>') 

    Watt - mengembalikan energi yang dikonsumsi dalam Wh * h, alltime - jam kerja ketel, kali - jumlah awal ketel. hexToDec - fungsi untuk mengkonversi ke format desimal.
  7. Baca mode operasi saat ini

     child.sendline("char-write-req 0x000e 55" + iter + "06aa") child.expect("value: ") child.expect("\r\n") statusStr = child.before[0:].decode("utf-8") answer = statusStr.split() status = str(answer[11]) temp = hexToDec(str(answer[8])) mode = str(answer[3]) 

    Contoh jawaban:
    nilai: 55 04 06 00 00 00 00 01 2a 1e 00 00 00 00 00 00 00 80 00 00 aa
    Bita keempat adalah mode operasi (mode): 00 - pendidihan, 01 - pemanasan hingga suhu, 03 - malam hari. Byte keenam adalah suhu heksadesimal yang perlu dipanaskan dalam mode pemanasan, dalam mode didih adalah 00. Byte kesembilan adalah suhu arus hex dari air (2a = 42 Celcius). Byte kedua belas adalah keadaan teko: 00 - off, 02 - on. Byte ketujuh belas adalah durasi ketel setelah mencapai suhu yang diinginkan, secara default adalah 80 dalam hex (tampaknya, ini adalah semacam unit relatif, tentu bukan detik).
  8. Rekam mode operasi saat ini

     child.sendline("char-write-req 0x000e 55" + iter + "05" + mode + "00" + temp + "00000000000000000000" + howMuchBoil + "0000aa") child.expect("value: ") child.expect("\r\n") statusStr = child.before[0:].decode("utf-8") answer = statusStr.split()[3] child.expect(r'\[LE\]>') 

    Mode parameter: 00 - pendidihan, 01 - pemanasan hingga suhu, 03 - malam hari. Parameter temp adalah suhu heksadesimal yang perlu dipanaskan dalam mode โ€œpemanasanโ€, dalam mode didih adalah 00. Parameter howMuchBoil adalah durasi ketel setelah mencapai suhu yang diinginkan, standarnya adalah 80 heksadesimal (tampaknya, ini adalah beberapa unit relatif , tentu saja bukan detik).

    Contoh jawaban:
    nilai: 55 05 05 01 aa
    Byte keempat dari respons menunjukkan keberhasilan pengaturan: 01 - berhasil, 00 - tidak berhasil.
  9. Jalankan mode operasi saat ini

     child.sendline("char-write-req 0x000e 55" + iter + "03aa") child.expect("value: ") child.expect("\r\n") statusStr = self.child.before[0:].decode("utf-8") answer = statusStr.split()[3] child.expect(r'\[LE\]>') 

    Contoh jawaban:
    nilai: 55 06 03 01 aa
    Byte keempat dari respons menunjukkan keberhasilan inklusi: 01 - berhasil, 00 - tidak berhasil.
  10. Hentikan mode operasi saat ini

     child.sendline("char-write-req 0x000e 55" + iter + "04aa") child.expect("value: ") child.expect("\r\n") statusStr = self.child.before[0:].decode("utf-8") answer = statusStr.split()[3] child.expect(r'\[LE\]>') 

    Contoh jawaban:
    nilai: 55 07 04 01 aa
    Byte keempat dari respons menunjukkan keberhasilan shutdown: 01 - berhasil, 00 - tidak berhasil.
  11. Tampilkan suhu saat ini dalam warna dalam kondisi idle

     child.sendline("char-write-req 0x000e 55" + iter + "37c8c8" + onoff + "aa") # 00 - off, 01 - on child.expect("value: ") child.expect("\r\n") child.expect(r'\[LE\]>') 

    Parameter onoff adalah 01 untuk mengaktifkan fungsi, atau 00 untuk menonaktifkan fungsi.

    Contoh jawaban:
    nilai: 55 08 37 00 aa
    Dalam semua percobaan saya, jawabannya selalu seperti ini.
  12. Rekam palet warna dari berbagai mode operasi
    Palet korespondensi antara warna LED dan suhu diatur dalam mode tampilan suhu saat ini dan mode pemanasan dan pendidihan, serta palet warna dalam mode lampu malam.

     child.sendline("char-write-req 0x000e 55" + iter + "32" + boilOrLight + scale_from + rand + rgb1 + scale_mid + rand + rgb_mid + scale_to + rand + rgb2 + "aa") child.expect("value: ") child.expect("\r\n") child.expect(r'\[LE\]>') 

    Parameter boilOrLight adalah 00 jika kita mengatur mode tampilan suhu saat ini atau 01 jika kita mengatur mode malam. Parameter scale_from menunjukkan awal rentang perubahan warna dan sama dengan 00 dalam mode lampu malam dan 28 dalam mode tampilan suhu saat ini (28 adalah 40 dalam format desimal dan dari suhu ini perubahan warna yang halus akan dimulai). Parameter scale_mid adalah tengah kisaran dan 32 dalam mode lampu malam dan 46 dalam mode tampilan suhu saat ini. Parameter scale_to menunjukkan akhir rentang warna dan 64 dalam kedua mode. Parameter rgb1 adalah warna hex dari awal palet. Parameter rgb_mid adalah warna hex tengah palet (saya menghitungnya sebagai tengah antara ujung kiri dan kanan, tetapi secara teoritis Anda dapat menentukan warna apa pun, ini hanya akan mempengaruhi kecantikan dan kehalusan perubahan warna). Parameter rgb2 adalah warna hex dari ujung palet. Parameter rand adalah parameter tertentu, nilai yang saya tidak mengerti persis, mungkin entah bagaimana terkait dengan kecerahan warna (contoh nilai: e5, cc).

    Contoh jawaban:
    nilai: 55 09 32 00 aa
    Dalam semua percobaan saya, jawabannya selalu seperti ini.
  13. Baca palet warna dari berbagai mode operasi

     child.sendline("char-write-req 0x000e 55" + iter + "33" + boilOrLight + "aa") child.expect("value: ") child.expect("\r\n") statusStr = self.child.before[0:].decode("utf-8") child.expect(r'\[LE\]>') 

    Parameter boilOrLight bisa 00 - jika kita mengatur mode tampilan suhu saat ini atau 01 - jika kita mengatur mode malam.

    Contoh jawaban:
    nilai: 55 10 33 01 00 7f 00 00 ff 32 7f 00 ff 00 64 7f ff 00 00 aa
    Di sini, byte keenam, kesebelas dan keenambelas (7f) adalah parameter rand dari item 12. Byte kelima adalah scale_from, byte kesepuluh adalah scale_mid, byte kelima belas adalah scale_to. Byte ketujuh + delapan + kesembilan adalah rgb_from. Byte kedua belas + ketiga belas + empat belas adalah rgb_mid. Ketujuh Belas + Delapan Belas + byte kesembilan - rgb_to.

Kesimpulan


Jika gatttool tidak ingin terhubung ke teko (ini mungkin pertama kali Anda terhubung ke perangkat yang tidak dikenal), maka cobalah mencari teko menggunakan os sebelum menghubungkan modul:

 sudo hciconfig device reset sudo timeout 1 hcitool lescan 


perangkat - id perangkat bluetooth Anda (misalnya, hci0). Pastikan alamat poppy ketel Anda ada dalam daftar perangkat yang ditemukan. Setelah itu:

 sudo hcitool lewladd mac sudo hcitool lerladd mac 


mac - alamat poppy dari teko Anda

UPD6 : Secara signifikan meningkatkan modul ketel:
1. Mentransfer modul dari platform ke mode integrasi
2. Setelah menambahkan, Anda akan secara otomatis memiliki 3 elemen: pemanas air (suhu saat ini, suhu target, perebusan dan pemanasan), sensor (waktu sinkronisasi, energi yang dihabiskan, jam operasi, jumlah start) dan cahaya (dapat digunakan sebagai lampu malam dan memilih warna apa pun) cahaya latar)
3. Modul sekarang tersedia di GitHub .
4. Modul ini mendukung instalasi melalui HACS
5. Contoh konfigurasi:
 r4s_kettler: device: 'hci0' mac: 'FF:FF:FF:FF:FF:FF' password: 'ffffffffffffffff' 


Tangkapan layar dari versi baru
gambar
gambar
gambar
gambar


UPD7 : Menghapus informasi yang tidak relevan

Source: https://habr.com/ru/post/id412583/


All Articles