-
Notifications
You must be signed in to change notification settings - Fork 432
Expand file tree
/
Copy pathAppEntitiesModel.swift
More file actions
122 lines (112 loc) · 4.85 KB
/
AppEntitiesModel.swift
File metadata and controls
122 lines (112 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import Foundation
import GRDB
import HAKit
import PromiseKit
public protocol AppEntitiesModelProtocol {
func updateModel(_ entities: Set<HAEntity>, server: Server)
}
public enum HAAppUsedContent {
public static let domains: [Domain] = [
.automation,
.scene,
.script,
.light,
.switch,
.sensor,
.binarySensor,
.cover,
.button,
.inputBoolean,
.inputButton,
.lock,
.camera,
.fan,
.todo,
]
public static var rawValues: [String] = domains.map(\.rawValue)
}
final class AppEntitiesModel: AppEntitiesModelProtocol {
static var shared = AppEntitiesModel()
/// ServerId: Date
private var lastDatabaseUpdate: [String: Date] = [:]
/// ServerId: Int
private var lastEntitiesCount: [String: Int] = [:]
public func updateModel(_ entities: Set<HAEntity>, server: Server) {
// Only update database after a few seconds or if the entities count changed
// First check for time to avoid unecessary filtering to check count
if !checkLastDatabaseUpdateRecently(server: server) {
let appRelatedEntities = filterDomains(entities)
Current.Log
.verbose(
"Updating App Entities for \(server.info.name) checkLastDatabaseUpdateLessThanMinuteAgo false, lastDatabaseUpdate \(String(describing: lastDatabaseUpdate)) "
)
updateLastUpdate(entitiesCount: appRelatedEntities.count, server: server)
handle(appRelatedEntities: appRelatedEntities, server: server)
} else {
let appRelatedEntities = filterDomains(entities)
if lastEntitiesCount[server.identifier.rawValue] != appRelatedEntities.count {
Current.Log
.verbose(
"Updating App Entities for \(server.info.name) entities count diff, count: last \(lastEntitiesCount), new \(appRelatedEntities.count)"
)
updateLastUpdate(entitiesCount: appRelatedEntities.count, server: server)
handle(appRelatedEntities: appRelatedEntities, server: server)
}
}
}
private func updateLastUpdate(entitiesCount: Int, server: Server) {
lastEntitiesCount[server.identifier.rawValue] = entitiesCount
lastDatabaseUpdate[server.identifier.rawValue] = Date()
}
private func filterDomains(_ entities: Set<HAEntity>) -> Set<HAEntity> {
entities.filter { HAAppUsedContent.rawValues.contains($0.domain) }
}
// Avoid updating database too often
private func checkLastDatabaseUpdateRecently(server: Server) -> Bool {
guard let lastDate = lastDatabaseUpdate[server.identifier.rawValue] else { return false }
return Date().timeIntervalSince(lastDate) < 15
}
private func handle(appRelatedEntities: Set<HAEntity>, server: Server) {
let appEntities = appRelatedEntities.map({ HAAppEntity(
id: ServerEntity.uniqueId(serverId: server.identifier.rawValue, entityId: $0.entityId),
entityId: $0.entityId,
serverId: server.identifier.rawValue,
domain: $0.domain,
name: $0.attributes.friendlyName ?? $0.entityId,
icon: $0.attributes.icon,
rawDeviceClass: $0.attributes.dictionary["device_class"] as? String
) }).sorted(by: { $0.id < $1.id })
do {
let cachedEntities = try Current.database().read { db in
try HAAppEntity
.filter(Column(DatabaseTables.AppEntity.serverId.rawValue) == server.identifier.rawValue)
.orderByPrimaryKey()
.fetchAll(db)
}
if appEntities != cachedEntities {
Current.Log
.verbose(
"Updating App Entities for \(server.info.name), cached entities were different than new entities"
)
try Current.database().write { db in
try HAAppEntity.deleteAll(db, ids: cachedEntities.map(\.id))
for entity in appEntities {
try entity.insert(db)
}
}
Current.clientEventStore.addEvent(ClientEvent(
text: "Updated database App Entities for \(server.info.name)",
type: .database,
payload: ["entities_count": appEntities.count]
))
}
} catch {
Current.Log.error("Failed to get cache for App Entities, error: \(error.localizedDescription)")
Current.clientEventStore.addEvent(ClientEvent(
text: "Update database App Entities FAILED for \(server.info.name)",
type: .database,
payload: ["error": error.localizedDescription]
))
}
}
}