bobaoskit - الملحقات ، dnssd و WebSocket


لذلك ، وصفت بنية نظام ملحقات البرامج المدارة.


يتضمن النموذج المبسط العملية الرئيسية ( bobaoskit.worker ) والبرامج النصية bobaoskit.sdk (باستخدام bobaoskit.accessory و bobaoskit.accessory ). من العملية الرئيسية يوجد طلب لملحق للتحكم في بعض الحقول. من الملحق ، بدوره ، هناك طلب إلى الشيء الرئيسي لتحديث الحالة.


خذ تتابع عادي كمثال.


باستخدام أمر وارد ، قد لا يغير المرحل في بعض الأحيان موضعه لأسباب متعددة (تجميد المعدات ، إلخ). وفقًا لذلك ، كم لن نرسل فرقًا ، لن يتغير الوضع. وفي حالة أخرى ، يمكن للمرحل أن يغير حالته عندما يقوده نظام تابع لجهة خارجية. في هذه الحالة ، ستتغير حالته ، يمكن للبرنامج النصي الملحق الاستجابة لحدث وارد حول تغيير الحالة وإرسال طلب إلى العملية الرئيسية.


الدافع


بعد أن قمت بتطبيق Apple HomeKit على العديد من الكائنات ، بدأت أبحث عن شيء مشابه لنظام Android ، لأنه لديّ فقط جهاز iPad يعمل من أجهزة iOS. المعيار الرئيسي هو القدرة على العمل على شبكة محلية ، دون الخدمات السحابية. أيضا ، ما كان مفقودا في HomeKit هو المعلومات المحدودة. على سبيل المثال ، يمكنك أن تأخذ ترموستات. يتم التحكم في كل التحكم في اختيار وضع التشغيل (إيقاف ، التدفئة والتبريد والسيارات) ودرجة الحرارة المحددة. أبسط هو أفضل ، ولكن ، في رأيي ، ليس دائما. لا توجد معلومات تشخيصية كافية. على سبيل المثال ، ما إذا كان مكيف الهواء ، المسخن ، ما هي معلمات التهوية تعمل. ربما لا يمكن أن يعمل مكيف الهواء بسبب خطأ داخلي. بالنظر إلى أنه يمكن النظر في هذه المعلومات ، فقد تقرر كتابة تنفيذها.


يمكن للمرء أن ينظر إلى خيارات مثل ioBroker و OpenHAB ومساعد منزلي.
لكن على node.js ، من هؤلاء المدرجين في القائمة ، ioBroker فقط (أثناء كتابة مقال ، لاحظت أن redis متورط أيضًا في العملية). وبحلول ذلك الوقت ، كان قد اكتشف كيفية تنظيم التواصل بين العمليات ، وكان من المثير للاهتمام التعامل مع redis ، والذي تم الاستماع إليه مؤخرًا.


يمكنك أيضًا الانتباه إلى المواصفات التالية:


الويب شيء API


الجهاز



تساعد Redis على التواصل بين العمليات ، وتعمل أيضًا كقاعدة بيانات للملحقات.


الوحدة النمطية bobaoskit.worker يحدث قائمة انتظار الطلب (أعلى redis باستخدام bee-queue ) ، تنفيذ الطلب ، يكتب / يقرأ من قاعدة البيانات.


في البرامج النصية للمستخدم ، يستمع كائن bobaoskit.accessory bee-queue منفصلة لهذا الملحق المحدد ، ويقوم بتنفيذ الإجراءات الموصوفة ، ويرسل الطلبات إلى قائمة انتظار العملية الرئيسية من خلال كائن bobaoskit.sdk .


البروتوكول


جميع الطلبات والرسائل المنشورة عبارة عن سلاسل بتنسيق JSON ، وتحتوي على حقول method payload . الحقول مطلوبة ، حتى إذا كانت payload = null .


