ble 0.0.3 → 0.1.0
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 +3 -0
- data/lib/ble/char_desc.rb +63 -0
- data/lib/ble/char_registry.rb +142 -0
- data/lib/ble/characteristic.rb +67 -136
- data/lib/ble/db_eddystone.rb +2 -2
- data/lib/ble/db_nordic.rb +2 -2
- data/lib/ble/db_sig_characteristic.rb +5 -5
- data/lib/ble/device.rb +259 -257
- data/lib/ble/notifications.rb +44 -0
- data/lib/ble/version.rb +9 -3
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cc1b7511a25f6f47653f9b5430a1a15c4361574
|
4
|
+
data.tar.gz: 776bcbf870dd96a1fb0575a8f9aefe7bcdb1199e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35ef023571bfd637245b82c814eaed2d08f265a14d207f29b17ad3633a9e62e30b072f4041e9b1ef4a3b6025e5607a0ed958c9cc187c98ab73eecc7534ae6cef
|
7
|
+
data.tar.gz: 118372609995212452776777eb03682579e5d87d57b19758cfa3e476415a10dccc2921d53e0f3ee64463876ed56edf3823f6304bece3da712f6e36d0d883afb3
|
data/lib/ble.rb
CHANGED
@@ -96,8 +96,11 @@ end
|
|
96
96
|
require_relative 'ble/version'
|
97
97
|
require_relative 'ble/uuid'
|
98
98
|
require_relative 'ble/adapter'
|
99
|
+
require_relative 'ble/char_desc'
|
100
|
+
require_relative 'ble/notifications'
|
99
101
|
require_relative 'ble/device'
|
100
102
|
require_relative 'ble/service'
|
103
|
+
require_relative 'ble/char_registry'
|
101
104
|
require_relative 'ble/characteristic'
|
102
105
|
require_relative 'ble/agent'
|
103
106
|
|
@@ -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
|
data/lib/ble/characteristic.rb
CHANGED
@@ -1,154 +1,85 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
module BLE
|
2
|
-
# Build information about {https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicsHome.aspx Bluetooth Characteristics}
|
3
|
-
#
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# vrfy: ->(x) { x >= 0 },
|
9
|
-
# in: ->(s) { s.unpack('s<').first },
|
10
|
-
# out: ->(v) { [ v ].pack('s<') }
|
11
|
-
#
|
12
|
-
# Returned characteristic description will be a hash:
|
13
|
-
# {
|
14
|
-
# name: "Bluetooth characteristic name",
|
15
|
-
# type: "org.bluetooth.characteristic.name",
|
16
|
-
# uuid: "128bit-uuid-string",
|
17
|
-
# vrfy: ->(x) { verify_value(x) },
|
18
|
-
# in: ->(s) { s.unpack(....) ... },
|
19
|
-
# out: ->(v) { [ v ].pack(....) ... }
|
20
|
-
# }
|
21
|
-
#
|
22
|
-
module Characteristic
|
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
|
+
|
23
9
|
# Notify of characteristic not found
|
24
|
-
class NotFound < BLE::NotFound
|
10
|
+
class NotFound < BLE::NotFound
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(desc)
|
14
|
+
@dbus_obj= desc[:obj]
|
15
|
+
@desc= CharDesc.new(desc)
|
25
16
|
end
|
26
17
|
|
18
|
+
def_delegators :@desc, :flag?, :uuid
|
19
|
+
|
27
20
|
private
|
28
21
|
FLAGS = [ 'broadcast',
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
DB_NICKNAME = {}
|
45
|
-
|
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
|
+
#++++++++++++++++++++++++++++
|
46
37
|
public
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
DB_NICKNAME[id]
|
38
|
+
#++++++++++++++++++++++++++++
|
39
|
+
|
40
|
+
def write(val, raw: false)
|
41
|
+
val= _serialize_value(val, raw: raw)
|
42
|
+
@dbus_obj[I_GATT_CHARACTERISTIC].WriteValue(val)
|
53
43
|
end
|
54
44
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
# @return [Hash] characteristic description
|
59
|
-
def self.by_uuid(id)
|
60
|
-
DB_UUID[id]
|
45
|
+
def read(raw: false)
|
46
|
+
val= @dbus_obj[I_GATT_CHARACTERISTIC].ReadValue().first
|
47
|
+
val= _deserialize_value(val, raw: raw)
|
61
48
|
end
|
62
49
|
|
63
|
-
#
|
64
|
-
#
|
65
|
-
|
66
|
-
|
67
|
-
def self.by_type(id)
|
68
|
-
DB_TYPE[id]
|
50
|
+
# Register to this characteristic for notifications when
|
51
|
+
# its value changes.
|
52
|
+
def notify!
|
53
|
+
@dbus_obj[I_GATT_CHARACTERISTIC].StartNotify
|
69
54
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
when UUID::REGEX then DB_UUID[id]
|
78
|
-
when String then DB_TYPE[id]
|
79
|
-
when Integer then DB_UUID[BLE::UUID(id)]
|
80
|
-
else raise ArgumentError, "invalid type for characteristic id"
|
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)
|
81
62
|
end
|
63
|
+
end
|
82
64
|
end
|
83
|
-
|
65
|
+
#----------------------------
|
66
|
+
private
|
67
|
+
#----------------------------
|
84
68
|
|
85
|
-
#
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
# in: ->(s) { s.unpack('s<').first.to_f / 100 },
|
93
|
-
# out: ->(v) { [ v*100 ].pack('s<') }
|
94
|
-
# end
|
95
|
-
#
|
96
|
-
# @example Add a characteristic description with a 128-bit uuid
|
97
|
-
# module Characteristic
|
98
|
-
# add 'c4935338-4307-47cf-ae1f-feac9e2b3ae7',
|
99
|
-
# name: 'Controlled mind',
|
100
|
-
# type: 'net.cortex-minus.characteristic.controlled_mind',
|
101
|
-
# vrfy: ->(x) { x >= 0 },
|
102
|
-
# in: ->(s) { s.unpack('s<').first },
|
103
|
-
# out: ->(v) { [ v ].pack('s<') }
|
104
|
-
# end
|
105
|
-
#
|
106
|
-
# @param uuid [Integer, String] 16-bit, 32-bit or 128-bit uuid
|
107
|
-
# @param name [String]
|
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
|
113
|
-
# @return [Hash] characteristic description
|
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
|
121
|
-
if opts.first
|
122
|
-
raise ArgumentError, "unknown keyword: #{opts.first[0]}"
|
123
|
-
end
|
124
|
-
|
125
|
-
|
126
|
-
desc = DB_UUID[uuid] = {
|
127
|
-
uuid: uuid,
|
128
|
-
name: name,
|
129
|
-
in: _in,
|
130
|
-
out: _out,
|
131
|
-
vrfy: vrfy
|
132
|
-
}
|
133
|
-
|
134
|
-
# Add type if specified
|
135
|
-
if type
|
136
|
-
type = type.downcase
|
137
|
-
desc.merge!(type: type)
|
138
|
-
DB_TYPE[type] = desc
|
139
|
-
end
|
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
|
140
76
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
if DB_NICKNAME.include?(nick)
|
147
|
-
raise ArgumentError,
|
148
|
-
"nickname '#{nick}' already registered (uuid: #{uuid})"
|
149
|
-
end
|
150
|
-
DB_NICKNAME[nick] = desc
|
151
|
-
end
|
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
|
152
82
|
end
|
153
|
-
|
83
|
+
|
84
|
+
end
|
154
85
|
end
|