Sesame-Server/Sources/App/DeviceManager.swift
2022-04-09 17:29:51 +02:00

109 lines
3.2 KiB
Swift

import Foundation
import WebSocketKit
import Vapor
final class DeviceManager {
/// The seconds to wait for a response from the device
static let deviceTimeout: Int64 = 20
/// The connection to the device
private var connection: WebSocket?
/// The authentication token of the device for the socket connection
private let deviceKey: String
/// Indicate that the socket is fully initialized with an authorized device
var deviceIsAuthenticated = false
private var isOpeningNewConnection = false
/// Indicator for device availability
var deviceIsConnected: Bool {
deviceIsAuthenticated && !(connection?.isClosed ?? true)
}
/// A promise to finish the request once the device responds or times out
private var requestInProgress: EventLoopPromise<DeviceResponse>?
init(deviceKey: String) {
self.deviceKey = deviceKey
}
// MARK: API
var deviceStatus: String {
deviceIsConnected ? "1" : "0"
}
func sendMessageToDevice(_ message: Message, on eventLoop: EventLoop) -> EventLoopFuture<DeviceResponse> {
guard let socket = connection, !socket.isClosed else {
connection = nil
return eventLoop.makeSucceededFuture(.deviceNotConnected)
}
guard requestInProgress == nil else {
return eventLoop.makeSucceededFuture(.operationInProgress)
}
requestInProgress = eventLoop.makePromise(of: DeviceResponse.self)
socket.send(message.bytes, promise: nil)
eventLoop.scheduleTask(in: .seconds(Self.deviceTimeout)) { [weak self] in
guard let promise = self?.requestInProgress else {
return
}
self?.requestInProgress = nil
promise.succeed(.deviceTimedOut)
}
return requestInProgress!.futureResult
}
func authenticateDevice(psk: String) {
guard psk == self.deviceKey else {
print("Invalid device key")
_ = connection?.close()
deviceIsAuthenticated = false
return
}
print("Device authenticated")
deviceIsAuthenticated = true
}
func processDeviceResponse(_ data: ByteBuffer) {
guard let promise = requestInProgress else {
return
}
defer { requestInProgress = nil }
promise.succeed(DeviceResponse(data) ?? .unexpectedSocketEvent)
}
func didCloseDeviceSocket() {
guard !isOpeningNewConnection else {
return
}
deviceIsAuthenticated = false
guard connection != nil else {
print("Socket closed, but no connection anyway")
return
}
connection = nil
print("Socket closed")
}
func removeDeviceConnection() {
deviceIsAuthenticated = false
guard let socket = connection else {
return
}
try? socket.close().wait()
connection = nil
print("Removed device connection")
}
func createNewDeviceConnection(_ socket: WebSocket) {
isOpeningNewConnection = true
removeDeviceConnection()
connection = socket
print("Socket connected")
isOpeningNewConnection = false
}
}