4 Commits
0.2.2 ... dev

Author SHA1 Message Date
061785d77b feat: Updates tracking state of sensors 2024-11-20 22:45:20 -05:00
c4c4fed4bc feat: Updates tracking state of sensors 2024-11-20 22:43:05 -05:00
e44c1c24c5 feat: Update ci workflow to only work on pushes to main branch and pull requests.
All checks were successful
CI / Run Tests (pull_request) Successful in 4m18s
CI / Run Tests (push) Successful in 4m17s
Create and publish a Docker image / build-and-push-image (push) Successful in 8m4s
2024-11-20 15:10:23 -05:00
df05898a65 fix: Fixes dewpoint and enthalpy not converting to imperial units.
Some checks failed
CI / Run Tests (push) Has been cancelled
2024-11-20 15:07:33 -05:00
4 changed files with 47 additions and 38 deletions

View File

@@ -2,6 +2,7 @@
name: CI name: CI
on: on:
push: push:
branches: ["main"]
pull_request: pull_request:
jobs: jobs:

View File

@@ -60,7 +60,10 @@ public struct TemperatureAndHumiditySensor: Identifiable, Sendable {
!temperature.value.isNaN, !temperature.value.isNaN,
!humidity.value.isNaN !humidity.value.isNaN
else { return nil } else { return nil }
return try? await psychrometrics.dewPoint(.dryBulb(temperature, relativeHumidity: humidity)) return try? await psychrometrics.dewPoint(.dryBulb(
.fahrenheit(temperature.fahrenheit),
relativeHumidity: humidity
))
} }
} }
@@ -73,7 +76,7 @@ public struct TemperatureAndHumiditySensor: Identifiable, Sendable {
!humidity.value.isNaN !humidity.value.isNaN
else { return nil } else { return nil }
return try? await psychrometrics.enthalpy.moistAir( return try? await psychrometrics.enthalpy.moistAir(
.dryBulb(temperature, relativeHumidity: humidity, altitude: altitude) .dryBulb(.fahrenheit(temperature.fahrenheit), relativeHumidity: humidity, altitude: altitude, units: .imperial)
) )
} }
} }
@@ -81,12 +84,11 @@ public struct TemperatureAndHumiditySensor: Identifiable, Sendable {
/// Check whether any of the sensor values have changed and need processed. /// Check whether any of the sensor values have changed and need processed.
/// ///
/// - Note: Setting a value will set to both the temperature and humidity properties. /// - Note: Setting a value will set to both the temperature and humidity properties.
public var needsProcessed: Bool { public var needsProcessed: Bool { $temperature.needsProcessed || $humidity.needsProcessed }
get { $temperature.needsProcessed || $humidity.needsProcessed }
set { public mutating func setHasProcessed() {
$temperature.needsProcessed = newValue $temperature.setHasProcessed()
$humidity.needsProcessed = newValue $humidity.setHasProcessed()
}
} }
/// Represents the different locations of a temperature and humidity sensor, which can /// Represents the different locations of a temperature and humidity sensor, which can

View File

@@ -5,24 +5,20 @@
@propertyWrapper @propertyWrapper
public struct TrackedChanges<Value: Sendable>: Sendable { public struct TrackedChanges<Value: Sendable>: Sendable {
/// The current tracking state. /// The current value wrapped in a tracking state.
private var tracking: TrackingState private var value: TrackingState
/// The current wrapped value.
private var value: Value
/// Used to check if a new value is equal to an old value. /// Used to check if a new value is equal to an old value.
private var isEqual: @Sendable (Value, Value) -> Bool private var isEqual: @Sendable (Value, Value) -> Bool
/// Access to the underlying property that we are wrapping. /// Access to the underlying property that we are wrapping.
public var wrappedValue: Value { public var wrappedValue: Value {
get { value } get { value.currentValue }
set { set {
// Check if the new value is equal to the old value. // Check if the new value is equal to the old value.
guard !isEqual(newValue, value) else { return } guard !isEqual(newValue, value.currentValue) else { return }
// If it's not equal then set it, as well as set the tracking to `.needsProcessed`. // If it's not equal then set it, as well as set the tracking to `.needsProcessed`.
value = newValue value = .needsProcessed(newValue)
tracking = .needsProcessed
} }
} }
@@ -37,31 +33,42 @@ public struct TrackedChanges<Value: Sendable>: Sendable {
needsProcessed: Bool = false, needsProcessed: Bool = false,
isEqual: @escaping @Sendable (Value, Value) -> Bool isEqual: @escaping @Sendable (Value, Value) -> Bool
) { ) {
self.value = wrappedValue self.value = .init(wrappedValue, needsProcessed: needsProcessed)
self.tracking = needsProcessed ? .needsProcessed : .hasProcessed
self.isEqual = isEqual self.isEqual = isEqual
} }
/// Represents whether a wrapped value has changed and needs processed or not. fileprivate enum TrackingState {
enum TrackingState { case hasProcessed(Value)
case needsProcessed(Value)
/// The state when nothing has changed and we've already processed the current value. init(_ value: Value, needsProcessed: Bool) {
case hasProcessed if needsProcessed {
self = .needsProcessed(value)
} else {
self = .hasProcessed(value)
}
}
/// The state when the value has changed and has not been processed yet. var currentValue: Value {
case needsProcessed switch self {
case let .hasProcessed(value):
return value
case let .needsProcessed(value):
return value
}
}
var needsProcessed: Bool {
guard case .needsProcessed = self else { return false }
return true
}
} }
/// Whether the value needs processed. /// Whether the value needs processed.
public var needsProcessed: Bool { var needsProcessed: Bool { value.needsProcessed }
get { tracking == .needsProcessed }
set { mutating func setHasProcessed() {
if newValue { value = .hasProcessed(value.currentValue)
tracking = .needsProcessed
} else {
tracking = .hasProcessed
}
}
} }
public var projectedValue: Self { public var projectedValue: Self {
@@ -95,6 +102,5 @@ extension TrackedChanges: Hashable where Value: Hashable {
public func hash(into hasher: inout Hasher) { public func hash(into hasher: inout Hasher) {
hasher.combine(wrappedValue) hasher.combine(wrappedValue)
hasher.combine(needsProcessed)
} }
} }

View File

@@ -135,7 +135,7 @@ public actor SensorsService: Service {
private func publishUpdates() async throws { private func publishUpdates() async throws {
for sensor in sensors.filter(\.needsProcessed) { for sensor in sensors.filter(\.needsProcessed) {
try await publish(sensor.dewPoint?.value, to: sensor.topics.dewPoint) try await publish(sensor.dewPoint?.fahrenheit, to: sensor.topics.dewPoint)
try await publish(sensor.enthalpy?.value, to: sensor.topics.enthalpy) try await publish(sensor.enthalpy?.value, to: sensor.topics.enthalpy)
try sensors.hasProcessed(sensor) try sensors.hasProcessed(sensor)
} }
@@ -172,7 +172,7 @@ private extension Array where Element == TemperatureAndHumiditySensor {
guard let index = firstIndex(where: { $0.id == sensor.id }) else { guard let index = firstIndex(where: { $0.id == sensor.id }) else {
throw SensorNotFoundError() throw SensorNotFoundError()
} }
self[index].needsProcessed = false self[index].setHasProcessed()
} }
} }
@@ -207,6 +207,6 @@ extension Humidity<Relative>: BufferInitalizable {
extension Temperature<DryAir>: BufferInitalizable { extension Temperature<DryAir>: BufferInitalizable {
init?(buffer: ByteBuffer) { init?(buffer: ByteBuffer) {
guard let value = Double(buffer: buffer) else { return nil } guard let value = Double(buffer: buffer) else { return nil }
self.init(value) self.init(value, units: .celsius)
} }
} }