۱. قبل از شروع
رابطهای برنامهنویسی کاربردی (API) گوگل هوم مجموعهای از کتابخانهها را برای توسعهدهندگان اندروید فراهم میکند تا بتوانند از اکوسیستم گوگل هوم استفاده کنند. با استفاده از این رابطهای برنامهنویسی کاربردی جدید، توسعهدهندگان میتوانند برنامههایی بسازند که به طور یکپارچه دستگاههای خانه هوشمند را راهاندازی و کنترل میکنند.
این ویدیو توضیح مختصری از برنامه موبایلی که قرار است بسازید ارائه میدهد، بنابراین همزمان با انجام آزمایش کد، ویدیو را دنبال کنید.
گوگل یک برنامه نمونه اندروید برای توسعهدهندگانی که میخواهند با استفاده از APIهای Google Home به یک نمونه کار دسترسی داشته باشند، ارائه میدهد. این آزمایشگاه کد بر اساس شاخهای از برنامه نمونه است که شما را در نحوه استفاده از APIهای مجوزها، راهاندازی، دستگاه و ساختار راهنمایی میکند.
پیشنیازها
- آشنایی با اکوسیستم گوگل هوم ( ابر به ابر و Matter ).
- یک ایستگاه کاری که اندروید استودیو (نسخه 2024.3.1 لیدیباگ یا بالاتر) روی آن نصب شده باشد.
- یک گوشی اندروید که الزامات APIهای Home (به پیشنیازها مراجعه کنید) را برآورده کند و سرویسهای گوگل پلی و برنامه Google Home روی آن نصب شده باشد. شبیهساز کار نمیکند ، فقط گوشیهای اندروید فیزیکی برای برنامه نمونه پشتیبانی میشوند.
- یک هاب گوگل هوم سازگار که از APIهای گوگل هوم پشتیبانی میکند.
- اختیاری - یک دستگاه خانه هوشمند سازگار با API های Google Home.
آنچه یاد خواهید گرفت
- نحوه ساخت یک برنامه اندروید با استفاده از API های Google Home با بهترین شیوه ها.
- نحوه استفاده از API های دستگاه و ساختار برای نمایش و کنترل یک خانه هوشمند.
- نحوه استفاده از API های راه اندازی برای افزودن دستگاه ها به اکوسیستم Google Home.
اختیاری: خانه خود را آماده کنید
قبل از استفاده از APIهای گوگل هوم، باید با استفاده از برنامه گوگل هوم، یک خانه در حساب گوگل خود راهاندازی کنید و چند دستگاه اضافه کنید. در این بخش نحوه انجام این کار با استفاده از Google Home Playground که دستگاههای خانه هوشمند مجازی را ارائه میدهد، مورد بحث قرار میگیرد.
home-playground.withgoogle.com را در مرورگر وب خود باز کنید، با حساب Google خود وارد شوید و ببینید آیا دستگاههای شبیهسازی شده زیر ظاهر میشوند یا خیر:
- پریز ۱: دوشاخه روشن/خاموش
- نور۲: نور قابل تنظیم
- چراغ ۳: چراغ روشن/خاموش
- ac3: تهویه مطبوع
- blinds4: پوشش پنجره
- ماشین لباسشویی ۵: ماشین لباسشویی هوشمند

برنامه Google Home را در دستگاه همراه خود باز کنید، روی دکمه افزودن ضربه بزنید و Works with Google Home را انتخاب کنید. در لیست عبارت "playground" را جستجو کنید، سپس پروژه "Google Home Playground" را انتخاب کنید و روی ادامه ضربه بزنید.



Google Home Playground یک صفحه تأیید حساب کاربری به شما نشان میدهد. روی «مجوز دادن» یا «ورود با گوگل» ضربه بزنید. تمام دستگاههایی را که از برنامه وب پیکربندی کردهاید، در برنامه تلفن همراه مشاهده خواهید کرد.


همه دستگاهها را انتخاب کنید و مراحل راهاندازی را تکمیل کنید. با بازگشت به صفحه اصلی، تمام دستگاههای موجود را مشاهده خواهید کرد.

دستگاههای پشتیبانیشده در این لیست اکنون برای استفاده با APIهای گوگل هوم در دسترس هستند.
۲. پروژه خود را تنظیم کنید
نمودار زیر معماری یک برنامه Home API را نشان میدهد:

