bobaoskit - aksesoris, dnssd dan WebSocket


Jadi, saya menggambarkan struktur sistem aksesori perangkat lunak yang dikelola.


Model yang disederhanakan meliputi proses utama ( bobaoskit.worker ) dan skrip aksesori (menggunakan objek bobaoskit.sdk dan bobaoskit.accessory ). Dari proses utama ada permintaan untuk aksesori untuk mengontrol beberapa bidang. Dari aksesori, pada gilirannya, ada permintaan untuk hal utama untuk memperbarui status.


Ambil relay biasa sebagai contoh.


Dengan perintah masuk, relai terkadang tidak dapat mengubah posisinya karena berbagai alasan (peralatan membeku, dll.). Dengan demikian, seberapa banyak kami tidak akan mengirim tim, statusnya tidak akan berubah. Dan, dalam situasi lain, relai dapat mengubah kondisinya ketika diperintahkan oleh sistem pihak ketiga. Dalam hal ini, statusnya akan berubah, skrip aksesori dapat menanggapi peristiwa yang masuk tentang perubahan status dan mengirim permintaan ke proses utama.


Motivasi


Setelah mengimplementasikan Apple HomeKit pada beberapa objek, saya mulai mencari sesuatu yang mirip dengan Android, karena Saya sendiri hanya memiliki iPad yang berfungsi dari perangkat iOS. Kriteria utamanya adalah kemampuan untuk bekerja di jaringan lokal, tanpa layanan cloud. Juga, apa yang hilang di HomeKit adalah informasi yang terbatas. Misalnya, Anda dapat mengambil termostat. Semua kontrolnya dikurangi menjadi pilihan mode operasi (mati, pemanasan, pendinginan dan otomatis) dan suhu yang disetel. Simpler lebih baik, tetapi, menurut saya, tidak selalu. Informasi diagnostik tidak cukup. Misalnya, apakah AC, convector, parameter ventilasi apa yang berfungsi. Mungkin AC tidak dapat bekerja karena kesalahan internal. Mengingat informasi ini dapat dipertimbangkan, diputuskan untuk menulis implementasinya.


Orang bisa melihat opsi seperti ioBroker, OpenHAB, asisten rumah.
Tetapi pada node.js, dari yang terdaftar, hanya ioBroker (saat saya menulis artikel, perhatikan bahwa redis juga terlibat dalam proses). Dan pada saat itu, dia telah menemukan cara mengatur komunikasi antarproses, dan menarik untuk berurusan dengan redis, yang telah terdengar belakangan ini.


Anda juga dapat memperhatikan spesifikasi berikut:


Web Thing API


Perangkat



Redis membantu komunikasi antarproses, dan juga bertindak sebagai basis data untuk aksesori.


Modul bobaoskit.worker terjadi antrian permintaan (di atas redis menggunakan bee-queue ), mengeksekusi permintaan, menulis / membaca dari database.


Dalam skrip pengguna, objek bobaoskit.accessory mendengarkan bee-queue terpisah untuk aksesori khusus ini, melakukan tindakan yang ditentukan, mengirimkan permintaan ke antrean proses utama melalui objek bobaoskit.sdk .


Protokol


Semua permintaan dan pesan yang diterbitkan adalah string dalam format JSON , berisi bidang method dan payload . Bidang wajib diisi, bahkan jika payload = null .


