ConfigureAlertsCommand.swift 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. //
  2. // ConfigureAlertsCommand.swift
  3. // OmniKit
  4. //
  5. // Created by Pete Schwamb on 2/22/18.
  6. // Copyright © 2018 Pete Schwamb. All rights reserved.
  7. //
  8. import Foundation
  9. public struct ConfigureAlertsCommand : NonceResyncableMessageBlock {
  10. public let blockType: MessageBlockType = .configureAlerts
  11. public var nonce: UInt32
  12. let configurations: [AlertConfiguration]
  13. public var data: Data {
  14. var data = Data([
  15. blockType.rawValue,
  16. UInt8(4 + configurations.count * AlertConfiguration.length),
  17. ])
  18. data.appendBigEndian(nonce)
  19. for config in configurations {
  20. data.append(contentsOf: config.data)
  21. }
  22. return data
  23. }
  24. public init(encodedData: Data) throws {
  25. if encodedData.count < 10 {
  26. throw MessageBlockError.notEnoughData
  27. }
  28. self.nonce = encodedData[2...].toBigEndian(UInt32.self)
  29. let length = Int(encodedData[1])
  30. let numConfigs = (length - 4) / AlertConfiguration.length
  31. var configs = [AlertConfiguration]()
  32. for i in 0..<numConfigs {
  33. let offset = 2 + 4 + i * AlertConfiguration.length
  34. configs.append(try AlertConfiguration(encodedData: encodedData.subdata(in: offset..<(offset+6))))
  35. }
  36. self.configurations = configs
  37. }
  38. public init(nonce: UInt32, configurations: [AlertConfiguration]) {
  39. self.nonce = nonce
  40. self.configurations = configurations
  41. }
  42. }
  43. // MARK: - AlertConfiguration encoding/decoding
  44. extension AlertConfiguration {
  45. public init(encodedData: Data) throws {
  46. if encodedData.count < 6 {
  47. throw MessageBlockError.notEnoughData
  48. }
  49. let alertTypeBits = encodedData[0] >> 4
  50. guard let alertType = AlertSlot(rawValue: alertTypeBits) else {
  51. throw MessageError.unknownValue(value: alertTypeBits, typeDescription: "AlertType")
  52. }
  53. self.slot = alertType
  54. self.active = encodedData[0] & 0b1000 != 0
  55. self.autoOffModifier = encodedData[0] & 0b10 != 0
  56. self.duration = TimeInterval(minutes: Double((Int(encodedData[0] & 0b1) << 8) + Int(encodedData[1])))
  57. let yyyy = (Int(encodedData[2]) << 8) + (Int(encodedData[3])) & 0x3fff
  58. if encodedData[0] & 0b100 != 0 {
  59. let volume = Double(yyyy * 2) * Pod.pulseSize
  60. self.trigger = .unitsRemaining(volume)
  61. } else {
  62. self.trigger = .timeUntilAlert(TimeInterval(minutes: Double(yyyy)))
  63. }
  64. let beepRepeatBits = encodedData[4]
  65. guard let beepRepeat = BeepRepeat(rawValue: beepRepeatBits) else {
  66. throw MessageError.unknownValue(value: beepRepeatBits, typeDescription: "BeepRepeat")
  67. }
  68. self.beepRepeat = beepRepeat
  69. let beepTypeBits = encodedData[5]
  70. guard let beepType = BeepType(rawValue: beepTypeBits) else {
  71. throw MessageError.unknownValue(value: beepTypeBits, typeDescription: "BeepType")
  72. }
  73. self.beepType = beepType
  74. }
  75. public var data: Data {
  76. var firstByte = slot.rawValue << 4
  77. firstByte += active ? (1 << 3) : 0
  78. if case .unitsRemaining = trigger {
  79. firstByte += 1 << 2
  80. }
  81. if autoOffModifier {
  82. firstByte += 1 << 1
  83. }
  84. // High bit of duration
  85. firstByte += UInt8((Int(duration.minutes) >> 8) & 0x1)
  86. var data = Data([
  87. firstByte,
  88. UInt8(Int(duration.minutes) & 0xff)
  89. ])
  90. switch trigger {
  91. case .unitsRemaining(let volume):
  92. let ticks = UInt16(volume / Pod.pulseSize / 2)
  93. data.appendBigEndian(ticks)
  94. case .timeUntilAlert(let secondsUntilAlert):
  95. // round the time to alert to the nearest minute
  96. let minutes = UInt16((secondsUntilAlert + 30).minutes)
  97. data.appendBigEndian(minutes)
  98. }
  99. data.append(beepRepeat.rawValue)
  100. data.append(beepType.rawValue)
  101. return data
  102. }
  103. }
  104. extension ConfigureAlertsCommand: CustomDebugStringConvertible {
  105. public var debugDescription: String {
  106. return "ConfigureAlertsCommand(nonce:\(Data(bigEndian: nonce).hexadecimalString), configurations:\(configurations))"
  107. }
  108. }