طلبات bobaoskit.worker :


  • الطريقة: ping ، الحمولة: null .
  • الطريقة: get general info ، الحمولة: null
  • طريقة: clear accessories ، الحمولة: null ،
  • طريقة: add accessory ،
    الحمولة:

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

  • الطريقة: remove accessory ، الحمولة: accessoryId/[acc1id, acc2id, ...]
  • الطريقة: get accessory info ، الحمولة: null/accId/[acc1id, acc2id...]
    في حقل payload ، يمكنك إرسال id null / id الملحق / الكتلة. إذا null إرسال قيمة null ، فسيتم إرجاع المعلومات المتعلقة بجميع الملحقات الموجودة.
  • الطريقة: get status value ، الحمولة: {id: accessoryId, status: fieldId}
    في حقل البيانات payload ، يمكنك إرسال كائن من النموذج {id: accessoryId, status: fieldId} ، (حيث يمكن أن يكون حقل status عبارة عن مجموعة من الحقول) ، أو يمكن أن تكون البيانات {id: accessoryId, status: fieldId} عبارة عن مجموعة من الكائنات من هذا النوع.
  • الطريقة: update status value ، الحمولة: {id: accessoryId, status: {field: fieldId, value: value}
    في حقل payload ، يمكنك إرسال كائن من النموذج {id: accessoryId, status: {field: fieldId, value: value}} ، (حيث يمكن أن يكون حقل status عبارة عن صفيف {field: fieldId, value: value} ) ، أو يمكن أن يكون payload عبارة عن صفيف من الكائنات بهذه نوع من.
  • الطريقة: control accessory value ، الحمولة: {id: accessoryId, control: {field: fieldId, value: value}} .
    في حقل payload ، يمكنك إرسال كائن من النموذج {id: accessoryId, control: {field: fieldId, value: value}} ، (حيث يمكن أن يكون حقل control صفيف {field: fieldId, value: value} ) ، أو يمكن أن يكون payload عبارة عن صفيف من الكائنات بهذه نوع من.

استجابة لأي طلب ، إذا نجحت ، رسالة من النموذج:


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


في حالة الفشل:


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


يتم أيضًا نشر الرسائل في redis PUB/SUB (المعرفة في config.json ) في الحالات التالية: يتم مسح جميع الملحقات ( clear accessories ) ؛ إضافة ملحق ( add accessory ) ؛ تم إزالة الملحق ( remove accessory ) ؛ ملحق تحديث الحالة ( update status value ).


تحتوي رسائل البث أيضًا على حقلين: method payload .


عميل SDK


الوصف


يسمح لك عميل SDK ( bobaoskit.accessory ) باستدعاء الأساليب المذكورة أعلاه من البرامج النصية js .


داخل الوحدة النمطية كائنين منشئ. الأول ينشئ كائن Sdk للوصول إلى الأساليب المذكورة أعلاه ، والثاني يقوم بإنشاء ملحق - غلاف أعلى هذه الوظائف.


 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" }); 

كائن sdk يدعم أساليب 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); 

كائن BobaosKit.Accessory({..}) عبارة عن مجمّع أعلى كائن BobaosKit.Sdk(...) .


بعد ذلك ، سأظهر كيف يتحول هذا:


 //     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 }); }; 

كلا الكائنات هي أيضا EventEmitter .
Sdk يدعو وظائف على الأحداث ready broadcasted event .
Accessory المكالمات وظائف في الأحداث ready ، error ، control accessory value .


مثال


 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); 

WebSocket API


يستمع bobaoskit.worker على منفذ WebSocket المحدد في ./config.json.


الطلبات الواردة هي سلاسل JSON التي يجب أن تحتوي على الحقول التالية: request_id ، method payload .


تقتصر واجهة برمجة التطبيقات على الطلبات التالية:


  • الطريقة: ping ، الحمولة: null
  • الطريقة: get general info ، الحمولة: null ،
  • الطريقة: get accessory info ، الحمولة: null/accId/[acc1Id, ...]
  • الطريقة: get status value ، الحمولة: {id: accId, status: field1/[field1, ...]}/[{id: ...}...]
  • الطريقة: control accessory value ، الحمولة: {id: accId, control: {field: field1, value: value}/[{field: .. value: ..}]}/[{id: ...}, ...]

تقبل get status value ، control accessory value طرق control accessory value حقل payload ككائن واحد أو كصفيف. يمكن أن تكون حقول control/status داخل payload إما كائن واحد أو صفيف.


يتم أيضًا إرسال رسائل الأحداث التالية من الخادم إلى جميع العملاء:


  • الطريقة: clear accessories ، حمولة: فارغة
  • الطريقة: remove accessory ، الحمولة: معرف ملحق
  • الطريقة: add accessory, payload : {id: ...}
  • الطريقة: update status value, payload : {id: ...}

dnssd


يُعلن التطبيق عن منفذ WebSocket على الشبكة المحلية كخدمة _bobaoskit._tcp ، وذلك بفضل وحدة dnssd .


عرض



سيتم كتابة مقال منفصل حول كيفية كتابة التطبيق مع الفيديو وانطباعات flutter .


خاتمة


وبالتالي ، تم الحصول على نظام بسيط لإدارة ملحقات البرامج.
يمكن أن تتناقض الملحقات مع كائنات من العالم الحقيقي: الأزرار ، وأجهزة الاستشعار ، والمفاتيح ، والحرارة ، وأجهزة الراديو. نظرًا لعدم وجود توحيد قياسي ، يمكنك تنفيذ أي ملحقات تتناسب مع طراز control < == > update .


ما الذي يمكن عمله بشكل أفضل:


  1. يسمح البروتوكول الثنائي بإرسال بيانات أقل. JSON ، من ناحية أخرى ، أسرع في التطور والفهم. يتطلب البروتوكول الثنائي أيضًا التقييس.

هذا كل شيء ، سأكون سعيدًا بأي ملاحظات.

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


All Articles