ble 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/ble.rb +12 -15
- data/lib/ble/characteristic.rb +32 -38
- 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_service.rb → db_sig_service.rb} +0 -1
- data/lib/ble/device.rb +43 -35
- data/lib/ble/service.rb +26 -33
- data/lib/ble/uuid.rb +25 -0
- data/lib/ble/version.rb +7 -2
- metadata +7 -5
- data/lib/ble/db_characteristic.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0d63de7a39417e54dd34e2fc70a326061d36cc6
|
4
|
+
data.tar.gz: ac360bfb08b81186e16840c3c7dac76f09788d6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4a84b8634507629242d89e81542d03e5036ca73faba52e1a4db5d128ea8140e8ac46925de9b45948e12fa6de894865a9e2835500b5ef28265aa2c597bae989a
|
7
|
+
data.tar.gz: ff96bdeadd3582a120bcd68b8b48864a736d611653c8754508daf08fe68f7fa5808cc7395397277c64be0c434a3a14f642f4bb2d445968f98927007e997caf4b
|
data/lib/ble.rb
CHANGED
@@ -61,9 +61,11 @@ module BLE
|
|
61
61
|
# on this device
|
62
62
|
class NotFound < Error ; end
|
63
63
|
class AccessUnavailable < Error ; end
|
64
|
-
|
64
|
+
class NotSupported < Error ; end
|
65
65
|
|
66
|
-
|
66
|
+
|
67
|
+
# Base UUID for GATT services defined with 16bit or 32bit UUID
|
68
|
+
GATT_BASE_UUID="00000000-0000-1000-8000-00805f9b34fb"
|
67
69
|
|
68
70
|
#"DisplayOnly", "DisplayYesNo", "KeyboardOnly",
|
69
71
|
# "NoInputNoOutput" and "KeyboardDisplay" which
|
@@ -73,9 +75,9 @@ module BLE
|
|
73
75
|
raise NotYetImplemented
|
74
76
|
bus = DBus.session_bus
|
75
77
|
service = bus.request_service("org.ruby.service")
|
76
|
-
|
78
|
+
|
77
79
|
service.export(BLE::Agent.new(agent_path))
|
78
|
-
|
80
|
+
|
79
81
|
o_bluez = BLUEZ.object('/org/bluez')
|
80
82
|
o_bluez.introspect
|
81
83
|
o_bluez[I_AGENT_MANAGER].RegisterAgent(agent_path, "NoInputNoOutput")
|
@@ -83,30 +85,25 @@ module BLE
|
|
83
85
|
|
84
86
|
|
85
87
|
|
86
|
-
def self.UUID(val)
|
87
|
-
val.downcase
|
88
|
-
end
|
89
|
-
|
90
|
-
class UUID
|
91
|
-
REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
92
|
-
end
|
93
|
-
|
94
88
|
|
95
89
|
# Check if Bluetooth underlying API is accessible
|
96
90
|
def self.ok?
|
97
91
|
BLUEZ.exists?
|
98
92
|
end
|
99
|
-
|
93
|
+
|
100
94
|
end
|
101
95
|
|
102
96
|
require_relative 'ble/version'
|
97
|
+
require_relative 'ble/uuid'
|
103
98
|
require_relative 'ble/adapter'
|
104
99
|
require_relative 'ble/device'
|
105
100
|
require_relative 'ble/service'
|
106
101
|
require_relative 'ble/characteristic'
|
107
102
|
require_relative 'ble/agent'
|
108
103
|
|
109
|
-
require_relative 'ble/
|
110
|
-
require_relative 'ble/
|
104
|
+
require_relative 'ble/db_sig_service'
|
105
|
+
require_relative 'ble/db_sig_characteristic'
|
106
|
+
require_relative 'ble/db_eddystone'
|
107
|
+
require_relative 'ble/db_nordic'
|
111
108
|
|
112
109
|
|
data/lib/ble/characteristic.rb
CHANGED
@@ -76,6 +76,7 @@ module Characteristic
|
|
76
76
|
when Symbol then DB_NICKNAME[id]
|
77
77
|
when UUID::REGEX then DB_UUID[id]
|
78
78
|
when String then DB_TYPE[id]
|
79
|
+
when Integer then DB_UUID[BLE::UUID(id)]
|
79
80
|
else raise ArgumentError, "invalid type for characteristic id"
|
80
81
|
end
|
81
82
|
end
|
@@ -104,56 +105,49 @@ module Characteristic
|
|
104
105
|
#
|
105
106
|
# @param uuid [Integer, String] 16-bit, 32-bit or 128-bit uuid
|
106
107
|
# @param name [String]
|
107
|
-
# @
|
108
|
-
# @option opts :
|
109
|
-
# @option opts :
|
110
|
-
# @option opts :
|
108
|
+
# @option opts :type [String] type
|
109
|
+
# @option opts :nick [Symbol] nickname
|
110
|
+
# @option opts :in [Proc] convert to ruby
|
111
|
+
# @option opts :out [Proc] convert to bluetooth data
|
112
|
+
# @option opts :vry [Proc] verify
|
111
113
|
# @return [Hash] characteristic description
|
112
|
-
def self.add(uuid, name:,
|
113
|
-
|
114
|
-
|
115
|
-
|
114
|
+
def self.add(uuid, name:, **opts)
|
115
|
+
uuid = BLE::UUID(uuid)
|
116
|
+
type = opts.delete :type
|
117
|
+
nick = opts.delete :nick
|
118
|
+
_in = opts.delete :in
|
119
|
+
_out = opts.delete :out
|
120
|
+
vrfy = opts.delete :vrfy
|
116
121
|
if opts.first
|
117
122
|
raise ArgumentError, "unknown keyword: #{opts.first[0]}"
|
118
123
|
end
|
119
124
|
|
120
|
-
uuid = case uuid
|
121
|
-
when Integer
|
122
|
-
if !(0..4294967296).include?(uuid)
|
123
|
-
raise ArgumentError, "not a 16bit or 32bit uuid"
|
124
|
-
end
|
125
|
-
([uuid].pack("L>").unpack('H*').first +
|
126
|
-
"-0000-1000-8000-00805F9B34FB")
|
127
|
-
|
128
|
-
when String
|
129
|
-
if uuid !~ UUID::REGEX
|
130
|
-
raise ArgumentError, "not a 128bit uuid string"
|
131
|
-
end
|
132
|
-
uuid
|
133
|
-
else raise ArgumentError, "invalid uuid type"
|
134
|
-
end
|
135
|
-
uuid = uuid.downcase
|
136
|
-
type = type.downcase
|
137
125
|
|
138
|
-
|
139
|
-
name: name,
|
140
|
-
type: type,
|
126
|
+
desc = DB_UUID[uuid] = {
|
141
127
|
uuid: uuid,
|
142
|
-
|
143
|
-
|
128
|
+
name: name,
|
129
|
+
in: _in,
|
130
|
+
out: _out,
|
144
131
|
vrfy: vrfy
|
145
132
|
}
|
146
133
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
134
|
+
# Add type if specified
|
135
|
+
if type
|
136
|
+
type = type.downcase
|
137
|
+
desc.merge!(type: type)
|
138
|
+
DB_TYPE[type] = desc
|
139
|
+
end
|
140
|
+
|
141
|
+
# Add nickname if specified or can be derived from type
|
142
|
+
if nick.nil? && type && type =~ /\.(?<key>[^.]+)$/
|
143
|
+
nick = $1.to_sym if type.start_with? 'org.bluetooth.characteristic'
|
144
|
+
end
|
145
|
+
if nick
|
146
|
+
if DB_NICKNAME.include?(nick)
|
153
147
|
raise ArgumentError,
|
154
|
-
"nickname '#{
|
148
|
+
"nickname '#{nick}' already registered (uuid: #{uuid})"
|
155
149
|
end
|
156
|
-
DB_NICKNAME[
|
150
|
+
DB_NICKNAME[nick] = desc
|
157
151
|
end
|
158
152
|
end
|
159
153
|
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
|
+
module 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
|
+
module 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
|
+
module 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
|
data/lib/ble/device.rb
CHANGED
@@ -19,19 +19,19 @@ class Device
|
|
19
19
|
@adapter, @dev = adapter, dev
|
20
20
|
@auto_refresh = auto_refresh
|
21
21
|
@services = {}
|
22
|
-
|
22
|
+
|
23
23
|
@n_adapter = adapter
|
24
24
|
@p_adapter = "/org/bluez/#{@n_adapter}"
|
25
25
|
@o_adapter = BLUEZ.object(@p_adapter)
|
26
26
|
@o_adapter.introspect
|
27
|
-
|
27
|
+
|
28
28
|
@n_dev = 'dev_' + dev.tr(':', '_')
|
29
29
|
@p_dev = "/org/bluez/#{@n_adapter}/#{@n_dev}"
|
30
30
|
@o_dev = BLUEZ.object(@p_dev)
|
31
31
|
@o_dev.introspect
|
32
|
-
|
32
|
+
|
33
33
|
self.refresh if @auto_refresh
|
34
|
-
|
34
|
+
|
35
35
|
@o_dev[I_PROPERTIES]
|
36
36
|
.on_signal('PropertiesChanged') do |intf, props|
|
37
37
|
case intf
|
@@ -43,7 +43,7 @@ class Device
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
# This removes the remote device object.
|
48
48
|
# It will remove also the pairing information.
|
49
49
|
# @return [Boolean]
|
@@ -58,8 +58,8 @@ class Device
|
|
58
58
|
else raise ScriptError
|
59
59
|
end
|
60
60
|
end
|
61
|
-
|
62
|
-
|
61
|
+
|
62
|
+
|
63
63
|
# This method will connect to the remote device,
|
64
64
|
# initiate pairing and then retrieve all SDP records
|
65
65
|
# (or GATT primary services).
|
@@ -71,6 +71,7 @@ class Device
|
|
71
71
|
# all other cases the default agent will handle this just fine.
|
72
72
|
# In case there is no application agent and also
|
73
73
|
# no default agent present, this method will fail.
|
74
|
+
#
|
74
75
|
# @return [Boolean]
|
75
76
|
def pair
|
76
77
|
@o_dev[I_DEVICE].Pair
|
@@ -88,7 +89,7 @@ class Device
|
|
88
89
|
else raise ScriptError
|
89
90
|
end
|
90
91
|
end
|
91
|
-
|
92
|
+
|
92
93
|
# This method can be used to cancel a pairing
|
93
94
|
# operation initiated by the Pair method.
|
94
95
|
# @return [Boolean]
|
@@ -102,7 +103,7 @@ class Device
|
|
102
103
|
else raise ScriptError
|
103
104
|
end
|
104
105
|
end
|
105
|
-
|
106
|
+
|
106
107
|
# This connect to the specified profile UUID or to any (:all)
|
107
108
|
# profiles the remote device supports that can be connected to
|
108
109
|
# and have been flagged as auto-connectable on our side. If
|
@@ -133,7 +134,7 @@ class Device
|
|
133
134
|
else raise ScriptError
|
134
135
|
end
|
135
136
|
end
|
136
|
-
|
137
|
+
|
137
138
|
# This method gracefully disconnects :all connected profiles
|
138
139
|
# and then terminates low-level ACL connection.
|
139
140
|
# ACL connection will be terminated even if some profiles
|
@@ -161,7 +162,7 @@ class Device
|
|
161
162
|
when E_INVALID_ARGUMENTS
|
162
163
|
raise ArgumentError, "unsupported profile (#{profile})"
|
163
164
|
when E_NOT_SUPPORTED
|
164
|
-
raise
|
165
|
+
raise NotSupported
|
165
166
|
when E_NOT_CONNECTED
|
166
167
|
true
|
167
168
|
when E_UNKNOWN_OBJECT
|
@@ -169,7 +170,7 @@ class Device
|
|
169
170
|
else raise ScriptError
|
170
171
|
end
|
171
172
|
end
|
172
|
-
|
173
|
+
|
173
174
|
# Indicates if the remote device is paired
|
174
175
|
def is_paired?
|
175
176
|
@o_dev[I_DEVICE]['Paired']
|
@@ -180,7 +181,7 @@ class Device
|
|
180
181
|
else raise ScriptError
|
181
182
|
end
|
182
183
|
end
|
183
|
-
|
184
|
+
|
184
185
|
# Indicates if the remote device is currently connected.
|
185
186
|
def is_connected?
|
186
187
|
@o_dev[I_DEVICE]['Connected']
|
@@ -191,7 +192,7 @@ class Device
|
|
191
192
|
else raise ScriptError
|
192
193
|
end
|
193
194
|
end
|
194
|
-
|
195
|
+
|
195
196
|
# List of available services as UUID.
|
196
197
|
#
|
197
198
|
# @raise [NotConnected] if device is not in a connected state
|
@@ -201,18 +202,25 @@ class Device
|
|
201
202
|
# @note This is the list of UUIDs for which we have an entry
|
202
203
|
# in the underlying api (bluez-dbus), which can be less
|
203
204
|
# that the list of advertised UUIDs.
|
205
|
+
# @example list available services
|
206
|
+
# $d.services.each {|uuid|
|
207
|
+
# info = BLE::Service[uuid]
|
208
|
+
# name = info.nil? ? uuid : info[:name]
|
209
|
+
# puts name
|
210
|
+
# }
|
211
|
+
#
|
204
212
|
# @return [Array<String>] List of service UUID
|
205
213
|
def services
|
206
214
|
raise NotConnected unless is_connected?
|
207
215
|
@services.keys
|
208
216
|
end
|
209
|
-
|
217
|
+
|
210
218
|
# Check if service is available on the device
|
211
219
|
# @return [Boolean]
|
212
220
|
def has_service?(service)
|
213
221
|
@service.key?(_uuid_service(service))
|
214
222
|
end
|
215
|
-
|
223
|
+
|
216
224
|
# List of available characteristics UUID for a service.
|
217
225
|
#
|
218
226
|
# @param service service can be a UUID, a service type or
|
@@ -229,21 +237,21 @@ class Device
|
|
229
237
|
chars.keys
|
230
238
|
end
|
231
239
|
end
|
232
|
-
|
240
|
+
|
233
241
|
# The Bluetooth device address of the remote device.
|
234
242
|
# @return [String] MAC address
|
235
243
|
def address
|
236
244
|
@o_dev[I_DEVICE]['Address']
|
237
245
|
end
|
238
|
-
|
246
|
+
|
239
247
|
# The Bluetooth remote name.
|
240
248
|
# It is better to always use the {#alias} when displaying the
|
241
|
-
# devices name.
|
249
|
+
# devices name.
|
242
250
|
# @return [String] name
|
243
251
|
def name # optional
|
244
252
|
@o_dev[I_DEVICE]['Name']
|
245
253
|
end
|
246
|
-
|
254
|
+
|
247
255
|
# The name alias for the remote device.
|
248
256
|
# The alias can be used to have a different friendly name for the
|
249
257
|
# remote device.
|
@@ -259,13 +267,13 @@ class Device
|
|
259
267
|
def alias=(val)
|
260
268
|
@o_dev[I_DEVICE]['Alias'] = val.nil? ? "" : val.to_str
|
261
269
|
end
|
262
|
-
|
270
|
+
|
263
271
|
# Is the device trusted?
|
264
272
|
# @return [Boolean]
|
265
273
|
def is_trusted?
|
266
274
|
@o_dev[I_DEVICE]['Trusted']
|
267
275
|
end
|
268
|
-
|
276
|
+
|
269
277
|
# Indicates if the remote is seen as trusted. This
|
270
278
|
# setting can be changed by the application.
|
271
279
|
# @param val [Boolean]
|
@@ -276,13 +284,13 @@ class Device
|
|
276
284
|
end
|
277
285
|
@o_dev[I_DEVICE]['Trusted'] = val
|
278
286
|
end
|
279
|
-
|
287
|
+
|
280
288
|
# Is the device blocked?
|
281
289
|
# @return [Boolean]
|
282
290
|
def is_blocked?
|
283
291
|
@o_dev[I_DEVICE]['Blocked']
|
284
292
|
end
|
285
|
-
|
293
|
+
|
286
294
|
# If set to true any incoming connections from the
|
287
295
|
# device will be immediately rejected. Any device
|
288
296
|
# drivers will also be removed and no new ones will
|
@@ -295,7 +303,7 @@ class Device
|
|
295
303
|
end
|
296
304
|
@o_dev[I_DEVICE]['Blocked'] = val
|
297
305
|
end
|
298
|
-
|
306
|
+
|
299
307
|
# Received Signal Strength Indicator of the remote
|
300
308
|
# device (inquiry or advertising).
|
301
309
|
# @return [Integer]
|
@@ -307,7 +315,7 @@ class Device
|
|
307
315
|
else raise ScriptError
|
308
316
|
end
|
309
317
|
end
|
310
|
-
|
318
|
+
|
311
319
|
# Advertised transmitted power level (inquiry or advertising).
|
312
320
|
# @return [Integer]
|
313
321
|
def tx_power # optional
|
@@ -318,8 +326,8 @@ class Device
|
|
318
326
|
else raise ScriptError
|
319
327
|
end
|
320
328
|
end
|
321
|
-
|
322
|
-
|
329
|
+
|
330
|
+
|
323
331
|
# Refresh list of services and characteristics
|
324
332
|
# @return [Boolean]
|
325
333
|
def refresh
|
@@ -328,7 +336,7 @@ class Device
|
|
328
336
|
rescue NotConnected, StalledObject
|
329
337
|
false
|
330
338
|
end
|
331
|
-
|
339
|
+
|
332
340
|
# Refresh list of services and characteristics
|
333
341
|
# @raise [NotConnected] if device is not in a connected state
|
334
342
|
# @return [self]
|
@@ -363,7 +371,7 @@ class Device
|
|
363
371
|
sleep(0.25) ; max_wait -= 0.25 ; retry
|
364
372
|
end
|
365
373
|
raise NotReady
|
366
|
-
|
374
|
+
|
367
375
|
else raise ScriptError
|
368
376
|
end
|
369
377
|
end
|
@@ -422,7 +430,7 @@ class Device
|
|
422
430
|
flags = char[:flags]
|
423
431
|
obj = char[:obj]
|
424
432
|
info = Characteristic[uuid]
|
425
|
-
|
433
|
+
|
426
434
|
if flags.include?('write') ||
|
427
435
|
flags.include?('write-without-response')
|
428
436
|
if !raw && info
|
@@ -442,9 +450,9 @@ class Device
|
|
442
450
|
end
|
443
451
|
nil
|
444
452
|
end
|
445
|
-
|
453
|
+
|
446
454
|
private
|
447
|
-
|
455
|
+
|
448
456
|
def _characteristics(service)
|
449
457
|
if srv = @services[_uuid_service(service)]
|
450
458
|
srv[:characteristics]
|
@@ -462,7 +470,7 @@ class Device
|
|
462
470
|
if uuid.nil?
|
463
471
|
raise ArgumentError, "unable to get UUID for service"
|
464
472
|
end
|
465
|
-
|
473
|
+
|
466
474
|
uuid
|
467
475
|
end
|
468
476
|
def _uuid_characteristic(characteristic)
|
@@ -477,7 +485,7 @@ class Device
|
|
477
485
|
if uuid.nil?
|
478
486
|
raise ArgumentError, "unable to get UUID for service"
|
479
487
|
end
|
480
|
-
|
488
|
+
|
481
489
|
uuid
|
482
490
|
end
|
483
491
|
end
|
data/lib/ble/service.rb
CHANGED
@@ -34,6 +34,7 @@ module Service
|
|
34
34
|
when Symbol then DB_NICKNAME[id]
|
35
35
|
when UUID::REGEX then DB_UUID[id]
|
36
36
|
when String then DB_TYPE[id]
|
37
|
+
when Integer then DB_UUID[BLE::UUID(id)]
|
37
38
|
else raise ArgumentError, "invalid type for service id"
|
38
39
|
end
|
39
40
|
end
|
@@ -79,49 +80,41 @@ module Service
|
|
79
80
|
#
|
80
81
|
# @param uuid [Integer, String] 16-bit, 32-bit or 128-bit uuid
|
81
82
|
# @param name [String]
|
82
|
-
# @
|
83
|
+
# @option opts :type [String] type
|
84
|
+
# @option opts :nick [Symbol] nickname
|
83
85
|
# @return [Hash] service description
|
84
|
-
def self.add(uuid, name:,
|
86
|
+
def self.add(uuid, name:, **opts)
|
87
|
+
uuid = BLE::UUID(uuid)
|
88
|
+
type = opts.delete :type
|
89
|
+
nick = opts.delete :nick
|
85
90
|
if opts.first
|
86
91
|
raise ArgumentError, "unknown keyword: #{opts.first[0]}"
|
87
92
|
end
|
88
93
|
|
89
|
-
uuid =
|
90
|
-
when Integer
|
91
|
-
if !(0..4294967296).include?(uuid)
|
92
|
-
raise ArgumentError, "not a 16bit or 32bit uuid"
|
93
|
-
end
|
94
|
-
([uuid].pack("L>").unpack('H*').first +
|
95
|
-
"-0000-1000-8000-00805F9B34FB")
|
96
|
-
|
97
|
-
when String
|
98
|
-
if uuid !~ UUID::REGEX
|
99
|
-
raise ArgumentError, "not a 128bit uuid string"
|
100
|
-
end
|
101
|
-
uuid
|
102
|
-
else raise ArgumentError, "invalid uuid type"
|
103
|
-
end
|
104
|
-
uuid = uuid.downcase
|
105
|
-
type = type.downcase
|
106
|
-
|
107
|
-
desc = DB_TYPE[type] = DB_UUID[uuid] = {
|
108
|
-
name: name,
|
109
|
-
type: type,
|
94
|
+
desc = DB_UUID[uuid] = {
|
110
95
|
uuid: uuid,
|
96
|
+
name: name,
|
111
97
|
}
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
98
|
+
|
99
|
+
# Add type if specified
|
100
|
+
if type
|
101
|
+
type = type.downcase
|
102
|
+
desc.merge!(type: type)
|
103
|
+
DB_TYPE[type] = desc
|
104
|
+
end
|
105
|
+
|
106
|
+
# Add nickname if specified or can be derived from type
|
107
|
+
if nick.nil? && type && type =~ /\.(?<key>[^.]+)$/
|
108
|
+
nick = $1.to_sym if type.start_with? 'org.bluetooth.service'
|
109
|
+
end
|
110
|
+
if nick
|
111
|
+
if DB_NICKNAME.include?(nick)
|
119
112
|
raise ArgumentError,
|
120
|
-
"nickname '#{
|
113
|
+
"nickname '#{nick}' already registered (uuid: #{uuid})"
|
121
114
|
end
|
122
|
-
DB_NICKNAME[
|
115
|
+
DB_NICKNAME[nick] = desc
|
123
116
|
end
|
124
|
-
|
117
|
+
|
125
118
|
desc
|
126
119
|
end
|
127
120
|
end
|
data/lib/ble/uuid.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module BLE
|
2
|
+
# Cast value to a well-formatted 128bit UUID string
|
3
|
+
# @param [String, Integer] val uuid
|
4
|
+
# @return [String] well formated 128bit UUID
|
5
|
+
def self.UUID(val)
|
6
|
+
case val
|
7
|
+
when Integer
|
8
|
+
if !(0..4294967295).include?(val) # 2**32-1
|
9
|
+
raise ArgumentError, "not a 16-bit or 32-bit UUID"
|
10
|
+
end
|
11
|
+
([val].pack("L>").unpack('H*').first + GATT_BASE_UUID[8..-1])
|
12
|
+
when String
|
13
|
+
if val !~ UUID::REGEX
|
14
|
+
raise ArgumentError, "not a 128bit uuid string"
|
15
|
+
end
|
16
|
+
val.downcase
|
17
|
+
else raise ArgumentError, "invalid uuid type"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class UUID
|
22
|
+
# Regular expression for matching UUID 128-bit string
|
23
|
+
REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
24
|
+
end
|
25
|
+
end
|
data/lib/ble/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ble
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephane D'Alu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-dbus
|
@@ -94,10 +94,13 @@ files:
|
|
94
94
|
- lib/ble/adapter.rb
|
95
95
|
- lib/ble/agent.rb
|
96
96
|
- lib/ble/characteristic.rb
|
97
|
-
- lib/ble/
|
98
|
-
- lib/ble/
|
97
|
+
- lib/ble/db_eddystone.rb
|
98
|
+
- lib/ble/db_nordic.rb
|
99
|
+
- lib/ble/db_sig_characteristic.rb
|
100
|
+
- lib/ble/db_sig_service.rb
|
99
101
|
- lib/ble/device.rb
|
100
102
|
- lib/ble/service.rb
|
103
|
+
- lib/ble/uuid.rb
|
101
104
|
- lib/ble/version.rb
|
102
105
|
homepage: http://github.com/sdalu/ruby-ble
|
103
106
|
licenses:
|
@@ -124,4 +127,3 @@ signing_key:
|
|
124
127
|
specification_version: 4
|
125
128
|
summary: Bluetooth Low Energy (BLE) API
|
126
129
|
test_files: []
|
127
|
-
has_rdoc: yard
|
@@ -1,70 +0,0 @@
|
|
1
|
-
module BLE
|
2
|
-
module 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
|
-
|
17
|
-
add 0x2A6E,
|
18
|
-
name: 'Temperature',
|
19
|
-
type: 'org.bluetooth.characteristic.temperature',
|
20
|
-
vrfy: ->(x) { (0..100).include?(x) },
|
21
|
-
in: ->(s) { s.unpack('s<').first.to_f / 100 },
|
22
|
-
out: ->(v) { [ v*100 ].pack('s<') }
|
23
|
-
|
24
|
-
add 0x2A76,
|
25
|
-
name: 'UV Index',
|
26
|
-
type: 'org.bluetooth.characteristic.uv_index',
|
27
|
-
in: ->(s) { s.unpack('C').first },
|
28
|
-
out: ->(v) { [ v ].pack('C') }
|
29
|
-
|
30
|
-
add 0x2A77,
|
31
|
-
name: 'Irradiance',
|
32
|
-
type: 'org.bluetooth.characteristic.irradiance',
|
33
|
-
in: ->(s) { s.unpack('S<').first.to_f / 10 },
|
34
|
-
out: ->(v) { [ v*10 ].pack('S<') }
|
35
|
-
|
36
|
-
add 0x2A7A,
|
37
|
-
name: 'Heat Index',
|
38
|
-
type: 'org.bluetooth.characteristic.heat_index',
|
39
|
-
in: ->(s) { s.unpack('c').first },
|
40
|
-
out: ->(v) { [ v ].pack('c') }
|
41
|
-
|
42
|
-
add 0x2A19,
|
43
|
-
name: 'Battery Level',
|
44
|
-
type: 'org.bluetooth.characteristic.battery_level',
|
45
|
-
vrfy: ->(x) { (0..100).include?(x) },
|
46
|
-
in: ->(s) { s.unpack('C').first },
|
47
|
-
out: ->(v) { [ v ].pack('C') }
|
48
|
-
|
49
|
-
add 0x2A6F,
|
50
|
-
name: 'Humidity',
|
51
|
-
type: 'org.bluetooth.characteristic.humidity',
|
52
|
-
vrfy: ->(x) { (0..100).include?(x) },
|
53
|
-
in: ->(s) { s.unpack('S<').first.to_f / 100 },
|
54
|
-
out: ->(v) { [ v*100 ].pack('S<') }
|
55
|
-
|
56
|
-
add 0x2A6D,
|
57
|
-
name: 'Pressure',
|
58
|
-
type: 'org.bluetooth.characteristic.pressure',
|
59
|
-
vrfy: ->(x) { x >= 0 },
|
60
|
-
in: ->(s) { s.unpack('L<').first.to_f / 10 },
|
61
|
-
out: ->(v) { [ v*10 ].pack('L<') }
|
62
|
-
|
63
|
-
add 0x2AB3,
|
64
|
-
name: 'Altitude',
|
65
|
-
type: 'org.bluetooth.characteristic.altitude',
|
66
|
-
vrfy: ->(x) { x >= 0 },
|
67
|
-
in: ->(s) { s.unpack('S<').first },
|
68
|
-
out: ->(v) { [ v ].pack('S<') }
|
69
|
-
end
|
70
|
-
end
|