mirror of
https://github.com/home-assistant/iOS.git
synced 2026-02-10 12:51:23 -06:00
<!-- Thank you for submitting a Pull Request and helping to improve Home Assistant. Please complete the following sections to help the processing and review of your changes. Please do not delete anything from this template. --> ## Summary <!-- Provide a brief summary of the changes you have made and most importantly what they aim to achieve --> ## Screenshots <!-- If this is a user-facing change not in the frontend, please include screenshots in light and dark mode. --> <img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 - 2026-01-14 at 16 42 40" src="https://github.com/user-attachments/assets/0e023ce2-f767-40a8-900f-1b1a00a127f5" /> ## Link to pull request in Documentation repository <!-- Pull requests that add, change or remove functionality must have a corresponding pull request in the Companion App Documentation repository (https://github.com/home-assistant/companion.home-assistant). Please add the number of this pull request after the "#" --> Documentation: home-assistant/companion.home-assistant# ## Any other notes <!-- If there is any other information of note, like if this Pull Request is part of a bigger change, please include it here. -->
131 lines
3.8 KiB
Swift
131 lines
3.8 KiB
Swift
import AppIntents
|
|
import HAKit
|
|
import Shared
|
|
import SwiftUI
|
|
|
|
/// Entity tile view specifically designed for use in HomeView
|
|
/// Handles business logic like device class lookup, icon color computation,
|
|
/// app intents integration, and more info dialog presentation
|
|
@available(iOS 26.0, *)
|
|
struct HomeEntityTileView: View {
|
|
let server: Server
|
|
let haEntity: HAEntity
|
|
let areaName: String?
|
|
|
|
@Namespace private var namespace
|
|
@State private var iconColor: Color = .secondary
|
|
@State private var showMoreInfoDialog = false
|
|
@State private var deviceClass: DeviceClass = .unknown
|
|
|
|
init(server: Server, haEntity: HAEntity, areaName: String? = nil) {
|
|
self.server = server
|
|
self.haEntity = haEntity
|
|
self.areaName = areaName
|
|
}
|
|
|
|
var body: some View {
|
|
EntityTileView(
|
|
entityName: entityName,
|
|
entityState: entityState,
|
|
icon: icon,
|
|
iconColor: iconColor,
|
|
isUnavailable: isUnavailable,
|
|
onIconTap: handleIconTap,
|
|
onTileTap: handleTileTap
|
|
)
|
|
.onChange(of: haEntity) { _, _ in
|
|
updateIconColor()
|
|
}
|
|
.onAppear {
|
|
getDeviceClass()
|
|
updateIconColor()
|
|
}
|
|
.matchedTransitionSource(id: haEntity.entityId, in: namespace)
|
|
.fullScreenCover(isPresented: $showMoreInfoDialog) {
|
|
EntityMoreInfoDialogView(
|
|
server: server,
|
|
haEntity: haEntity
|
|
)
|
|
.navigationTransition(.zoom(sourceID: haEntity.entityId, in: namespace))
|
|
}
|
|
}
|
|
|
|
// MARK: - Computed Properties
|
|
|
|
private var entityName: String {
|
|
haEntity.attributes.friendlyName ?? haEntity.entityId
|
|
}
|
|
|
|
private var entityState: String {
|
|
let state = Domain(entityId: haEntity.entityId)?.contextualStateDescription(for: haEntity) ?? haEntity.state
|
|
|
|
if let areaName {
|
|
return "\(state) · \(areaName)"
|
|
}
|
|
|
|
return state
|
|
}
|
|
|
|
private var icon: MaterialDesignIcons {
|
|
if let entityIcon = haEntity.attributes.icon {
|
|
return MaterialDesignIcons(serversideValueNamed: entityIcon)
|
|
} else if let domain = Domain(entityId: haEntity.entityId) {
|
|
let stateString = haEntity.state
|
|
let domainState = Domain.State(rawValue: stateString) ?? .unknown
|
|
return domain.icon(deviceClass: deviceClass.rawValue, state: domainState)
|
|
} else {
|
|
return .homeIcon
|
|
}
|
|
}
|
|
|
|
private var isUnavailable: Bool {
|
|
let state = haEntity.state.lowercased()
|
|
return [Domain.State.unavailable.rawValue, Domain.State.unknown.rawValue].contains(state)
|
|
}
|
|
|
|
// MARK: - Actions
|
|
|
|
private func handleIconTap() {
|
|
#if os(iOS)
|
|
// Execute the app intent for the entity
|
|
let intent = AppIntentProvider.intent(for: haEntity, server: server)
|
|
Task {
|
|
_ = try? await intent.perform()
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private func handleTileTap() {
|
|
showMoreInfoDialog = true
|
|
}
|
|
|
|
// MARK: - Business Logic
|
|
|
|
private func getDeviceClass() {
|
|
deviceClass = DeviceClassProvider.deviceClass(
|
|
for: haEntity.entityId,
|
|
serverId: server.identifier.rawValue
|
|
)
|
|
}
|
|
|
|
private func updateIconColor() {
|
|
let state = haEntity.state
|
|
let colorMode = haEntity.attributes["color_mode"] as? String
|
|
let rgbColor = haEntity.attributes["rgb_color"] as? [Int]
|
|
let hsColor = haEntity.attributes["hs_color"] as? [Double]
|
|
|
|
if isUnavailable {
|
|
iconColor = .gray
|
|
return
|
|
}
|
|
|
|
iconColor = EntityIconColorProvider.iconColor(
|
|
domain: Domain(entityId: haEntity.entityId) ?? .switch,
|
|
state: state,
|
|
colorMode: colorMode,
|
|
rgbColor: rgbColor,
|
|
hsColor: hsColor
|
|
)
|
|
}
|
|
}
|