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 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,10 @@
1
+ require 'mkmf'
2
+
3
+ #have_library 'stdc++'
4
+ extension_name = 'rubyeddystoneurl/rubyeddystoneurl'
5
+ dir_config extension_name
6
+
7
+ $CPPFLAGS << " -I/usr/include/bluetooth"
8
+ $LOCAL_LIBS << " -lbluetooth"
9
+
10
+ create_makefile extension_name
@@ -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: []