- کد برنامه: کد اصلی که توسعهدهندگان برای ساخت رابط کاربری برنامه و منطق تعامل با SDK رابطهای برنامهنویسی کاربردی خانگی (Home APIs) روی آن کار میکنند.
- کیت توسعه نرمافزاری رابطهای برنامهنویسی کاربردی (SDK) رابطهای برنامهنویسی کاربردی ...
- GMSCore در اندروید: GMSCore که با نام سرویسهای گوگل پلی نیز شناخته میشود، یک پلتفرم گوگل است که سرویسهای سیستم اصلی را ارائه میدهد و قابلیتهای کلیدی را در تمام دستگاههای اندروید دارای گواهینامه فعال میکند. ماژول خانگی سرویسهای گوگل پلی شامل سرویسهایی است که با APIهای خانگی تعامل دارند.
راهاندازی کیت توسعه نرمافزار خانگی (Home SDK)
برای دریافت جدیدترین SDK، مراحل ذکر شده در بخش «راهاندازی SDK» را دنبال کنید.
دریافت نمونه برنامه
کد منبع برنامه نمونه در گیتهاب موجود است. این آزمایشگاه کد از مثالهای موجود در شاخه codelab-branch-1 برنامه نمونه استفاده میکند.
به جایی که میخواهید پروژه را ذخیره کنید بروید و شاخه codelab-branch-1 را کلون کنید:
$ git clone -b codelab-branch-1 https://github.com/google-home/google-home-api-sample-app-android.git
ساخت برنامه نمونه
مراحل ۱ تا ۵ را در ساخت برنامه انجام دهید.

