iOS/Sources/CarPlay/Templates/Areas/CarPlayAreasViewModel.swift

134 lines
5.0 KiB
Swift

import CarPlay
import Foundation
import HAKit
import Shared
@available(iOS 16.0, *)
final class CarPlayAreasViewModel {
private var request: HACancellable?
weak var templateProvider: CarPlayAreasZonesTemplate?
var entitiesListTemplate: CarPlayEntitiesListTemplate?
private var preferredServerId: String {
prefs.string(forKey: CarPlayServersListTemplate.carPlayPreferredServerKey) ?? ""
}
func cancelRequest() {
request?.cancel()
}
func update() {
guard let server = Current.servers.server(forServerIdentifier: preferredServerId) ?? Current.servers.all.first else {
templateProvider?.template.updateSections([])
return
}
guard let connection = Current.api(for: server)?.connection else {
Current.Log.error("No API available to update CarPlayAreasViewModel")
return
}
request?.cancel()
request = connection.send(HATypedRequest<[HAAreaResponse]>.fetchAreas(), completion: { [weak self] result in
switch result {
case let .success(data):
self?.fetchEntitiesForAreas(data, server: server)
case let .failure(error):
self?.templateProvider?.template.updateSections([])
Current.Log.error(userInfo: ["Failed to retrieve areas": error.localizedDescription])
}
})
}
private func fetchEntitiesForAreas(_ areas: [HAAreaResponse], server: Server) {
guard let connection = Current.api(for: server)?.connection else {
Current.Log.error("No API available to fetch entities for areas")
return
}
request?.cancel()
request = connection.send(
HATypedRequest<[HAEntityAreaResponse]>.fetchEntitiesWithAreas(),
completion: { [weak self] result in
switch result {
case let .success(data):
self?.fetchDeviceForAreas(areas, entitiesWithAreas: data, server: server)
case let .failure(error):
self?.templateProvider?.template.updateSections([])
Current.Log.error(userInfo: ["Failed to retrieve areas and entities": error.localizedDescription])
}
}
)
}
private func fetchDeviceForAreas(
_ areas: [HAAreaResponse],
entitiesWithAreas: [HAEntityAreaResponse],
server: Server
) {
guard let connection = Current.api(for: server)?.connection else {
Current.Log.error("No API available to fetch devices for areas")
return
}
request?.cancel()
request = connection.send(
HATypedRequest<[HADeviceAreaResponse]>.fetchDevicesWithAreas(),
completion: { [weak self] result in
switch result {
case let .success(data):
self?.updateAreas(areas, entitiesAndAreas: entitiesWithAreas, devicesAndAreas: data, server: server)
case let .failure(error):
self?.templateProvider?.template.updateSections([])
Current.Log.error(userInfo: ["Failed to retrieve areas and devices": error.localizedDescription])
}
}
)
}
private func updateAreas(
_ areas: [HAAreaResponse],
entitiesAndAreas: [HAEntityAreaResponse],
devicesAndAreas: [HADeviceAreaResponse],
server: Server
) {
let allEntitiesPerArea = AreaProvider.getAllEntitiesFromArea(
devicesAndAreas: devicesAndAreas,
entitiesAndAreas: entitiesAndAreas
)
let items = areas.sorted(by: { a1, a2 in
a1.name < a2.name
}).compactMap { area -> CPListItem? in
guard let entityIdsForAreaId = allEntitiesPerArea[area.areaId] else { return nil }
let icon = MaterialDesignIcons(
serversideValueNamed: area.icon ?? "mdi:circle"
).carPlayIcon()
let item = CPListItem(text: area.name, detailText: nil, image: icon)
item.accessoryType = .disclosureIndicator
item.handler = { [weak self] _, completion in
self?.listItemHandler(area: area, entityIdsForAreaId: Array(entityIdsForAreaId), server: server)
completion()
}
return item
}
templateProvider?.paginatedList.updateItems(items: items)
}
// swiftlint:enable cyclomatic_complexity
private func listItemHandler(area: HAAreaResponse, entityIdsForAreaId: [String], server: Server) {
guard let entitiesCachedStates = Current.api(for: server)?.connection?.caches.states.value else { return }
entitiesListTemplate = CarPlayEntitiesListTemplate.build(
title: area.name,
filterType: .areaId(entityIds: entityIdsForAreaId),
server: server,
entitiesCachedStates: entitiesCachedStates
)
guard let entitiesListTemplate else { return }
templateProvider?.presentEntitiesList(template: entitiesListTemplate)
}
}