Class: RuuviDecoder::V8Data
- Defined in:
- lib/ruuvi_decoder/v8_data.rb
Overview
Decoder for V8 formatted encrypted data. (DRAFT specification subject to change!) docs.ruuvi.com/communication/bluetooth-advertisements/data-format-8-encrypted-environmental
Constant Summary collapse
- MANUFACTURER_ID =
0x0499- DATA_LENGTH_BYTES =
24- VERSION_TAG =
0x08- CRC8_TABLE =
[ # rubocop:disable Metrics/CollectionLiteralLength 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 ].freeze
Class Method Summary collapse
Instance Method Summary collapse
- #battery_v ⇒ Object
- #humidity_pct ⇒ Object
-
#initialize(raw_data, ruuvi_tag_id, password) ⇒ V8Data
constructor
A new instance of V8Data.
- #inspect ⇒ Object
- #mac_address ⇒ Object
- #movement_counter ⇒ Object
- #pressure_hpa ⇒ Object
- #sequence_number ⇒ Object
- #temperature_c ⇒ Object
- #tx_power_dbm ⇒ Object
Constructor Details
#initialize(raw_data, ruuvi_tag_id, password) ⇒ V8Data
Returns a new instance of V8Data.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 48 def initialize(raw_data, ruuvi_tag_id, password) super(raw_data) @ruuvi_tag_id = RuuviDecoder.normalize_raw_data(ruuvi_tag_id) @password = case password when String password.bytes when Enumerable password else raise 'password must be a String or Enumerable of bytes' end raise ArgumentError, 'ruuvi_tag_id must be 8 bytes' if @ruuvi_tag_id.size != 8 # fixed 16 byte key size is a quirk # https://github.com/ruuvi/ruuvi.firmware.c/blob/f5e159e82d150f76988a41acf39f9d2699debc85/src/app_dataformats.c#L34 raise ArgumentError, 'password must be at least 8 bytes' if @password.size != 16 end |
Class Method Details
.detect(raw_data) ⇒ Object
67 68 69 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 67 def self.detect(raw_data) raw_data.size == DATA_LENGTH_BYTES && raw_data[0] == VERSION_TAG end |
Instance Method Details
#battery_v ⇒ Object
96 97 98 99 100 101 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 96 def battery_v return @battery_v if defined?(@battery_v) @battery_v = decode_bitmasked_unsigned(raw_power_info >> 5, 0x07FF, offset: 1600, multiplier: 0.001, invalid: 2047) end |
#humidity_pct ⇒ Object
77 78 79 80 81 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 77 def humidity_pct return @humidity_pct if defined?(@humidity_pct) @humidity_pct = decode_16_bits_unsigned(decrypted_raw_data[2..3], multiplier: 0.0025, invalid: 65_535) end |
#inspect ⇒ Object
129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 129 def inspect <<~INSPECT.chomp <#{self.class.name} temperature: #{temperature_c} C, humidity: #{humidity_pct} %, pressure: #{pressure_hpa} hPa, battery: #{battery_v} V, tx_power: #{tx_power_dbm} dBm, movement_counter: #{movement_counter}, sequence_number: #{sequence_number}, mac_address: #{mac_address} > INSPECT end |
#mac_address ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 115 def mac_address return @mac_address if defined?(@mac_address) @mac_address = begin bytes = raw_data[18..24] if bytes.all?(0xff) nil else bytes.map { |b| format('%02x', b) }.join(':') end end end |
#movement_counter ⇒ Object
103 104 105 106 107 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 103 def movement_counter return @movement_counter if defined?(@movement_counter) @movement_counter = decode_16_bits_unsigned(decrypted_raw_data[8..9], invalid: 65_535) end |
#pressure_hpa ⇒ Object
83 84 85 86 87 88 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 83 def pressure_hpa return @pressure_hpa if defined?(@pressure_hpa) @pressure_hpa = decode_16_bits_unsigned(decrypted_raw_data[4..5], offset: 50_000, invalid: 65_535, multiplier: 0.01) end |
#sequence_number ⇒ Object
109 110 111 112 113 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 109 def sequence_number return @sequence_number if defined?(@sequence_number) @sequence_number = decode_16_bits_unsigned(decrypted_raw_data[10..11], invalid: 65_535) end |
#temperature_c ⇒ Object
71 72 73 74 75 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 71 def temperature_c return @temperature_c if defined?(@temperature_c) @temperature_c = decode_16_bits_signed(decrypted_raw_data[0..1], multiplier: 0.005, invalid: 0x8000) end |
#tx_power_dbm ⇒ Object
90 91 92 93 94 |
# File 'lib/ruuvi_decoder/v8_data.rb', line 90 def tx_power_dbm return @tx_power_dbm if defined?(@tx_power_dbm) @tx_power_dbm = decode_bitmasked_unsigned(raw_power_info, 0x001F, offset: -20, multiplier: 2, invalid: 31) end |