وقتی برنامه با موفقیت روی گوشی شما اجرا شود، صفحه اصلی برنامه نمونه را مشاهده خواهید کرد. اما تا زمانی که احراز هویت OAuth را تنظیم نکنید و بخشهای گمشده را با استفاده از Permission API پیادهسازی نکنید، نمیتوانید وارد سیستم شوید.
۳. تنظیم احراز هویت
رابطهای برنامهنویسی کاربردی (API) خانه (Home) از OAuth 2.0 برای اعطای دسترسی به دستگاههای موجود در ساختار استفاده میکنند. OAuth به کاربر اجازه میدهد بدون نیاز به افشای اطلاعات ورود به سیستم، به یک برنامه یا سرویس مجوز دهد.
برای پیکربندی صفحه رضایت، دستورالعملهای موجود در بخش «تنظیم رضایت OAuth» را دنبال کنید. حتماً حداقل یک حساب آزمایشی ایجاد کنید.
سپس دستورالعملهای موجود در بخش «تنظیم اعتبارنامههای OAuth» را برای ایجاد اعتبارنامههای برنامه دنبال کنید.
۴. مقداردهی اولیه و مدیریت مجوزها
در این بخش، یاد خواهید گرفت که چگونه SDK را مقداردهی اولیه کنید و با تکمیل بخشهای گمشده با استفاده از Permissions API، مجوزهای کاربر را مدیریت کنید.
تعریف انواع و ویژگیهای پشتیبانیشده
هنگام توسعه یک برنامه، باید به صراحت مشخص کنید که برنامه از چه نوع و ویژگیهای دستگاهی پشتیبانی خواهد کرد. در برنامه نمونه، ما این کار را با تعریف لیستهای استاتیک در شیء همراه در HomeApp.kt انجام میدهیم که در صورت نیاز میتوان در سراسر برنامه به آنها ارجاع داد:
companion object {
// List of supported device types by this app:
val supportedTypes: List<DeviceTypeFactory<out DeviceType>> = listOf(
OnOffLightDevice,
DimmableLightDevice,
// ...
)
// List of supported device traits by this app:
val supportedTraits: List<TraitFactory<out Trait>> = listOf(
OnOff,
LevelControl,
// ...
)
}
برای مشاهدهی انواع و ویژگیهای دستگاههای پشتیبانیشده، به انواع دستگاههای پشتیبانیشده و فهرست ویژگیها در اندروید مراجعه کنید.
مراحل ۴.۱.۱ و ۴.۱.۲ در فایل منبع HomeApp.kt را از حالت کامنت خارج کنید تا کد منبعی که درخواست مجوز میکند، فعال شود.
companion object {
// List of supported device types by this app:
val supportedTypes: List<DeviceTypeFactory<out DeviceType>> = listOf(
// TODO: 4.1.1 - Non-registered device types will be unsupported
// ContactSensorDevice,
// ColorTemperatureLightDevice,
// DimmableLightDevice,
// ExtendedColorLightDevice,
// GenericSwitchDevice,
// GoogleDisplayDevice,
// GoogleTVDevice,
// OccupancySensorDevice,
// OnOffLightDevice,
// OnOffLightSwitchDevice,
// OnOffPluginUnitDevice,
// OnOffSensorDevice,
// RootNodeDevice,
// SpeakerDevice,
// ThermostatDevice,
)
// List of supported device traits by this app:
val supportedTraits: List<TraitFactory<out Trait>> = listOf(
// TODO: 4.1.2 - Non-registered traits will be unsupported
// AreaAttendanceState,
// AreaPresenceState,
// Assistant,
// AssistantBroadcast,
// AssistantFulfillment,
// BasicInformation,
// BooleanState,
// OccupancySensing,
// OnOff,
// Notification,
// LevelControl,
// TemperatureControl,
// TemperatureMeasurement,
// Thermostat,
// Time,
// Volume,
)
}
مقداردهی اولیه شیء HomeClient
تمام برنامههایی که از APIهای Home استفاده میکنند، یک شیء HomeClient را مقداردهی اولیه میکنند که رابط اصلی برای تعامل با APIها است. ما این شیء را در مقداردهی اولیه کلاس HomeApp ( HomeApp.kt ) آماده میکنیم.
// Registry to record device types and traits used in this app:
val registry = FactoryRegistry(
types = supportedTypes,
traits = supportedTraits
)
// Configuration options for the HomeClient:
val config = HomeConfig(
coroutineContext = Dispatchers.IO,
factoryRegistry = registry
)
// Initialize the HomeClient, which is the primary object to use all Home APIs:
homeClient = Home.getClient(context = context, homeConfig = config)
ابتدا، با استفاده از انواع و ویژگیهای پشتیبانیشدهای که قبلاً تعریف کردیم، یک FactoryRegistry ایجاد میکنیم. سپس، با استفاده از این رجیستری، یک HomeConfig مقداردهی اولیه میکنیم که شامل پیکربندی مورد نیاز برای اجرای APIها است. در مرحله بعد، از فراخوانی Home.getClient(...) برای دریافت نمونه HomeClient استفاده میکنیم.
تعاملات ما با APIهای Home تماماً از طریق این شیء HomeClient خواهد بود.
استفاده از API مجوزها
احراز هویت کاربر برای رابطهای برنامهنویسی کاربردی (API) خانه (Home) از طریق رابط برنامهنویسی کاربردی مجوزها (Permissions API) انجام میشود. فایل منبع PermissionsManager.kt برنامه نمونه (Sample App) حاوی کدی برای احراز هویت کاربر است. برای فعال کردن مجوزها برای برنامه نمونه، محتویات توابع checkPermissions(...) و requestPermissions(...) را از حالت کامنت خارج کنید.
ثبت نام:
homeClient.registerActivityResultCallerForPermissions(activity)
راه اندازی:
try {
val result: PermissionsResult
result = homeClient.requestPermissions(forceLaunch = true)
when (result.status) {
PermissionsResultStatus.SUCCESS -> // Success Case
PermissionsResultStatus.CANCELLED -> // User Cancelled
PermissionsResultStatus.ERROR -> // Some Error
else -> // Unsupported Case
}
}
catch (e: HomeException) { ... }
بررسی:
try {
val state: PermissionsState
state = homeClient.hasPermissions().first { state ->
state != PermissionsState.PERMISSIONS_STATE_UNINITIALIZED
}
when (state) {
PermissionsState.GRANTED -> // Signed In
PermissionsState.NOT_GRANTED -> // Not Signed In
PermissionsState.PERMISSIONS_STATE_UNAVAILABLE -> // ...
PermissionsState.PERMISSIONS_STATE_UNINITIALIZED -> // ...
else -> // Unsupported case
}
}
catch (e: HomeException) { ... }
اشتراک:
homeClient.hasPermissions().collect( { state ->
// Track the changes on state
} )
مرحله ۴.۳.۱ در PermissionsManager.kt را از حالت کامنت خارج کنید تا کدی که درخواست مجوزها را میکند فعال شود:
fun requestPermissions() {
scope.launch {
try {
// TODO: 4.3.1 - Request the permissions from the Permissions API
// // Request permissions from the Permissions API and record the result:
// val result: PermissionsResult = client.requestPermissions(forceLaunch = true)
// // Adjust the sign-in status according to permission result:
// if (result.status == PermissionsResultStatus.SUCCESS)
// isSignedIn.emit(true)
// // Report the permission result:
// reportPermissionResult(result)
}
catch (e: HomeException) { MainActivity.showError(this, e.message.toString()) }
}
}
حالا برنامه را روی گوشی خود اجرا کنید، مراحل را دنبال کنید و مجوزها را بدهید. باید جریان زیر را ببینید:





