{"id":489571,"date":"2026-08-03T12:56:57","date_gmt":"2026-08-03T12:56:57","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=489571"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=489571","title":{"rendered":"One HomeKit dashboard for iPadOS and tvOS: HomeDeck for HomeKit architecture and the limits of two platforms"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>I am building HomeDeck for HomeKit, a full-screen dashboard for an existing Apple Home on iPad and Apple TV. This article is not a product showcase. It is an engineering case study about turning the heterogeneous HomeKit object graph into one stable snapshot, separating the system layer from the UI, synchronizing user configuration, and keeping shared SwiftUI code from becoming a forest of platform checks.<\/p>\n<p>The project started as an Apple TV app. After trying the first builds, smart-home enthusiasts began asking for an iPad version, mainly as a wall-mounted or tabletop touch panel. What began as one tvOS application became a two-platform product with shared data and very different interaction models.<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/a78\/c9c\/343\/a78c9c3439fd0d437f3d52f628b46e78.png\" alt=\"HomeDeck for HomeKit on Apple TV\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/\/post_images\/a78\/c9c\/343\/a78c9c3439fd0d437f3d52f628b46e78.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/a78\/c9c\/343\/a78c9c3439fd0d437f3d52f628b46e78.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>HomeDeck for HomeKit on Apple TV<\/figcaption><\/div>\n<\/figure>\n<h3>The initial problem<\/h3>\n<p>HomeKit does not return ready-made UI cards. It exposes an object graph of homes, rooms, accessories, services, and characteristics, while the actual capabilities depend on each accessory.<\/p>\n<p>The dashboard had to combine all of the following on one screen:<\/p>\n<ul>\n<li>\n<p>rooms and zones;<\/p>\n<\/li>\n<li>\n<p>scenes;<\/p>\n<\/li>\n<li>\n<p>lights, outlets, blinds, climate controls, and other devices;<\/p>\n<\/li>\n<li>\n<p>temperature, humidity, motion, contact, and smoke sensors;<\/p>\n<\/li>\n<li>\n<p>cameras;<\/p>\n<\/li>\n<li>\n<p>security status;<\/p>\n<\/li>\n<li>\n<p>events and control commands.<\/p>\n<\/li>\n<\/ul>\n<p>The second part of the problem was the difference between the two platforms. iPad is built around touch, reordering, and detailed configuration. Apple TV uses directional focus, a remote, and an interface read from several meters away. The data is shared, but the interaction model is not.<\/p>\n<p>There was also a product reason to focus on tvOS: Apple TV does not provide a full Home app comparable to the one on iPhone and iPad. The system exposes selected controls, but not a persistent full-screen representation of the entire home.<\/p>\n<h3>A layer between HomeKit and SwiftUI<\/h3>\n<p>Binding views directly to <code>HMAccessory<\/code>, <code>HMService<\/code>, and <code>HMCharacteristic<\/code> proved inconvenient. The UI quickly learned too much about HomeKit, while previews, tests, and a demo mode would all require a live home.<\/p>\n<p>The system layer is therefore hidden behind a provider protocol:<\/p>\n<pre><code class=\"swift\">@MainActorprotocol HomeDashboardProviding {    func loadSnapshot() async throws -&gt; DashboardSnapshot    func eventStream() -&gt; AsyncStream&lt;HomeEvent&gt;    func configurationChangeStream() -&gt; AsyncStream&lt;Void&gt;    func performScene(id: ScenePreset.ID) async throws    func setDevicePower(id: DeviceTile.ID, isOn: Bool) async throws    func setDeviceLevel(id: DeviceTile.ID, level: Double) async throws    func setCurtainLevel(id: DeviceTile.ID, level: Double) async throws    func setThermostatTarget(id: DeviceTile.ID, target: Double) async throws}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>The production <code>HomeKitDashboardProvider<\/code> implements this protocol through <code>HMHomeManager<\/code>. A mock provider returns a deterministic home. SwiftUI receives a <code>DashboardSnapshot<\/code>, a presentation model that does not depend on HomeKit classes.<\/p>\n<p>This provided three practical benefits:<\/p>\n<ol>\n<li>\n<p>UI tests no longer depend on the developer\u2019s home infrastructure.<\/p>\n<\/li>\n<li>\n<p>App Store screenshots are reproducible.<\/p>\n<\/li>\n<li>\n<p>Views do not have to interpret accessory characteristics.<\/p>\n<\/li>\n<\/ol>\n<h3>Why a snapshot instead of observing every characteristic directly<\/h3>\n<p>At startup, <code>HMHomeManager<\/code> already contains the latest values known to the system. Reading every characteristic of a large home before the first render can noticeably delay launch, especially when the home is reached through a home hub.<\/p>\n<p>HomeDeck uses a two-stage approach:<\/p>\n<ol>\n<li>\n<p>Build the first snapshot from the available HomeKit object graph.<\/p>\n<\/li>\n<li>\n<p>Refresh supported characteristics in the background and receive subsequent changes through delegates and an event stream.<\/p>\n<\/li>\n<\/ol>\n<p>Observation is registered before the background refresh so a change cannot be lost between the initial snapshot and an explicit read.<\/p>\n<p>The snapshot contains normalized UI entities such as <code>ScenePreset<\/code>, <code>DeviceTile<\/code>, <code>SensorTile<\/code>, <code>HomeCamera<\/code>, rooms, and zones. A view should not have to determine whether <code>CurrentTemperature<\/code> belongs to a thermostat or a standalone sensor. That decision is made while building the model.<\/p>\n<h3>Normalizing devices<\/h3>\n<p>Accessories with similar names can expose different services and characteristics. A card type therefore cannot be determined reliably from the accessory name or manufacturer alone.<\/p>\n<p>The provider analyzes the actual service and its associated services. From that information it derives:<\/p>\n<ul>\n<li>\n<p>the control type;<\/p>\n<\/li>\n<li>\n<p>supported ranges and step values;<\/p>\n<\/li>\n<li>\n<p>the current state;<\/p>\n<\/li>\n<li>\n<p>support for color, level, or mode changes;<\/p>\n<\/li>\n<li>\n<p>room membership;<\/p>\n<\/li>\n<li>\n<p>accessory availability.<\/p>\n<\/li>\n<\/ul>\n<p>A command is sent back through the stable identifier of the normalized entity. After writing a value, the UI waits for the confirmed HomeKit state instead of living indefinitely in an optimistic version of reality.<\/p>\n<p>Cameras require a separate path. HomeKit may return a camera profile, but a live stream is available only when the corresponding <code>streamControl<\/code> and an allowed configuration are present. A camera appearing in the home does not guarantee identical behavior across models.<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/ec6\/d16\/0fe\/ec6d160fed4975176a8a98058d01c2c3.png\" alt=\"HomeDeck for HomeKit dashboard\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/\/post_images\/ec6\/d16\/0fe\/ec6d160fed4975176a8a98058d01c2c3.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/ec6\/d16\/0fe\/ec6d160fed4975176a8a98058d01c2c3.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>HomeDeck for HomeKit dashboard<\/figcaption><\/div>\n<\/figure>\n<h3>Shared data, different interfaces<\/h3>\n<p>Trying to use exactly the same composition on iPad and Apple TV breaks down quickly.<\/p>\n<p>On iPad, the important concerns are:<\/p>\n<ul>\n<li>\n<p>touch targets and gestures;<\/p>\n<\/li>\n<li>\n<p>drag and drop for ordering items;<\/p>\n<\/li>\n<li>\n<p>window resizing and Split View;<\/p>\n<\/li>\n<li>\n<p>Dynamic Type;<\/p>\n<\/li>\n<li>\n<p>pointer and keyboard as additional input methods.<\/p>\n<\/li>\n<\/ul>\n<p>On tvOS, the priorities are different:<\/p>\n<ul>\n<li>\n<p>a predictable focus graph;<\/p>\n<\/li>\n<li>\n<p>clearly visible focused, pressed, and selected states;<\/p>\n<\/li>\n<li>\n<p>enough spacing for focus scaling;<\/p>\n<\/li>\n<li>\n<p>readability from the sofa;<\/p>\n<\/li>\n<li>\n<p>correct Back button and Siri Remote behavior.<\/p>\n<\/li>\n<\/ul>\n<p>HomeDeck therefore shares the data layer, not the top-level composition. Models, storage, and most components are common, while the platform layout is selected separately:<\/p>\n<pre><code class=\"swift\">#if os(tvOS)DashboardAppleTVLayout(...)#elseDashboardIPadLayout(...)#endif<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>This is easier to maintain than two independent applications and safer than solving every difference with dozens of modifiers inside one huge view.<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/b55\/208\/c57\/b55208c578cefc8f1900139d0479c4ea.png\" alt=\"HomeDeck for HomeKit on a wall-mounted iPad\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/\/post_images\/b55\/208\/c57\/b55208c578cefc8f1900139d0479c4ea.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/b55\/208\/c57\/b55208c578cefc8f1900139d0479c4ea.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>HomeDeck for HomeKit on a wall-mounted iPad<\/figcaption><\/div>\n<\/figure>\n<h3>Security on Apple TV is an architectural concern<\/h3>\n<p>tvOS cannot be treated as iPadOS without a touch screen. The platform handles sensitive HomeKit operations differently. Controls for locks and security systems should not be transferred mechanically to a shared television.<\/p>\n<p>The available actions therefore depend not only on the accessory characteristics but also on the platform. This is a domain rule, not a cosmetic decision to hide a button. The UI does not present an action the app should not offer on that device.<\/p>\n<p>There is another limitation: an application cannot replace the tvOS system screen saver. Ambient mode works only while HomeDeck remains open. This must be communicated clearly and accounted for in the application lifecycle.<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/f9a\/eba\/5f3\/f9aeba5f31a68b1c6ace875ee411a1e5.png\" alt=\"Ambient mode on Apple TV\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/\/post_images\/f9a\/eba\/5f3\/f9aeba5f31a68b1c6ace875ee411a1e5.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/f9a\/eba\/5f3\/f9aeba5f31a68b1c6ace875ee411a1e5.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>Ambient mode on Apple TV<\/figcaption><\/div>\n<\/figure>\n<h3>Synchronizing configuration without a custom server<\/h3>\n<p>The user configures the dashboard on iPad by choosing favorites, hiding unnecessary items, and changing their order. The same configuration should then appear on Apple TV.<\/p>\n<p>For this small amount of data, <code>NSUbiquitousKeyValueStore<\/code> is sufficient. Settings are stored separately for each HomeKit home, while local <code>UserDefaults<\/code> acts as a cache:<\/p>\n<pre><code class=\"swift\">func save(_ references: [FavoriteReference], homeID: String) {    let key = storageKey(homeID: homeID)    let values = references.map(\\.storageValue)    localStore.set(values, forKey: key)    cloudStore?.set(values, forKey: key)    cloudStore?.synchronize()}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>The external-change observer must be installed before the first <code>synchronize()<\/code> call. Otherwise a fresh Apple TV installation can miss the initial synchronization notification.<\/p>\n<p>There is no need to store the entire home snapshot in iCloud. Only identifiers and the user\u2019s ordering are synchronized. Current names and states are read from HomeKit again on each device.<\/p>\n<h3>Privacy by architecture<\/h3>\n<p>HomeDeck has no separate account system and no custom server that stores a copy of the user\u2019s home. HomeKit objects and compatible camera streams are processed on the device through Apple\u2019s system frameworks. Configuration is synchronized through the user\u2019s iCloud account, weather comes from WeatherKit, and purchases are handled by StoreKit.<\/p>\n<p>This is more than a marketing promise. It reduces the number of states that must be synchronized and protected. The trade-off is that the application accepts the limits of Apple\u2019s APIs and cannot compensate for missing characteristics through a manufacturer\u2019s server integration.<\/p>\n<h3>What I would define on day one<\/h3>\n<p>If I started a similar project again, I would establish five decisions immediately:<\/p>\n<ol>\n<li>\n<p>A normalized model between HomeKit and the UI.<\/p>\n<\/li>\n<li>\n<p>A provider protocol with production and deterministic demo implementations.<\/p>\n<\/li>\n<li>\n<p>A fast initial snapshot followed by a separate background refresh.<\/p>\n<\/li>\n<li>\n<p>A shared data layer but separate iPadOS and tvOS compositions.<\/p>\n<\/li>\n<li>\n<p>Platform security limitations represented as part of the domain model.<\/p>\n<\/li>\n<\/ol>\n<p>The main lesson is that cross-platform SwiftUI saves code only when the shared layer is chosen correctly. In HomeDeck, the data and basic components are shared. Navigation, screen density, and interaction remain platform-specific. That is what prevents the television interface from becoming a stretched iPad app.<\/p>\n<p>The project is under active development, with new builds appearing almost every week. Feedback from early users regularly turns into bug fixes, accessory compatibility improvements, and new features.<\/p>\n<p>HomeDeck for HomeKit is available in the App Store. More information is available at <a href=\"https:\/\/www.homedeck.co\/\" rel=\"noopener noreferrer nofollow\">https:\/\/www.homedeck.co\/<\/a>. New builds can be tested through TestFlight: <a href=\"https:\/\/testflight.apple.com\/join\/V1GdMNgw\" rel=\"noopener noreferrer nofollow\">https:\/\/testflight.apple.com\/join\/V1GdMNgw<\/a>. The Telegram group for feedback and smart-home discussion is available at <a href=\"https:\/\/t.me\/+d3TdcBnvujphM2Y6\" rel=\"noopener noreferrer nofollow\">https:\/\/t.me\/+d3TdcBnvujphM2Y6<\/a>.<\/p>\n<p>If there is interest, a follow-up article can cover the conversion of HomeKit services into typed cards or the focus-navigation architecture on tvOS.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1066106\/\">https:\/\/habr.com\/ru\/articles\/1066106\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I am building HomeDeck for HomeKit, a full-screen dashboard for an existing Apple Home on iPad and Apple TV. This article is not a product showcase. It is an engineering case study about turning the heterogeneous HomeKit object graph into one stable snapshot, separating the system layer from the UI, synchronizing user configuration, and keeping shared SwiftUI code from becoming a forest of platform checks.The project started as an Apple TV app. After trying the first builds, smart-home enthusiasts began asking for an iPad version, mainly as a wall-mounted or tabletop touch panel. What began as one tvOS application became a two-platform product with shared data and very different interaction models.HomeDeck for HomeKit on Apple TVThe initial problemHomeKit does not return ready-made UI cards. It exposes an object graph of homes, rooms, accessories, services, and characteristics, while the actual capabilities depend on each accessory.The dashboard had to combine all of the following on one screen:rooms and zones;scenes;lights, outlets, blinds, climate controls, and other devices;temperature, humidity, motion, contact, and smoke sensors;cameras;security status;events and control commands.The second part of the problem was the difference between the two platforms. iPad is built around touch, reordering, and detailed configuration. Apple TV uses directional focus, a remote, and an interface read from several meters away. The data is shared, but the interaction model is not.There was also a product reason to focus on tvOS: Apple TV does not provide a full Home app comparable to the one on iPhone and iPad. The system exposes selected controls, but not a persistent full-screen representation of the entire home.A layer between HomeKit and SwiftUIBinding views directly to HMAccessory, HMService, and HMCharacteristic proved inconvenient. The UI quickly learned too much about HomeKit, while previews, tests, and a demo mode would all require a live home.The system layer is therefore hidden behind a provider protocol:@MainActorprotocol HomeDashboardProviding {    func loadSnapshot() async throws -&gt; DashboardSnapshot    func eventStream() -&gt; AsyncStream&lt;HomeEvent&gt;    func configurationChangeStream() -&gt; AsyncStream&lt;Void&gt;    func performScene(id: ScenePreset.ID) async throws    func setDevicePower(id: DeviceTile.ID, isOn: Bool) async throws    func setDeviceLevel(id: DeviceTile.ID, level: Double) async throws    func setCurtainLevel(id: DeviceTile.ID, level: Double) async throws    func setThermostatTarget(id: DeviceTile.ID, target: Double) async throws}The production HomeKitDashboardProvider implements this protocol through HMHomeManager. A mock provider returns a deterministic home. SwiftUI receives a DashboardSnapshot, a presentation model that does not depend on HomeKit classes.This provided three practical benefits:UI tests no longer depend on the developer\u2019s home infrastructure.App Store screenshots are reproducible.Views do not have to interpret accessory characteristics.Why a snapshot instead of observing every characteristic directlyAt startup, HMHomeManager already contains the latest values known to the system. Reading every characteristic of a large home before the first render can noticeably delay launch, especially when the home is reached through a home hub.HomeDeck uses a two-stage approach:Build the first snapshot from the available HomeKit object graph.Refresh supported characteristics in the background and receive subsequent changes through delegates and an event stream.Observation is registered before the background refresh so a change cannot be lost between the initial snapshot and an explicit read.The snapshot contains normalized UI entities such as ScenePreset, DeviceTile, SensorTile, HomeCamera, rooms, and zones. A view should not have to determine whether CurrentTemperature belongs to a thermostat or a standalone sensor. That decision is made while building the model.Normalizing devicesAccessories with similar names can expose different services and characteristics. A card type therefore cannot be determined reliably from the accessory name or manufacturer alone.The provider analyzes the actual service and its associated services. From that information it derives:the control type;supported ranges and step values;the current state;support for color, level, or mode changes;room membership;accessory availability.A command is sent back through the stable identifier of the normalized entity. After writing a value, the UI waits for the confirmed HomeKit state instead of living indefinitely in an optimistic version of reality.Cameras require a separate path. HomeKit may return a camera profile, but a live stream is available only when the corresponding streamControl and an allowed configuration are present. A camera appearing in the home does not guarantee identical behavior across models.HomeDeck for HomeKit dashboardShared data, different interfacesTrying to use exactly the same composition on iPad and Apple TV breaks down quickly.On iPad, the important concerns are:touch targets and gestures;drag and drop for ordering items;window resizing and Split View;Dynamic Type;pointer and keyboard as additional input methods.On tvOS, the priorities are different:a predictable focus graph;clearly visible focused, pressed, and selected states;enough spacing for focus scaling;readability from the sofa;correct Back button and Siri Remote behavior.HomeDeck therefore shares the data layer, not the top-level composition. Models, storage, and most components are common, while the platform layout is selected separately:#if os(tvOS)DashboardAppleTVLayout(&#8230;)#elseDashboardIPadLayout(&#8230;)#endifThis is easier to maintain than two independent applications and safer than solving every difference with dozens of modifiers inside one huge view.HomeDeck for HomeKit on a wall-mounted iPadSecurity on Apple TV is an architectural concerntvOS cannot be treated as iPadOS without a touch screen. The platform handles sensitive HomeKit operations differently. Controls for locks and security systems should not be transferred mechanically to a shared television.The available actions therefore depend not only on the accessory characteristics but also on the platform. This is a domain rule, not a cosmetic decision to hide a button. The UI does not present an action the app should not offer on that device.There is another limitation: an application cannot replace the tvOS system screen saver. Ambient mode works only while HomeDeck remains open. This must be communicated clearly and accounted for in the application lifecycle.Ambient mode on Apple TVSynchronizing configuration without a custom serverThe user configures the dashboard on iPad by choosing favorites, hiding unnecessary items, and changing their order. The same configuration should then appear on Apple TV.For this small amount of data, NSUbiquitousKeyValueStore is sufficient. Settings are stored separately for each HomeKit home, while local UserDefaults acts as a cache:func save(_ references: [FavoriteReference], homeID: String) {    let key = storageKey(homeID: homeID)    let values = references.map(\\.storageValue)    localStore.set(values, forKey: key)    cloudStore?.set(values, forKey: key)    cloudStore?.synchronize()}The external-change observer must be installed before the first synchronize() call. Otherwise a fresh Apple TV installation can miss the initial synchronization notification.There is no need to store the entire home snapshot in iCloud. Only identifiers and the user\u2019s ordering are synchronized. Current names and states are read from HomeKit again on each device.Privacy by architectureHomeDeck has no separate account system and no custom server that stores a copy of the user\u2019s home. HomeKit objects and compatible camera streams are processed on the device through Apple\u2019s system frameworks. Configuration is synchronized through the user\u2019s iCloud account, weather comes from WeatherKit, and purchases are handled by StoreKit.This is more than a marketing promise. It reduces the number of states that must be synchronized and protected. The trade-off is that the application accepts the limits of Apple\u2019s APIs and cannot compensate for missing characteristics through a manufacturer\u2019s server integration.What I would define on day oneIf I started a similar project again, I would establish five decisions immediately:A normalized model between HomeKit and the UI.A provider protocol with production and deterministic demo implementations.A fast initial snapshot followed by a separate background refresh.A shared data layer but separate iPadOS and tvOS compositions.Platform security limitations represented as part of the domain model.The main lesson is that cross-platform SwiftUI saves code only when the shared layer is chosen correctly. In HomeDeck, the data and basic components are shared. Navigation, screen density, and interaction remain platform-specific. That is what prevents the television interface from becoming a stretched iPad app.The project is under active development, with new builds appearing almost every week. Feedback from early users regularly turns into bug fixes, accessory compatibility improvements, and new features.HomeDeck for HomeKit is available in the App Store. More information is available at https:\/\/www.homedeck.co\/. New builds can be tested through TestFlight: https:\/\/testflight.apple.com\/join\/V1GdMNgw. The Telegram group for feedback and smart-home discussion is available at https:\/\/t.me\/+d3TdcBnvujphM2Y6.If there is interest, a follow-up article can cover the conversion of HomeKit services into typed cards or the focus-navigation architecture on tvOS.\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 https:\/\/habr.com\/ru\/articles\/1066106\/<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-489571","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/489571","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=489571"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/489571\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=489571"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=489571"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=489571"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}