You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

130 lines
2.4 KiB

  1. package utils
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "github.com/AFASystems/presence/internal/pkg/model"
  6. )
  7. // ParseADFast efficiently parses Advertising Data structures
  8. // Returns slice of [startIndex, endIndex] pairs for each AD structure
  9. func ParseADFast(b []byte) [][2]int {
  10. var res [][2]int
  11. i := 0
  12. for i < len(b) {
  13. l := int(b[i])
  14. if l == 0 || i+1+l > len(b) {
  15. break
  16. }
  17. res = append(res, [2]int{i, i + 1 + l})
  18. i += 1 + l
  19. }
  20. return res
  21. }
  22. // RemoveFlagBytes removes Bluetooth advertising flag bytes if present
  23. // Some beacons include flag bytes as the first AD structure
  24. func RemoveFlagBytes(b []byte) []byte {
  25. if len(b) > 1 && b[1] == 0x01 {
  26. l := int(b[0])
  27. if 1+l <= len(b) {
  28. return b[1+l:]
  29. }
  30. }
  31. return b
  32. }
  33. // Generate event based on the Beacon type
  34. func LoopADStructures(b []byte, i [][2]int, id string) model.BeaconEvent {
  35. be := model.BeaconEvent{}
  36. for _, r := range i {
  37. ad := b[r[0]:r[1]]
  38. if !isValidADStructure(ad) {
  39. break
  40. }
  41. if checkIngics(ad) {
  42. be = parseIngicsState(ad)
  43. be.ID = id
  44. be.Name = id
  45. break
  46. } else if checkEddystoneTLM(ad) {
  47. be = parseEddystoneState(ad)
  48. be.ID = id
  49. be.Name = id
  50. break
  51. } else if checkMinewB7(ad) {
  52. fmt.Println("Minew B7 vendor format")
  53. break
  54. }
  55. }
  56. return be
  57. }
  58. // IsValidADStructure validates if an AD structure is well-formed
  59. func isValidADStructure(data []byte) bool {
  60. if len(data) < 2 {
  61. return false
  62. }
  63. length := int(data[0])
  64. return length > 0 && int(length)+1 <= len(data)
  65. }
  66. func checkIngics(ad []byte) bool {
  67. if len(ad) >= 6 &&
  68. ad[1] == 0xFF &&
  69. ad[2] == 0x59 &&
  70. ad[3] == 0x00 &&
  71. ad[4] == 0x80 &&
  72. ad[5] == 0xBC {
  73. return true
  74. }
  75. return false
  76. }
  77. func parseIngicsState(ad []byte) model.BeaconEvent {
  78. return model.BeaconEvent{
  79. Battery: uint32(binary.LittleEndian.Uint16(ad[6:8])),
  80. Event: int(ad[8]),
  81. Type: "Ingics",
  82. }
  83. }
  84. func checkEddystoneTLM(ad []byte) bool {
  85. if len(ad) >= 4 &&
  86. ad[1] == 0x16 &&
  87. ad[2] == 0xAA &&
  88. ad[3] == 0xFE &&
  89. ad[4] == 0x20 {
  90. return true
  91. }
  92. return false
  93. }
  94. func parseEddystoneState(ad []byte) model.BeaconEvent {
  95. return model.BeaconEvent{
  96. Battery: uint32(binary.BigEndian.Uint16(ad[6:8])),
  97. Type: "Eddystone",
  98. }
  99. }
  100. // I dont think this is always true, but for testing is ok
  101. func checkMinewB7(ad []byte) bool {
  102. if len(ad) >= 4 &&
  103. ad[1] == 0x16 &&
  104. ad[2] == 0xE1 &&
  105. ad[3] == 0xFF {
  106. return true
  107. }
  108. return false
  109. }