پیام "در حال بارگذاری" هرگز از بین نمیرود، اما این به این دلیل است که ما کدی را که ساختار و دستگاهها را میخواند، پیادهسازی نکردهایم. این کار را در بخش بعدی انجام خواهیم داد.
۵. مدل داده را درک کنید
در APIهای Home، مدل داده از موارد زیر تشکیل شده است:
-
Structureخانهای را نشان میدهد که شامل اتاقها و دستگاهها است. -
Roomبخشی از یک سازه است و شامل دستگاهها میشود. - دستگاهها (که به عنوان
HomeDeviceتعریف میشوند) میتوانند به یک سازه (یا خانه) یا یک اتاق در سازه اختصاص داده شوند. - دستگاهها از یک یا چند نمونه
DeviceTypeتشکیل شدهاند. -
DeviceTypeاز نمونههایTraitتشکیل شده است. -
Traitاز نمونههایAttribute(برای خواندن/نوشتن)، نمونههایCommand(برای کنترل ویژگیها) و نمونههایEvent(برای خواندن یا اشتراکگذاری رکوردهای تغییرات گذشته) تشکیل شده است. - نمونههای
Automationبخشی از یک ساختار هستند و از فرادادهها و دستگاههای خانگی برای خودکارسازی وظایف در خانه استفاده میکنند.

در این بخش، یاد خواهید گرفت که چگونه کد منبع را توسعه دهید تا نشان دهید چگونه از API ساختار برای تجزیه و رندر سازههای خانه، اتاقها، دستگاهها و غیره استفاده کنید.
خواندن ساختارها
طراحی رابطهای برنامهنویسی کاربردی (API) خانه (Home) بر اساس جریانهای کاتلین (Kotlin Flows ) است تا اشیاء مدل داده (مثلاً Structure ، HomeDevice و غیره) را به صورت جریانی (stream) منتشر کند. توسعهدهندگان برای دریافت تمام اشیاء موجود در شیء (مثلاً Structure ، Room و غیره) در یک Flow مشترک میشوند.
برای بازیابی تمام ساختارها، تابع structures() را فراخوانی کنید، که جریانی از ساختارها را برمیگرداند. سپس، تابع list را روی جریان فراخوانی کنید تا تمام ساختارهایی را که کاربر در اختیار دارد، دریافت کنید.
// Get the a snapshot of all structures from the current homeClient
val allStructures : Set<Structure> =
homeClient.structures() // HomeObjectsFlow<Structure>
.list() // Set<Structure>
راهنمای معماری برنامه اکیداً توصیه میکند که برای بهبود جریان دادههای برنامه و مدیریت وضعیت، از یک رویکرد برنامهنویسی واکنشی مدرن استفاده شود.
در اینجا نحوه پایبندی برنامه نمونه به سبک کدنویسی واکنشگرا آورده شده است:
- مدلهای نمایش (مانند
StructureViewModelوDeviceViewModel، به عنوان دارنده وضعیت) در جریانهای دریافتی از SDK APIهای Home مشترک میشوند تا تغییرات مقادیر را دریافت کرده و آخرین وضعیتها را حفظ کنند. - نماها (مانند
StructureViewوDeviceView) برای دریافت حالتها و رندر رابط کاربری برای انعکاس آن تغییرات، در مدلهای نما مشترک میشوند. - وقتی کاربر روی دکمهای در یک نما کلیک میکند (مثلاً دکمهی «روشن» یک دستگاه روشنایی)، رویدادها توابع مدل نما را فعال میکنند که توابع APIهای Home پاسخدهنده را فراخوانی میکنند (مثلاً دستور
Onدر ویژگیOnOff).
در مرحله 5.1.1 در HomeAppViewModel.kt ، ما با فراخوانی تابع collect() در رویدادهای تغییر ساختار مشترک میشویم. بخشی را که از structureSet که توسط پاسخ Structures API برگردانده شده و در StateFlow StructureViewModel's تحویل داده شده است، عبور میکند، از حالت کامنت خارج کنید. این به برنامه اجازه میدهد تا تغییرات وضعیت ساختار را رصد کند:
private suspend fun subscribeToStructures() {
// TODO: 5.1.1 - Subscribe the structure data changes
// // Subscribe to structures returned by the Structures API:
// homeApp.homeClient.structures().collect { structureSet ->
// val structureVMList: MutableList<StructureViewModel> = mutableListOf()
// // Store structures in container ViewModels:
// for (structure in structureSet) {
// structureVMList.add(StructureViewModel(structure))
// }
// // Store the ViewModels:
// structureVMs.emit(structureVMList)
//
// // If a structure isn't selected yet, select the first structure from the list:
// if (selectedStructureVM.value == null && structureVMList.isNotEmpty())
// selectedStructureVM.emit(structureVMList.first())
//
// }
}
در DevicesView.kt ، برنامه به StructureViewModel'sStateFlow, متصل میشود که هنگام تغییر دادههای ساختار، ترکیببندی رابط کاربری را فعال میکند. برای رندر کردن لیست ساختار به عنوان یک منوی کشویی، کد منبع را در مرحله 5.1.2 از حالت کامنت خارج کنید:
val structureVMs: List<StructureViewModel> = homeAppVM.structureVMs.collectAsState().value
...
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
// TODO: 5.1.2 - Show list of structures in DropdownMenu
// for (structure in structureVMs) {
// DropdownMenuItem(
// text = { Text(structure.name) },
// onClick = {
// scope.launch { homeAppVM.selectedStructureVM.emit(structure) }
// expanded = false
// }
// )
// }
}
...
برنامه را دوباره اجرا کنید. وقتی روی فلش ضربه بزنید، باید منو را ببینید:

