From f901be37e26fb438d9cb2ee28aecd0a881bae5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantalea=CC=83o?= Date: Thu, 14 Mar 2024 11:05:03 +0100 Subject: [PATCH] Add stt data request --- Source/HAConnection.swift | 4 +-- Source/Internal/HAConnectionImpl.swift | 25 ++++++++++--------- .../HARequestController.swift | 8 +++--- Source/Requests/HARequestType.swift | 10 ++++---- Tests/HAConnectionImpl.test.swift | 12 ++++++--- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/Source/HAConnection.swift b/Source/HAConnection.swift index 104d87e..dc248ed 100644 --- a/Source/HAConnection.swift +++ b/Source/HAConnection.swift @@ -183,6 +183,6 @@ public protocol HAConnection: AnyObject { /// Write data to websocket connection /// - Parameters: - /// - request: The data request containing data to be written - func write(_ dataRequest: HARequest) + /// - sttDataRequest: The data request containing sttBinaryHandlerId and data (as base64 string) to be written + func write(_ sttDataRequest: HARequest) } diff --git a/Source/Internal/HAConnectionImpl.swift b/Source/Internal/HAConnectionImpl.swift index 36342cf..a34afc3 100644 --- a/Source/Internal/HAConnectionImpl.swift +++ b/Source/Internal/HAConnectionImpl.swift @@ -305,14 +305,10 @@ internal class HAConnectionImpl: HAConnection { // MARK: - Write - public func write(_ dataRequest: HARequest) { - if case .data = dataRequest.type { - defer { connectAutomaticallyIfNeeded() } - let invocation = HARequestInvocationSingle(request: dataRequest) { _ in } - requestController.add(invocation) - } else { - HAGlobal.log(.error, "Write operation can only be executed by data HARequest") - } + public func write(_ sttDataRequest: HARequest) { + defer { connectAutomaticallyIfNeeded() } + let invocation = HARequestInvocationSingle(request: sttDataRequest) { _ in } + requestController.add(invocation) } } @@ -404,9 +400,14 @@ extension HAConnectionImpl { } } - private func sendWrite(_ data: Data) { + private func sendWrite(_ sttBinaryHandlerId: UInt8, audioDataString: String?) { + // If there is no audioData, handlerID will be the payload alone indicating end of audio + var audioData = Data(base64Encoded: audioDataString ?? "") ?? Data() + + // Prefix audioData with handler ID so the API can map the binary data + audioData.insert(sttBinaryHandlerId, at: 0) workQueue.async { [connection] in - connection?.write(data: data) + connection?.write(data: audioData) } } @@ -419,8 +420,8 @@ extension HAConnectionImpl { sendWebSocket(identifier: identifier, request: request, command: command) case let .rest(method, command): sendRest(identifier: identifier!, request: request, method: method, command: command) - case let .data(data): - sendWrite(data) + case let .sttData(sttBinaryHandlerId): + sendWrite(sttBinaryHandlerId, audioDataString: request.data["audioData"] as? String) } } } diff --git a/Source/Internal/RequestController/HARequestController.swift b/Source/Internal/RequestController/HARequestController.swift index 765e0d7..05c7421 100644 --- a/Source/Internal/RequestController/HARequestController.swift +++ b/Source/Internal/RequestController/HARequestController.swift @@ -5,8 +5,8 @@ internal struct HARequestControllerAllowedSendKind: OptionSet { static let webSocket: Self = .init(rawValue: 0b1) static let rest: Self = .init(rawValue: 0b10) - static let data: Self = .init(rawValue: 0b10) - static let all: Self = [.webSocket, .rest, .data] + static let sttData: Self = .init(rawValue: 0b10) + static let all: Self = [.webSocket, .rest, .sttData] func allows(requestType: HARequestType) -> Bool { switch requestType { @@ -14,8 +14,8 @@ internal struct HARequestControllerAllowedSendKind: OptionSet { return contains(.webSocket) case .rest: return contains(.rest) - case .data: - return contains(.data) + case .sttData: + return contains(.sttData) } } } diff --git a/Source/Requests/HARequestType.swift b/Source/Requests/HARequestType.swift index 33c948f..1207b2a 100644 --- a/Source/Requests/HARequestType.swift +++ b/Source/Requests/HARequestType.swift @@ -6,8 +6,8 @@ public enum HARequestType: Hashable, Comparable, ExpressibleByStringLiteral { case webSocket(String) /// Sent over REST, the HTTP method to use and the post-`api/` path case rest(HAHTTPMethod, String) - /// Sent over WebSocket, the binary data to write - case data(Data) + /// Sent over WebSocket, the stt binary handler id + case sttData(UInt8) /// Create a WebSocket request type by string literal /// - Parameter value: The name of the WebSocket command @@ -20,7 +20,7 @@ public enum HARequestType: Hashable, Comparable, ExpressibleByStringLiteral { switch self { case let .webSocket(command), let .rest(_, command): return command - case .data: + case .sttData: return "" } } @@ -28,7 +28,7 @@ public enum HARequestType: Hashable, Comparable, ExpressibleByStringLiteral { /// The request is issued outside of the lifecycle of a connection public var isPerpetual: Bool { switch self { - case .webSocket, .data: return false + case .webSocket, .sttData: return false case .rest: return true } } @@ -45,7 +45,7 @@ public enum HARequestType: Hashable, Comparable, ExpressibleByStringLiteral { case let (.webSocket(lhsCommand), .webSocket(rhsCommand)), let (.rest(_, lhsCommand), .rest(_, rhsCommand)): return lhsCommand < rhsCommand - case (.data, _), (_, .data): + case (.sttData, _), (_, .sttData): return false } } diff --git a/Tests/HAConnectionImpl.test.swift b/Tests/HAConnectionImpl.test.swift index c05c7e8..2196eea 100644 --- a/Tests/HAConnectionImpl.test.swift +++ b/Tests/HAConnectionImpl.test.swift @@ -1464,11 +1464,17 @@ internal class HAConnectionImplTests: XCTestCase { func testWriteDataWritesData() { let expectedData = "Fake data".data(using: .utf8)! + let request = HARequest( + type: .sttData(1), + data: [ + "audioData": expectedData.base64EncodedString() + ] + ) connection.connect() - connection.write(.init(type: .data(expectedData))) + connection.write(request) XCTAssertNotNil(requestController.added.first(where: { invocation in - if case let .data(data) = invocation.request.type { - return data == expectedData + if case let .sttData(sttBinaryHandlerId) = invocation.request.type { + return sttBinaryHandlerId == 1 && invocation.request.data["audioData"] as! String == expectedData.base64EncodedString() } return false }))