iOS/Sources/Shared/API/Webhook/Sensors/ActiveSensor.swift
Zac West 17fc1898f9
Fix crash updating active sensor idle time setting (#1803)
## Summary
Fixes a crash caused by infinitely recursing the value change.

## Any other notes
This also allows setting fractional minute values, every 15 seconds. The minimum timer which we check is 5 seconds, and that low means we won't detect changes if they occur in a 10 second period which feels too low.
2021-07-19 21:02:56 -07:00

75 lines
2.3 KiB
Swift

import Foundation
import PromiseKit
final class ActiveSensorUpdateSignaler: SensorProviderUpdateSignaler, ActiveStateObserver {
let signal: () -> Void
init(signal: @escaping () -> Void) {
self.signal = signal
Current.activeState.register(observer: self)
}
func activeStateDidChange(for manager: ActiveStateManager) {
signal()
}
}
final class ActiveSensor: SensorProvider {
public enum ActiveError: Error, Equatable {
case noActiveState
}
let request: SensorProviderRequest
init(request: SensorProviderRequest) {
self.request = request
}
func sensors() -> Promise<[WebhookSensor]> {
let activeState = Current.activeState
guard activeState.canTrackActiveStatus else {
return .init(error: ActiveError.noActiveState)
}
let isActive = activeState.isActive
let sensor = WebhookSensor(
name: "Active",
uniqueID: "active",
icon: isActive ? "mdi:monitor" : "mdi:monitor-off",
state: isActive
)
sensor.Type = "binary_sensor"
sensor.Attributes = activeState.states.attributes
let durationFormatter = with(DateComponentsFormatter()) {
$0.allowedUnits = [.minute, .second]
$0.allowsFractionalUnits = true
$0.formattingContext = .standalone
$0.unitsStyle = .short
}
sensor.Settings = [
.init(
type: .stepper(
getter: { activeState.minimumIdleTime.converted(to: .minutes).value },
setter: { activeState.minimumIdleTime = .init(value: $0, unit: .minutes) },
minimum: 0.25,
maximum: .greatestFiniteMagnitude,
step: 0.25,
displayValueFor: { value in
let valueMeasurement = Measurement<UnitDuration>(value: value ?? 0, unit: .minutes)
return durationFormatter.string(from: valueMeasurement.converted(to: .seconds).value)
}
),
title: L10n.Sensors.Active.Setting.timeUntilIdle
),
]
// Set up our observer
let _: ActiveSensorUpdateSignaler = request.dependencies.updateSignaler(for: self)
return .value([sensor])
}
}