271 lines
8.2 KiB
Swift
271 lines
8.2 KiB
Swift
//
|
|
// UniversalBleHelper.swift
|
|
// universal_ble
|
|
//
|
|
// Created by Rohit Sangwan on 25/10/23.
|
|
//
|
|
|
|
import CoreBluetooth
|
|
import Foundation
|
|
#if os(iOS)
|
|
import Flutter
|
|
#elseif os(OSX)
|
|
import FlutterMacOS
|
|
#endif
|
|
|
|
enum BleInputProperty: Int {
|
|
case disabled = 0
|
|
case notification = 1
|
|
case indication = 2
|
|
}
|
|
|
|
enum BleOutputProperty: Int {
|
|
case withResponse = 0
|
|
case withoutResponse = 1
|
|
}
|
|
|
|
enum BlueConnectionState: Int64 {
|
|
case connected = 0
|
|
case disconnected = 1
|
|
case connecting = 2
|
|
case disconnecting = 3
|
|
}
|
|
|
|
enum AvailabilityState: Int64 {
|
|
case unknown = 0
|
|
case resetting = 1
|
|
case unsupported = 2
|
|
case unauthorized = 3
|
|
case poweredOff = 4
|
|
case poweredOn = 5
|
|
}
|
|
|
|
enum CharacteristicProperty: Int64 {
|
|
case broadcast = 0
|
|
case read = 1
|
|
case writeWithoutResponse = 2
|
|
case write = 3
|
|
case notify = 4
|
|
case indicate = 5
|
|
case authenticatedSignedWrites = 6
|
|
case extendedProperties = 7
|
|
}
|
|
|
|
extension CBCharacteristicProperties {
|
|
var toCharacteristicProperty: [Int64] {
|
|
var properties = [Int64]()
|
|
if contains(.broadcast) {
|
|
properties.append(CharacteristicProperty.broadcast.rawValue)
|
|
}
|
|
if contains(.read) {
|
|
properties.append(CharacteristicProperty.read.rawValue)
|
|
}
|
|
if contains(.writeWithoutResponse) {
|
|
properties.append(CharacteristicProperty.writeWithoutResponse.rawValue)
|
|
}
|
|
if contains(.write) {
|
|
properties.append(CharacteristicProperty.write.rawValue)
|
|
}
|
|
if contains(.notify) {
|
|
properties.append(CharacteristicProperty.notify.rawValue)
|
|
}
|
|
if contains(.indicate) {
|
|
properties.append(CharacteristicProperty.indicate.rawValue)
|
|
}
|
|
if contains(.authenticatedSignedWrites) {
|
|
properties.append(CharacteristicProperty.authenticatedSignedWrites.rawValue)
|
|
}
|
|
if contains(.extendedProperties) {
|
|
properties.append(CharacteristicProperty.extendedProperties.rawValue)
|
|
}
|
|
return properties
|
|
}
|
|
}
|
|
|
|
extension CBManagerState {
|
|
func toAvailabilityState() -> AvailabilityState {
|
|
switch self {
|
|
case .unknown:
|
|
return AvailabilityState.unknown
|
|
case .resetting:
|
|
return AvailabilityState.resetting
|
|
case .unsupported:
|
|
return AvailabilityState.unsupported
|
|
case .unauthorized:
|
|
return AvailabilityState.unauthorized
|
|
case .poweredOff:
|
|
return AvailabilityState.poweredOff
|
|
case .poweredOn:
|
|
return AvailabilityState.poweredOn
|
|
@unknown default:
|
|
return AvailabilityState.unknown
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Maps string error codes to UniversalBleErrorCode enum
|
|
func mapErrorCodeToEnum(_ code: String) -> UniversalBleErrorCode {
|
|
switch code.lowercased() {
|
|
case "notsupported", "not_supported":
|
|
return .notSupported
|
|
case "notimplemented", "not_implemented":
|
|
return .notImplemented
|
|
case "channel-error", "channelerror":
|
|
return .channelError
|
|
case "failed":
|
|
return .failed
|
|
case "devicedisconnected", "device_disconnected":
|
|
return .deviceDisconnected
|
|
case "illegalargument", "illegal_argument":
|
|
return .illegalArgument
|
|
case "invalidaction", "invalid_action":
|
|
return .invalidAction
|
|
case "readfailed", "read_failed":
|
|
return .readFailed
|
|
case "devicenotfound", "device_not_found":
|
|
return .deviceNotFound
|
|
case "servicenotfound", "service_not_found":
|
|
return .serviceNotFound
|
|
case "characteristicnotfound", "characteristic_not_found":
|
|
return .characteristicNotFound
|
|
case "invalidserviceuuid", "invalid_service_uuid":
|
|
return .invalidServiceUuid
|
|
case "characteristicdoesnotsupportread":
|
|
return .characteristicDoesNotSupportRead
|
|
case "characteristicdoesnotsupportwrite":
|
|
return .characteristicDoesNotSupportWrite
|
|
case "characteristicdoesnotsupportwritewithoutresponse":
|
|
return .characteristicDoesNotSupportWriteWithoutResponse
|
|
case "characteristicdoesnotsupportnotify":
|
|
return .characteristicDoesNotSupportNotify
|
|
case "characteristicdoesnotsupportindicate":
|
|
return .characteristicDoesNotSupportIndicate
|
|
default:
|
|
return .unknownError
|
|
}
|
|
}
|
|
|
|
/// Creates a PigeonError with the error code enum in details
|
|
func createFlutterError(
|
|
code: UniversalBleErrorCode,
|
|
message: String? = nil,
|
|
details: String? = nil
|
|
) -> PigeonError {
|
|
// Pass the enum's rawValue (Int) in code as string, and enum name in details
|
|
return PigeonError(
|
|
code: code.rawValue.description,
|
|
message: message,
|
|
details: details ?? code.rawValue
|
|
)
|
|
}
|
|
|
|
extension Error {
|
|
func toFlutterError() -> PigeonError {
|
|
let nsError = self as NSError
|
|
let errorCode: String = .init(nsError.code)
|
|
let errorDescription: String = nsError.localizedDescription
|
|
let mappedCode = mapErrorCodeToEnum(errorCode)
|
|
return createFlutterError(code: mappedCode, message: errorDescription, details: errorCode)
|
|
}
|
|
}
|
|
|
|
public extension CBUUID {
|
|
var uuidStr: String {
|
|
uuidString.lowercased()
|
|
}
|
|
}
|
|
|
|
public extension CBPeripheral {
|
|
// FIXME: https://forums.developer.apple.com/thread/84375
|
|
var uuid: UUID {
|
|
value(forKey: "identifier") as! NSUUID as UUID
|
|
}
|
|
|
|
func getCharacteristic(_ characteristic: String, of service: String) -> CBCharacteristic? {
|
|
let GSS_SUFFIX = "0000-1000-8000-00805f9b34fb"
|
|
let s = services?.first {
|
|
$0.uuid.uuidStr.lowercased() == service.lowercased() || service.lowercased() == "0000\($0.uuid.uuidStr)-\(GSS_SUFFIX)".lowercased()
|
|
}
|
|
let c = s?.characteristics?.first {
|
|
$0.uuid.uuidStr.lowercased() == characteristic.lowercased() || characteristic.lowercased() == "0000\($0.uuid.uuidStr)-\(GSS_SUFFIX)".lowercased()
|
|
}
|
|
return c
|
|
}
|
|
|
|
func setNotifiable(_ bleInputProperty: String, for characteristic: String, of service: String) {
|
|
guard let characteristic = getCharacteristic(characteristic, of: service) else {
|
|
return
|
|
}
|
|
setNotifyValue(bleInputProperty != "disabled", for: characteristic)
|
|
}
|
|
}
|
|
|
|
extension FlutterStandardTypedData {
|
|
func toData() -> Data {
|
|
return Data(data)
|
|
}
|
|
}
|
|
|
|
// Future classes
|
|
class CharacteristicReadFuture {
|
|
let deviceId: String
|
|
let characteristicId: String
|
|
let serviceId: String?
|
|
let result: (Result<FlutterStandardTypedData, Error>) -> Void
|
|
|
|
init(deviceId: String, characteristicId: String, serviceId: String?, result: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
|
|
self.deviceId = deviceId
|
|
self.characteristicId = characteristicId
|
|
self.serviceId = serviceId
|
|
self.result = result
|
|
}
|
|
}
|
|
|
|
class CharacteristicWriteFuture {
|
|
let deviceId: String
|
|
let characteristicId: String
|
|
let serviceId: String?
|
|
let result: (Result<Void, Error>) -> Void
|
|
|
|
init(deviceId: String, characteristicId: String, serviceId: String?, result: @escaping (Result<Void, Error>) -> Void) {
|
|
self.deviceId = deviceId
|
|
self.characteristicId = characteristicId
|
|
self.serviceId = serviceId
|
|
self.result = result
|
|
}
|
|
}
|
|
|
|
class CharacteristicNotifyFuture {
|
|
let deviceId: String
|
|
let characteristicId: String
|
|
let serviceId: String?
|
|
let result: (Result<Void, Error>) -> Void
|
|
|
|
init(deviceId: String, characteristicId: String, serviceId: String?, result: @escaping (Result<Void, Error>) -> Void) {
|
|
self.deviceId = deviceId
|
|
self.characteristicId = characteristicId
|
|
self.serviceId = serviceId
|
|
self.result = result
|
|
}
|
|
}
|
|
|
|
class DiscoverServicesFuture {
|
|
let deviceId: String
|
|
let result: (Result<[UniversalBleService], Error>) -> Void
|
|
|
|
init(deviceId: String, result: @escaping (Result<[UniversalBleService], Error>) -> Void) {
|
|
self.deviceId = deviceId
|
|
self.result = result
|
|
}
|
|
}
|
|
|
|
class RssiReadFuture {
|
|
let deviceId: String
|
|
let result: (Result<Int64, Error>) -> Void
|
|
|
|
init(deviceId: String, result: @escaping (Result<Int64, Error>) -> Void) {
|
|
self.deviceId = deviceId
|
|
self.result = result
|
|
}
|
|
}
|