feat: Completes hvac-system-performance views and api call.

This commit is contained in:
2025-02-28 10:50:33 -05:00
parent 7cd02971a3
commit d08e4b0839
14 changed files with 330 additions and 19 deletions

View File

@@ -0,0 +1,56 @@
import Foundation
public enum DehumidifierSize {
/// Represents the request for determining dehumidifier size based on
/// latent load and indoor conditions.
public struct Request: Codable, Equatable, Sendable {
public let latentLoad: Double
public let temperature: Double
public let humidity: Double
public init(latentLoad: Double, temperature: Double, humidity: Double) {
self.latentLoad = latentLoad
self.temperature = temperature
self.humidity = humidity
}
}
/// Represents the response for determining dehumidifier size based on
/// latent load and indoor conditions.
public struct Response: Codable, Equatable, Sendable {
public let requiredCapacity: Double
public let pintsPerDay: Double
public let recommendedSize: Int
public let recommendedUrl: String?
public let warnings: [String]
public init(
requiredCapacity: Double,
pintsPerDay: Double,
recommendedSize: Int,
recommendedUrl: String? = nil,
warnings: [String] = []
) {
self.requiredCapacity = requiredCapacity
self.pintsPerDay = pintsPerDay
self.recommendedSize = recommendedSize
self.recommendedUrl = recommendedUrl
self.warnings = warnings
}
}
}
#if DEBUG
public extension DehumidifierSize.Response {
static var mock: Self {
.init(requiredCapacity: 100, pintsPerDay: 100, recommendedSize: 100, recommendedUrl: "#", warnings: [
"A warning.", "B warning"
])
}
}
#endif

View File

@@ -0,0 +1,29 @@
import Elementary
import ElementaryHTMX
public extension HTMLAttribute.hx {
@Sendable
static func get(route: SiteRoute.View) -> HTMLAttribute {
get(SiteRoute.View.router.path(for: route))
}
@Sendable
static func patch(route: SiteRoute.View) -> HTMLAttribute {
patch(SiteRoute.View.router.path(for: route))
}
@Sendable
static func post(route: SiteRoute.View) -> HTMLAttribute {
post(SiteRoute.View.router.path(for: route))
}
@Sendable
static func put(route: SiteRoute.View) -> HTMLAttribute {
put(SiteRoute.View.router.path(for: route))
}
@Sendable
static func delete(route: SiteRoute.Api) -> HTMLAttribute {
delete(SiteRoute.Api.router.path(for: route))
}
}

View File