Permintaan ke bobaoskit.worker :


  • metode: ping , payload: null
  • metode: get general info , payload: null
  • metode: clear accessories , payload: null ,
  • metode: add accessory ,
    muatan:

 { id: "accessoryId", type: "switch/sensor/etc", name: "Accessory Display Name", control: [<array of control fields>], status: [<array of status fields>] } 

  • metode: remove accessory , payload: accessoryId/[acc1id, acc2id, ...]
  • metode: get accessory info , payload: null/accId/[acc1id, acc2id...]
    Di bidang payload , Anda dapat mengirim null / id aksesori / id massal. Jika null dikirim, maka informasi tentang semua aksesori yang ada akan dikembalikan.
  • metode: get status value , muatan: {id: accessoryId, status: fieldId}
    Di bidang payload , Anda bisa mengirim objek bentuk {id: accessoryId, status: fieldId} , (di mana bidang status bisa berupa array bidang), atau payload bisa berupa array objek semacam ini.
  • metode: update status value , muatan: {id: accessoryId, status: {field: fieldId, value: value}
    Di bidang payload , Anda dapat mengirim objek dari bentuk {id: accessoryId, status: {field: fieldId, value: value}} , (di mana bidang status bisa berupa array {field: fieldId, value: value} ), atau payload dapat berupa larik objek seperti itu jenis.
  • metode: control accessory value , payload: {id: accessoryId, control: {field: fieldId, value: value}} .
    Di bidang payload , Anda bisa mengirim objek berupa {id: accessoryId, control: {field: fieldId, value: value}} , (di mana field control bisa berupa array {field: fieldId, value: value} ), atau payload bisa berupa array objek seperti itu jenis.

Menanggapi permintaan apa pun, jika berhasil, pesan berupa:


{ method: "success", payload: <...> }


Dalam hal kegagalan:


{ method: "error", payload: "Error description" }


Pesan juga diterbitkan dalam saluran redis PUB/SUB (didefinisikan dalam config.json ) dalam kasus berikut: semua aksesori dihapus ( clear accessories ); aksesori ditambahkan ( add accessory ); aksesori dilepas ( remove accessory ); Aksesori yang diperbarui status ( update status value ).


Pesan siaran juga mengandung dua bidang: method dan payload .


SDK klien


Deskripsi


SDK klien ( bobaoskit.accessory ) memungkinkan Anda untuk memanggil metode di atas dari skrip js .


Di dalam modul ada dua objek konstruktor. Yang pertama membuat objek Sdk untuk mengakses metode di atas, dan yang kedua membuat aksesori - pembungkus di atas fungsi-fungsi ini.


 const BobaosKit = require("bobaoskit.accessory"); //   sdk. //  , //     , //     sdk, const sdk = BobaosKit.Sdk({ redis: redisClient // optional job_channel: "bobaoskit_job", // optional. default: bobaoskit_job broadcast_channel: "bobaoskit_bcast" // optional. default: bobaoskit_bcast }); //   const dummySwitchAcc = BobaosKit.Accessory({ id: "dummySwitch", // required name: "Dummy Switch", // required type: "switch", // required control: ["state"], // requried. ,   . status: ["state"], // required.   . sdk: sdk, // optional. //   ,   sdk   //     redis: undefined, job_channel: "bobaoskit_job", broadcast_channel: "bobaoskit_bcast" }); 

Objek SDK mendukung metode Promise :


 sdk.ping(); sdk.getGeneralInfo(); sdk.clearAccessories(); sdk.addAccessory(payload); sdk.removeAccessory(payload); sdk.getAccessoryInfo(payload); sdk.getStatusValue(payload); sdk.updateStatusValue(payload); sdk.controlAccessoryValue(payload); 

Objek BobaosKit.Accessory({..}) adalah pembungkus di atas objek BobaosKit.Sdk(...) .


Selanjutnya, saya akan menunjukkan bagaimana ini berubah:


 //     self.getAccessoryInfo = _ => { return _sdk.getAccessoryInfo(id); }; self.getStatusValue = payload => { return _sdk.getStatusValue({ id: id, status: payload }); }; self.updateStatusValue = payload => { return _sdk.updateStatusValue({ id: id, status: payload }); }; 

Kedua objek juga EventEmitter .
Sdk panggilan fungsi pada acara ready dan broadcasted event .
Panggilan Accessory berfungsi pada acara yang ready , error , control accessory value .


Contoh


 const BobaosKit = require("bobaoskit.accessory"); const Bobaos = require("bobaos.sub"); // init bobaos with default params const bobaos = Bobaos(); // init sdk with default params const accessorySdk = BobaosKit.Sdk(); const SwitchAccessory = params => { let { id, name, controlDatapoint, stateDatapoint } = params; // init accessory const swAcc = BobaosKit.Accessory({ id: id, name: name, type: "switch", control: ["state"], status: ["state"], sdk: accessorySdk }); //       state //     KNX  bobaos swAcc.on("control accessory value", async (payload, cb) => { const processOneAccessoryValue = async payload => { let { field, value } = payload; if (field === "state") { await bobaos.setValue({ id: controlDatapoint, value: value }); } }; if (Array.isArray(payload)) { await Promise.all(payload.map(processOneAccessoryValue)); return; } await processOneAccessoryValue(payload); }); const processOneBaosValue = async payload => { let { id, value } = payload; if (id === stateDatapoint) { await swAcc.updateStatusValue({ field: "state", value: value }); } }; //      KNX //   state  bobaos.on("datapoint value", payload => { if (Array.isArray(payload)) { return payload.forEach(processOneBaosValue); } return processOneBaosValue(payload); }); return swAcc; }; const switches = [ { id: "sw651", name: "", controlDatapoint: 651, stateDatapoint: 652 }, { id: "sw653", name: " 1", controlDatapoint: 653, stateDatapoint: 653 }, { id: "sw655", name: " 2", controlDatapoint: 655, stateDatapoint: 656 }, { id: "sw657", name: " 1", controlDatapoint: 657, stateDatapoint: 658 }, { id: "sw659", name: "", controlDatapoint: 659, stateDatapoint: 660 } ]; switches.forEach(SwitchAccessory); 

API WebSocket


bobaoskit.worker mendengarkan pada port WebSocket yang didefinisikan dalam ./config.json .


Permintaan yang masuk adalah string JSON yang harus memiliki bidang berikut: request_id , method , dan payload .


API terbatas pada permintaan berikut:


  • metode: ping , payload: null
  • metode: get general info , payload: null ,
  • metode: get accessory info , payload: null/accId/[acc1Id, ...]
  • metode: get status value , payload: {id: accId, status: field1/[field1, ...]}/[{id: ...}...]
  • metode: control accessory value , payload: {id: accId, control: {field: field1, value: value}/[{field: .. value: ..}]}/[{id: ...}, ...]

Nilai get status value , control accessory value metode control accessory value menerima bidang payload sebagai objek tunggal atau sebagai array. Bidang control/status di dalam payload juga bisa berupa objek tunggal atau array.


Pesan acara berikut dari server juga dikirim ke semua klien:


  • metode: clear accessories , payload: null
  • metode: remove accessory , payload: id aksesori
  • metode: add accessory, payload : {id: ...}
  • metode: update status value, payload : {id: ...}

dnssd


Aplikasi ini mengiklankan port WebSocket di jaringan lokal sebagai layanan _bobaoskit._tcp , terima kasih kepada modul npm dnssd .


Demo



Artikel terpisah akan ditulis tentang bagaimana aplikasi itu ditulis dengan video dan tayangan flutter .


Kata penutup


Dengan demikian, sistem sederhana untuk mengelola aksesori perangkat lunak diperoleh.
Asesoris dapat dikontraskan dengan benda-benda dari dunia nyata: tombol, sensor, sakelar, termostat, radio. Karena tidak ada standardisasi, Anda dapat menerapkan aksesori apa pun yang sesuai dengan model control < == > update .


Apa yang bisa dilakukan dengan lebih baik:


  1. Protokol biner akan memungkinkan lebih sedikit data untuk dikirim. JSON , di sisi lain, lebih cepat dikembangkan dan dipahami. Protokol biner juga memerlukan standarisasi.

Itu saja, saya akan senang dengan umpan balik apa pun.

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


All Articles