rubyeddystoneurl 1.0.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 +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: []
|