ساختار را تجزیه کنید
مرحله بعدی پیمایش اشیاء خانه در یک ساختار است. اتاقها را از ساختار بازیابی کنید:
val rooms: Set<Room>
rooms = structure.rooms().list()
سپس میتوانید برای بازیابی دستگاهها، اتاقها را پیمایش کنید:
val devices: Set<HomeDevice>
devices = room.devices().list()
مهم: در مدل دادهی Home APIها، یک ساختار میتواند شامل دستگاههایی باشد که به یک اتاق اختصاص داده نشدهاند ، بنابراین حتماً دستگاههای بدون اتاق را نیز در برنامهی خود ثبت کنید:
val devicesWithoutRooms: MutableSet<HomeDevice> = mutableSetOf()
for (device in structure.devices().list())
if (device.roomId == null)
devicesWithoutRooms.add(device)
دوباره، در کد نمونه موجود، ما در یک جریان مشترک میشویم تا آخرین لیست اتاقها و دستگاهها را دریافت کنیم. کد مراحل 5.2.1 و 5.2.2 را در فایل منبع StructureViewModel.kt بررسی کنید و آن را از حالت کامنت خارج کنید تا اشتراک دادههای اتاق فعال شود:
val roomVMs : MutableStateFlow<List<RoomViewModel>>
val deviceVMs : MutableStateFlow<List<DeviceViewModel>>
val deviceVMsWithoutRooms : MutableStateFlow<List<DeviceViewModel>>
private suspend fun subscribeToRooms() {
// TODO: 5.2.1 - Subscribe the room data changes
// // Subscribe to changes on rooms:
// structure.rooms().collect { roomSet ->
// val roomVMs = mutableListOf<RoomViewModel>()
// // Store rooms in container ViewModels:
// for (room in roomSet) {
// roomVMs.add(RoomViewModel(room))
// }
// // Store the ViewModels:
// this.roomVMs.emit(roomVMs)
// }
}
private suspend fun subscribeToDevices() {
// TODO: 5.2.2 - Subscribe the device data changes in a structure
// // Subscribe to changes on devices:
// structure.devices().collect { deviceSet ->
// val deviceVMs = mutableListOf<DeviceViewModel>()
// val deviceWithoutRoomVMs = mutableListOf<DeviceViewModel>()
// // Store devices in container ViewModels:
// for (device in deviceSet) {
// val deviceVM = DeviceViewModel(device)
// deviceVMs.add(deviceVM)
// // For any device that's not in a room, additionally keep track of a separate list:
// if (device.roomId == null)
// deviceWithoutRoomVMs.add(deviceVM)
// }
// // Store the ViewModels:
// this.deviceVMs.emit(deviceVMs)
// deviceVMsWithoutRooms.emit(deviceWithoutRoomVMs)
// }
}
مراحل ۵.۲.۳ و ۵.۲.۴ را در فایل منبع DevicesView.kt از حالت کامنت خارج کنید تا لیست اتاقها به صورت یک منو نمایش داده شود:
val selectedRoomVMs: List<RoomViewModel> =
selectedStructureVM.roomVMs.collectAsState().value
...
for (roomVM in selectedRoomVMs) {
// TODO: 5.2.3 - Render the list of rooms
// RoomListItem(roomVM)
// TODO: 5.2.4 - Render the list of devices in a room
// val deviceVMsInRoom: List<DeviceViewModel> = roomVM.deviceVMs.collectAsState().value
//
// for (deviceVM in deviceVMsInRoom) {
// DeviceListItem(deviceVM, homeAppVM)
// }
}
حالا که دستگاهها را دارید، یاد میگیریم که چگونه با آنها کار کنیم.

