ble 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|