همیشه این سوال برایم جالب بود که وقتی QR کد یک دستگاه هوشمند را اسکن میکنم و یک لامپ در اپ Home ظاهر میشود، واقعاً چه اتفاقی در حال رخ دادن است؟ وقتی روی دکمه "روشن" ضربه میزنم، دقیقاً چه بایتهایی در شبکه من سفر میکنند؟ بهترین راهی که برای درک یک فناوری میشناسم، ساختن آن است. به همین دلیل، تصمیم گرفتم یک لامپ مجازی HomeKit را با زبان Go پیادهسازی کنم. در این مقاله، شما را گام به گام همراهی میکنم تا پرده از اسرار پروتکلهای خانه هوشمند برداریم و به طور عمقی بفهمیم که چگونه کار میکنند.
هدف از این مقاله، تنها ارائه یک کد نمونه نیست؛ بلکه هدف، ارائه یک درک عمیق و فنی از نحوه عملکرد اکوسیستم HomeKit اپل است. ما قصد داریم با ساختن یک لوازم جانبی (Accessory) ساده که توسط اپ Home به رسمیت شناخته میشود، به سوالات بنیادین زیر پاسخ دهیم:
با دنبال کردن این مسیر، نه تنها با HomeKit، بلکه با مفاهیم پایهای امنیت شبکه و ارتباطات دستگاههای IoT آشنا خواهیم شد.
برای همراهی در این سفر فنی، نیاز به آمادهسازی یک محیط توسعه دارید. این پروژه بر دو پایه اصلی استوار است:
brutella/hap استفاده میکنیم که با نسخههای جدید Go بهترین عملکرد را دارد.یک نکته مهم که ممکن است باعث مشکل شود، مسدود نبودن ترافیک mDNS است. اگر کد را روی یک سرور لینوکس یا داخل یک کانتینر اجرا میکنید، مطمئن شوید فایروال شما اجازه عبور ترافیک UDP روی پورت ۵۳۵۳ (برای کشف mDNS) و پورتی که لوازم جانبی روی آن اجرا میشود (ما از ۵۱۸۲۶ استفاده میکنیم) را میدهد.
در ادامه این مقاله، به ترتیب مراحل زیر را به طور مفصل بررسی خواهیم کرد تا به درک کاملی از HomeKit دست یابیم:
در نهایت، با کنار هم گذاشتن تمام این قطعات، یک پیادهسازی کامل را مشاهده خواهیم کرد و نکات کلیدی که از این پروژه آموخته شد را مرور میکنیم. آماده باشید تا به عمق دنیای خانههای هوشمند شیرجه بزنیم.
قبل از شروع کدنویسی و ساخت یک لامپ مجازی HomeKit، باید محیط توسعه خود را آماده کنید. این پروژه به دو پیشنیاز اساسی نیاز دارد که بدون آنها نمیتوانید ادامه دهید. اولی مربوط به ابزارهای برنامهنویسی و دومی مربوط به سختافزار و شبکه است. این ملزومات تضمین میکنند که کتابخانههای مورد استفاده به درستی کار کنند و دستگاه تلفن همراه شما بتواند با کدی که مینویسید ارتباط برقرار کند.
برای این پروژه، شما به نسخه ۱.۲۱ یا جدیدتر زبان برنامهنویسی Go نیاز دارید. این کتابخانه از قابلیتهای مدرن Go استفاده میکند و با نسخههای قدیمیتر ممکن است با مشکل مواجه شوید. برای بررسی نسخه نصبشده روی سیستم خود، از دستور go version در ترمینال استفاده کنید. اگر نیاز به ارتقا دارید، آخرین نسخه پایدار را از وبسایت رسمی go.dev دانلود و نصب کنید. کتابخانه اصلی مورد استفاده در این آموزش، کتابخانه brutella/hap است که پیادهسازی پروتکل HAP را بسیار ساده میکند.
از آنجایی که هدف، تعامل با اکوسیستم اپل است، شما به یک دستگاه آیفون یا آیپد با سیستم عامل iOS 15 یا جدیدتر و برنامه Home (خانه) نیاز دارید. نکته بسیار مهم این است که دستگاه تلفن همراه شما و کامپیوتری که کد Go روی آن اجرا میشود، باید روی یک شبکه Wi-Fi یکسان باشند. دلیل این امر این است که HomeKit یک پروتکل کاملاً محلی است و دستورات مستقیماً از طریق شبکه داخلی (LAN) ارسال میشوند و هیچ سرور ابری در فرآیند کنترل دستگاه دخیل نیست.
یکی از متداولترین مشکلات در راهاندازی، مسدود شدن ترافیک mDNS (مولتیکست DNS) توسط فایروال سیستم یا روتر است. این پروتکل که با نام Bonjour نیز شناخته میشود، برای کشف دستگاههای HomeKit در شبکه ضروری است. اگر پروژه خود را روی یک سرور لینوکس یا داخل یک کانتینر اجرا میکنید، باید مطمئن شوید که فایروال شما اجازه عبور ترافیک روی پورت UDP 5353 (برای کشف mDNS) و پورتی که لوازم جانبی شما روی آن اجرا میشود (معمولاً 51826) را میدهد. در سیستمعامل مک، این تنظیمات معمولاً به صورت پیشفرض به درستی پیکربندی شدهاند و مشکلی ایجاد نمیکنند.
برای عیبیابی، میتوانید از ابزارهایی مانند Wireshark برای نظارت بر ترافیک شبکه استفاده کنید تا ببینید آیا بستههای mDNS ارسال و دریافت میشوند یا خیر. اگر دستگاه شما در برنامه Home ظاهر نمیشود، اولین گام بررسی تنظیمات فایروال است.
پس از تأیید نسخه Go، نصب کتابخانههای لازم و اطمینان از صحت تنظیمات شبکه، شما به طور کامل آماده شروع کدنویسی هستید. این پیشنیازها پایه و اساس یک پروژه پایدار هستند که در آن یک لوازم جانبی مجازی میتواند به درستی توسط اکوسیستم HomeKit کشف شده و جفتسازی شود. در بخش بعدی، به بررسی خود پروتکل HomeKit و جزئیات فنی آن خواهیم پرداخت.
فرآیند کشف در HomeKit بر اساس فناوریای به نام mDNS یا سامانه نام دامنه چندپخشی عمل میکند که در پلتفرم اپل با نام Bonjour شناخته میشود. هنگامی که شما یک لوازم جانبی HomeKit (مانند یک لامپ هوشمند) را در شبکه وایفای خود روشن میکنید، این دستگاه به صورت فعال شروع به اعلام حضور خود در شبکه میکند. این کار با ارسال پیامهای پخشی (Broadcast) روی پورت UDP 5353 انجام میپذیرد. این پیامها حاوی یک رکورد سرویس هستند که شبیه به `_hap._tcp.local.` میباشد. یک پارامتر حیاتی در این رکورد، `sf` یا Status Flags است. اگر مقدار این پارامتر برابر با ۱ باشد، به این معنی است که دستگاه هنوز جفتسازی (Pairing) نشده و آماده اتصال است. اپلیکیشن Home روی آیفون یا آیپد شما به طور مداوم در حال گوش دادن به این پیامهای پخشی است. هنگامی که یک دستگاه با `sf=1` را شناسایی کند، آن را در بخش "افزودن لوازم جانبی" به عنوان یک گزینه در دسترس نمایش میدهد. برای توسعهدهندگان، استفاده از یک کتابخانه مانند `brutella/hap` در زبان Go این فرآیند را به صورت خودکار مدیریت میکند. با فراخوانی تابع `ListenAndServe`، سرور به طور خودکار یک شناسه دستگاه یکتا تولید میکند، روی پورت پیشفرض 51826 منتظر ارتباط میماند و رکورد سرویس mDNS را ثبت میکند.
هنگامی که شما کیوآر کد روی یک دستگاه HomeKit را اسکن میکنید یا کد PIN را به صورت دستی وارد مینمایید، یک فرآیند جفتسازی امن به نام SRP یا پروتکل رمز عبور راهدور امن آغاز میشود. این همان پروتکلی است که در برنامههایی مانند 1Password برای احراز هویت استفاده میشود و تضمین میکند که رمز عبور واقعی هرگز از طریق شبکه ارسال نمیشود. توالی این فرآیند به شرح زیر است:
کل این فرآیند تقریباً تنها ۲ ثانیه طول میکشد. پس از اتمام موفقیتآمیز، مقدار `sf` در رکورد mDNS از ۱ به ۰ تغییر مییابد و دستگاه از لیست "افزودن لوازم جانبی" ناپدید میشود.
کیوآر کدی که روی جعبه دستگاههای HomeKit چاپ شده است، در واقع یک URI است که با `X-HM://` آغاز میشود. هنگامی که دوربین آیفون این پیشوند را تشخیص دهد، میفهمد که با یک کد HomeKit سر و کار دارد. این URI حاوی یک محموله فشرده است که تمام اطلاعات لازم برای جفتسازی را تنها در ۴۵ بیت کدگذاری کرده است. این اطلاعات شامل سه بخش اصلی میشود:
این ساختار به آیفون امکان میدهد تنها با یک بار اسکن کیوآر کد، هم آیکون صحیح را نمایش دهد و هم کد PIN را بدون نیاز به تایپ دستی، استخراج کند.
پس از جفتسازی موفق، لوازم جانبی باید اطلاعات حیاتی را به طور دائمی ذخیره کند تا در صورت راهاندازی مجدد برنامه یا دستگاه، بتواند ارتباط خود با کنترلرهای HomeKit (مانند آیفون شما) را حفظ کند. این اطلاعات شامل کلید عمومی Ed25519 کنترلر، یک شناسه یکتا برای کنترلر و سطح دسترسی (مدیر یا کاربر عادی) میشود. همچنین لوازم جانبی باید جفت کلید عمومی و خصوصی خودش را نیز ذخیره نماید. از دست دادن این اطلاعات منجر به "یتیم شدن" کنترلرها میشود؛ به این معنی که کنترلر فکر میکند هنوز جفتسازی شده، اما لوازم جانبی آن را نمیشناسد و در اپلیکیشن Home وضعیت "No Response" نمایش داده میشود. برای یک لوازم جانبی ساده، ذخیره این اطلاعات در یک فایل JSON کافی است. کتابخانه HAP به طور خودکار این کلیدها را مدیریت میکند، اما توسعهدهنده مسئولیت دارد مطمئن شود که این دادهها پس از جفتسازی در یک مکان پایدار (مانند یک فایل) ذخیره میشوند.
HomeKit Accessory Protocol یا HAP، پروتکل اصلی و بومی اپل برای دستگاههای خانگی هوشمند است که از سال ۲۰۱۴ معرفی شده است. این پروتکل بر بستر شبکه محلی WiFi عمل میکند و برای کشف دستگاهها از mDNS (موسوم به Bonjour در پلتفرم اپل) استفاده میکند. یکی از جنبههای مهم HAP، مدل امنیتی قوی آن است که تمامی ارتباطات را با استفاده از الگوریتمهای مدرن رمزنگاری مانند Curve25519 برای تبادل کلید و ChaCha20-Poly1305 برای رمزگذاری محتوای ارتباط، ایمن میسازد. این بدان معناست که هر دستوری که از طریق برنامه Home ارسال میشود، پیش از ارسال به صورت کامل رمزگذاری میشود.
وقتی یک لوازم جانبی HomeKit (مانند لامپ مجازی که در این پروژه ساخته میشود) در شبکه راهاندازی میشود، به صورت فعال خود را به سایر دستگاهها معرفی میکند. این کار با ارسال یک رکورد سرویس mDNS با مشخصه `_hap._tcp.local.` انجام میپذیرد. یک پارامتر حیاتی در این رکورد، پرچم وضعیت یا `sf` است. هنگامی که مقدار این پارامتر برابر با ۱ باشد، به معنای آن است که دستگاه هنوز جفتسازی نشده و در بخش "افزودن لوازم جانبی" در برنامه Home روی آیفون شما قابل مشاهده خواهد بود. فرآیند جفتسازی که با اسکن کد QR یا وارد کردن کد PIN انجام میشود، از پروتکل امن SRP استفاده میکند. در این پروتکل، خود کد PIN هرگز از طریق شبکه ارسال نمیشود؛ بلکه تنها یک "اثبات" مبتنی بر آن مبادله میشود که درستی کد را تأیید میکند. پس از تأیید موفق، دو طرف یک کلید مشترک محرمانه ایجاد کرده و کلیدهای عمومی بلندمدت Ed25519 را با یکدیگر مبادله میکنند. این کلیدها به صورت دائمی ذخیره میشوند تا ارتباط پس از راهاندازی مجدد دستگاه نیز پایدار بماند.
HomeKit دستگاهها را در یک سلسلهمراتب مشخص سازماندهی میکند: هر دستگاه شامل سرویسها و هر سرویس شامل ویژگیها (Characteristics) است. برای مثال، یک لامپ سرویسی با نوع "چراغ" دارد که خود شامل ویژگیهایی مانند "روشن/خاموش" و "میزان روشنایی" است. هر ویژگی دارای یک شناسه نمونه (iid) منحصربهفرد است. زمانی که شما دکمه روشن کردن لامپ را در برنامه Home فشار میدهید، یک درخواست HTTP-like رمزگذاری شده به آدرس مشخصی ارسال میشود. این درخواست در قالب یک PUT به مسیری مانند `/characteristics` ارسال شده و حاوی پارامترهایی مانند `aid` (شناسه لوازم جانبی) و `iid` (شناسه ویژگی) و مقدار جدید (`value`) است. به عنوان نمونه، برای روشن کردن لامپ، درخواست به صورت زیر خواهد بود:
PUT /characteristics
بدنه درخواست: `{"characteristics":[{"aid":1, "iid":10, "value":true}]}`
لوازم جانبی این درخواست را پردازش کرده و یک پاسخ موفقیتآمیز برمیگرداند. این مدل استاندارد باعث میشود تمامی لامپهای سازندگان مختلف، ساختاری یکسان داشته و به صورت یکپارچه در اکوسیستم HomeKit کار کنند.
یکی از قابلیتهای مهم HomeKit، پشتیبانی از اعلانهای فوری (Event Notifications) است. این ویژگی به لوازم جانبی اجازه میدهد تا در صورت تغییر وضعیت (مثلاً زمانی که یک کلید فیزیکی روی لامپ را فشار میدهید)، بلافاصله تمام کنترلکنندههای متصل (مانند آیفونهای اعضای خانه) را مطلع کند. در پشت صحنه، لوازم جانبی اتصالات پایدار با کنترلکنندهها نگه میدارد. هنگامی که مقدار یک ویژگی تغییر میکند، یک پیام EVENT برای هر کنترلکننده ارسال میشود. این مکانیسم دلیل بروزرسانی بلادرنگ وضعیت دستگاهها در برنامه Home، هنگامی که فرد دیگری در خانه لامپی را کنترل میکند، است.
ذخیرهسازی ایمن اطلاعات جفتسازی برای عملکرد صحیح دستگاه حیاتی است. این اطلاعات شامل کلیدهای عمومی کنترلکنندهها، شناسههای آنها و همچنین کلیدهای خصوصی و عمومی خود لوازم جانبی میشود. در این پروژه، این دادهها در یک فایل ساده JSON ذخیره میگردند. اگر این اطلاعات به هر دلیلی (مانند حذف فایل ذخیرهسازی) از دست برود، یک وضعیت "عدم پاسخ" (No Response) در برنامه Home رخ میدهد. در این حالت، کنترلکننده فکر میکند که هنوز جفتسازی شده است، اما لوازم جانبی آن را نمیشناسد. راه حل این مشکل، حذف دستگاه از برنامه Home و انجام فرآیند جفتسازی مجدد با اسکن کد QR است.
پیادهسازی یک لوازم جانبی ساده HomeKit درک روشنی از ساختار پروتکل و ارتباطات آن ارائه میدهد. چند نکته کلیدی که از این تجربه میتوان آموخت عبارتند از: ارتباطات HomeKit کاملاً محلی است و هیچ سرور ابری در مسیر فرمانهای کنترلی وجود ندارد. مدل امنیتی آن بسیار مستحکم است و از ارسال اطلاعات حساس روی شبکه جلوگیری میکند. و در نهایت، با وجود ظهور استاندارد جدید Matter، پروتکل HAP هنوز هسته اصلی ارتباط در اکوسیستم اپل باقی مانده است. پس از عبور از لایه رمزنگاری، این پروتکل در اصل بر مبنای درخواستهای ساده HTTP (مانند PUT و GET) عمل میکند که درک و پیادهسازی آن را نسبتاً ساده میسازد.
یکی از جنبههای حیاتی در پیادهسازی لوازم جانبی HomeKit، مدیریت صحیح دادههای جفتسازی است. هنگامی که یک کنترلر (مانند iPhone) با لوازم جانبی شما جفت میشود، کلیدهای عمومی Ed25519، شناسه کنترلر و سطح دسترسی باید به طور پایدار ذخیره شوند. از دست دادن این دادهها منجر به وضعیت "No Response" در برنامه Home میشود، زیرا لوازم جانبی کنترلرهای قبلی را نمیشناسد. برای پروژههای ساده، استفاده از یک فایل JSON برای ذخیرهسازی این اطلاعات کافی است. این فایل شامل uuid منحصربهفرد لوازم جانبی، جفت کلید عمومی/خصوصی Ed25519 و اطلاعات جفتسازی کنترلرها میشود.
HomeKit از یک مدل سلسلهمراتبی استاندارد برای سازماندهی قابلیتهای لوازم جانبی استفاده میکند. هر لوازم جانبی شامل سرویسها و ویژگیهای مشخصی است. برای یک لامپ، سرویس اصلی شامل ویژگیهایی مانند روشن/خاموش و تنظیم روشنایی میباشد. هر ویژگی دارای یک شناسه instance منحصربهفرد است که در درخواستهای PUT برای تغییر مقادیر مورد استفاده قرار میگیرد. این استانداردسازی باعث میشود تمام لوازم جانبی HomeKit بدون در نظر گرفتن سازنده، با اکوسیستم Apple سازگار باشند.
HomeKit از اعلانهای push برای بهروزرسانی بلادرنگ وضعیت لوازم جانبی پشتیبانی میکند. هنگامی که وضعیت یک ویژگی تغییر میکند (مثلاً لامپ از طریق یک کلید فیزیکی روشن میشود)، لوازم جانبی میتواند تمام کنترلرهای متصل را مطلع کند. این کار از طریق ارسال پیام EVENT از طریق اتصالات پایدار انجام میشود. این قابلیت تضمین میکند که برنامه Home شما به محض تغییر وضعیت لوازم جانبی توسط دیگران، بهروزرسانی میشود و تجربهای یکپارچه ارائه میدهد.
با ترکیب تمامی این اجزا، یک لوازم جانبی مجازی کاملاً کاربردی ایجاد میشود. پس از اجرای کد و جفتسازی موفقیتآمیز، میتوانید تغییرات وضعیت لامپ را از طریق برنامه Home کنترل کرده و پاسخهای آن را در ترمینال مشاهده کنید. هر تغییر وضعیت نشاندهنده تکمیل یک درخواست رمزنگاریشده است که از تلفن شما، از طریق روتر، به فرآیند Go ارسال شده است. این پروژه نه تنها درک عمیقی از پروتکل HomeKit ارائه میدهد، بلکه اهمیت مدیریت دادههای پایدار و ارتباطات امن در دستگاههای IoT را برجسته میکند. برای توسعه بیشتر، میتوانید قابلیتهایی مانند تنظیم روشنایی یا ایجاد انواع دیگر لوازم جانبی را اضافه کنید.