rubyeddystoneurl 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/ext/rubyeddystoneurl/extconf.rb +10 -0
- data/ext/rubyeddystoneurl/rubyeddystoneurl.c +171 -0
- data/lib/rubyeddystoneurl.rb +113 -0
- metadata +46 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 286da3a462d1cd40888d6ddff5d93b264223be60
|
4
|
+
data.tar.gz: 50afcba5208938ec2ff1849eeffcadc14250ce6a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 19c2f242e3171fe80ede5e68b974594d716a25560d09b455c174af7a3e7f48cdcdbbc7c8efa1087998e707d907794a4381a510c514d837e49b965d3bc9ff18da
|
7
|
+
data.tar.gz: 1db4459ee61eb9510c1ae127386b41b88c7ca028e8596ff6b95b7a7d0abb5d650c41de8b03c3b2dd49d4fef6598e29e37b1e083085701dce167ecd0ababe75a8
|
@@ -0,0 +1,171 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include <errno.h>
|
3
|
+
#include <signal.h>
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <stdlib.h>
|
6
|
+
#include <sys/ioctl.h>
|
7
|
+
#include <sys/prctl.h>
|
8
|
+
#include <unistd.h>
|
9
|
+
|
10
|
+
#include <bluetooth/bluetooth.h>
|
11
|
+
#include <bluetooth/hci.h>
|
12
|
+
#include <bluetooth/hci_lib.h>
|
13
|
+
|
14
|
+
VALUE RubyEddystoneURL = Qnil;
|
15
|
+
void Init_rubyeddystoneurl();
|
16
|
+
VALUE method_scan(VALUE, VALUE);
|
17
|
+
|
18
|
+
void Init_rubyeddystoneurl(){
|
19
|
+
VALUE RubyEddystoneURL = rb_define_module("RubyEddystoneURL");
|
20
|
+
rb_define_method(RubyEddystoneURL, "scan", method_scan,1);
|
21
|
+
}
|
22
|
+
|
23
|
+
VALUE method_scan(VALUE self, VALUE timeout) {
|
24
|
+
VALUE retArray = rb_ary_new();
|
25
|
+
|
26
|
+
int hciDeviceId = 0;
|
27
|
+
int hciSocket;
|
28
|
+
struct hci_dev_info hciDevInfo;
|
29
|
+
|
30
|
+
struct hci_filter oldHciFilter;
|
31
|
+
struct hci_filter newHciFilter;
|
32
|
+
socklen_t oldHciFilterLen;
|
33
|
+
|
34
|
+
int currentAdapterState;
|
35
|
+
int timeoutCounter = 0;
|
36
|
+
|
37
|
+
unsigned char hciEventBuf[HCI_MAX_EVENT_SIZE];
|
38
|
+
int hciEventLen;
|
39
|
+
evt_le_meta_event *leMetaEvent;
|
40
|
+
le_advertising_info *leAdvertisingInfo;
|
41
|
+
char btAddress[18];
|
42
|
+
int i;
|
43
|
+
int8_t rssi;
|
44
|
+
fd_set rfds;
|
45
|
+
struct timeval tv;
|
46
|
+
int selectRetval;
|
47
|
+
char btInfo[500];
|
48
|
+
|
49
|
+
if(TYPE(timeout) != T_FIXNUM) {
|
50
|
+
rb_raise(rb_eTypeError, "Invalid parameter");
|
51
|
+
return Qnil;
|
52
|
+
}
|
53
|
+
|
54
|
+
memset(&hciDevInfo, 0x00, sizeof(hciDevInfo));
|
55
|
+
|
56
|
+
//use first available device
|
57
|
+
hciDeviceId = hci_get_route(NULL);
|
58
|
+
|
59
|
+
if (hciDeviceId < 0) {
|
60
|
+
hciDeviceId = 0; // use device 0, if device id is invalid
|
61
|
+
}
|
62
|
+
|
63
|
+
// setup HCI socket
|
64
|
+
hciSocket = hci_open_dev(hciDeviceId);
|
65
|
+
|
66
|
+
if (hciSocket == -1) {
|
67
|
+
rb_raise(rb_eStandardError, "Unsupported");
|
68
|
+
return Qnil;
|
69
|
+
}
|
70
|
+
hciDevInfo.dev_id = hciDeviceId;
|
71
|
+
|
72
|
+
// get old HCI filter
|
73
|
+
oldHciFilterLen = sizeof(oldHciFilter);
|
74
|
+
getsockopt(hciSocket, SOL_HCI, HCI_FILTER, &oldHciFilter, &oldHciFilterLen);
|
75
|
+
|
76
|
+
// setup new HCI filter
|
77
|
+
hci_filter_clear(&newHciFilter);
|
78
|
+
hci_filter_set_ptype(HCI_EVENT_PKT, &newHciFilter);
|
79
|
+
hci_filter_set_event(EVT_LE_META_EVENT, &newHciFilter);
|
80
|
+
setsockopt(hciSocket, SOL_HCI, HCI_FILTER, &newHciFilter, sizeof(newHciFilter));
|
81
|
+
|
82
|
+
// disable scanning, it may have been left on, if so hci_le_set_scan_parameters will fail without this
|
83
|
+
hci_le_set_scan_enable(hciSocket, 0x00, 0, 1000);
|
84
|
+
|
85
|
+
// get HCI dev info for adapter state
|
86
|
+
ioctl(hciSocket, HCIGETDEVINFO, (void *)&hciDevInfo);
|
87
|
+
currentAdapterState = hci_test_bit(HCI_UP, &hciDevInfo.flags);
|
88
|
+
|
89
|
+
if (!currentAdapterState) {
|
90
|
+
//powered off
|
91
|
+
currentAdapterState = 0;
|
92
|
+
} else if (hci_le_set_scan_parameters(hciSocket, 0x01, htobs(0x0010), htobs(0x0010), 0x00, 0, 1000) < 0) {
|
93
|
+
if (EPERM == errno) {
|
94
|
+
//unauthorized
|
95
|
+
currentAdapterState = 1;
|
96
|
+
} else if (EIO == errno) {
|
97
|
+
//unsupported
|
98
|
+
currentAdapterState = 2;
|
99
|
+
} else {
|
100
|
+
//unknown
|
101
|
+
currentAdapterState = 3;
|
102
|
+
}
|
103
|
+
} else {
|
104
|
+
//powered on
|
105
|
+
currentAdapterState = 4;
|
106
|
+
}
|
107
|
+
|
108
|
+
if(currentAdapterState == 4) {
|
109
|
+
//scan devices
|
110
|
+
hci_le_set_scan_enable(hciSocket, 0x00, 1, 1000);
|
111
|
+
hci_le_set_scan_enable(hciSocket, 0x01, 1, 1000);
|
112
|
+
|
113
|
+
while(1) {
|
114
|
+
FD_ZERO(&rfds);
|
115
|
+
FD_SET(hciSocket, &rfds);
|
116
|
+
|
117
|
+
tv.tv_sec = 1;
|
118
|
+
tv.tv_usec = 0;
|
119
|
+
|
120
|
+
selectRetval = select(hciSocket + 1, &rfds, NULL, NULL, &tv);
|
121
|
+
|
122
|
+
if(selectRetval) {
|
123
|
+
//get scan devices
|
124
|
+
hciEventLen = read(hciSocket, hciEventBuf, sizeof(hciEventBuf));
|
125
|
+
leMetaEvent = (evt_le_meta_event *)(hciEventBuf + (1 + HCI_EVENT_HDR_SIZE));
|
126
|
+
hciEventLen -= (1 + HCI_EVENT_HDR_SIZE);
|
127
|
+
|
128
|
+
if (leMetaEvent->subevent == 0x02) {
|
129
|
+
leAdvertisingInfo = (le_advertising_info *)(leMetaEvent->data + 1);
|
130
|
+
ba2str(&leAdvertisingInfo->bdaddr, btAddress);
|
131
|
+
|
132
|
+
VALUE hash = rb_hash_new();
|
133
|
+
rb_hash_aset(hash, rb_str_new2("address"), rb_str_new2(btAddress));
|
134
|
+
|
135
|
+
rssi = *(leAdvertisingInfo->data + leAdvertisingInfo->length);
|
136
|
+
|
137
|
+
rb_hash_aset(hash, rb_str_new2("rssi"), rb_int_new((int)rssi));
|
138
|
+
|
139
|
+
btInfo[0] = '\0';
|
140
|
+
|
141
|
+
for (i = 0; i < leAdvertisingInfo->length; i++) {
|
142
|
+
sprintf(btInfo + strlen(btInfo), "%02x", leAdvertisingInfo->data[i]);
|
143
|
+
}
|
144
|
+
|
145
|
+
//printf("%s\n", btInfo);
|
146
|
+
|
147
|
+
rb_hash_aset(hash, rb_str_new2("info"), rb_str_new2(btInfo));
|
148
|
+
rb_ary_push(retArray, hash);
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
timeoutCounter += 1;
|
153
|
+
sleep(1);
|
154
|
+
|
155
|
+
if(timeoutCounter == FIX2INT(timeout)) {
|
156
|
+
//stop scan
|
157
|
+
hci_le_set_scan_enable(hciSocket, 0x00, 0, 1000);
|
158
|
+
break;
|
159
|
+
}
|
160
|
+
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
// restore original filter
|
165
|
+
setsockopt(hciSocket, SOL_HCI, HCI_FILTER, &oldHciFilter, sizeof(oldHciFilter));
|
166
|
+
// disable LE scan
|
167
|
+
hci_le_set_scan_enable(hciSocket, 0x00, 0, 1000);
|
168
|
+
close(hciSocket);
|
169
|
+
|
170
|
+
return retArray;
|
171
|
+
}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
=begin
|
2
|
+
* 2018 http://ferdinandsilva.com
|
3
|
+
*
|
4
|
+
* Ruby C Extension To Scan Eddystone-URL Beacons
|
5
|
+
*
|
6
|
+
* Author:
|
7
|
+
* Ferdinand Silva <ferdinandsilva@ferdinandsilva.com>
|
8
|
+
*
|
9
|
+
=end
|
10
|
+
require 'rubyeddystoneurl/rubyeddystoneurl'
|
11
|
+
include RubyEddystoneURL
|
12
|
+
|
13
|
+
def discover(timeout)
|
14
|
+
ret = Hash.new
|
15
|
+
|
16
|
+
devices = scan(timeout)
|
17
|
+
|
18
|
+
devices.each do |dev|
|
19
|
+
if !ret.key?(dev['address'])
|
20
|
+
# add to return
|
21
|
+
ret[dev['address']] = {
|
22
|
+
"name" => "",
|
23
|
+
"url" => "",
|
24
|
+
"rssi" => dev['rssi']
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
if dev['info'].length > 28
|
29
|
+
# valid info
|
30
|
+
lineToRemove = dev['info'][0..25]
|
31
|
+
cleanLine = dev['info'].sub lineToRemove, ""
|
32
|
+
|
33
|
+
if cleanLine[/^(00|01|02|03)/]
|
34
|
+
#URL
|
35
|
+
justStarted = true
|
36
|
+
urlStr = ""
|
37
|
+
|
38
|
+
while cleanLine.length > 0
|
39
|
+
lineToRemove = cleanLine[0..1]
|
40
|
+
|
41
|
+
#decode URL
|
42
|
+
if justStarted
|
43
|
+
justStarted = false
|
44
|
+
|
45
|
+
if lineToRemove == "00"
|
46
|
+
urlStr = "http://www."
|
47
|
+
elsif lineToRemove == "01"
|
48
|
+
urlStr = "https://www."
|
49
|
+
elsif lineToRemove == "02"
|
50
|
+
urlStr = "http://"
|
51
|
+
else
|
52
|
+
# 03
|
53
|
+
urlStr = "https://"
|
54
|
+
end
|
55
|
+
cleanLine = cleanLine.sub lineToRemove, ""
|
56
|
+
next
|
57
|
+
end
|
58
|
+
|
59
|
+
if lineToRemove == "00"
|
60
|
+
urlStr += ".com/"
|
61
|
+
elsif lineToRemove == "01"
|
62
|
+
urlStr += ".org/"
|
63
|
+
elsif lineToRemove == "02"
|
64
|
+
urlStr += ".edu/"
|
65
|
+
elsif lineToRemove == "03"
|
66
|
+
urlStr += ".net/"
|
67
|
+
elsif lineToRemove == "04"
|
68
|
+
urlStr += ".info/"
|
69
|
+
elsif lineToRemove == "05"
|
70
|
+
urlStr += ".biz/"
|
71
|
+
elsif lineToRemove == "06"
|
72
|
+
urlStr += ".gov/"
|
73
|
+
elsif lineToRemove == "07"
|
74
|
+
urlStr += ".com"
|
75
|
+
elsif lineToRemove == "08"
|
76
|
+
urlStr += ".org"
|
77
|
+
elsif lineToRemove == "09"
|
78
|
+
urlStr += ".edu"
|
79
|
+
elsif lineToRemove == "0a"
|
80
|
+
urlStr += ".net"
|
81
|
+
elsif lineToRemove == "0b"
|
82
|
+
urlStr += ".info"
|
83
|
+
elsif lineToRemove == "0c"
|
84
|
+
urlStr += ".biz"
|
85
|
+
elsif lineToRemove == "0d"
|
86
|
+
urlStr += ".gov"
|
87
|
+
else
|
88
|
+
urlStr += [lineToRemove].pack 'H*'
|
89
|
+
end
|
90
|
+
|
91
|
+
cleanLine = cleanLine.sub lineToRemove, ""
|
92
|
+
end
|
93
|
+
|
94
|
+
ret[dev['address']]['url'] = urlStr
|
95
|
+
else
|
96
|
+
#name
|
97
|
+
lineToRemove = cleanLine[0..3]
|
98
|
+
cleanLine = cleanLine.sub lineToRemove, ""
|
99
|
+
ret[dev['address']]['name'] = [cleanLine].pack 'H*'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# clean up devices
|
105
|
+
# remove devices that doesn't have a url
|
106
|
+
ret.each do |k,v|
|
107
|
+
if ret[k]["url"] == ""
|
108
|
+
ret.delete k
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
return ret
|
113
|
+
end
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubyeddystoneurl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ferdinand E. Silva
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-12-11 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Ruby C Extension To Scan Eddystone-URL Bluetooth Low Energy Beacons
|
14
|
+
email: ferdinandsilva@ferdinandsilva.com
|
15
|
+
executables: []
|
16
|
+
extensions:
|
17
|
+
- ext/rubyeddystoneurl/extconf.rb
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ext/rubyeddystoneurl/extconf.rb
|
21
|
+
- ext/rubyeddystoneurl/rubyeddystoneurl.c
|
22
|
+
- lib/rubyeddystoneurl.rb
|
23
|
+
homepage: http://ferdinandsilva.com
|
24
|
+
licenses: []
|
25
|
+
metadata: {}
|
26
|
+
post_install_message:
|
27
|
+
rdoc_options: []
|
28
|
+
require_paths:
|
29
|
+
- lib
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
requirements: []
|
41
|
+
rubyforge_project:
|
42
|
+
rubygems_version: 2.5.2.1
|
43
|
+
signing_key:
|
44
|
+
specification_version: 4
|
45
|
+
summary: Ruby C Extension To Scan Eddystone-URL Bluetooth Low Energy Beacons
|
46
|
+
test_files: []
|