Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ORLib/ConsoleProviders/ESPProvision/DeviceConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ class DeviceConnection {
}
}

func sendOpenRemoteConfig(mqttBrokerUrl: String, mqttUser: String, mqttPassword: String, assetId: String) async throws {
func sendOpenRemoteConfig(mqttBrokerUrl: String, mqttUser: String, mqttPassword: String, assetId: String, properties: [String: String] = [:]) async throws {
if !isConnected {
throw ESPProviderError(errorCode: .notConnected, errorMessage: "No connection established to device")
}
do {
try await configChannel!.sendOpenRemoteConfig(mqttBrokerUrl: mqttBrokerUrl, mqttUser: mqttUser, mqttPassword: mqttPassword, assetId: assetId)
try await configChannel!.sendOpenRemoteConfig(mqttBrokerUrl: mqttBrokerUrl, mqttUser: mqttUser, mqttPassword: mqttPassword, assetId: assetId, properties: properties)
} catch {
throw ESPProviderError(errorCode: .communicationError, errorMessage: error.localizedDescription)
}
Expand Down
15 changes: 12 additions & 3 deletions ORLib/ConsoleProviders/ESPProvision/DeviceProvision.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ import os
import RandomPasswordGenerator

protocol DeviceProvisionAPI {
func provision(modelName: String, deviceId: String, password: String, token: String) async throws -> String
func provision(modelName: String, deviceId: String, password: String, token: String) async throws -> ProvisionResult
}

struct ProvisionResult {
let assetId: String
let properties: [String: String]
}

class DeviceProvision {
Expand Down Expand Up @@ -60,10 +65,14 @@ class DeviceProvision {

let password = try generatePassword()

let assetId = try await deviceProvisionAPI.provision(modelName: deviceInfo.modelName, deviceId: deviceInfo.deviceId, password: password, token: userToken)
let result = try await deviceProvisionAPI.provision(modelName: deviceInfo.modelName, deviceId: deviceInfo.deviceId, password: password, token: userToken)
let userName = deviceInfo.deviceId.lowercased(with: Locale(identifier: "en"))

try await deviceConnection!.sendOpenRemoteConfig(mqttBrokerUrl: mqttURL, mqttUser: userName, mqttPassword: password, assetId: assetId)
try await deviceConnection!.sendOpenRemoteConfig(mqttBrokerUrl: mqttURL,
mqttUser: userName,
mqttPassword: password,
assetId: result.assetId,
properties: result.properties)

var status = BackendConnectionStatus.connecting
let startTime = timeSource.now
Expand Down
7 changes: 4 additions & 3 deletions ORLib/ConsoleProviders/ESPProvision/DeviceProvisionAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ struct DeviceProvisionAPIREST: DeviceProvisionAPI {

private var apiURL: URL

func provision(modelName: String, deviceId: String, password: String, token: String) async throws -> String {
func provision(modelName: String, deviceId: String, password: String, token: String) async throws -> ProvisionResult {
/*
curl -v http://localhost:8080/api/master/rest/battery -d'{
"model": 0,
Expand Down Expand Up @@ -69,8 +69,8 @@ struct DeviceProvisionAPIREST: DeviceProvisionAPI {
mimeType == "application/json",
let dataString = String(data: data, encoding: .utf8) {
ORLogger.espprovisioning.info("Received JSON response from server \(dataString)")
let assetId = try JSONDecoder().decode(ProvisionResponseBody.self, from: data).assetId
return assetId
let body = try JSONDecoder().decode(ProvisionResponseBody.self, from: data)
return ProvisionResult(assetId: body.assetId, properties: body.properties ?? [:])
}
} catch {
ORLogger.espprovisioning.error("\(error.localizedDescription)")
Expand All @@ -87,6 +87,7 @@ struct DeviceProvisionAPIREST: DeviceProvisionAPI {

struct ProvisionResponseBody: Decodable {
var assetId: String
var properties: [String: String]?
}
}

Expand Down
4 changes: 3 additions & 1 deletion ORLib/ConsoleProviders/ESPProvision/ORConfigChannel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,16 @@ struct ORConfigChannel {
mqttUser: String,
mqttPassword: String,
realm: String = "master",
assetId: String) async throws {
assetId: String,
properties: [String: String] = [:]) async throws {
var openRemoteConfigRequest = Request()
var config = Request.OpenRemoteConfig()
config.mqttBrokerURL = mqttBrokerUrl
config.user = mqttUser
config.mqttPassword = mqttPassword
config.assetID = assetId
config.realm = realm
config.properties = properties
openRemoteConfigRequest.body = .openRemoteConfig(config)

let response = try await sendRequest(openRemoteConfigRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,8 @@

var assetID: String = String()

var properties: Dictionary<String,String> = [:]

var unknownFields = SwiftProtobuf.UnknownStorage()

init() {}
Expand Down Expand Up @@ -800,7 +802,7 @@

extension Request.OpenRemoteConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = Request.protoMessageName + ".OpenRemoteConfig"
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}mqtt_broker_url\0\u{1}user\0\u{3}mqtt_password\0\u{1}realm\0\u{3}asset_id\0")
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}mqtt_broker_url\0\u{1}user\0\u{3}mqtt_password\0\u{1}realm\0\u{3}asset_id\0\u{1}properties\0")

Check warning on line 805 in ORLib/ConsoleProviders/ESPProvision/ORConfigChannelProtocol.swift

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this constant "_protobuf_nameMap" to match the regular expression ^[a-z][a-zA-Z0-9]*$.

See more on https://sonarcloud.io/project/issues?id=openremote_console-ios-lib&issues=AZ5EflxlAgU2uZTj3WH_&open=AZ5EflxlAgU2uZTj3WH_&pullRequest=28

mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
Expand All @@ -813,6 +815,7 @@
case 3: try { try decoder.decodeSingularStringField(value: &self.mqttPassword) }()
case 4: try { try decoder.decodeSingularStringField(value: &self.realm) }()
case 5: try { try decoder.decodeSingularStringField(value: &self.assetID) }()
case 6: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: &self.properties) }()
default: break
}
}
Expand All @@ -834,6 +837,9 @@
if !self.assetID.isEmpty {
try visitor.visitSingularStringField(value: self.assetID, fieldNumber: 5)
}
if !self.properties.isEmpty {
try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: self.properties, fieldNumber: 6)
}
try unknownFields.traverse(visitor: &visitor)
}

Expand All @@ -843,6 +849,7 @@
if lhs.mqttPassword != rhs.mqttPassword {return false}
if lhs.realm != rhs.realm {return false}
if lhs.assetID != rhs.assetID {return false}
if lhs.properties != rhs.properties {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
Expand Down
1 change: 1 addition & 0 deletions ORLib/ORConfigChannelProtocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ message Request {
string mqtt_password = 3;
string realm = 4;
string asset_id = 5;
map<string, string> properties = 6;
}
message ExitProvisioning {};
string id = 1;
Expand Down
4 changes: 2 additions & 2 deletions Tests/DeviceProvisionAPIMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ class DeviceProvisionAPIMock: DeviceProvisionAPI {
var receivedToken: String?
var provisionCallCount = 0

func provision(modelName: String, deviceId: String, password: String, token: String) async throws -> String {
func provision(modelName: String, deviceId: String, password: String, token: String) async throws -> ProvisionResult {
provisionCallCount += 1
receivedModelName = modelName
receivedDeviceId = deviceId
receivedPassword = password
receivedToken = token
return "AssetID"
return ProvisionResult(assetId: "AssetID", properties: [:])
}
}
39 changes: 39 additions & 0 deletions Tests/ORConfigChannelTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ struct ORConfigChannelTest {
deviceMock.addMockData(Self.responseData(body: .openRemoteConfig(expectedOpenRemoteConfig)))

_ = try await channel.sendOpenRemoteConfig(mqttBrokerUrl: "mqtts://", mqttUser: "test", mqttPassword: "pwd", assetId: "123")

let sentConfig = try Self.openRemoteConfig(from: deviceMock.receivedData)
#expect(sentConfig.properties.isEmpty)
#expect(sentConfig.mqttBrokerURL == "mqtts://")
#expect(sentConfig.user == "test")
#expect(sentConfig.mqttPassword == "pwd")
#expect(sentConfig.assetID == "123")
}

@Test func validSendOpenRemoteConfigFailure() async throws {
Expand All @@ -184,6 +191,27 @@ struct ORConfigChannelTest {
}
}

@Test func sendOpenRemoteConfigForwardsProperties() async throws {
var channel = ORConfigChannel(device: deviceMock)

var expectedOpenRemoteConfig = Response.OpenRemoteConfig()
expectedOpenRemoteConfig.status = .success
deviceMock.addMockData(Self.responseData(body: .openRemoteConfig(expectedOpenRemoteConfig)))

let properties = [
"ota_url": "https://ota.example.com",
"ota_token": "secret-token"
]
try await channel.sendOpenRemoteConfig(mqttBrokerUrl: "mqtts://",
mqttUser: "test",
mqttPassword: "pwd",
assetId: "123",
properties: properties)

let sentConfig = try Self.openRemoteConfig(from: deviceMock.receivedData)
#expect(sentConfig.properties == properties)
}

// MARK: GetBackendConnectionStatus

@Test func validGetBackendConnectionStatus() async throws {
Expand Down Expand Up @@ -244,4 +272,15 @@ struct ORConfigChannelTest {
response.body = body
return try! response.serializedBytes()
}

static func openRemoteConfig(from receivedData: [Data]) throws -> Request.OpenRemoteConfig {
guard let lastData = receivedData.last else {
throw ORConfigChannelError.invalidRequest("No request sent")
}
let request = try Request(serializedBytes: lastData)
guard case let .openRemoteConfig(config) = request.body else {
throw ORConfigChannelError.invalidRequest("Last request was not OpenRemoteConfig")
}
return config
}
}
Loading