From 320a733d12b515951b3f1adb259992dd43624880 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Sat, 9 Nov 2024 12:15:59 -0500 Subject: [PATCH] feat: Removing old libraries --- Package.swift | 62 +--- Sources/Bootstrap/Bootstrap.swift | 303 +++++++++--------- Sources/Client/Interface.swift | 43 --- Sources/ClientLive/Helpers.swift | 260 --------------- Sources/ClientLive/Live.swift | 40 --- Sources/ClientLive/SensorsClient.swift | 262 --------------- Sources/DewPointEnvironment/Environment.swift | 23 -- Sources/{EnvVars => Models}/EnvVars.swift | 2 - Sources/Models/Mode.swift | 39 --- Sources/Models/State.swift | 102 ------ Sources/Models/Topics.swift | 262 --------------- Sources/TopicsLive/Live.swift | 53 --- Tests/ClientTests/ClientTests.swift | 186 ----------- 13 files changed, 151 insertions(+), 1486 deletions(-) delete mode 100755 Sources/Client/Interface.swift delete mode 100755 Sources/ClientLive/Helpers.swift delete mode 100755 Sources/ClientLive/Live.swift delete mode 100644 Sources/ClientLive/SensorsClient.swift delete mode 100755 Sources/DewPointEnvironment/Environment.swift rename Sources/{EnvVars => Models}/EnvVars.swift (98%) delete mode 100755 Sources/Models/Mode.swift delete mode 100755 Sources/Models/State.swift delete mode 100755 Sources/Models/Topics.swift delete mode 100755 Sources/TopicsLive/Live.swift delete mode 100755 Tests/ClientTests/ClientTests.swift diff --git a/Package.swift b/Package.swift index 6de88a3..2db2a77 100755 --- a/Package.swift +++ b/Package.swift @@ -14,11 +14,7 @@ let package = Package( products: [ .executable(name: "dewPoint-controller", targets: ["dewPoint-controller"]), .library(name: "Bootstrap", targets: ["Bootstrap"]), - .library(name: "DewPointEnvironment", targets: ["DewPointEnvironment"]), - .library(name: "EnvVars", targets: ["EnvVars"]), .library(name: "Models", targets: ["Models"]), - .library(name: "Client", targets: ["Client"]), - .library(name: "ClientLive", targets: ["ClientLive"]), .library(name: "MQTTConnectionService", targets: ["MQTTConnectionService"]), .library(name: "SensorsService", targets: ["SensorsService"]) ], @@ -33,8 +29,6 @@ let package = Package( name: "dewPoint-controller", dependencies: [ "Bootstrap", - "ClientLive", - "TopicsLive", .product(name: "MQTTNIO", package: "mqtt-nio"), .product(name: "NIO", package: "swift-nio") ] @@ -46,30 +40,12 @@ let package = Package( .target( name: "Bootstrap", dependencies: [ - "DewPointEnvironment", - "EnvVars", - "ClientLive", "Models", .product(name: "MQTTNIO", package: "mqtt-nio"), .product(name: "NIO", package: "swift-nio") ], swiftSettings: swiftSettings ), - .target( - name: "DewPointEnvironment", - dependencies: [ - "EnvVars", - "Client", - "Models", - .product(name: "MQTTNIO", package: "mqtt-nio") - ], - swiftSettings: swiftSettings - ), - .target( - name: "EnvVars", - dependencies: [], - swiftSettings: swiftSettings - ), .target( name: "Models", dependencies: [ @@ -77,37 +53,10 @@ let package = Package( ], swiftSettings: swiftSettings ), - .target( - name: "Client", - dependencies: [ - "Models", - // .product(name: "CoreUnitTypes", package: "swift-psychrometrics"), - .product(name: "NIO", package: "swift-nio"), - .product(name: "PsychrometricClient", package: "swift-psychrometrics") - ], - swiftSettings: swiftSettings - ), - .target( - name: "ClientLive", - dependencies: [ - "Client", - "EnvVars", - .product(name: "MQTTNIO", package: "mqtt-nio"), - .product(name: "ServiceLifecycle", package: "swift-service-lifecycle") - ], - swiftSettings: swiftSettings - ), - .testTarget( - name: "ClientTests", - dependencies: [ - "Client", - "ClientLive" - ] - ), .target( name: "MQTTConnectionService", dependencies: [ - "EnvVars", + "Models", .product(name: "MQTTNIO", package: "mqtt-nio"), .product(name: "ServiceLifecycle", package: "swift-service-lifecycle") ], @@ -133,17 +82,8 @@ let package = Package( name: "SensorsServiceTests", dependencies: [ "SensorsService", - // TODO: Remove. - "ClientLive", .product(name: "PsychrometricClientLive", package: "swift-psychrometrics") ] - ), - .target( - name: "TopicsLive", - dependencies: [ - "Models" - ], - swiftSettings: swiftSettings ) ] ) diff --git a/Sources/Bootstrap/Bootstrap.swift b/Sources/Bootstrap/Bootstrap.swift index 05058b5..965953c 100755 --- a/Sources/Bootstrap/Bootstrap.swift +++ b/Sources/Bootstrap/Bootstrap.swift @@ -1,6 +1,3 @@ -import ClientLive -import DewPointEnvironment -import EnvVars import Foundation import Logging import Models @@ -13,153 +10,153 @@ import NIO /// - eventLoopGroup: The event loop group for the application. /// - logger: An optional logger for debugging. /// - autoConnect: A flag whether to auto-connect to the MQTT broker or not. -public func bootstrap( - eventLoopGroup: EventLoopGroup, - logger: Logger? = nil, - autoConnect: Bool = true -) -> EventLoopFuture { - logger?.debug("Bootstrapping Dew Point Controller...") - - return loadEnvVars(eventLoopGroup: eventLoopGroup, logger: logger) - .and(loadTopics(eventLoopGroup: eventLoopGroup, logger: logger)) - .makeDewPointEnvironment(eventLoopGroup: eventLoopGroup, logger: logger) - .connectToMQTTBroker(autoConnect: autoConnect, logger: logger) -} - -/// Loads the ``EnvVars`` either using the defualts, from a file in the root directory under `.dewPoint-env` or in the shell / application environment. -/// -/// - Parameters: -/// - eventLoopGroup: The event loop group for the application. -/// - logger: An optional logger for debugging. -private func loadEnvVars( - eventLoopGroup: EventLoopGroup, - logger: Logger? -) -> EventLoopFuture { - logger?.debug("Loading env vars...") - - // TODO: Need to have the env file path passed in / dynamic. - let envFilePath = URL(fileURLWithPath: #file) - .deletingLastPathComponent() - .deletingLastPathComponent() - .deletingLastPathComponent() - .appendingPathComponent(".dewPoint-env") - - let decoder = JSONDecoder() - let encoder = JSONEncoder() - - let defaultEnvVars = EnvVars() - - let defaultEnvDict = (try? encoder.encode(defaultEnvVars)) - .flatMap { try? decoder.decode([String: String].self, from: $0) } - ?? [:] - - // Read from file `.dewPoint-env` file if it exists. - let localEnvVarsDict = (try? Data(contentsOf: envFilePath)) - .flatMap { try? decoder.decode([String: String].self, from: $0) } - ?? [:] - - // Merge with variables in the shell environment. - let envVarsDict = defaultEnvDict - .merging(localEnvVarsDict, uniquingKeysWith: { $1 }) - .merging(ProcessInfo.processInfo.environment, uniquingKeysWith: { $1 }) - - // Produces the final env vars from the merged items or uses defaults if something - // went wrong. - let envVars = (try? JSONSerialization.data(withJSONObject: envVarsDict)) - .flatMap { try? decoder.decode(EnvVars.self, from: $0) } - ?? defaultEnvVars - - logger?.debug("Done loading env vars...") - return eventLoopGroup.next().makeSucceededFuture(envVars) -} - -// MARK: TODO perhaps make loading from file an option passed in when app is launched. - -/// Load the topics from file in application root directory at `.topics`, if available or fall back to the defualt. -/// -/// - Parameters: -/// - eventLoopGroup: The event loop group for the application. -/// - logger: An optional logger for debugging. -private func loadTopics(eventLoopGroup: EventLoopGroup, logger: Logger?) -> EventLoopFuture { - logger?.debug("Loading topics from file...") - - let topicsFilePath = URL(fileURLWithPath: #file) - .deletingLastPathComponent() - .deletingLastPathComponent() - .deletingLastPathComponent() - .appendingPathComponent(".topics") - - let decoder = JSONDecoder() - - // Attempt to load the topics from file in root directory. - let localTopics = (try? Data(contentsOf: topicsFilePath)) - .flatMap { try? decoder.decode(Topics.self, from: $0) } - - logger?.debug( - localTopics == nil - ? "Failed to load topics from file, falling back to defaults." - : "Done loading topics from file." - ) - - // If we were able to load from file use that, else fallback to the defaults. - return eventLoopGroup.next().makeSucceededFuture(localTopics ?? .init()) -} - -private extension EventLoopFuture where Value == (EnvVars, Topics) { - - /// Creates the ``DewPointEnvironment`` for the application after the ``EnvVars`` have been loaded. - /// - /// - Parameters: - /// - eventLoopGroup: The event loop group for the application. - /// - logger: An optional logger for the application. - func makeDewPointEnvironment( - eventLoopGroup: EventLoopGroup, - logger: Logger? - ) -> EventLoopFuture { - map { envVars, topics in - let mqttClient = MQTTClient(envVars: envVars, eventLoopGroup: eventLoopGroup, logger: logger) - return DewPointEnvironment( - envVars: envVars, - mqttClient: mqttClient, - topics: topics - ) - } - } -} - -private extension EventLoopFuture where Value == DewPointEnvironment { - - /// Connects to the MQTT broker after the ``DewPointEnvironment`` has been setup. - /// - /// - Parameters: - /// - logger: An optional logger for debugging. - func connectToMQTTBroker(autoConnect: Bool, logger: Logger?) -> EventLoopFuture { - guard autoConnect else { return self } - return flatMap { environment in - logger?.debug("Connecting to MQTT Broker...") - return environment.mqttClient.connect() - .map { _ in - logger?.debug("Successfully connected to MQTT Broker...") - return environment - } - } - } -} - -private extension MQTTNIO.MQTTClient { - - convenience init(envVars: EnvVars, eventLoopGroup: EventLoopGroup, logger: Logger?) { - self.init( - host: envVars.host, - port: envVars.port != nil ? Int(envVars.port!) : nil, - identifier: envVars.identifier, - eventLoopGroupProvider: .shared(eventLoopGroup), - logger: logger, - configuration: .init( - version: .v5_0, - userName: envVars.userName, - password: envVars.password - ) - ) - } -} +// public func bootstrap( +// eventLoopGroup: EventLoopGroup, +// logger: Logger? = nil, +// autoConnect: Bool = true +// ) -> EventLoopFuture { +// logger?.debug("Bootstrapping Dew Point Controller...") +// +// return loadEnvVars(eventLoopGroup: eventLoopGroup, logger: logger) +// .and(loadTopics(eventLoopGroup: eventLoopGroup, logger: logger)) +// .makeDewPointEnvironment(eventLoopGroup: eventLoopGroup, logger: logger) +// .connectToMQTTBroker(autoConnect: autoConnect, logger: logger) +// } +// +// /// Loads the ``EnvVars`` either using the defualts, from a file in the root directory under `.dewPoint-env` or in the shell / application environment. +// /// +// /// - Parameters: +// /// - eventLoopGroup: The event loop group for the application. +// /// - logger: An optional logger for debugging. +// private func loadEnvVars( +// eventLoopGroup: EventLoopGroup, +// logger: Logger? +// ) -> EventLoopFuture { +// logger?.debug("Loading env vars...") +// +// // TODO: Need to have the env file path passed in / dynamic. +// let envFilePath = URL(fileURLWithPath: #file) +// .deletingLastPathComponent() +// .deletingLastPathComponent() +// .deletingLastPathComponent() +// .appendingPathComponent(".dewPoint-env") +// +// let decoder = JSONDecoder() +// let encoder = JSONEncoder() +// +// let defaultEnvVars = EnvVars() +// +// let defaultEnvDict = (try? encoder.encode(defaultEnvVars)) +// .flatMap { try? decoder.decode([String: String].self, from: $0) } +// ?? [:] +// +// // Read from file `.dewPoint-env` file if it exists. +// let localEnvVarsDict = (try? Data(contentsOf: envFilePath)) +// .flatMap { try? decoder.decode([String: String].self, from: $0) } +// ?? [:] +// +// // Merge with variables in the shell environment. +// let envVarsDict = defaultEnvDict +// .merging(localEnvVarsDict, uniquingKeysWith: { $1 }) +// .merging(ProcessInfo.processInfo.environment, uniquingKeysWith: { $1 }) +// +// // Produces the final env vars from the merged items or uses defaults if something +// // went wrong. +// let envVars = (try? JSONSerialization.data(withJSONObject: envVarsDict)) +// .flatMap { try? decoder.decode(EnvVars.self, from: $0) } +// ?? defaultEnvVars +// +// logger?.debug("Done loading env vars...") +// return eventLoopGroup.next().makeSucceededFuture(envVars) +// } +// +// // MARK: TODO perhaps make loading from file an option passed in when app is launched. +// +// /// Load the topics from file in application root directory at `.topics`, if available or fall back to the defualt. +// /// +// /// - Parameters: +// /// - eventLoopGroup: The event loop group for the application. +// /// - logger: An optional logger for debugging. +// private func loadTopics(eventLoopGroup: EventLoopGroup, logger: Logger?) -> EventLoopFuture { +// logger?.debug("Loading topics from file...") +// +// let topicsFilePath = URL(fileURLWithPath: #file) +// .deletingLastPathComponent() +// .deletingLastPathComponent() +// .deletingLastPathComponent() +// .appendingPathComponent(".topics") +// +// let decoder = JSONDecoder() +// +// // Attempt to load the topics from file in root directory. +// let localTopics = (try? Data(contentsOf: topicsFilePath)) +// .flatMap { try? decoder.decode(Topics.self, from: $0) } +// +// logger?.debug( +// localTopics == nil +// ? "Failed to load topics from file, falling back to defaults." +// : "Done loading topics from file." +// ) +// +// // If we were able to load from file use that, else fallback to the defaults. +// return eventLoopGroup.next().makeSucceededFuture(localTopics ?? .init()) +// } +// +// private extension EventLoopFuture where Value == (EnvVars, Topics) { +// +// /// Creates the ``DewPointEnvironment`` for the application after the ``EnvVars`` have been loaded. +// /// +// /// - Parameters: +// /// - eventLoopGroup: The event loop group for the application. +// /// - logger: An optional logger for the application. +// func makeDewPointEnvironment( +// eventLoopGroup: EventLoopGroup, +// logger: Logger? +// ) -> EventLoopFuture { +// map { envVars, topics in +// let mqttClient = MQTTClient(envVars: envVars, eventLoopGroup: eventLoopGroup, logger: logger) +// return DewPointEnvironment( +// envVars: envVars, +// mqttClient: mqttClient, +// topics: topics +// ) +// } +// } +// } +// +// private extension EventLoopFuture where Value == DewPointEnvironment { +// +// /// Connects to the MQTT broker after the ``DewPointEnvironment`` has been setup. +// /// +// /// - Parameters: +// /// - logger: An optional logger for debugging. +// func connectToMQTTBroker(autoConnect: Bool, logger: Logger?) -> EventLoopFuture { +// guard autoConnect else { return self } +// return flatMap { environment in +// logger?.debug("Connecting to MQTT Broker...") +// return environment.mqttClient.connect() +// .map { _ in +// logger?.debug("Successfully connected to MQTT Broker...") +// return environment +// } +// } +// } +// } +// +// private extension MQTTNIO.MQTTClient { +// +// convenience init(envVars: EnvVars, eventLoopGroup: EventLoopGroup, logger: Logger?) { +// self.init( +// host: envVars.host, +// port: envVars.port != nil ? Int(envVars.port!) : nil, +// identifier: envVars.identifier, +// eventLoopGroupProvider: .shared(eventLoopGroup), +// logger: logger, +// configuration: .init( +// version: .v5_0, +// userName: envVars.userName, +// password: envVars.password +// ) +// ) +// } +// } diff --git a/Sources/Client/Interface.swift b/Sources/Client/Interface.swift deleted file mode 100755 index 271085d..0000000 --- a/Sources/Client/Interface.swift +++ /dev/null @@ -1,43 +0,0 @@ -// import Foundation -// import Logging -// import Models -// import NIO -// import PsychrometricClient -// -// public struct Client { -// -// /// Add the publish listeners to the MQTT Broker, to be notified of published changes. -// public var addListeners: () -> Void -// -// /// Connect to the MQTT Broker. -// public var connect: () -> EventLoopFuture -// -// public var publishSensor: (SensorPublishRequest) -> EventLoopFuture -// -// /// Subscribe to appropriate topics / events. -// public var subscribe: () -> EventLoopFuture -// -// /// Disconnect and close the connection to the MQTT Broker. -// public var shutdown: () -> EventLoopFuture -// -// public init( -// addListeners: @escaping () -> Void, -// connect: @escaping () -> EventLoopFuture, -// publishSensor: @escaping (SensorPublishRequest) -> EventLoopFuture, -// shutdown: @escaping () -> EventLoopFuture, -// subscribe: @escaping () -> EventLoopFuture -// ) { -// self.addListeners = addListeners -// self.connect = connect -// self.publishSensor = publishSensor -// self.shutdown = shutdown -// self.subscribe = subscribe -// } -// -// public enum SensorPublishRequest { -// case mixed(State.Sensors.TemperatureHumiditySensor) -// case postCoil(State.Sensors.TemperatureHumiditySensor) -// case `return`(State.Sensors.TemperatureHumiditySensor) -// case supply(State.Sensors.TemperatureHumiditySensor) -// } -// } diff --git a/Sources/ClientLive/Helpers.swift b/Sources/ClientLive/Helpers.swift deleted file mode 100755 index 435dc51..0000000 --- a/Sources/ClientLive/Helpers.swift +++ /dev/null @@ -1,260 +0,0 @@ -// import Logging -// import Models -// import MQTTNIO -// import NIO -// import NIOFoundationCompat -// import PsychrometricClient -// -// /// Represents a type that can be initialized by a ``ByteBuffer``. -// protocol BufferInitalizable { -// init?(buffer: inout ByteBuffer) -// } -// -// extension Double: BufferInitalizable { -// -// /// Attempt to create / parse a double from a byte buffer. -// init?(buffer: inout ByteBuffer) { -// guard let string = buffer.readString( -// length: buffer.readableBytes, -// encoding: String.Encoding.utf8 -// ) -// else { return nil } -// self.init(string) -// } -// } -// -// extension Temperature: BufferInitalizable { -// /// Attempt to create / parse a temperature from a byte buffer. -// init?(buffer: inout ByteBuffer) { -// guard let value = Double(buffer: &buffer) else { return nil } -// self.init(value, units: .celsius) -// } -// } -// -// extension RelativeHumidity: BufferInitalizable { -// /// Attempt to create / parse a relative humidity from a byte buffer. -// init?(buffer: inout ByteBuffer) { -// guard let value = Double(buffer: &buffer) else { return nil } -// self.init(value) -// } -// } -// -// // TODO: Remove below when migrated to async client. -// extension MQTTNIO.MQTTClient { -// /// Logs a failure for a given topic and error. -// func logFailure(topic: String, error: Error) { -// logger.error("\(topic): \(error)") -// } -// } -// -// extension Result where Success == MQTTPublishInfo { -// func logIfFailure(client: MQTTNIO.MQTTClient, topic: String) -> ByteBuffer? { -// switch self { -// case let .success(value): -// guard value.topicName == topic else { return nil } -// return value.payload -// case let .failure(error): -// client.logFailure(topic: topic, error: error) -// return nil -// } -// } -// } -// -// extension Optional where Wrapped == ByteBuffer { -// -// func parse(as _: T.Type) -> T? where T: BufferInitalizable { -// switch self { -// case var .some(buffer): -// return T(buffer: &buffer) -// case .none: -// return nil -// } -// } -// } -// -// private struct TemperatureAndHumiditySensorKeyPathEnvelope { -// -// let humidityTopic: KeyPath -// let temperatureTopic: KeyPath -// let temperatureState: WritableKeyPath -// let humidityState: WritableKeyPath -// -// func addListener(to client: MQTTNIO.MQTTClient, topics: Topics, state: State) { -// let temperatureTopic = topics.sensors[keyPath: temperatureTopic] -// client.logger.trace("Adding listener for topic: \(temperatureTopic)") -// client.addPublishListener(named: temperatureTopic) { result in -// result.logIfFailure(client: client, topic: temperatureTopic) -// .parse(as: Temperature.self) -// .map { temperature in -// state.sensors[keyPath: temperatureState] = temperature -// } -// } -// -// let humidityTopic = topics.sensors[keyPath: humidityTopic] -// client.logger.trace("Adding listener for topic: \(humidityTopic)") -// client.addPublishListener(named: humidityTopic) { result in -// result.logIfFailure(client: client, topic: humidityTopic) -// .parse(as: RelativeHumidity.self) -// .map { humidity in -// state.sensors[keyPath: humidityState] = humidity -// } -// } -// } -// } -// -// extension Array where Element == TemperatureAndHumiditySensorKeyPathEnvelope { -// func addListeners(to client: MQTTNIO.MQTTClient, topics: Topics, state: State) { -// _ = map { envelope in -// envelope.addListener(to: client, topics: topics, state: state) -// } -// } -// } -// -// extension Array where Element == MQTTSubscribeInfo { -// static func sensors(topics: Topics) -> Self { -// [ -// .init(topicFilter: topics.sensors.mixedAirSensor.temperature, qos: .atLeastOnce), -// .init(topicFilter: topics.sensors.mixedAirSensor.humidity, qos: .atLeastOnce), -// .init(topicFilter: topics.sensors.postCoilSensor.temperature, qos: .atLeastOnce), -// .init(topicFilter: topics.sensors.postCoilSensor.humidity, qos: .atLeastOnce), -// .init(topicFilter: topics.sensors.returnAirSensor.temperature, qos: .atLeastOnce), -// .init(topicFilter: topics.sensors.returnAirSensor.humidity, qos: .atLeastOnce), -// .init(topicFilter: topics.sensors.supplyAirSensor.temperature, qos: .atLeastOnce), -// .init(topicFilter: topics.sensors.supplyAirSensor.humidity, qos: .atLeastOnce) -// ] -// } -// } -// -// extension State { -// func addSensorListeners(to client: MQTTNIO.MQTTClient, topics: Topics) { -// let envelopes: [TemperatureAndHumiditySensorKeyPathEnvelope] = [ -// .init( -// humidityTopic: \.mixedAirSensor.humidity, -// temperatureTopic: \.mixedAirSensor.temperature, -// temperatureState: \.mixedAirSensor.temperature, -// humidityState: \.mixedAirSensor.humidity -// ), -// .init( -// humidityTopic: \.postCoilSensor.humidity, -// temperatureTopic: \.postCoilSensor.temperature, -// temperatureState: \.postCoilSensor.temperature, -// humidityState: \.postCoilSensor.humidity -// ), -// .init( -// humidityTopic: \.returnAirSensor.humidity, -// temperatureTopic: \.returnAirSensor.temperature, -// temperatureState: \.returnAirSensor.temperature, -// humidityState: \.returnAirSensor.humidity -// ), -// .init( -// humidityTopic: \.supplyAirSensor.humidity, -// temperatureTopic: \.supplyAirSensor.temperature, -// temperatureState: \.supplyAirSensor.temperature, -// humidityState: \.supplyAirSensor.humidity -// ) -// ] -// envelopes.addListeners(to: client, topics: topics, state: self) -// } -// } -// -// extension Client.SensorPublishRequest { -// -// func dewPointData(topics: Topics, units: PsychrometricEnvironment.Units?) -> (DewPoint, String)? { -// switch self { -// case let .mixed(sensor): -// guard let dp = sensor.dewPoint(units: units) else { return nil } -// return (dp, topics.sensors.mixedAirSensor.dewPoint) -// case let .postCoil(sensor): -// guard let dp = sensor.dewPoint(units: units) else { return nil } -// return (dp, topics.sensors.postCoilSensor.dewPoint) -// case let .return(sensor): -// guard let dp = sensor.dewPoint(units: units) else { return nil } -// return (dp, topics.sensors.returnAirSensor.dewPoint) -// case let .supply(sensor): -// guard let dp = sensor.dewPoint(units: units) else { return nil } -// return (dp, topics.sensors.supplyAirSensor.dewPoint) -// } -// } -// -// func enthalpyData(altitude: Length, topics: Topics, units: PsychrometricEnvironment.Units?) -> (EnthalpyOf, String)? { -// switch self { -// case let .mixed(sensor): -// guard let enthalpy = sensor.enthalpy(altitude: altitude, units: units) else { return nil } -// return (enthalpy, topics.sensors.mixedAirSensor.enthalpy) -// case let .postCoil(sensor): -// guard let enthalpy = sensor.enthalpy(altitude: altitude, units: units) else { return nil } -// return (enthalpy, topics.sensors.postCoilSensor.enthalpy) -// case let .return(sensor): -// guard let enthalpy = sensor.enthalpy(altitude: altitude, units: units) else { return nil } -// return (enthalpy, topics.sensors.returnAirSensor.enthalpy) -// case let .supply(sensor): -// guard let enthalpy = sensor.enthalpy(altitude: altitude, units: units) else { return nil } -// return (enthalpy, topics.sensors.supplyAirSensor.enthalpy) -// } -// } -// -// func setHasProcessed(state: State) { -// switch self { -// case .mixed: -// state.sensors.mixedAirSensor.needsProcessed = false -// case .postCoil: -// state.sensors.postCoilSensor.needsProcessed = false -// case .return: -// state.sensors.returnAirSensor.needsProcessed = false -// case .supply: -// state.sensors.supplyAirSensor.needsProcessed = false -// } -// } -// } -// -// extension MQTTNIO.MQTTClient { -// -// func publishDewPoint( -// request: Client.SensorPublishRequest, -// state: State, -// topics: Topics -// ) -> EventLoopFuture<(MQTTNIO.MQTTClient, Client.SensorPublishRequest, State, Topics)> { -// guard let (dewPoint, topic) = request.dewPointData(topics: topics, units: state.units) -// else { -// logger.trace("No dew point for sensor.") -// return eventLoopGroup.next().makeSucceededFuture((self, request, state, topics)) -// } -// let roundedDewPoint = round(dewPoint.rawValue * 100) / 100 -// logger.debug("Publishing dew-point: \(dewPoint), to: \(topic)") -// return publish( -// to: topic, -// payload: ByteBufferAllocator().buffer(string: "\(roundedDewPoint)"), -// qos: .atLeastOnce, -// retain: true -// ) -// .map { (self, request, state, topics) } -// } -// } -// -// extension EventLoopFuture where Value == (Client.SensorPublishRequest, State) { -// func setHasProcessed() -> EventLoopFuture { -// map { request, state in -// request.setHasProcessed(state: state) -// } -// } -// } -// -// extension EventLoopFuture where Value == (MQTTNIO.MQTTClient, Client.SensorPublishRequest, State, Topics) { -// func publishEnthalpy() -> EventLoopFuture<(Client.SensorPublishRequest, State)> { -// flatMap { client, request, state, topics in -// guard let (enthalpy, topic) = request.enthalpyData(altitude: state.altitude, topics: topics, units: state.units) -// else { -// client.logger.trace("No enthalpy for sensor.") -// return client.eventLoopGroup.next().makeSucceededFuture((request, state)) -// } -// let roundedEnthalpy = round(enthalpy.rawValue * 100) / 100 -// client.logger.debug("Publishing enthalpy: \(enthalpy), to: \(topic)") -// return client.publish( -// to: topic, -// payload: ByteBufferAllocator().buffer(string: "\(roundedEnthalpy)"), -// qos: .atLeastOnce -// ) -// .map { (request, state) } -// } -// } -// } diff --git a/Sources/ClientLive/Live.swift b/Sources/ClientLive/Live.swift deleted file mode 100755 index a499f0e..0000000 --- a/Sources/ClientLive/Live.swift +++ /dev/null @@ -1,40 +0,0 @@ -// @_exported import Client -// import Foundation -// import Models -// import MQTTNIO -// import NIO -// import PsychrometricClient -// -// public extension Client { -// -// // The state passed in here needs to be a class or we get escaping errors in the `addListeners` method. -// static func live( -// client: MQTTNIO.MQTTClient, -// state: State, -// topics: Topics -// ) -> Self { -// .init( -// addListeners: { -// state.addSensorListeners(to: client, topics: topics) -// }, -// connect: { -// client.connect() -// .map { _ in } -// }, -// publishSensor: { request in -// client.publishDewPoint(request: request, state: state, topics: topics) -// .publishEnthalpy() -// .setHasProcessed() -// }, -// shutdown: { -// client.disconnect() -// .map { try? client.syncShutdownGracefully() } -// }, -// subscribe: { -// // Sensor subscriptions -// client.subscribe(to: .sensors(topics: topics)) -// .map { _ in } -// } -// ) -// } -// } diff --git a/Sources/ClientLive/SensorsClient.swift b/Sources/ClientLive/SensorsClient.swift deleted file mode 100644 index f77fedb..0000000 --- a/Sources/ClientLive/SensorsClient.swift +++ /dev/null @@ -1,262 +0,0 @@ -// import EnvVars -// import Logging -// import Models -// import MQTTNIO -// import NIO -// import PsychrometricClient -// import ServiceLifecycle -// -// // TODO: Remove. -// // TODO: Pass in eventLoopGroup and MQTTClient. -// public actor SensorsClient { -// -// public static let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) -// public let client: MQTTClient -// public private(set) var shuttingDown: Bool -// public private(set) var sensors: [TemperatureAndHumiditySensor] -// -// var logger: Logger { client.logger } -// -// public init( -// envVars: EnvVars, -// logger: Logger, -// sensors: [TemperatureAndHumiditySensor] = [] -// ) { -// let config = MQTTClient.Configuration( -// version: .v3_1_1, -// userName: envVars.userName, -// password: envVars.password, -// useSSL: false, -// useWebSockets: false, -// tlsConfiguration: nil, -// webSocketURLPath: nil -// ) -// self.client = MQTTClient( -// host: envVars.host, -// identifier: envVars.identifier, -// eventLoopGroupProvider: .shared(Self.eventLoopGroup), -// logger: logger, -// configuration: config -// ) -// self.shuttingDown = false -// self.sensors = sensors -// } -// -// public func addSensor(_ sensor: TemperatureAndHumiditySensor) async throws { -// guard sensors.firstIndex(where: { $0.location == sensor.location }) == nil else { -// throw SensorExists() -// } -// sensors.append(sensor) -// } -// -// public func connect(cleanSession: Bool = true) async { -// do { -// try await client.connect(cleanSession: cleanSession) -// client.addCloseListener(named: "SensorsClient") { [self] _ in -// guard !self.shuttingDown else { return } -// Task { -// self.logger.debug("Connection closed.") -// self.logger.debug("Reconnecting...") -// await self.connect() -// } -// } -// logger.debug("Connection successful.") -// } catch { -// logger.trace("Connection Failed.\(error)") -// } -// } -// -// public func start() async throws { -// await withGracefulShutdownHandler { -// await withThrowingTaskGroup(of: Void.self) { group in -// group.addTask { try await self.subscribeToSensors() } -// group.addTask { try await self.addSensorListeners() } -// } -// } onGracefulShutdown: { -// Task { await self.shutdown() } -// } -// // do { -// // try await subscribeToSensors() -// // try await addSensorListeners() -// // logger.debug("Begin listening to sensors...") -// // } catch { -// // logger.trace("Error:(error)") -// // throw error -// // } -// } -// -// public func shutdown() async { -// shuttingDown = true -// try? await client.disconnect() -// try? await client.shutdown() -// } -// -// /// Subscribe to changes of the temperature and humidity sensors. -// func subscribeToSensors(qos: MQTTQoS = .exactlyOnce) async throws { -// for sensor in sensors { -// try await client.subscribeToSensor(sensor, qos: qos) -// } -// } -// -// private func _addSensorListeners(qos _: MQTTQoS = .exactlyOnce) async throws { -// // try await withThrowingDiscardingTaskGroup { group in -// // group.addTask { try await self.subscribeToSensors(qos: qos) } -// -// for await result in client.createPublishListener() { -// switch result { -// case let .failure(error): -// logger.trace("Error:\(error)") -// case let .success(value): -// let topic = value.topicName -// logger.trace("Received new value for topic: \(topic)") -// if topic.contains("temperature") { -// // do something. -// var buffer = value.payload -// guard let temperature = Temperature(buffer: &buffer) else { -// logger.trace("Decoding error for topic: \(topic)") -// throw DecodingError() -// } -// try sensors.update(topic: topic, keyPath: \.temperature, with: temperature) -// // group.addTask { -// Task { -// try await self.publishUpdates() -// } -// -// } else if topic.contains("humidity") { -// var buffer = value.payload -// // Decode and update the temperature value -// guard let humidity = RelativeHumidity(buffer: &buffer) else { -// logger.debug("Failed to decode humidity from buffer: \(buffer)") -// throw DecodingError() -// } -// try sensors.update(topic: topic, keyPath: \.humidity, with: humidity) -// // group.addTask { -// Task { -// try await self.publishUpdates() -// } -// } -// // } -// } -// } -// } -// -// func addSensorListeners(qos: MQTTQoS = .exactlyOnce) async throws { -// try await subscribeToSensors(qos: qos) -// client.addPublishListener(named: "SensorsClient") { result in -// do { -// switch result { -// case let .success(value): -// var buffer = value.payload -// let topic = value.topicName -// self.logger.trace("Received new value for topic: \(topic)") -// -// if topic.contains("temperature") { -// // Decode and update the temperature value -// guard let temperature = Temperature(buffer: &buffer) else { -// self.logger.debug("Failed to decode temperature from buffer: \(buffer)") -// throw DecodingError() -// } -// try self.sensors.update(topic: topic, keyPath: \.temperature, with: temperature) -// Task { try await self.publishUpdates() } -// } else if topic.contains("humidity") { -// // Decode and update the temperature value -// guard let humidity = RelativeHumidity(buffer: &buffer) else { -// self.logger.debug("Failed to decode humidity from buffer: \(buffer)") -// throw DecodingError() -// } -// try self.sensors.update(topic: topic, keyPath: \.humidity, with: humidity) -// Task { try await self.publishUpdates() } -// } -// -// case let .failure(error): -// self.logger.trace("Error:\(error)") -// throw error -// } -// } catch { -// self.logger.trace("Error:\(error)") -// } -// } -// } -// -// private func publish(double: Double?, to topic: String) async throws { -// guard let double else { return } -// let rounded = round(double * 100) / 100 -// logger.debug("Publishing \(rounded), to: \(topic)") -// try await client.publish( -// to: topic, -// payload: ByteBufferAllocator().buffer(string: "\(rounded)"), -// qos: .exactlyOnce, -// retain: true -// ) -// } -// -// private func publishUpdates() async throws { -// for sensor in sensors.filter(\.needsProcessed) { -// try await publish(double: sensor.dewPoint?.rawValue, to: sensor.topics.dewPoint) -// try await publish(double: sensor.enthalpy?.rawValue, to: sensor.topics.enthalpy) -// try sensors.hasProcessed(sensor) -// } -// } -// } -// -// // MARK: - Helpers -// -// private extension MQTTClient { -// -// func subscribeToSensor( -// _ sensor: TemperatureAndHumiditySensor, -// qos: MQTTQoS = .exactlyOnce -// ) async throws { -// do { -// _ = try await subscribe(to: [ -// MQTTSubscribeInfo(topicFilter: sensor.topics.temperature, qos: qos), -// MQTTSubscribeInfo(topicFilter: sensor.topics.humidity, qos: qos) -// ]) -// logger.debug("Subscribed to temperature-humidity sensor: \(sensor.id)") -// } catch { -// logger.trace("Failed to subscribe to temperature-humidity sensor: \(sensor.id)") -// throw error -// } -// } -// } -// -// struct DecodingError: Error {} -// struct NotFoundError: Error {} -// struct SensorExists: Error {} -// -// private extension TemperatureAndHumiditySensor.Topics { -// func contains(_ topic: String) -> Bool { -// temperature == topic || humidity == topic -// } -// } -// -// // TODO: Move to dewpoint-controller/main.swift -// public extension Array where Element == TemperatureAndHumiditySensor { -// static var live: Self { -// TemperatureAndHumiditySensor.Location.allCases.map { -// TemperatureAndHumiditySensor(location: $0) -// } -// } -// } -// -// private extension Array where Element == TemperatureAndHumiditySensor { -// -// mutating func update( -// topic: String, -// keyPath: WritableKeyPath, -// with value: V -// ) throws { -// guard let index = firstIndex(where: { $0.topics.contains(topic) }) else { -// throw NotFoundError() -// } -// self[index][keyPath: keyPath] = value -// } -// -// mutating func hasProcessed(_ sensor: TemperatureAndHumiditySensor) throws { -// guard let index = firstIndex(where: { $0.id == sensor.id }) else { -// throw NotFoundError() -// } -// self[index].needsProcessed = false -// } -// -// } diff --git a/Sources/DewPointEnvironment/Environment.swift b/Sources/DewPointEnvironment/Environment.swift deleted file mode 100755 index bbfbdf1..0000000 --- a/Sources/DewPointEnvironment/Environment.swift +++ /dev/null @@ -1,23 +0,0 @@ -import Client -import EnvVars -import Models -import MQTTNIO - -// TODO: Remove - -public struct DewPointEnvironment: Sendable { - - public var envVars: EnvVars - public var mqttClient: MQTTNIO.MQTTClient - public var topics: Topics - - public init( - envVars: EnvVars, - mqttClient: MQTTNIO.MQTTClient, - topics: Topics = .init() - ) { - self.envVars = envVars - self.mqttClient = mqttClient - self.topics = topics - } -} diff --git a/Sources/EnvVars/EnvVars.swift b/Sources/Models/EnvVars.swift similarity index 98% rename from Sources/EnvVars/EnvVars.swift rename to Sources/Models/EnvVars.swift index 0297ebd..926f1f2 100755 --- a/Sources/EnvVars/EnvVars.swift +++ b/Sources/Models/EnvVars.swift @@ -1,7 +1,5 @@ import Foundation -// TODO: Move to Models - /// Holds common settings for connecting to your MQTT broker. The default values can be used, /// they can be loaded from the shell environment, or from a file located in the root directory. /// diff --git a/Sources/Models/Mode.swift b/Sources/Models/Mode.swift deleted file mode 100755 index d54b448..0000000 --- a/Sources/Models/Mode.swift +++ /dev/null @@ -1,39 +0,0 @@ -import PsychrometricClient - -// TODO: Remove - -/// Represents the different modes that the controller can be in. -public enum Mode: Equatable { - - /// Allows controller to run in humidify or dehumidify mode. - case auto - - /// Only handle humidify mode. - case humidifyOnly(HumidifyMode) - - /// Only handle dehumidify mode. - case dehumidifyOnly(DehumidifyMode) - - /// Don't control humidify or dehumidify modes. - case off - - /// Represents the control modes for the humidify control state. - public enum HumidifyMode: Equatable { - - /// Control humidifying based off dew-point. - case dewPoint(DewPoint) - - /// Control humidifying based off relative humidity. - case relativeHumidity(RelativeHumidity) - } - - /// Represents the control modes for the dehumidify control state. - public enum DehumidifyMode: Equatable { - - /// Control dehumidifying based off dew-point. - case dewPoint(high: DewPoint, low: DewPoint) - - /// Control humidifying based off relative humidity. - case relativeHumidity(high: RelativeHumidity, low: RelativeHumidity) - } -} diff --git a/Sources/Models/State.swift b/Sources/Models/State.swift deleted file mode 100755 index ddaf804..0000000 --- a/Sources/Models/State.swift +++ /dev/null @@ -1,102 +0,0 @@ -import Foundation -import PsychrometricClient - -// TODO: Remove -// TODO: Make this a struct, then create a Store class that holds the state?? -public final class State { - - public var altitude: Length - public var sensors: Sensors - public var units: PsychrometricUnits - - public init( - altitude: Length = .seaLevel, - sensors: Sensors = .init(), - units: PsychrometricUnits = .imperial - ) { - self.altitude = altitude - self.sensors = sensors - self.units = units - } - - public struct Sensors: Equatable { - - public var mixedAirSensor: TemperatureHumiditySensor - public var postCoilSensor: TemperatureHumiditySensor - public var returnAirSensor: TemperatureHumiditySensor - public var supplyAirSensor: TemperatureHumiditySensor - - public init( - mixedAirSensor: TemperatureHumiditySensor = .init(), - postCoilSensor: TemperatureHumiditySensor = .init(), - returnAirSensor: TemperatureHumiditySensor = .init(), - supplyAirSensor: TemperatureHumiditySensor = .init() - ) { - self.mixedAirSensor = mixedAirSensor - self.postCoilSensor = postCoilSensor - self.returnAirSensor = returnAirSensor - self.supplyAirSensor = supplyAirSensor - } - - public var needsProcessed: Bool { - mixedAirSensor.needsProcessed - || postCoilSensor.needsProcessed - || returnAirSensor.needsProcessed - || supplyAirSensor.needsProcessed - } - } -} - -public extension State.Sensors { - - struct TemperatureHumiditySensor: Equatable { - - @TrackedChanges - public var temperature: DryBulb? - - @TrackedChanges - public var humidity: RelativeHumidity? - - public var needsProcessed: Bool { - get { $temperature.needsProcessed || $humidity.needsProcessed } - set { - $temperature.needsProcessed = newValue - $humidity.needsProcessed = newValue - } - } - - // WARN: Fix me. - public func dewPoint(units _: PsychrometricUnits? = nil) -> DewPoint? { - guard let temperature = temperature, - let humidity = humidity - else { return nil } - return nil - // return .init(dryBulb: temperature, humidity: humidity, units: units) - } - - // WARN: Fix me. - public func enthalpy(altitude _: Length, units _: PsychrometricUnits? = nil) -> EnthalpyOf? { - guard let temperature = temperature, - let humidity = humidity - else { return nil } - return nil - // return .init(dryBulb: temperature, humidity: humidity, altitude: altitude, units: units) - } - - public init( - temperature: DryBulb? = nil, - humidity: RelativeHumidity? = nil, - needsProcessed: Bool = false - ) { - self._temperature = .init(wrappedValue: temperature, needsProcessed: needsProcessed) - self._humidity = .init(wrappedValue: humidity, needsProcessed: needsProcessed) - } - } - - // MARK: - Temperature / Humidity Sensor Location Namespaces - - enum MixedAir {} - enum PostCoil {} - enum Return {} - enum Supply {} -} diff --git a/Sources/Models/Topics.swift b/Sources/Models/Topics.swift deleted file mode 100755 index ae750f3..0000000 --- a/Sources/Models/Topics.swift +++ /dev/null @@ -1,262 +0,0 @@ -// TODO: Remove - -/// A container for all the different MQTT topics that are needed by the application. -public struct Topics: Codable, Equatable, Sendable { - /// The command topics the application can publish to. - public var commands: Commands - - /// The sensor topics the application can read from / write to. - public var sensors: Sensors - - /// The set point topics the application can read set point values from. - public var setPoints: SetPoints - - /// The state topics the application can read state values from. - public var states: States - - /// Create the topics required by the application. - /// - /// - Parameters: - /// - sensors: The sensor topics. - /// - setPoints: The set point topics - /// - states: The states topics - /// - relays: The relay topics - public init( - commands: Commands = .init(), - sensors: Sensors = .init(), - setPoints: SetPoints = .init(), - states: States = .init() - ) { - self.commands = commands - self.sensors = sensors - self.setPoints = setPoints - self.states = states - } - - /// Represents the sensor topics. - public struct Sensors: Codable, Equatable, Sendable { - public var mixedAirSensor: TemperatureAndHumiditySensor - public var postCoilSensor: TemperatureAndHumiditySensor - public var returnAirSensor: TemperatureAndHumiditySensor - public var supplyAirSensor: TemperatureAndHumiditySensor - - public init( - mixedAirSensor: TemperatureAndHumiditySensor = .default(location: "mixed-air"), - postCoilSensor: TemperatureAndHumiditySensor = .default(location: "post-coil"), - returnAirSensor: TemperatureAndHumiditySensor = .default(location: "return"), - supplyAirSensor: TemperatureAndHumiditySensor = .default(location: "supply") - ) { - self.mixedAirSensor = mixedAirSensor - self.postCoilSensor = postCoilSensor - self.returnAirSensor = returnAirSensor - self.supplyAirSensor = supplyAirSensor - } - - public struct TemperatureAndHumiditySensor: Codable, Equatable, Sendable { - public var temperature: String - public var humidity: String - public var dewPoint: String - public var enthalpy: String - - /// Create a new sensor topic container. - /// - /// - Parameters: - /// - temperature: The temperature sensor topic. - /// - humidity: The humidity sensor topic. - /// - dewPoint: The dew point sensor topic. - public init( - temperature: String, - humidity: String, - dewPoint: String, - enthalpy: String - ) { - self.temperature = temperature - self.humidity = humidity - self.dewPoint = dewPoint - self.enthalpy = enthalpy - } - } - } - - /// A container for set point related topics used by the application. - public struct SetPoints: Codable, Equatable, Sendable { - /// The topic for the humidify set point. - public var humidify: Humidify - - /// The topics for dehumidification set points. - public var dehumidify: Dehumidify - - /// Create a new set point topic container. - /// - /// - Parameters: - /// - humidify: The topic for humidification set points. - /// - dehumidify: The topics for dehumidification set points. - public init( - humidify: Humidify = .init(), - dehumidify: Dehumidify = .init() - ) { - self.humidify = humidify - self.dehumidify = dehumidify - } - - /// A container for the humidification set point topics used by the application. - public struct Humidify: Codable, Equatable, Sendable { - /// The topic for dew point control mode set point. - public var dewPoint: String - - /// The topic for relative humidity control mode set point. - public var relativeHumidity: String - - /// Create a new container for the humidification set point topics. - /// - /// - Parameters: - /// - dewPoint: The topic for dew point control mode set point. - /// - relativeHumidity: The topic for relative humidity control mode set point. - public init( - dewPoint: String = "set_points/humidify/dew_point", - relativeHumidity: String = "set_points/humidify/relative_humidity" - ) { - self.dewPoint = dewPoint - self.relativeHumidity = relativeHumidity - } - } - - /// A container for dehumidifcation set point topics. - public struct Dehumidify: Codable, Equatable, Sendable { - /// A low setting for dew point control modes. - public var lowDewPoint: String - - /// A high setting for dew point control modes. - public var highDewPoint: String - - /// A low setting for relative humidity control modes. - public var lowRelativeHumidity: String - - /// A high setting for relative humidity control modes. - public var highRelativeHumidity: String - - /// Create a new container for dehumidification set point topics. - /// - /// - Parameters: - /// - lowDewPoint: A low setting for dew point control modes. - /// - highDewPoint: A high setting for dew point control modes. - /// - lowRelativeHumidity: A low setting for relative humidity control modes. - /// - highRelativeHumidity: A high setting for relative humidity control modes. - public init( - lowDewPoint: String = "set_points/dehumidify/low_dew_point", - highDewPoint: String = "set_points/dehumidify/high_dew_point", - lowRelativeHumidity: String = "set_points/dehumidify/low_relative_humidity", - highRelativeHumidity: String = "set_points/dehumidify/high_relative_humidity" - ) { - self.lowDewPoint = lowDewPoint - self.highDewPoint = highDewPoint - self.lowRelativeHumidity = lowRelativeHumidity - self.highRelativeHumidity = highRelativeHumidity - } - } - } - - /// A container for control state topics used by the application. - public struct States: Codable, Equatable, Sendable { - /// The topic for the control mode. - public var mode: String - - /// The relay state topics. - public var relays: Relays - - /// Create a new container for control state topics. - /// - /// - Parameters: - /// - mode: The topic for the control mode. - public init( - mode: String = "states/mode", - relays: Relays = .init() - ) { - self.mode = mode - self.relays = relays - } - - /// A container for reading the current state of a relay. - public struct Relays: Codable, Equatable, Sendable { - /// The dehumidification stage-1 relay topic. - public var dehumdification1: String - - /// The dehumidification stage-2 relay topic. - public var dehumidification2: String - - /// The humidification relay topic. - public var humdification: String - - /// Create a new container for relay state topics. - /// - /// - Parameters: - /// - dehumidification1: The dehumidification stage-1 relay topic. - /// - dehumidification2: The dehumidification stage-2 relay topic. - /// - humidification: The humidification relay topic. - public init( - dehumidefication1: String = "states/relays/dehumidification_1", - dehumidification2: String = "states/relays/dehumidification_2", - humidification: String = "states/relays/humidification" - ) { - self.dehumdification1 = dehumidefication1 - self.dehumidification2 = dehumidification2 - self.humdification = humidification - } - } - } - - /// A container for commands topics that the application can publish to. - public struct Commands: Codable, Equatable, Sendable { - /// The relay command topics. - public var relays: Relays - - /// Create a new command topics container. - /// - /// - Parameters: - /// - relays: The relay command topics. - public init(relays: Relays = .init()) { - self.relays = relays - } - - /// A container for relay command topics used by the application. - public struct Relays: Codable, Equatable, Sendable { - /// The dehumidification stage-1 relay topic. - public var dehumidification1: String - - /// The dehumidification stage-2 relay topic. - public var dehumidification2: String - - /// The humidification relay topic. - public var humidification: String - - /// Create a new container for commanding relays. - /// - /// - Parameters: - /// - dehumidification1: The dehumidification stage-1 relay topic. - /// - dehumidification2: The dehumidification stage-2 relay topic. - /// - humidification: The humidification relay topic. - public init( - dehumidification1: String = "relays/dehumidification_1", - dehumidification2: String = "relays/dehumidification_2", - humidification: String = "relays/humidification" - ) { - self.dehumidification1 = dehumidification1 - self.dehumidification2 = dehumidification2 - self.humidification = humidification - } - } - } -} - -// MARK: Helpers - -public extension Topics.Sensors.TemperatureAndHumiditySensor { - static func `default`(location: String) -> Self { - .init( - temperature: "sensors/\(location)/temperature", - humidity: "sensors/\(location)/humidity", - dewPoint: "sensors/\(location)/dew-point", - enthalpy: "sensors/\(location)/enthalpy" - ) - } -} diff --git a/Sources/TopicsLive/Live.swift b/Sources/TopicsLive/Live.swift deleted file mode 100755 index a4efc8b..0000000 --- a/Sources/TopicsLive/Live.swift +++ /dev/null @@ -1,53 +0,0 @@ -import Models - -// TODO: Fix other live topics -public extension Topics { - - static let live = Self( - commands: .init(), - sensors: .init( - mixedAirSensor: .live(location: .mixedAir), - postCoilSensor: .live(location: .postCoil), - returnAirSensor: .live(location: .return), - supplyAirSensor: .live(location: .supply) - ), - setPoints: .init(), - states: .init() - ) -} - -private extension Topics.Sensors { - enum Location: CustomStringConvertible { - case mixedAir - case postCoil - case `return` - case supply - - var description: String { - switch self { - case .mixedAir: - return "mixed_air" - case .postCoil: - return "post_coil" - case .return: - return "return" - case .supply: - return "supply" - } - } - } -} - -private extension Topics.Sensors.TemperatureAndHumiditySensor { - static func live( - prefix: String = "frankensystem", - location: Topics.Sensors.Location - ) -> Self { - .init( - temperature: "\(prefix)/sensor/\(location.description)_temperature/state", - humidity: "\(prefix)/sensor/\(location.description)_humidity/state", - dewPoint: "\(prefix)/sensor/\(location.description)_dew_point/state", - enthalpy: "\(prefix)/sensor/\(location.description)_enthalpy/state" - ) - } -} diff --git a/Tests/ClientTests/ClientTests.swift b/Tests/ClientTests/ClientTests.swift deleted file mode 100755 index 535b0ab..0000000 --- a/Tests/ClientTests/ClientTests.swift +++ /dev/null @@ -1,186 +0,0 @@ -// import Client -// @testable import ClientLive -// import CoreUnitTypes -// import Foundation -// import Logging -// import Models -// import MQTTNIO -// import NIO -// import NIOConcurrencyHelpers -// import XCTest -// -// final class ClientLiveTests: XCTestCase { -// static let hostname = ProcessInfo.processInfo.environment["MOSQUITTO_SERVER"] ?? "localhost" -// let topics = Topics() -// -// // func test_mqtt_subscription() throws { -// // let mqttClient = createMQTTClient(identifier: "test_subscription") -// // _ = try mqttClient.connect().wait() -// // let sub = try mqttClient.v5.subscribe( -// // to: [mqttClient.mqttSubscription(topic: "test/subscription")] -// // ).wait() -// // XCTAssertEqual(sub.reasons[0], .grantedQoS1) -// // try mqttClient.disconnect().wait() -// // try mqttClient.syncShutdownGracefully() -// // } -// -// func test_mqtt_listener() throws { -// let lock = Lock() -// var publishRecieved: [MQTTPublishInfo] = [] -// let payloadString = "test" -// let payload = ByteBufferAllocator().buffer(string: payloadString) -// -// let client = createMQTTClient(identifier: "testMQTTListener_publisher") -// _ = try client.connect().wait() -// client.addPublishListener(named: "test") { result in -// switch result { -// case let .success(publish): -// var buffer = publish.payload -// let string = buffer.readString(length: buffer.readableBytes) -// XCTAssertEqual(string, payloadString) -// lock.withLock { -// publishRecieved.append(publish) -// } -// case let .failure(error): -// XCTFail("\(error)") -// } -// } -// -// try client.publish(to: "testMQTTSubscribe", payload: payload, qos: .atLeastOnce, retain: true).wait() -// let sub = try client.v5.subscribe(to: [.init(topicFilter: "testMQTTSubscribe", qos: .atLeastOnce)]).wait() -// XCTAssertEqual(sub.reasons[0], .grantedQoS1) -// -// Thread.sleep(forTimeInterval: 2) -// lock.withLock { -// XCTAssertEqual(publishRecieved.count, 1) -// } -// -// try client.disconnect().wait() -// try client.syncShutdownGracefully() -// } -// -// func test_client2_returnTemperature_listener() throws { -// let mqttClient = createMQTTClient(identifier: "return-temperature-tests") -// let state = State() -// let topics = Topics() -// let client = Client.live(client: mqttClient, state: state, topics: topics) -// -// client.addListeners() -// try client.connect().wait() -// try client.subscribe().wait() -// -// _ = try mqttClient.publish( -// to: topics.sensors.returnAirSensor.temperature, -// payload: ByteBufferAllocator().buffer(string: "75.1234"), -// qos: .atLeastOnce -// ).wait() -// -// Thread.sleep(forTimeInterval: 2) -// -// XCTAssertEqual(state.sensors.returnAirSensor.temperature, .celsius(75.1234)) -// try mqttClient.disconnect().wait() -// try mqttClient.syncShutdownGracefully() -// -// // try client.shutdown().wait() -// } -// -// func test_client2_returnSensor_publish() throws { -// let mqttClient = createMQTTClient(identifier: "return-temperature-tests") -// let state = State() -// let topics = Topics() -// let client = Client.live(client: mqttClient, state: state, topics: topics) -// -// client.addListeners() -// try client.connect().wait() -// try client.subscribe().wait() -// -// _ = try mqttClient.publish( -// to: topics.sensors.returnAirSensor.temperature, -// payload: ByteBufferAllocator().buffer(string: "75.1234"), -// qos: .atLeastOnce -// ).wait() -// -// _ = try mqttClient.publish( -// to: topics.sensors.returnAirSensor.humidity, -// payload: ByteBufferAllocator().buffer(string: "\(50.0)"), -// qos: .atLeastOnce -// ).wait() -// -// Thread.sleep(forTimeInterval: 2) -// XCTAssert(state.sensors.returnAirSensor.needsProcessed) -// -// try client.publishSensor(.return(state.sensors.returnAirSensor)).wait() -// XCTAssertFalse(state.sensors.returnAirSensor.needsProcessed) -// -// try mqttClient.disconnect().wait() -// try mqttClient.syncShutdownGracefully() -// -// // try client.shutdown().wait() -// } -// -// // func test_fetch_humidity() throws { -// // let lock = Lock() -// // let publishClient = createMQTTClient(identifier: "publishHumidity") -// // let mqttClient = createMQTTClient(identifier: "fetchHumidity") -// // _ = try publishClient.connect().wait() -// // let client = try createClient(mqttClient: mqttClient) -// // var humidityRecieved: [RelativeHumidity] = [] -// // -// // _ = try publishClient.publish( -// // to: topics.sensors.humidity, -// // payload: ByteBufferAllocator().buffer(string: "\(50.0)"), -// // qos: .atLeastOnce -// // ).wait() -// // -// // Thread.sleep(forTimeInterval: 2) -// // try publishClient.disconnect().wait() -// // let humidity = try client.fetchHumidity(.init(topic: self.topics.sensors.humidity)).wait() -// // XCTAssertEqual(humidity, 50) -// // Thread.sleep(forTimeInterval: 2) -// // lock.withLock { -// // humidityRecieved.append(humidity) -// // } -// // try mqttClient.disconnect().wait() -// // try mqttClient.syncShutdownGracefully() -// // } -// -// // MARK: - Helpers -// -// func createMQTTClient(identifier: String) -> MQTTNIO.MQTTClient { -// MQTTNIO.MQTTClient( -// host: Self.hostname, -// port: 1883, -// identifier: identifier, -// eventLoopGroupProvider: .shared(eventLoopGroup), -// logger: logger, -// configuration: .init(version: .v5_0) -// ) -// } -// -// // func createWebSocketClient(identifier: String) -> MQTTNIO.MQTTClient { -// // MQTTNIO.MQTTClient( -// // host: Self.hostname, -// // port: 8080, -// // identifier: identifier, -// // eventLoopGroupProvider: .createNew, -// // logger: self.logger, -// // configuration: .init(useWebSockets: true, webSocketURLPath: "/mqtt") -// // ) -// // } -// -// // Uses default topic names. -// // func createClient(mqttClient: MQTTNIO.MQTTClient, autoConnect: Bool = true) throws -> Client.MQTTClient { -// // if autoConnect { -// // _ = try mqttClient.connect().wait() -// // } -// // return .live(client: mqttClient, topics: .init()) -// // } -// -// let logger: Logger = { -// var logger = Logger(label: "MQTTTests") -// logger.logLevel = .trace -// return logger -// }() -// -// let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) -// }