sb-ble 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/.semver +5 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +130 -0
- data/LICENSE +21 -0
- data/README.org +62 -0
- data/Rakefile +51 -0
- data/bin/rble +0 -0
- data/lib/ble.rb +112 -0
- data/lib/ble/adapter.rb +162 -0
- data/lib/ble/agent.rb +46 -0
- data/lib/ble/char_desc.rb +63 -0
- data/lib/ble/char_registry.rb +142 -0
- data/lib/ble/characteristic.rb +85 -0
- data/lib/ble/db_eddystone.rb +50 -0
- data/lib/ble/db_nordic.rb +26 -0
- data/lib/ble/db_sig_characteristic.rb +142 -0
- data/lib/ble/db_sig_service.rb +59 -0
- data/lib/ble/device.rb +494 -0
- data/lib/ble/notifications.rb +44 -0
- data/lib/ble/service.rb +121 -0
- data/lib/ble/uuid.rb +25 -0
- data/lib/ble/version.rb +15 -0
- data/sb-ble.gemspec +112 -0
- data/spec/ruby-ble_spec.rb +7 -0
- data/spec/spec_helper.rb +29 -0
- metadata +296 -0
data/lib/ble/agent.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module BLE
|
2
|
+
class Agent < DBus::Object
|
3
|
+
@log = Logger.new($stdout)
|
4
|
+
# https://kernel.googlesource.com/pub/scm/bluetooth/bluez/+/refs/heads/master/doc/agent-api.txt
|
5
|
+
dbus_interface I_AGENT do
|
6
|
+
dbus_method :Release do
|
7
|
+
@log.debug "Release()"
|
8
|
+
exit false
|
9
|
+
end
|
10
|
+
|
11
|
+
dbus_method :RequestPinCode, "in device:o, out ret:s" do |device|
|
12
|
+
@log.debug{ "RequestPinCode(#{device})" }
|
13
|
+
["0000"]
|
14
|
+
end
|
15
|
+
|
16
|
+
dbus_method :RequestPasskey, "in device:o, out ret:u" do |device|
|
17
|
+
@log.debug{ "RequestPasskey(#{device})" }
|
18
|
+
raise DBus.error("org.bluez.Error.Rejected")
|
19
|
+
end
|
20
|
+
|
21
|
+
dbus_method :DisplayPasskey, "in device:o, in passkey:u, in entered:y" do |device, passkey, entered|
|
22
|
+
@log.debug{ "DisplayPasskey(#{device}, #{passkey}, #{entered})" }
|
23
|
+
raise DBus.error("org.bluez.Error.Rejected")
|
24
|
+
end
|
25
|
+
|
26
|
+
dbus_method :RequestConfirmation, "in device:o, in passkey:u" do |device, passkey|
|
27
|
+
@log.debug{ "RequestConfirmation(#{device}, #{passkey})" }
|
28
|
+
raise DBus.error("org.bluez.Error.Rejected")
|
29
|
+
end
|
30
|
+
|
31
|
+
dbus_method :Authorize, "in device:o, in uuid:s" do |device, uuid|
|
32
|
+
@log.debug{ "Authorize(#{device}, #{uuid})" }
|
33
|
+
end
|
34
|
+
|
35
|
+
dbus_method :ConfirmModeChange, "in mode:s" do |mode|
|
36
|
+
@log.debug{ "ConfirmModeChange(#{mode})" }
|
37
|
+
raise DBus.error("org.bluez.Error.Rejected")
|
38
|
+
end
|
39
|
+
|
40
|
+
dbus_method :Cancel do
|
41
|
+
@log.debug "Cancel()"
|
42
|
+
raise DBus.error("org.bluez.Error.Rejected")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Encapsulates Characteristic descriptors and offers easy
|
2
|
+
# access to characteristic related information.
|
3
|
+
#
|
4
|
+
module BLE
|
5
|
+
class CharDesc
|
6
|
+
attr_reader :desc
|
7
|
+
# @param desc [Hash] Descriptors.
|
8
|
+
def initialize(desc)
|
9
|
+
@desc= desc
|
10
|
+
end
|
11
|
+
def flags
|
12
|
+
@flags||= @desc[:flags]
|
13
|
+
end
|
14
|
+
def config
|
15
|
+
@config||= Characteristic[@desc[:uuid]]
|
16
|
+
end
|
17
|
+
|
18
|
+
def uuid
|
19
|
+
@desc[:uuid]
|
20
|
+
end
|
21
|
+
def flag?(flag_name)
|
22
|
+
flags.include?(flag_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Does outgoing values have processors configured?
|
26
|
+
# If yes, the value needs to be pre-processed before being send.
|
27
|
+
def write_processors?
|
28
|
+
verifier? or write_pre_processor?
|
29
|
+
end
|
30
|
+
# Does incoming values have processors configured?
|
31
|
+
# If yes, the value needs to be post-processed after being received.
|
32
|
+
def read_processors?
|
33
|
+
config && read_post_processor?
|
34
|
+
end
|
35
|
+
# It has been configured a verifier preprocessor to check
|
36
|
+
# outgoing data?
|
37
|
+
def verifier?
|
38
|
+
config[:vrfy]
|
39
|
+
end
|
40
|
+
def write_pre_processor?
|
41
|
+
config[:out]
|
42
|
+
end
|
43
|
+
def read_post_processor?
|
44
|
+
config[:in]
|
45
|
+
end
|
46
|
+
# Is the received value verified by the verifier?
|
47
|
+
def verifies?(val)
|
48
|
+
config[:vrfy].call(val)
|
49
|
+
end
|
50
|
+
def pre_process(val)
|
51
|
+
if verifier? && !verifies?(val)
|
52
|
+
raise ArgumentError,
|
53
|
+
"bad value for characteristic '#{uuid}'"
|
54
|
+
end
|
55
|
+
val = config[:out].call(val) if write_pre_processor?
|
56
|
+
end
|
57
|
+
|
58
|
+
def post_process(val)
|
59
|
+
val = config[:in].call(val) if read_post_processor?
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# To add a new characteristic description:
|
2
|
+
# BLE::Characteristic.add 'c4935338-4307-47cf-ae1f-feac9e2b3ae7',
|
3
|
+
# name: 'Controlled mind',
|
4
|
+
# type: 'net.cortex-minus.characteristic.controlled_mind'
|
5
|
+
# vrfy: ->(x) { x >= 0 },
|
6
|
+
# in: ->(s) { s.unpack('s<').first },
|
7
|
+
# out: ->(v) { [ v ].pack('s<') }
|
8
|
+
#
|
9
|
+
# Returned characteristic description will be a hash:
|
10
|
+
# {
|
11
|
+
# name: "Bluetooth characteristic name",
|
12
|
+
# type: "org.bluetooth.characteristic.name",
|
13
|
+
# uuid: "128bit-uuid-string",
|
14
|
+
# vrfy: ->(x) { verify_value(x) },
|
15
|
+
# in: ->(s) { s.unpack(....) ... },
|
16
|
+
# out: ->(v) { [ v ].pack(....) ... }
|
17
|
+
# }
|
18
|
+
#
|
19
|
+
module BLE
|
20
|
+
module CharRegistry
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
DB_UUID = {}
|
25
|
+
DB_TYPE = {}
|
26
|
+
DB_NICKNAME = {}
|
27
|
+
|
28
|
+
public
|
29
|
+
def self.included(klass)
|
30
|
+
class << klass
|
31
|
+
# Get characteristic description from nickname.
|
32
|
+
#
|
33
|
+
# @param id [Symbol] nickname
|
34
|
+
# @return [CharDesc] characteristic description
|
35
|
+
def by_nickname(id)
|
36
|
+
DB_NICKNAME[id]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get characteristic description from uuid.
|
40
|
+
#
|
41
|
+
# @param id [String] uuid
|
42
|
+
# @return [CharDesc] characteristic description
|
43
|
+
def by_uuid(id)
|
44
|
+
DB_UUID[id]
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get characteristic description from type
|
48
|
+
#
|
49
|
+
# @param id [Strig] type
|
50
|
+
# @return [CharDesc] characteristic description
|
51
|
+
def by_type(id)
|
52
|
+
DB_TYPE[id]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Get a characteristic description from it's id
|
56
|
+
# @param id [Symbol,String]
|
57
|
+
# @return [CharDesc]
|
58
|
+
def [](id)
|
59
|
+
case id
|
60
|
+
when Symbol then DB_NICKNAME[id]
|
61
|
+
when UUID::REGEX then DB_UUID[id]
|
62
|
+
when String then DB_TYPE[id]
|
63
|
+
when Integer then DB_UUID[BLE::UUID(id)]
|
64
|
+
else raise ArgumentError, "invalid type for characteristic id"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
# Add a characteristic description.
|
70
|
+
# @example Add a characteristic description with a 16-bit uuid
|
71
|
+
# module Characteristic
|
72
|
+
# add 0x2A6E,
|
73
|
+
# name: 'Temperature',
|
74
|
+
# type: 'org.bluetooth.characteristic.temperature',
|
75
|
+
# vrfy: ->(x) { (0..100).include?(x) },
|
76
|
+
# in: ->(s) { s.unpack('s<').first.to_f / 100 },
|
77
|
+
# out: ->(v) { [ v*100 ].pack('s<') }
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# @example Add a characteristic description with a 128-bit uuid
|
81
|
+
# module Characteristic
|
82
|
+
# add 'c4935338-4307-47cf-ae1f-feac9e2b3ae7',
|
83
|
+
# name: 'Controlled mind',
|
84
|
+
# type: 'net.cortex-minus.characteristic.controlled_mind',
|
85
|
+
# vrfy: ->(x) { x >= 0 },
|
86
|
+
# in: ->(s) { s.unpack('s<').first },
|
87
|
+
# out: ->(v) { [ v ].pack('s<') }
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# @param uuid [Integer, String] 16-bit, 32-bit or 128-bit uuid
|
91
|
+
# @param name [String]
|
92
|
+
# @option opts :type [String] type
|
93
|
+
# @option opts :nick [Symbol] nickname
|
94
|
+
# @option opts :in [Proc] convert to ruby
|
95
|
+
# @option opts :out [Proc] convert to bluetooth data
|
96
|
+
# @option opts :vry [Proc] verify
|
97
|
+
# @return [CharDesc] characteristic description
|
98
|
+
def add(uuid, name:, **opts)
|
99
|
+
uuid = BLE::UUID(uuid)
|
100
|
+
type = opts.delete :type
|
101
|
+
nick = opts.delete :nick
|
102
|
+
_in = opts.delete :in
|
103
|
+
_out = opts.delete :out
|
104
|
+
vrfy = opts.delete :vrfy
|
105
|
+
if opts.first
|
106
|
+
raise ArgumentError, "unknown keyword: #{opts.first[0]}"
|
107
|
+
end
|
108
|
+
|
109
|
+
char_config= DB_UUID[uuid] = {
|
110
|
+
uuid: uuid,
|
111
|
+
name: name,
|
112
|
+
in: _in,
|
113
|
+
out: _out,
|
114
|
+
vrfy: vrfy
|
115
|
+
}
|
116
|
+
|
117
|
+
# Add type if specified
|
118
|
+
if type
|
119
|
+
type = type.downcase
|
120
|
+
char_config.merge!(type: type)
|
121
|
+
DB_TYPE[type] = char_config
|
122
|
+
end
|
123
|
+
|
124
|
+
# Add nickname if specified or can be derived from type
|
125
|
+
if nick.nil? && type && type =~ /\.(?<key>[^.]+)$/
|
126
|
+
nick = $1.to_sym if type.start_with? 'org.bluetooth.characteristic'
|
127
|
+
end
|
128
|
+
if nick
|
129
|
+
if DB_NICKNAME.include?(nick)
|
130
|
+
raise ArgumentError,
|
131
|
+
"nickname '#{nick}' already registered (uuid: #{uuid})"
|
132
|
+
end
|
133
|
+
DB_NICKNAME[nick] = char_config
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
module BLE
|
3
|
+
# Build information about {https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicsHome.aspx Bluetooth Characteristics}
|
4
|
+
#
|
5
|
+
class Characteristic
|
6
|
+
include CharRegistry
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# Notify of characteristic not found
|
10
|
+
class NotFound < BLE::NotFound
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(desc)
|
14
|
+
@dbus_obj= desc[:obj]
|
15
|
+
@desc= CharDesc.new(desc)
|
16
|
+
end
|
17
|
+
|
18
|
+
def_delegators :@desc, :flag?, :uuid
|
19
|
+
|
20
|
+
private
|
21
|
+
FLAGS = [ 'broadcast',
|
22
|
+
'read',
|
23
|
+
'write-without-response',
|
24
|
+
'write',
|
25
|
+
'notify',
|
26
|
+
'indicate',
|
27
|
+
'authenticated-signed-writes',
|
28
|
+
'reliable-write',
|
29
|
+
'writable-auxiliaries',
|
30
|
+
'encrypt-read',
|
31
|
+
'encrypt-write',
|
32
|
+
'encrypt-authenticated-read',
|
33
|
+
'encrypt-authenticated-write' ]
|
34
|
+
|
35
|
+
|
36
|
+
#++++++++++++++++++++++++++++
|
37
|
+
public
|
38
|
+
#++++++++++++++++++++++++++++
|
39
|
+
|
40
|
+
def write(val, raw: false)
|
41
|
+
val= _serialize_value(val, raw: raw)
|
42
|
+
@dbus_obj[I_GATT_CHARACTERISTIC].WriteValue(val)
|
43
|
+
end
|
44
|
+
|
45
|
+
def read(raw: false)
|
46
|
+
val= @dbus_obj[I_GATT_CHARACTERISTIC].ReadValue().first
|
47
|
+
val= _deserialize_value(val, raw: raw)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Register to this characteristic for notifications when
|
51
|
+
# its value changes.
|
52
|
+
def notify!
|
53
|
+
@dbus_obj[I_GATT_CHARACTERISTIC].StartNotify
|
54
|
+
end
|
55
|
+
|
56
|
+
def on_change(raw: false, &callback)
|
57
|
+
@dbus_obj[I_PROPERTIES].on_signal('PropertiesChanged') do |intf, props|
|
58
|
+
case intf
|
59
|
+
when I_GATT_CHARACTERISTIC
|
60
|
+
val= _deserialize_value(props['Value'], raw: raw)
|
61
|
+
callback.call(val)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
#----------------------------
|
66
|
+
private
|
67
|
+
#----------------------------
|
68
|
+
|
69
|
+
# Convert Arrays of bytes returned by DBus to Strings of bytes.
|
70
|
+
def _serialize_value(val, raw: false)
|
71
|
+
if !raw && @desc.write_processors?
|
72
|
+
val= @desc.pre_process(val)
|
73
|
+
end
|
74
|
+
val.unpack('C*')
|
75
|
+
end
|
76
|
+
|
77
|
+
# Convert Arrays of bytes returned by DBus to Strings of bytes.
|
78
|
+
def _deserialize_value(val, raw: false)
|
79
|
+
val = val.pack('C*')
|
80
|
+
val = @desc.post_process(val) if !raw && @desc.read_processors?
|
81
|
+
val
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module BLE
|
2
|
+
module Service
|
3
|
+
add 'ee0c2080-8786-40ba-ab96-99b91ac981d8',
|
4
|
+
name: 'Eddystone-URL Beacon Configuration'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module BLE
|
9
|
+
class Characteristic
|
10
|
+
add 'ee0c2081-8786-40ba-ab96-99b91ac981d8',
|
11
|
+
name: 'Eddystone Lock State',
|
12
|
+
nick: :esurl_lockstate
|
13
|
+
|
14
|
+
add 'ee0c2082-8786-40ba-ab96-99b91ac981d8',
|
15
|
+
name: 'Eddystone Lock',
|
16
|
+
nick: :esurl_lock
|
17
|
+
|
18
|
+
add 'ee0c2083-8786-40ba-ab96-99b91ac981d8',
|
19
|
+
name: 'Eddystone Unlock',
|
20
|
+
nick: :esurl_unlock
|
21
|
+
|
22
|
+
add 'ee0c2084-8786-40ba-ab96-99b91ac981d8',
|
23
|
+
name: 'Eddystone URL Data',
|
24
|
+
nick: :esurl_data
|
25
|
+
|
26
|
+
add 'ee0c2085-8786-40ba-ab96-99b91ac981d8',
|
27
|
+
name: 'Eddystone Flags',
|
28
|
+
nick: :esurl_flags
|
29
|
+
|
30
|
+
add 'ee0c2086-8786-40ba-ab96-99b91ac981d8',
|
31
|
+
name: 'Eddystone Adv Tx Power Levels',
|
32
|
+
nick: :esurl_adv_txpower_levels
|
33
|
+
|
34
|
+
add 'ee0c2087-8786-40ba-ab96-99b91ac981d8',
|
35
|
+
name: 'Eddystone TX power mode',
|
36
|
+
nick: :esurl_txpower_mode
|
37
|
+
|
38
|
+
add 'ee0c2088-8786-40ba-ab96-99b91ac981d8',
|
39
|
+
name: 'Eddystone Beacon period',
|
40
|
+
nick: :esurl_beacon_period
|
41
|
+
|
42
|
+
add 'ee0c2089-8786-40ba-ab96-99b91ac981d8',
|
43
|
+
name: 'Eddystone Reset',
|
44
|
+
nick: :esurl_reset
|
45
|
+
|
46
|
+
add 'ee0c208a-8786-40ba-ab96-99b91ac981d8',
|
47
|
+
name: 'Eddystone Radio Tx Power Levels',
|
48
|
+
nick: :esurl_radio_txpower_levels
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module BLE
|
2
|
+
module Service
|
3
|
+
add '00001530-1212-efde-1523-785feabcd123',
|
4
|
+
name: 'Nordic Device Firmware Update Service'
|
5
|
+
|
6
|
+
add '6e400001-b5a3-f393-e0a9-e50e24dcca9e',
|
7
|
+
name: 'Nordic UART Service'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
module BLE
|
13
|
+
class Characteristic
|
14
|
+
add '00001531-1212-efde-1523-785feabcd123', # WriteWithoutResponse
|
15
|
+
name: 'DFU Packet'
|
16
|
+
|
17
|
+
add '00001532-1212-efde-1523-785feabcd123', # Write,Notify
|
18
|
+
name: 'DFU Control Point'
|
19
|
+
|
20
|
+
add '6e400002-b5a3-f393-e0a9-e50e24dcca9e',
|
21
|
+
name: 'UART TX'
|
22
|
+
|
23
|
+
add '6e400003-b5a3-f393-e0a9-e50e24dcca9e',
|
24
|
+
name: 'UART RX'
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module BLE
|
2
|
+
class Characteristic
|
3
|
+
# C | Integer | 8-bit unsigned (unsigned char)
|
4
|
+
# S | Integer | 16-bit unsigned, native endian (uint16_t)
|
5
|
+
# L | Integer | 32-bit unsigned, native endian (uint32_t)
|
6
|
+
# Q | Integer | 64-bit unsigned, native endian (uint64_t)
|
7
|
+
# | |
|
8
|
+
# c | Integer | 8-bit signed (signed char)
|
9
|
+
# s | Integer | 16-bit signed, native endian (int16_t)
|
10
|
+
# l | Integer | 32-bit signed, native endian (int32_t)
|
11
|
+
# q | Integer | 64-bit signed, native endian (int64_t)
|
12
|
+
#
|
13
|
+
# < | Little endian
|
14
|
+
# > | Big endian
|
15
|
+
|
16
|
+
add 0x2A07,
|
17
|
+
name: 'Tx Power Level',
|
18
|
+
type: 'org.bluetooth.characteristic.tx_power_level',
|
19
|
+
vrfy: ->(x) { (-100..20).include?(c) },
|
20
|
+
in: ->(s) { s.unpack('c').first },
|
21
|
+
out: ->(v) { [ v ].pack('c') }
|
22
|
+
|
23
|
+
add 0x2A23,
|
24
|
+
name: 'System ID',
|
25
|
+
type: 'org.bluetooth.characteristic.system_id',
|
26
|
+
vrfy: ->(x) { (0..1099511627775).include?(x[0]) &&
|
27
|
+
(0..16777215 ).include?(x[1])},
|
28
|
+
in: ->(s) { raise NotYetImplemented },
|
29
|
+
out: ->(v) { raise NotYetImplemented }
|
30
|
+
|
31
|
+
add 0x2A24,
|
32
|
+
name: 'Model Number String',
|
33
|
+
type: 'org.bluetooth.characteristic.model_number_string',
|
34
|
+
in: ->(s) { s.force_encoding('UTF-8') },
|
35
|
+
out: ->(v) { v.encode('UTF-8') }
|
36
|
+
|
37
|
+
add 0x2A25,
|
38
|
+
name: 'Serial Number String',
|
39
|
+
type: 'org.bluetooth.characteristic.serial_number_string',
|
40
|
+
in: ->(s) { s.force_encoding('UTF-8') },
|
41
|
+
out: ->(v) { v.encode('UTF-8') }
|
42
|
+
|
43
|
+
add 0x2A26,
|
44
|
+
name: 'Firmware Revision String',
|
45
|
+
type: 'org.bluetooth.characteristic.firmware_revision_string',
|
46
|
+
in: ->(s) { s.force_encoding('UTF-8') },
|
47
|
+
out: ->(v) { v.encode('UTF-8') }
|
48
|
+
|
49
|
+
add 0x2A27,
|
50
|
+
name: 'Hardware Revision String',
|
51
|
+
type: 'org.bluetooth.characteristic.hardware_revision_string',
|
52
|
+
in: ->(s) { s.force_encoding('UTF-8') },
|
53
|
+
out: ->(v) { v.encode('UTF-8') }
|
54
|
+
|
55
|
+
add 0x2A28,
|
56
|
+
name: 'Software Revision String',
|
57
|
+
type: 'org.bluetooth.characteristic.software_revision_string',
|
58
|
+
in: ->(s) { s.force_encoding('UTF-8') },
|
59
|
+
out: ->(v) { v.encode('UTF-8') }
|
60
|
+
|
61
|
+
add 0x2A29,
|
62
|
+
name: 'Manufacturer Name String',
|
63
|
+
type: 'org.bluetooth.characteristic.manufacturer_name_string',
|
64
|
+
in: ->(s) { s.force_encoding('UTF-8') },
|
65
|
+
out: ->(v) { v.encode('UTF-8') }
|
66
|
+
|
67
|
+
add 0x2A2A,
|
68
|
+
name: 'IEEE 11073-20601 Regulatory Certification Data List',
|
69
|
+
type: 'org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list',
|
70
|
+
in: ->(s) { raise NotYetImplemented },
|
71
|
+
out: ->(v) { raise NotYetImplemented }
|
72
|
+
|
73
|
+
add 0x2A50,
|
74
|
+
name: 'PnP ID',
|
75
|
+
type: 'org.bluetooth.characteristic.pnp_id',
|
76
|
+
in: ->(s) { vendor_src, vendor_id,
|
77
|
+
product_id, product_version = s.unpack('CS<S<S<')
|
78
|
+
vendor_src = case vendor_src
|
79
|
+
when 1 then :bluetooth_sig
|
80
|
+
when 2 then :usb_forum
|
81
|
+
else :reserved
|
82
|
+
end
|
83
|
+
[ vendor_src, vendor_id,
|
84
|
+
product_id, product_version ] },
|
85
|
+
out: ->(v) { raise NotYetImplemented }
|
86
|
+
|
87
|
+
|
88
|
+
add 0x2A6E,
|
89
|
+
name: 'Temperature',
|
90
|
+
type: 'org.bluetooth.characteristic.temperature',
|
91
|
+
vrfy: ->(x) { (0..100).include?(x) },
|
92
|
+
in: ->(s) { s.unpack('s<').first.to_f / 100 },
|
93
|
+
out: ->(v) { [ v*100 ].pack('s<') }
|
94
|
+
|
95
|
+
add 0x2A76,
|
96
|
+
name: 'UV Index',
|
97
|
+
type: 'org.bluetooth.characteristic.uv_index',
|
98
|
+
in: ->(s) { s.unpack('C').first },
|
99
|
+
out: ->(v) { [ v ].pack('C') }
|
100
|
+
|
101
|
+
add 0x2A77,
|
102
|
+
name: 'Irradiance',
|
103
|
+
type: 'org.bluetooth.characteristic.irradiance',
|
104
|
+
in: ->(s) { s.unpack('S<').first.to_f / 10 },
|
105
|
+
out: ->(v) { [ v*10 ].pack('S<') }
|
106
|
+
|
107
|
+
add 0x2A7A,
|
108
|
+
name: 'Heat Index',
|
109
|
+
type: 'org.bluetooth.characteristic.heat_index',
|
110
|
+
in: ->(s) { s.unpack('c').first },
|
111
|
+
out: ->(v) { [ v ].pack('c') }
|
112
|
+
|
113
|
+
add 0x2A19,
|
114
|
+
name: 'Battery Level',
|
115
|
+
type: 'org.bluetooth.characteristic.battery_level',
|
116
|
+
vrfy: ->(x) { (0..100).include?(x) },
|
117
|
+
in: ->(s) { s.unpack('C').first },
|
118
|
+
out: ->(v) { [ v ].pack('C') }
|
119
|
+
|
120
|
+
add 0x2A6F,
|
121
|
+
name: 'Humidity',
|
122
|
+
type: 'org.bluetooth.characteristic.humidity',
|
123
|
+
vrfy: ->(x) { (0..100).include?(x) },
|
124
|
+
in: ->(s) { s.unpack('S<').first.to_f / 100 },
|
125
|
+
out: ->(v) { [ v*100 ].pack('S<') }
|
126
|
+
|
127
|
+
add 0x2A6D,
|
128
|
+
name: 'Pressure',
|
129
|
+
type: 'org.bluetooth.characteristic.pressure',
|
130
|
+
vrfy: ->(x) { x >= 0 },
|
131
|
+
in: ->(s) { s.unpack('L<').first.to_f / 10 },
|
132
|
+
out: ->(v) { [ v*10 ].pack('L<') }
|
133
|
+
|
134
|
+
add 0x2AB3,
|
135
|
+
name: 'Altitude',
|
136
|
+
type: 'org.bluetooth.characteristic.altitude',
|
137
|
+
vrfy: ->(x) { x >= 0 },
|
138
|
+
in: ->(s) { s.unpack('S<').first },
|
139
|
+
out: ->(v) { [ v ].pack('S<') }
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|