feat: Working on dehumidifier sizing, api and routes implemented, views are not complete.

This commit is contained in:
2025-02-27 17:10:25 -05:00
parent fad00520b0
commit dc01477c3e
12 changed files with 344 additions and 14 deletions

View File

@@ -0,0 +1,116 @@
import Logging
import Routes
public extension DehumidifierSize.Request {
private static let minTempEfficiency = 65.0
private static let minRHEfficiency = 60.0
private static let dehumidifierSizes = [
33: "https://www.santa-fe-products.com/product/ultramd33-dehumidifier/",
70: "https://www.santa-fe-products.com/product/santa-fe-ultra70-dehumidifier/",
100: "https://www.santa-fe-products.com/product/ultra98-dehumidifier/",
118: "https://www.santa-fe-products.com/product/ultra120-dehumidifier/",
155: "https://www.santa-fe-products.com/product/ultra1559dehumidifier/",
205: "https://www.santa-fe-products.com/product/ultra205-dehumidifier/"
]
func respond(_ logger: Logger) async throws -> DehumidifierSize.Response {
try validate()
let pintsPerDay = (latentLoad / 1054) * 24
var capacityFactor = 1.0
var warnings: [String] = []
if temperature < Self.minTempEfficiency {
if temperature < 60 {
capacityFactor *= 0.5
warnings.append(
"Very low temperature will severely reduce dehumidifier efficiency."
)
} else {
capacityFactor *= 0.7
warnings.append(
"Low temperature will severely reduce dehumidifier efficiency."
)
}
}
if humidity < Self.minRHEfficiency {
capacityFactor *= 0.8
warnings.append(
"Low relative humidity will significantly reduce moisture removal rate."
)
}
let requiredCapacity = pintsPerDay / capacityFactor
addDefaultWarnings(&warnings)
// TODO: Return early here ??
if requiredCapacity > 205 {
logger.debug("Required capacity exceeds residential unit.")
warnings.append(
"Required capacity exceeds largest standard residential unit - consider multiple units or a commercial system"
)
}
let (recommendedSize, recommendedUrl) = parseRecommendedSize(requiredCapacity)
logger.debug("Recommend size: \(recommendedSize)")
return .init(
requiredCapacity: requiredCapacity,
pintsPerDay: pintsPerDay,
recommendedSize: recommendedSize,
recommendedUrl: recommendedUrl,
warnings: warnings
)
}
private func validate() throws {
guard latentLoad > 0 else {
throw DehumidiferSizeValidationError.latentLoadShouldBeGreaterThanZero
}
guard temperature > 0 else {
throw DehumidiferSizeValidationError.temperatureShouldBeGreaterThanZero
}
guard humidity > 0 else {
throw DehumidiferSizeValidationError.humidityShouldBeGreaterThanZero
}
}
private func parseRecommendedSize(_ requiredCapacity: Double) -> (Int, String) {
for (key, value) in Self.dehumidifierSizes where Double(key) >= requiredCapacity {
return (key, value)
}
return (205, Self.dehumidifierSizes[205]!)
}
private func addDefaultWarnings(_ warnings: inout [String]) {
// High humidity warnings
if humidity > 65 {
warnings.append(
"High relative humidity - unit may need to run continuously."
)
}
if humidity > 80 {
warnings.append(
"Extreme humidity levels - address moisture sources and improve ventilation before dehumidification."
)
}
// Temperature warnings
if temperature < 60 {
warnings.append(
"Temperature too low for effective dehumidification - consider raising space temperature first."
)
}
}
}
enum DehumidiferSizeValidationError: Error {
case latentLoadShouldBeGreaterThanZero
case temperatureShouldBeGreaterThanZero
case humidityShouldBeGreaterThanZero
}

View File

@@ -0,0 +1,72 @@
import Logging
import PsychrometricClient
import Routes
public extension PsychrometricClient {
private func calculateProperties(_ request: MoldRisk.Request) async throws -> PsychrometricProperties {
try await psychrometricProperties(.dryBulb(request.dryBulb, relativeHumidity: request.relativeHumidity))
}
func respond(_ request: MoldRisk.Request, _ logger: Logger? = nil) async throws -> MoldRisk.Response {
let properties = try await calculateProperties(request)
let riskLevel = MoldRisk.RiskLevel(humidity: request.relativeHumidity)
return .init(
psychrometricProperties: properties,
riskLevel: riskLevel,
daysToMold: riskLevel.daysToMold,
recommendations: .recommendations(for: request, dewPoint: properties.dewPoint, riskLevel: riskLevel)
)
}
}
private extension MoldRisk.RiskLevel {
init(humidity: RelativeHumidity) {
if humidity < 60 {
self = .low
} else if humidity < 70 {
self = .moderate
} else if humidity < 80 {
self = .high
} else {
self = .severe
}
}
var daysToMold: Int? {
switch self {
case .low: return nil
case .moderate: return 30
case .high: return 14
case .severe: return 7
}
}
}
private extension Array where Element == String {
static func recommendations(
for request: MoldRisk.Request,
dewPoint: DewPoint,
riskLevel: MoldRisk.RiskLevel
) -> Self {
var recommendations = [String]()
if request.humidity < 60 {
recommendations.append(
"Reduce indoor relative humidity below 60% using dehumidification"
)
}
if (request.dryBulb.fahrenheit - dewPoint.fahrenheit) < 4 {
recommendations.append(
"Increase air temperature or improve insulation to prevent condensation"
)
}
if riskLevel != .low {
recommendations.append(contentsOf: [
"Improve ventilation to reduce moisture accumulation",
"Inspect for and repair any water leaks or intrusion"
])
}
return recommendations
}
}