@@ -0,0 +1,123 @@
import Dependencies
import PsychrometricClient
public enum HVACSystemPerformance {
public struct Request: Codable, Equatable, Sendable {
public let altitude: Double?
public let airflow: Double
public let returnAirTemperature: Double
public let returnAirHumidity: Double
public let supplyAirTemperature: Double
public let supplyAirHumidity: Double
public let systemSize: Double
public init(
altitude: Double? = nil,
airflow: Double,
returnAirTemperature: Double,
returnAirHumidity: Double,
supplyAirTemperature: Double,
supplyAirHumidity: Double,
systemSize: Double
) {
self.altitude = altitude
self.airflow = airflow
self.returnAirTemperature = returnAirTemperature
self.returnAirHumidity = returnAirHumidity
self.supplyAirTemperature = supplyAirTemperature
self.supplyAirHumidity = supplyAirHumidity
self.systemSize = systemSize
}
}
public struct Response: Codable, Equatable, Sendable {
public let returnAirProperties: PsychrometricProperties
public let supplyAirProperties: PsychrometricProperties
public let capacity: Capacity
public let systemMetrics: SystemMetrics
public init(
returnAirProperties: PsychrometricProperties,
supplyAirProperties: PsychrometricProperties,
capacity: HVACSystemPerformance.Capacity,
systemMetrics: HVACSystemPerformance.SystemMetrics
) {
self.returnAirProperties = returnAirProperties
self.supplyAirProperties = supplyAirProperties
self.capacity = capacity
self.systemMetrics = systemMetrics
}
}
// TODO: Add delta-enthalpy.
public struct Capacity: Codable, Equatable, Sendable {
public let total: Double
public let sensible: Double
public let latent: Double
public var shr: Double { sensible / total }
public init(total: Double, sensible: Double, latent: Double) {
self.total = total
self.sensible = sensible
self.latent = latent
}
}
public struct SystemMetrics: Codable, Equatable, Sendable {
public let cfmPerTon: Double
public let targetTemperatureSplit: Double
public let actualTemperatureSplit: Double
public let condensationRatePoundsPerHour: Double // lb/hr
public let condensationRateGallonsPerHour: Double // gal/hr
public init(
cfmPerTon: Double,
targetTemperatureSplit: Double,
actualTemperatureSplit: Double,
condensationRatePoundsPerHour: Double
) {
self.cfmPerTon = cfmPerTon
self.targetTemperatureSplit = targetTemperatureSplit
self.actualTemperatureSplit = actualTemperatureSplit
self.condensationRatePoundsPerHour = condensationRatePoundsPerHour
self.condensationRateGallonsPerHour = condensationRatePoundsPerHour * 0.12
}
}
}
#if DEBUG
public extension HVACSystemPerformance.Response {
static func mock() async throws -> Self {
@Dependency(\.psychrometricClient) var psychrometricClient
return try await .init(
returnAirProperties: psychrometricClient.psychrometricProperties(.dryBulb(75, relativeHumidity: 50%)),
supplyAirProperties: psychrometricClient.psychrometricProperties(.dryBulb(55, relativeHumidity: 87%)),
capacity: .mock,
systemMetrics: .mock
)
}
}
public extension HVACSystemPerformance.Capacity {
static var mock: Self {
.init(total: 24000, sensible: 19000, latent: 5000)
}
}
public extension HVACSystemPerformance.SystemMetrics {
static let mock = Self(
cfmPerTon: 400,
targetTemperatureSplit: 20.8,
actualTemperatureSplit: 18.7,
condensationRatePoundsPerHour: 16981.3
)
}
#endif

View File

@@ -0,0 +1,11 @@
public enum HVACSystemSize: Double, Codable, Equatable, Sendable {
case one = 1
case oneAndAHalf = 1.5
case two = 2
case twoAndAHalf = 2.5
case three = 3
case threeAndAHalf = 3.5
case four = 4
case fourAndAHalf = 4.5
case five = 5
}

View File

@@ -0,0 +1,80 @@
import Foundation
import PsychrometricClient
public enum MoldRisk {
public struct Request: Codable, Equatable, Sendable {
public let temperature: Double
public let humidity: Double
public init(temperature: Double, humidity: Double) {
self.temperature = temperature
self.humidity = humidity
}
}
public struct Response: Codable, Equatable, Sendable {
public let psychrometricProperties: PsychrometricProperties
public let riskLevel: RiskLevel
public let daysToMold: Int?
public let recommendations: [String]
public init(
psychrometricProperties: PsychrometricProperties,
riskLevel: MoldRisk.RiskLevel,
daysToMold: Int? = nil,
recommendations: [String]
) {
self.psychrometricProperties = psychrometricProperties
self.riskLevel = riskLevel
self.daysToMold = daysToMold
self.recommendations = recommendations
}
}
public enum RiskLevel: String, Codable, Equatable, Sendable {
case low
case moderate
case high
case severe
}
}
public extension MoldRisk.Request {
var dryBulb: DryBulb {
.fahrenheit(temperature)
}
var relativeHumidity: RelativeHumidity { humidity% }
}
#if DEBUG
import Dependencies
public extension MoldRisk.Response {
static var mock: Self {
return .init(
psychrometricProperties: .init(
absoluteHumidity: .zero,
atmosphericPressure: .zero,
degreeOfSaturation: .zero,
density: .zero, dewPoint: 59.4,
dryBulb: 75,
enthalpy: .zero,
grainsOfMoisture: .zero,
humidityRatio: .zero,
relativeHumidity: 50%,
specificVolume: .zero,
vaporPressure: .zero,
wetBulb: .zero,
units: .imperial
),
riskLevel: .low,
recommendations: []
)
}
}
#endif