
لذلك ، وصفت بنية نظام ملحقات البرامج المدارة.
يتضمن النموذج المبسط العملية الرئيسية ( 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
.
ما الذي يمكن عمله بشكل أفضل:
- يسمح البروتوكول الثنائي بإرسال بيانات أقل.
JSON
، من ناحية أخرى ، أسرع في التطور والفهم. يتطلب البروتوكول الثنائي أيضًا التقييس.
هذا كل شيء ، سأكون سعيدًا بأي ملاحظات.