۶. کار با دستگاهها
رابطهای برنامهنویسی کاربردی (API) خانه (Home) از یک شیء HomeDevice برای ثبت دستگاه و قابلیتهای آن استفاده میکنند. توسعهدهندگان میتوانند ویژگیهای دستگاه را ثبت کرده و از آنها برای نمایش دستگاههای خانه هوشمند در برنامههای خود استفاده کنند.
خواندن وضعیت دستگاه
شیء HomeDevice مجموعهای از مقادیر استاتیک مانند نام دستگاه یا وضعیت اتصال را ارائه میدهد. به عنوان یک توسعهدهنده، میتوانید این موارد را بلافاصله پس از دریافت دستگاه از APIها بازیابی کنید:
val id: String = device.id.id
val name: String = device.name
val connectivity: ConnectivityState =
device.sourceConnectivity.connectivityState
برای دریافت قابلیتهای دستگاه، باید انواع و ویژگیها را از HomeDevice بازیابی کنید. برای انجام این کار، میتوانید به صورت زیر در جریان نوع دستگاه مشترک شوید و ویژگیها را از انواع دستگاه بازیابی کنید:
device.types().collect { typeSet ->
var primaryType : DeviceType = UnknownDeviceType()
for (typeInSet in typeSet)
if (typeInSet.metadata.isPrimaryType)
primaryType = typeInSet
val traits: List<Trait> = mutableListOf()
for (trait in primaryType.traits())
if (trait.factory in myTraits)
traits.add(trait)
for (trait in traits)
parseTrait(trait, primaryType)
}
هر دستگاه شامل مجموعهای از DeviceType پشتیبانیشده (قابلیتهای بستهبندیشده) است که میتوانید با استفاده از device.types() را بازیابی کنید. این نوع دستگاهها حاوی ویژگیهایی هستند که میتوان با استفاده از type.traits() آنها را بازیابی کرد. هر دستگاه یکی از انواع خود را به عنوان نوع اصلی (که میتوان با استفاده از type.metadata.isPrimaryType بررسی کرد) علامتگذاری میکند که باید در برنامه خود نمایش دهید. برای ارائه یک تجربه کامل به کاربران، توصیه میکنیم همه انواع برگشتی را پیمایش کرده و همه ویژگیهای موجود را ادغام کنید.
پس از بازیابی یک ویژگی، میتوانید آن را با استفاده از تابعی مانند تابع زیر تجزیه کنید تا مقادیر را تفسیر کنید:
fun <T : Trait?> parseTrait(trait : T, type: DeviceType) {
val status : String = when (trait) {
is OnOff -> { if (trait.onOff) "On" else "Off" }
is LevelControl -> { trait.currentLevel.toString() }
is BooleanState -> {
when (type.factory) {
ContactSensorDevice -> {
if (trait.stateValue) "Closed"
else "Open"
}
else -> ...
}
}
else -> ...
}
}
توجه داشته باشید که بسته به نوع دستگاهی که یک ویژگی (trait) را نشان میدهد، میتواند تفاوتهایی در آنچه که یک ویژگی نشان میدهد وجود داشته باشد (به BooleanState در مثال قبلی مراجعه کنید)، بنابراین باید از زمینه هر نوع دستگاه آگاه باشید تا بفهمید ویژگیهای آنها واقعاً چه چیزی را نشان میدهند.
مراحل ۶.۱.۱ و ۶.۱.۲ در فایل منبع DeviceViewModel.kt را برای بازیابی حالتها از حالت کامنت خارج کنید:
private suspend fun subscribeToType() {
// Subscribe to changes on device type, and the traits/attributes within:
device.types().collect { typeSet ->
// Container for the primary type for this device:
var primaryType : DeviceType = UnknownDeviceType()
...
// TODO: 6.1.1 - Determine the primary type for this device
// // Among all the types returned for this device, find the primary one:
// for (typeInSet in typeSet)
// if (typeInSet.metadata.isPrimaryType)
// primaryType = typeInSet
//
// // Optional: For devices with a single type that did not define a primary:
// if (primaryType is UnknownDeviceType && typeSet.size == 1)
// primaryType = typeSet.first()
// Container for list of supported traits present on the primary device type:
val supportedTraits: List<Trait> = getSupportedTraits(primaryType.traits())
...
}
fun getSupportedTraits(traits: Set<Trait>) : List<Trait> {
val supportedTraits: MutableList<Trait> = mutableListOf()
// TODO: 6.1.2 - Get only the supported traits for this device
// for (trait in traits)
// if (trait.factory in HomeApp.supportedTraits)
// supportedTraits.add(trait)
return supportedTraits
}
مرحله 6.1.3 در DeviceView.kt را از حالت کامنت خارج کنید تا یک ویژگی OnOff، شامل نام و وضعیت آن، به عنوان یک String شود:
Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
when (trait) {
is OnOff -> {
// TODO: 6.1.3 - Render controls based on the trait type
// Column (Modifier.fillMaxWidth()) {
// Text(trait.factory.toString(), fontSize = 20.sp)
// Text(DeviceViewModel.getTraitStatus(trait, type), fontSize = 16.sp)
// }
...
}
is LevelControl -> {
...
}
is BooleanState -> {
...
}
is OccupancySensing -> {
...
}
...
}
اگر اکنون برنامه را با انواع دستگاههای پشتیبانیشده (مثلاً یک دستگاه Light) اجرا کنید، باید وضعیت بهروز بودن همه دستگاهها را نشان دهد.

صدور دستورات دستگاه
برای صدور دستورات به دستگاهها، APIهای Home توابع راحتی را روی اشیاء Trait مانند trait.on() یا trait.moveToLevel(...) ارائه میدهند:
fun <T : Trait?> issueCommand(trait : T) {
when (trait) {
is OnOff -> {
// trait.on()
// trait.off()
}
is LevelControl -> {
// trait.moveToLevel(...)
// trait.moveToLevelWithOnOff(...)
}
}
}
نکته: پس از تعیین نوع ویژگی، از ویژگی تکمیل خودکار اندروید استودیو استفاده کنید تا ببینید چه نوع اقداماتی برای تعامل با ویژگی در دسترس است.
مرحله ۶.۲.۱ در DeviceView.kt را از حالت کامنت خارج کنید تا کنترلهای کاربردی در برنامه اضافه شوند:
Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
when (trait) {
is OnOff -> {
....
// TODO: 6.2.1 - Render controls based on the trait type
// Switch (checked = (trait.onOff == true), modifier = Modifier.align(Alignment.CenterEnd),
// onCheckedChange = { state ->
// scope.launch { if (state) trait.on() else trait.off() }
// },
// enabled = isConnected
// )
}
اگر اکنون برنامه را اجرا کنید، باید به شما امکان کنترل دستگاههای فیزیکی واقعی را بدهد.
اگر دکمه روشن/خاموش روی لامپ را فشار دهید، دستگاه باید روشن شود.

برای اطلاعات بیشتر در مورد نحوه کنترل دستگاهها، به بخش کنترل دستگاهها در اندروید مراجعه کنید.
۷. دستگاههای کمیسیون
API راهاندازی به توسعهدهندگان اجازه میدهد تا دستگاههایی را به اکوسیستم Google Home اضافه کنند و آنها را برای کنترل با استفاده از APIهای Home در دسترس قرار دهند. فقط دستگاههای Matter پشتیبانی میشوند. در این بخش بررسی خواهیم کرد که چگونه میتوانید راهاندازی دستگاه را در برنامههای خود فعال کنید.
قبل از شروع این بخش، مطمئن شوید که پیشنیازهای زیر را رعایت کردهاید:
- یک برنامهی پشتیبانیکننده از گوگل هاب Matter که در همان شبکهای که گوشی اندروید شما قرار دارد، قرار دارد، به برنامهی گوگل هوم شما اضافه شده است.
- شما یک پروژه توسعهدهنده در کنسول توسعهدهندگان گوگل هوم با VID
0xFFF1و PID0x8000ایجاد کردهاید.
اگر یک دستگاه فیزیکی Matter با کد QR برای راهاندازی دارید، میتوانید از بخش فعالسازی API راهاندازی صرفنظر کنید. در غیر این صورت، به بخش بعدی بروید، جایی که در مورد نحوه استفاده از برنامه Matter Virtual Device (MVD) برای ایجاد دستگاههای مجازی قابل راهاندازی بحث خواهیم کرد.
اختیاری: یک دستگاه قابل سفارش Matter تهیه کنید
سادهترین راه برای آمادهسازی یک دستگاه قابل سفارش Matter، استفاده از یک دستگاه شبیهسازی شده است که توسط برنامه Matter Virtual Device (MVD) ارائه میشود.
پس از نصب MVD و تنظیم فایروال، MVD را اجرا کنید:

یک دستگاه OnOff ایجاد کنید. توجه داشته باشید که هنوز راهاندازی نشده است - بعداً در این آزمایشگاه کد آن را راهاندازی خواهید کرد.

فعال کردن API راهاندازی
API راهاندازی خارج از Activity برنامه کار میکند، بنابراین راهاندازی باید متفاوت از سایر APIهای Home انجام شود. برای اینکه برنامه شما برای راهاندازی آماده شود، به دو متغیر نیاز دارید.
یکی از متغیرها ActivityResultLauncher است که برای ارسال قصد راهاندازی و مدیریت فراخوانی نتیجه استفاده میشود. متغیر دیگر CommissioningResult است که شیء مورد استفاده برای ذخیره نتیجه راهاندازی است. برای نحوه تنظیم راهاندازی، به مثال زیر مراجعه کنید:
var launcher: ActivityResultLauncher<IntentSenderRequest>
lateinit var commissioningResult: CommissioningResult?
launcher = activity.registerForActivityResult(StartIntentSenderForResult()) { result ->
try {
commissioningResult = CommissioningResult.fromIntentSenderResult(
result.resultCode, result.data)
} catch (exception: ApiException) {
// Catch any issues
}
}
پس از تنظیم جریان راهاندازی، شما قصد راهاندازی خود را خواهید ساخت و آن را با استفاده از لانچری که در مثال قبلی ایجاد کردیم، راهاندازی خواهید کرد. توصیه میکنیم قصد و لانچر را در یک تابع اختصاصی مانند زیر قرار دهید. یک تابع اختصاصی میتواند به یک عنصر رابط کاربری (مانند دکمه +Add Device ) متصل شود و بر اساس درخواست کاربر فراخوانی شود:
fun requestCommissioning() {
// Retrieve the onboarding payload used when commissioning devices:
val payload = activity.intent?.getStringExtra(Matter.EXTRA_ONBOARDING_PAYLOAD)
scope.launch {
// Create a commissioning request to store the device in Google's Fabric:
val request = CommissioningRequest.builder()
.setStoreToGoogleFabric(true)
.setOnboardingPayload(payload)
.build()
// Initialize client and sender for commissioning intent:
val client: CommissioningClient = Matter.getCommissioningClient(context)
val sender: IntentSender = client.commissionDevice(request).await()
// Launch the commissioning intent on the launcher:
launcher.launch(IntentSenderRequest.Builder(sender).build())
}
}
مرحله 7.1.1 در CommissioningManager.kt را از حالت کامنت خارج کنید تا قابلیت راهاندازی فعال شود و دکمه +Add Device در برنامه نمونه کار کند.
// Called by +Add Device button in DeviceView.kt
fun requestCommissioning() {
// Retrieve the onboarding payload used when commissioning devices:
val payload = activity.intent?.getStringExtra(Matter.EXTRA_ONBOARDING_PAYLOAD)
// TODO: 7.1.1 - Launch the commissioning intent
// scope.launch {
// // Create a commissioning request to store the device in Google's Fabric:
// val request = CommissioningRequest.builder()
// .setStoreToGoogleFabric(true)
// .setOnboardingPayload(payload)
// .build()
// // Initialize client and sender for commissioning intent:
// val client: CommissioningClient = Matter.getCommissioningClient(context)
// val sender: IntentSender = client.commissionDevice(request).await()
// // Launch the commissioning intent on the launcher:
// launcher.launch(IntentSenderRequest.Builder(sender).build())
// }
}
اجرای این تابع باید جریان راهاندازی (Commissioning Flow) را آغاز کند، که باید صفحهای شبیه به تصویر زیر را نمایش دهد:

جریان راهاندازی را درک کنید
روند راهاندازی شامل مجموعهای از صفحات است که کاربر را در افزودن دستگاه به حساب گوگل خود راهنمایی میکند:





کاربران با یک اسکنر کد QR مواجه میشوند که میتوانند از آن برای اسکن کدهای QR از دستگاههای Matter استفاده کنند. سپس روند کار شامل نمایش توافقنامه کاربری، کشف و راهاندازی دستگاه و نامگذاری دستگاه خواهد بود. پس از اتمام روند، تمرکز دوباره به برنامه برمیگردد و نتیجه راهاندازی را به تابع فراخوانی که در بخش قبل طراحی کردیم، ارسال میکند.
یکی از مزایای APIهای راهاندازی این است که جریان UX توسط SDK مدیریت میشود، بنابراین توسعهدهندگان میتوانند خیلی سریع شروع به کار کنند. این امر همچنین به کاربران تجربهای یکپارچه هنگام افزودن دستگاهها در برنامههای مختلف میدهد.
برای کسب اطلاعات بیشتر در مورد API راهاندازی، به صفحه API راهاندازی در اندروید مراجعه کنید.
۸. تبریک میگویم!
تبریک! شما با موفقیت یک برنامه اندروید با استفاده از APIهای Google Home ایجاد کردید. در طول این آزمایشگاه کد، مجوزها، دستگاهها، ساختارها و APIهای راهاندازی را بررسی کردید. در آزمایشگاه کد بعدی، ایجاد اتوماسیونهای پیشرفته با استفاده از APIهای Home در آزمایشگاه کد اندروید ، APIهای اتوماسیون و کشف را بررسی کرده و برنامه را تکمیل خواهیم کرد.
امیدواریم از ساخت برنامههایی که به صورت خلاقانه دستگاههای موجود در اکوسیستم گوگل هوم را کنترل میکنند، لذت ببرید.
مراحل بعدی
- با تکمیل دومین آزمایشگاه کد در این مجموعه، به بخش بعدی سفر خود در یادگیری APIهای Home در اندروید ادامه دهید: ایجاد اتوماسیونهای پیشرفته با استفاده از APIهای Home در اندروید .
- شما میتوانید با هرگونه توصیهای با ما تماس بگیرید یا هرگونه مشکلی را از طریق ردیاب مشکلات ، موضوع پشتیبانی خانه هوشمند گزارش دهید.