PeripheralManager+G5.swift 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. //
  2. // PeripheralManager+G5.swift
  3. // xDripG5
  4. //
  5. // Copyright © 2017 LoopKit Authors. All rights reserved.
  6. //
  7. import CoreBluetooth
  8. import os.log
  9. private let log = OSLog(category: "PeripheralManager+G5")
  10. extension PeripheralManager {
  11. private func getCharacteristicWithUUID(_ uuid: CGMServiceCharacteristicUUID) -> CBCharacteristic? {
  12. return peripheral.getCharacteristicWithUUID(uuid)
  13. }
  14. func setNotifyValue(_ enabled: Bool,
  15. for characteristicUUID: CGMServiceCharacteristicUUID,
  16. timeout: TimeInterval = 2) throws
  17. {
  18. guard let characteristic = getCharacteristicWithUUID(characteristicUUID) else {
  19. throw PeripheralManagerError.unknownCharacteristic
  20. }
  21. try setNotifyValue(enabled, for: characteristic, timeout: timeout)
  22. }
  23. func readMessage<R: TransmitterRxMessage>(
  24. for characteristicUUID: CGMServiceCharacteristicUUID,
  25. timeout: TimeInterval = 2
  26. ) throws -> R
  27. {
  28. guard let characteristic = getCharacteristicWithUUID(characteristicUUID) else {
  29. throw PeripheralManagerError.unknownCharacteristic
  30. }
  31. var capturedResponse: R?
  32. try runCommand(timeout: timeout) {
  33. addCondition(.valueUpdate(characteristic: characteristic, matching: { (data) -> Bool in
  34. guard let value = data else {
  35. return false
  36. }
  37. guard let response = R(data: value) else {
  38. // We don't recognize the contents. Keep listening.
  39. return false
  40. }
  41. capturedResponse = response
  42. return true
  43. }))
  44. peripheral.readValue(for: characteristic)
  45. }
  46. guard let response = capturedResponse else {
  47. // TODO: This is an "unknown value" issue, not a timeout
  48. if let value = characteristic.value {
  49. log.error("Unknown response data: %{public}@", value.hexadecimalString)
  50. }
  51. throw PeripheralManagerError.timeout
  52. }
  53. return response
  54. }
  55. /// - Throws: PeripheralManagerError
  56. func writeMessage<T: RespondableMessage>(_ message: T,
  57. for characteristicUUID: CGMServiceCharacteristicUUID,
  58. type: CBCharacteristicWriteType = .withResponse,
  59. timeout: TimeInterval = 2
  60. ) throws -> T.Response
  61. {
  62. guard let characteristic = getCharacteristicWithUUID(characteristicUUID) else {
  63. throw PeripheralManagerError.unknownCharacteristic
  64. }
  65. var capturedResponse: T.Response?
  66. try runCommand(timeout: timeout) {
  67. if case .withResponse = type {
  68. addCondition(.write(characteristic: characteristic))
  69. }
  70. if characteristic.isNotifying {
  71. addCondition(.valueUpdate(characteristic: characteristic, matching: { (data) -> Bool in
  72. guard let value = data else {
  73. return false
  74. }
  75. guard let response = T.Response(data: value) else {
  76. // We don't recognize the contents. Keep listening.
  77. return false
  78. }
  79. capturedResponse = response
  80. return true
  81. }))
  82. }
  83. peripheral.writeValue(message.data, for: characteristic, type: type)
  84. }
  85. guard let response = capturedResponse else {
  86. // TODO: This is an "unknown value" issue, not a timeout
  87. if let value = characteristic.value {
  88. log.error("Unknown response data: %{public}@", value.hexadecimalString)
  89. }
  90. throw PeripheralManagerError.timeout
  91. }
  92. return response
  93. }
  94. /// - Throws: PeripheralManagerError
  95. func writeMessage(_ message: TransmitterTxMessage,
  96. for characteristicUUID: CGMServiceCharacteristicUUID,
  97. type: CBCharacteristicWriteType = .withResponse,
  98. timeout: TimeInterval = 2) throws
  99. {
  100. guard let characteristic = getCharacteristicWithUUID(characteristicUUID) else {
  101. throw PeripheralManagerError.unknownCharacteristic
  102. }
  103. try writeValue(message.data, for: characteristic, type: type, timeout: timeout)
  104. }
  105. }
  106. fileprivate extension CBPeripheral {
  107. func getServiceWithUUID(_ uuid: TransmitterServiceUUID) -> CBService? {
  108. return services?.itemWithUUIDString(uuid.rawValue)
  109. }
  110. func getCharacteristicForServiceUUID(_ serviceUUID: TransmitterServiceUUID, withUUIDString UUIDString: String) -> CBCharacteristic? {
  111. guard let characteristics = getServiceWithUUID(serviceUUID)?.characteristics else {
  112. return nil
  113. }
  114. return characteristics.itemWithUUIDString(UUIDString)
  115. }
  116. func getCharacteristicWithUUID(_ uuid: CGMServiceCharacteristicUUID) -> CBCharacteristic? {
  117. return getCharacteristicForServiceUUID(.cgmService, withUUIDString: uuid.rawValue)
  118. }
  119. }