tamashii-bluetooth 0.1.0.beta.1
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/.gitignore +21 -0
- data/.overcommit.yml +33 -0
- data/.rspec +3 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +5 -0
- data/Gemfile +10 -0
- data/README.md +120 -0
- data/Rakefile +16 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/ext/bluetooth/bluetooth.h +38 -0
- data/ext/bluetooth/extconf.rb +28 -0
- data/ext/bluetooth/init.h +35 -0
- data/ext/bluetooth/osx/bluetooth.c +107 -0
- data/ext/bluetooth/osx/event.c +76 -0
- data/ext/bluetooth/osx/xpc.c +67 -0
- data/ext/bluetooth/osx.h +45 -0
- data/lib/tamashii/bluetooth/beacon/line.rb +75 -0
- data/lib/tamashii/bluetooth/beacon.rb +10 -0
- data/lib/tamashii/bluetooth/device.rb +29 -0
- data/lib/tamashii/bluetooth/version.rb +7 -0
- data/lib/tamashii/bluetooth.rb +12 -0
- data/tamashii-bluetooth.gemspec +32 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c1ff2cef1ee5114af03c06796095de46d2636320
|
4
|
+
data.tar.gz: 7a60d10486b1336b23841a749ac6073001845e4d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 59bda2a80fab368e623dd79c17a875cb0c2b9fb75ef1020ce046e51170ca6bba37817faf10934a8ed788be610b1bdf23948135e6627cee1b00cb9791922a14a7
|
7
|
+
data.tar.gz: cbbbecc34b4a0de84cddf48d8ef2aec6d37f4b651f41f468e1dab37a29c9e118e356c98e30c4e5049210a7d3ec22092f0e4d4c2195978b614bac4b60c5a3e0a5
|
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/_yardoc/
|
4
|
+
/coverage/
|
5
|
+
/doc/
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/tmp/
|
9
|
+
*.bundle
|
10
|
+
*.so
|
11
|
+
*.o
|
12
|
+
*.a
|
13
|
+
mkmf.log
|
14
|
+
|
15
|
+
# rspec failure tracking
|
16
|
+
.rspec_status
|
17
|
+
|
18
|
+
# YCM Config
|
19
|
+
.ycm_extra_conf.py
|
20
|
+
|
21
|
+
Gemfile.lock
|
data/.overcommit.yml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Use this file to configure the Overcommit hooks you wish to use. This will
|
2
|
+
# extend the default configuration defined in:
|
3
|
+
# https://github.com/brigade/overcommit/blob/master/config/default.yml
|
4
|
+
#
|
5
|
+
# At the topmost level of this YAML file is a key representing type of hook
|
6
|
+
# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can
|
7
|
+
# customize each hook, such as whether to only run it on certain files (via
|
8
|
+
# `include`), whether to only display output if it fails (via `quiet`), etc.
|
9
|
+
#
|
10
|
+
# For a complete list of hooks, see:
|
11
|
+
# https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook
|
12
|
+
#
|
13
|
+
# For a complete list of options that you can use to customize hooks, see:
|
14
|
+
# https://github.com/brigade/overcommit#configuration
|
15
|
+
#
|
16
|
+
# Uncomment the following lines to make the configuration take effect.
|
17
|
+
|
18
|
+
PreCommit:
|
19
|
+
AuthorName:
|
20
|
+
enabled: false
|
21
|
+
RuboCop:
|
22
|
+
enabled: true
|
23
|
+
on_warn: fail # Treat all warnings as failures
|
24
|
+
|
25
|
+
TrailingWhitespace:
|
26
|
+
enabled: true
|
27
|
+
|
28
|
+
# PostCheckout:
|
29
|
+
# ALL: # Special hook name that customizes all hooks of this type
|
30
|
+
# quiet: true # Change all post-checkout hooks to only display output on failure
|
31
|
+
#
|
32
|
+
# IndexTags:
|
33
|
+
# enabled: true # Generate a tags file with `ctags` each time HEAD changes
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# Tamashii Bluetooth
|
2
|
+
|
3
|
+
The Bluetooth implement for Tamashii to building IoT device with BLE (Bluetooth Low Engery) function.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'tamashii-bluetooth'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install tamashii-bluetooth
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Current only support running on macOS by using iBeacon format.
|
24
|
+
|
25
|
+
### Initialize Bluetooth device
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
require 'tamashii/bluetooth'
|
29
|
+
|
30
|
+
device = Tamashii::Bluetooth::Device.new
|
31
|
+
device.open
|
32
|
+
|
33
|
+
# Do something ...
|
34
|
+
|
35
|
+
# This is async in macOS
|
36
|
+
device.stop
|
37
|
+
```
|
38
|
+
|
39
|
+
### Handling Event
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
require 'tamashii/bluetooth'
|
43
|
+
|
44
|
+
device = Tamashii::Bluetooth::Device.new
|
45
|
+
device.open
|
46
|
+
|
47
|
+
# Ensure your ruby is running otherwise the Bluetooth device will be closed
|
48
|
+
Thread.new do
|
49
|
+
until device.events.empty?
|
50
|
+
event = device.events.pop
|
51
|
+
|
52
|
+
if event.type == :state_changed && device.powered_on?
|
53
|
+
# Advertising
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Free CPU
|
58
|
+
sleep 1
|
59
|
+
end.join
|
60
|
+
```
|
61
|
+
|
62
|
+
### Advertising (LINE Beacon)
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
require 'tamashii/bluetooth'
|
66
|
+
require 'tamashii/bluetooth/beacon'
|
67
|
+
|
68
|
+
device = Tamashii::Bluetooth::Device.new
|
69
|
+
device.open
|
70
|
+
|
71
|
+
# beacon = Tamashii::Bluetooth::Advertisment.new
|
72
|
+
# beacon.data = [0x02, ...]
|
73
|
+
beacon = Tamashii::Bluetooth::Beacon::LINE.new('001facddd')
|
74
|
+
|
75
|
+
# Ensure your ruby is running otherwise the Bluetooth device will be closed
|
76
|
+
Thread.new do
|
77
|
+
until device.events.empty?
|
78
|
+
event = device.events.pop
|
79
|
+
|
80
|
+
if event.type == :state_changed && device.powered_on?
|
81
|
+
device.advertising(beacon)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Free CPU
|
86
|
+
sleep 1
|
87
|
+
end.join
|
88
|
+
```
|
89
|
+
|
90
|
+
## Roadmap
|
91
|
+
|
92
|
+
### MVP (~> 0.1)
|
93
|
+
|
94
|
+
* [x] macOS support (XPC / CoreBluetooth)
|
95
|
+
* [ ] Linux support (libbluetooth)
|
96
|
+
* [ ] Advertising
|
97
|
+
* [x] iBeacon
|
98
|
+
* [ ] iBeacon Class
|
99
|
+
* [x] LINE Beacon Class
|
100
|
+
* [ ] EddyStone
|
101
|
+
* [ ] Tests
|
102
|
+
* [ ] Events
|
103
|
+
* [x] State Changes
|
104
|
+
* [x] Start
|
105
|
+
* [x] Stop
|
106
|
+
* [ ] Error Handling
|
107
|
+
|
108
|
+
### Full ( >= 1.0 )
|
109
|
+
|
110
|
+
We plan to add full Bluetooth support with breaking changes with MVP version.
|
111
|
+
|
112
|
+
## Development
|
113
|
+
|
114
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
115
|
+
|
116
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
117
|
+
|
118
|
+
## Contributing
|
119
|
+
|
120
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/tamashii-io/tamashii-bluetooth.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
require 'rake/extensiontask'
|
9
|
+
|
10
|
+
task build: :compile
|
11
|
+
|
12
|
+
Rake::ExtensionTask.new('bluetooth') do |ext|
|
13
|
+
ext.lib_dir = 'lib/tamashii/bluetooth'
|
14
|
+
end
|
15
|
+
|
16
|
+
task default: %i[clobber compile spec]
|
data/bin/console
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#ifndef TAMASHII_BLUETOOTH_H
|
2
|
+
#define TAMASHII_BLUETOOTH_H
|
3
|
+
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <time.h>
|
6
|
+
|
7
|
+
#include "ruby.h"
|
8
|
+
#include "ruby/intern.h"
|
9
|
+
#include "ruby/thread.h"
|
10
|
+
|
11
|
+
#ifdef OS_OSX
|
12
|
+
#include "osx.h"
|
13
|
+
#endif
|
14
|
+
|
15
|
+
#include "event.h"
|
16
|
+
|
17
|
+
// TODO: Prevent crash if program stop after initialize device
|
18
|
+
|
19
|
+
// Ruby Object Define
|
20
|
+
extern VALUE rb_mTamashiiBluetooth;
|
21
|
+
|
22
|
+
extern VALUE rb_cTamashiiBluetoothDevice;
|
23
|
+
extern VALUE rb_cTamashiiBluetoothEvent;
|
24
|
+
|
25
|
+
extern VALUE rb_cTamashiiBluetoothAdvertisment;
|
26
|
+
|
27
|
+
// Device Methods
|
28
|
+
VALUE rb_tamashii_bt_device_allocate(VALUE);
|
29
|
+
void rb_tamashii_bt_device_deallocate(void*);
|
30
|
+
|
31
|
+
VALUE rb_tamashii_bt_device_initialize(VALUE);
|
32
|
+
VALUE rb_tamashii_bt_device_open(VALUE);
|
33
|
+
VALUE rb_tamashii_bt_device_advertising(VALUE, VALUE);
|
34
|
+
VALUE rb_tamashii_bt_device_stop(VALUE);
|
35
|
+
|
36
|
+
// Event Methods
|
37
|
+
|
38
|
+
#endif /* TAMASHII_BLUETOOTH_H */
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mkmf'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
def add_define(name)
|
7
|
+
$defs.push("-D#{name.upcase}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def define_os_version
|
11
|
+
version = RUBY_PLATFORM[/[[:digit:]]+$/].to_i
|
12
|
+
add_define('osx_yosemite') if version < 17
|
13
|
+
end
|
14
|
+
|
15
|
+
os = case RUBY_PLATFORM
|
16
|
+
when /linux/ then
|
17
|
+
add_define 'os_linux'
|
18
|
+
abort 'Linux support is comming soon'
|
19
|
+
when /darwin/ then
|
20
|
+
add_define 'os_osx'
|
21
|
+
define_os_version
|
22
|
+
'osx'
|
23
|
+
else
|
24
|
+
abort "Unsupport platform #{RUBY_PLATFORM}"
|
25
|
+
end
|
26
|
+
|
27
|
+
source_path = Pathname.new(__FILE__).expand_path.dirname.join(os)
|
28
|
+
create_makefile('tamashii/bluetooth/bluetooth', source_path.to_s)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#ifndef TAMASHII_BLUETOOTH_INIT_H
|
2
|
+
#define TAMASHII_BLUETOOTH_INIT_H
|
3
|
+
|
4
|
+
#include "bluetooth.h"
|
5
|
+
|
6
|
+
VALUE rb_mTamashiiBluetooth;
|
7
|
+
|
8
|
+
VALUE rb_cTamashiiBluetoothDevice;
|
9
|
+
VALUE rb_cTamashiiBluetoothEvent;
|
10
|
+
|
11
|
+
VALUE rb_cTamashiiBluetoothAdvertisment;
|
12
|
+
|
13
|
+
// Initialize Extension
|
14
|
+
extern void Init_bluetooth(void)
|
15
|
+
{
|
16
|
+
VALUE rb_mTamashii = rb_const_get(rb_cObject, rb_intern("Tamashii"));
|
17
|
+
rb_mTamashiiBluetooth = rb_define_module_under(rb_mTamashii, "Bluetooth");
|
18
|
+
|
19
|
+
rb_cTamashiiBluetoothEvent = rb_define_class_under(rb_mTamashiiBluetooth, "Event", rb_cObject);
|
20
|
+
rb_define_attr(rb_cTamashiiBluetoothEvent, "device", 1, 0);
|
21
|
+
rb_define_attr(rb_cTamashiiBluetoothEvent, "type", 1, 0);
|
22
|
+
|
23
|
+
rb_cTamashiiBluetoothDevice = rb_define_class_under(rb_mTamashiiBluetooth, "Device", rb_cObject);
|
24
|
+
rb_define_attr(rb_cTamashiiBluetoothDevice, "events", 1, 0);
|
25
|
+
rb_define_alloc_func(rb_cTamashiiBluetoothDevice, rb_tamashii_bt_device_allocate);
|
26
|
+
rb_define_method(rb_cTamashiiBluetoothDevice, "initialize", rb_tamashii_bt_device_initialize, 0);
|
27
|
+
rb_define_method(rb_cTamashiiBluetoothDevice, "open", rb_tamashii_bt_device_open, 0);
|
28
|
+
rb_define_method(rb_cTamashiiBluetoothDevice, "advertising", rb_tamashii_bt_device_advertising, 1);
|
29
|
+
rb_define_method(rb_cTamashiiBluetoothDevice, "stop", rb_tamashii_bt_device_stop, 0);
|
30
|
+
|
31
|
+
rb_cTamashiiBluetoothAdvertisment = rb_define_class_under(rb_mTamashiiBluetooth, "Advertisment", rb_cObject);
|
32
|
+
rb_define_attr(rb_cTamashiiBluetoothAdvertisment, "data", 1, 1);
|
33
|
+
}
|
34
|
+
|
35
|
+
#endif
|
@@ -0,0 +1,107 @@
|
|
1
|
+
#include "../bluetooth.h"
|
2
|
+
#include "../init.h"
|
3
|
+
|
4
|
+
xpc_object_t tamashii_create_message(int message_id, xpc_object_t args)
|
5
|
+
{
|
6
|
+
xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
|
7
|
+
|
8
|
+
xpc_dictionary_set_value(message, "kCBMsgId", xpc_int64_create(message_id));
|
9
|
+
if (args) {
|
10
|
+
xpc_dictionary_set_value(message, "kCBMsgArgs", args);
|
11
|
+
}
|
12
|
+
|
13
|
+
return message;
|
14
|
+
}
|
15
|
+
|
16
|
+
xpc_object_t tamashii_create_message_from_advertisment(VALUE object)
|
17
|
+
{
|
18
|
+
xpc_object_t args = xpc_dictionary_create(NULL, NULL, 0);
|
19
|
+
VALUE data = rb_ivar_get(object, rb_intern("@data"));
|
20
|
+
|
21
|
+
xpc_dictionary_set_value(args, "kCBAdvDataAppleMfgData", rb_create_xpc_data(data));
|
22
|
+
|
23
|
+
return tamashii_create_message(BLUED_MSG_ADVERTISING, args);
|
24
|
+
}
|
25
|
+
|
26
|
+
void rb_tamashii_bt_device_deallocate(void *bt)
|
27
|
+
{
|
28
|
+
free(bt);
|
29
|
+
}
|
30
|
+
|
31
|
+
VALUE rb_tamashii_bt_device_allocate(VALUE klass)
|
32
|
+
{
|
33
|
+
tamatshii_bt_device_t * bt = malloc(sizeof(tamatshii_bt_device_t));
|
34
|
+
|
35
|
+
return Data_Wrap_Struct(klass, NULL, rb_tamashii_bt_device_deallocate, bt);
|
36
|
+
}
|
37
|
+
|
38
|
+
VALUE rb_tamashii_bt_device_open(VALUE self)
|
39
|
+
{
|
40
|
+
tamatshii_bt_device_t *bt;
|
41
|
+
Data_Get_Struct(self, tamatshii_bt_device_t, bt);
|
42
|
+
|
43
|
+
xpc_object_t args = xpc_dictionary_create(NULL, NULL, 0);
|
44
|
+
xpc_object_t options = xpc_dictionary_create(NULL, NULL, 0);
|
45
|
+
|
46
|
+
char* name;
|
47
|
+
name = malloc(13 + sizeof(time_t) * 2);
|
48
|
+
sprintf(name, "tamashii-ble-%ld", time(NULL));
|
49
|
+
|
50
|
+
xpc_dictionary_set_value(options, "kCBInitOptionShowPowerAlert", xpc_int64_create(1));
|
51
|
+
|
52
|
+
xpc_dictionary_set_value(args, "kCBMsgArgName", xpc_string_create(name));
|
53
|
+
xpc_dictionary_set_value(args, "kCBMsgArgType", xpc_int64_create(1));
|
54
|
+
xpc_dictionary_set_value(args, "kCBMsgArgOptions", options);
|
55
|
+
|
56
|
+
xpc_connection_send_message(bt->connection, tamashii_create_message(BLUED_MSG_INIT, args));
|
57
|
+
return Qnil;
|
58
|
+
}
|
59
|
+
|
60
|
+
VALUE rb_tamashii_bt_device_advertising(VALUE self, VALUE object)
|
61
|
+
{
|
62
|
+
tamatshii_bt_device_t *bt;
|
63
|
+
Data_Get_Struct(self, tamatshii_bt_device_t, bt);
|
64
|
+
|
65
|
+
xpc_object_t message;
|
66
|
+
|
67
|
+
if (rb_obj_is_kind_of(object, rb_cTamashiiBluetoothAdvertisment)) {
|
68
|
+
message = tamashii_create_message_from_advertisment(object);
|
69
|
+
} else {
|
70
|
+
rb_raise(rb_eArgError, "The object should be a kind of Tamashii::Bluetooth::Advertisment");
|
71
|
+
}
|
72
|
+
|
73
|
+
xpc_connection_send_message(bt->connection, message);
|
74
|
+
return Qnil;
|
75
|
+
}
|
76
|
+
|
77
|
+
VALUE rb_tamashii_bt_device_stop(VALUE self)
|
78
|
+
{
|
79
|
+
tamatshii_bt_device_t *bt;
|
80
|
+
Data_Get_Struct(self, tamatshii_bt_device_t, bt);
|
81
|
+
|
82
|
+
xpc_connection_send_message(bt->connection, tamashii_create_message(BLUED_MSG_STOP, NULL));
|
83
|
+
return Qnil;
|
84
|
+
}
|
85
|
+
|
86
|
+
VALUE rb_tamashii_bt_device_initialize(VALUE self)
|
87
|
+
{
|
88
|
+
tamatshii_bt_device_t * bt;
|
89
|
+
Data_Get_Struct(self, tamatshii_bt_device_t, bt);
|
90
|
+
|
91
|
+
bt->connection = xpc_connection_create_mach_service(service_name, NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
|
92
|
+
|
93
|
+
VALUE rb_cQueue = rb_const_get(rb_cObject, rb_intern("Queue"));
|
94
|
+
VALUE queue = rb_class_new_instance(0, NULL, rb_cQueue);
|
95
|
+
rb_ivar_set(self, rb_intern("@events"), queue);
|
96
|
+
rb_ivar_set(self, rb_intern("@state"), INT2FIX(0));
|
97
|
+
|
98
|
+
xpc_connection_set_event_handler(bt->connection, ^(xpc_object_t event) {
|
99
|
+
xpc_retain(event);
|
100
|
+
VALUE tamashii_event = rb_tamashii_bt_event_create(event, self);
|
101
|
+
rb_funcall(queue, rb_intern("push"), 1, tamashii_event);
|
102
|
+
});
|
103
|
+
|
104
|
+
xpc_connection_resume(bt->connection);
|
105
|
+
|
106
|
+
return self;
|
107
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#include "../bluetooth.h"
|
2
|
+
|
3
|
+
VALUE resolve_tamashii_bt_event(VALUE self, tamashii_bt_event_t *event) {
|
4
|
+
int msg_id = xpc_dictionary_get_int64(event->object, "kCBMsgId");
|
5
|
+
char* type;
|
6
|
+
|
7
|
+
// TODO: Refactor Event Type define
|
8
|
+
switch(msg_id) {
|
9
|
+
case BLUED_STATE_CHANGED:
|
10
|
+
{
|
11
|
+
xpc_object_t args = xpc_dictionary_get_value(event->object, "kCBMsgArgs");
|
12
|
+
|
13
|
+
int state = xpc_dictionary_get_int64(args, "kCBMsgArgState");
|
14
|
+
VALUE device = rb_ivar_get(self, rb_intern("@device"));
|
15
|
+
rb_ivar_set(device, rb_intern("@state"), INT2FIX(state));
|
16
|
+
|
17
|
+
type = "state_changed";
|
18
|
+
break;
|
19
|
+
}
|
20
|
+
case BLUED_START_ADVERTISING:
|
21
|
+
{
|
22
|
+
// TODO: Handle Error
|
23
|
+
type = "start";
|
24
|
+
break;
|
25
|
+
}
|
26
|
+
case BLUED_STOP_ADVERTISING:
|
27
|
+
{
|
28
|
+
type = "stop";
|
29
|
+
break;
|
30
|
+
}
|
31
|
+
default:
|
32
|
+
// TODO: Add all supported event
|
33
|
+
type = "unknown";
|
34
|
+
return Qnil;
|
35
|
+
}
|
36
|
+
|
37
|
+
rb_ivar_set(self, rb_intern("@type"), ID2SYM(rb_intern(type)));
|
38
|
+
return Qtrue;
|
39
|
+
}
|
40
|
+
|
41
|
+
void rb_tamashii_bt_event_free(void *ptr)
|
42
|
+
{
|
43
|
+
tamashii_bt_event_t *event = (tamashii_bt_event_t*)ptr;
|
44
|
+
xpc_release(event->object);
|
45
|
+
free(event);
|
46
|
+
}
|
47
|
+
|
48
|
+
VALUE rb_tamashii_bt_event_create(xpc_object_t xpc_event, VALUE device)
|
49
|
+
{
|
50
|
+
VALUE rb_event;
|
51
|
+
// TODO: Is it necessary allocate memory or simple reference xpc object?
|
52
|
+
tamashii_bt_event_t *event = malloc(sizeof(tamashii_bt_event_t));
|
53
|
+
event->object = xpc_event;
|
54
|
+
event->type = xpc_get_type(xpc_event);
|
55
|
+
|
56
|
+
rb_event = Data_Wrap_Struct(rb_cTamashiiBluetoothEvent, 0, rb_tamashii_bt_event_free, event);
|
57
|
+
rb_ivar_set(rb_event, rb_intern("@device"), device);
|
58
|
+
|
59
|
+
if(event->type == XPC_TYPE_ERROR) {
|
60
|
+
const char* message = "Unknow Error";
|
61
|
+
|
62
|
+
if (event->object == XPC_ERROR_CONNECTION_INTERRUPTED) {
|
63
|
+
message = "Connection Interrupted";
|
64
|
+
} else if (event->object == XPC_ERROR_CONNECTION_INVALID) {
|
65
|
+
message = "Connection Invalid";
|
66
|
+
}
|
67
|
+
|
68
|
+
rb_raise(rb_eRuntimeError, "%s", message);
|
69
|
+
} else if (event->type == XPC_TYPE_DICTIONARY) {
|
70
|
+
resolve_tamashii_bt_event(rb_event, event);
|
71
|
+
}
|
72
|
+
|
73
|
+
return rb_event;
|
74
|
+
}
|
75
|
+
|
76
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#include "../bluetooth.h"
|
2
|
+
|
3
|
+
xpc_object_t rb_create_xpc_data(VALUE object)
|
4
|
+
{
|
5
|
+
int i;
|
6
|
+
int length = (int)RARRAY_LEN(object);
|
7
|
+
unsigned char *bytes;
|
8
|
+
VALUE *ptr = RARRAY_PTR(object);
|
9
|
+
|
10
|
+
bytes = malloc(sizeof(unsigned char) * length);
|
11
|
+
|
12
|
+
for(i = 0; i < length; i++) {
|
13
|
+
bytes[i] = (char)FIX2UINT(ptr[i]);
|
14
|
+
}
|
15
|
+
|
16
|
+
return xpc_data_create(bytes, length);
|
17
|
+
}
|
18
|
+
|
19
|
+
xpc_object_t rb_object_to_xpc_value(VALUE object)
|
20
|
+
{
|
21
|
+
xpc_object_t xpc_object;
|
22
|
+
|
23
|
+
switch(TYPE(object)) {
|
24
|
+
case T_FIXNUM:
|
25
|
+
xpc_object = xpc_int64_create(FIX2INT(object));
|
26
|
+
break;
|
27
|
+
case T_HASH:
|
28
|
+
xpc_object = rb_hash_to_xpc_object(object);
|
29
|
+
break;
|
30
|
+
case T_STRING:
|
31
|
+
xpc_object = xpc_string_create(StringValuePtr(object));
|
32
|
+
break;
|
33
|
+
case T_ARRAY:
|
34
|
+
xpc_object = rb_create_xpc_data(object);
|
35
|
+
break;
|
36
|
+
default:
|
37
|
+
rb_raise(rb_eTypeError, "Unsupported Type");
|
38
|
+
}
|
39
|
+
|
40
|
+
return xpc_object;
|
41
|
+
}
|
42
|
+
|
43
|
+
xpc_object_t rb_hash_to_xpc_object(VALUE hash)
|
44
|
+
{
|
45
|
+
xpc_object_t xpc_object = xpc_dictionary_create(NULL, NULL, 0);
|
46
|
+
xpc_object_t xpc_value;
|
47
|
+
// TODO: Check rb_hash_keys location
|
48
|
+
VALUE keys = rb_funcall(hash, rb_intern("keys"), 0);
|
49
|
+
VALUE key, key_to_s, value, value_to_s;
|
50
|
+
st_index_t size = RHASH_SIZE(hash);
|
51
|
+
st_index_t i = 0;
|
52
|
+
|
53
|
+
for(; i < size; i++) {
|
54
|
+
key = rb_ary_entry(keys, i);
|
55
|
+
key_to_s = rb_funcall(key, rb_intern("to_s"), 0);
|
56
|
+
value = rb_hash_aref(hash, key);
|
57
|
+
|
58
|
+
xpc_value = rb_object_to_xpc_value(value);
|
59
|
+
xpc_dictionary_set_value(xpc_object, StringValuePtr(key_to_s), xpc_value);
|
60
|
+
|
61
|
+
if(xpc_value) {
|
62
|
+
xpc_release(xpc_value);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
return xpc_object;
|
67
|
+
}
|
data/ext/bluetooth/osx.h
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#ifndef TAMASHII_BLUETOOTH_OSX_H
|
2
|
+
#define TAMASHII_BLUETOOTH_OSX_H 1
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
#include "xpc/xpc.h"
|
6
|
+
|
7
|
+
#define BLUED_STATE_CHANGED 6
|
8
|
+
#define BLUED_MSG_INIT 1
|
9
|
+
|
10
|
+
#ifdef OSX_YOSEMITE
|
11
|
+
#define BLUED_MSG_ADVERTISING 8
|
12
|
+
#define BLUED_MSG_STOP 9
|
13
|
+
#define BLUED_START_ADVERTISING 16
|
14
|
+
#define BLUED_STOP_ADVERTISING 17
|
15
|
+
#else
|
16
|
+
#define BLUED_MSG_ADVERTISING 16
|
17
|
+
#define BLUED_MSG_STOP 17
|
18
|
+
#define BLUED_START_ADVERTISING 27
|
19
|
+
#define BLUED_STOP_ADVERTISING 28
|
20
|
+
#endif
|
21
|
+
|
22
|
+
static const char* service_name = "com.apple.blued";
|
23
|
+
|
24
|
+
typedef struct tamashii_bluetooth_device {
|
25
|
+
xpc_connection_t connection;
|
26
|
+
} tamatshii_bt_device_t;
|
27
|
+
|
28
|
+
typedef struct tamashii_bluetooth_event {
|
29
|
+
xpc_object_t object;
|
30
|
+
xpc_type_t type;
|
31
|
+
} tamashii_bt_event_t;
|
32
|
+
|
33
|
+
// TODO: Refactor below methods
|
34
|
+
xpc_object_t rb_hash_to_xpc_object(VALUE);
|
35
|
+
xpc_object_t rb_object_to_xpc_value(VALUE);
|
36
|
+
xpc_object_t rb_create_xpc_data(VALUE);
|
37
|
+
|
38
|
+
xpc_object_t tamashii_create_message(int, xpc_object_t);
|
39
|
+
xpc_object_t tamashii_create_message_from_advertisment(VALUE);
|
40
|
+
|
41
|
+
void rb_tamashii_bt_event_free(void *);
|
42
|
+
VALUE rb_tamashii_bt_event_create(xpc_object_t, VALUE);
|
43
|
+
|
44
|
+
VALUE resolve_tamashii_bt_event(VALUE self, tamashii_bt_event_t*);
|
45
|
+
#endif
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tamashii
|
4
|
+
module Bluetooth
|
5
|
+
module Beacon
|
6
|
+
# Line Beacon Format
|
7
|
+
class LINE < Advertisment
|
8
|
+
ADTYPE_FLAGS = 0x01
|
9
|
+
ADTYPE_COMPLETE_16_BIT_SERVICE_UUID = 0x03
|
10
|
+
ADTYPE_SERVICE_DATA = 0x16
|
11
|
+
UUID16LE_FOR_LINECORP = [0x6F, 0xFE].freeze
|
12
|
+
|
13
|
+
attr_reader :hardware_id
|
14
|
+
|
15
|
+
def initialize(id)
|
16
|
+
# TODO: Support append message
|
17
|
+
@hardware_id = id
|
18
|
+
@data = create_data
|
19
|
+
end
|
20
|
+
|
21
|
+
def frame_type
|
22
|
+
[0x02]
|
23
|
+
end
|
24
|
+
|
25
|
+
def tx_power
|
26
|
+
[0x7F]
|
27
|
+
end
|
28
|
+
|
29
|
+
def hardware_hex
|
30
|
+
hardware_id.scan(/../).map(&:hex)
|
31
|
+
end
|
32
|
+
|
33
|
+
def beacon_frame
|
34
|
+
[
|
35
|
+
frame_type,
|
36
|
+
hardware_hex,
|
37
|
+
tx_power,
|
38
|
+
[0x00] # Message
|
39
|
+
].flatten
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_data
|
43
|
+
[
|
44
|
+
flag,
|
45
|
+
completed_uuid,
|
46
|
+
service_data
|
47
|
+
].flatten
|
48
|
+
end
|
49
|
+
|
50
|
+
def flag
|
51
|
+
add_structure(ADTYPE_FLAGS, [0x06])
|
52
|
+
end
|
53
|
+
|
54
|
+
def completed_uuid
|
55
|
+
add_structure(
|
56
|
+
ADTYPE_COMPLETE_16_BIT_SERVICE_UUID,
|
57
|
+
UUID16LE_FOR_LINECORP
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def service_data
|
62
|
+
add_structure(
|
63
|
+
ADTYPE_SERVICE_DATA,
|
64
|
+
UUID16LE_FOR_LINECORP + beacon_frame
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_structure(type, buffer)
|
69
|
+
header = [buffer.length + 1, type]
|
70
|
+
[header, buffer]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tamashii
|
4
|
+
module Bluetooth
|
5
|
+
# Device
|
6
|
+
class Device
|
7
|
+
STATE = %i[
|
8
|
+
unknown
|
9
|
+
resetting
|
10
|
+
unsupported
|
11
|
+
unauthorized
|
12
|
+
powered_off
|
13
|
+
powered_on
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
def state
|
17
|
+
STATE[@state]
|
18
|
+
end
|
19
|
+
|
20
|
+
def on?
|
21
|
+
state == :powered_on
|
22
|
+
end
|
23
|
+
|
24
|
+
def off?
|
25
|
+
state == :powered_off
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
lib = File.expand_path('lib', __dir__)
|
5
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
+
require 'tamashii/bluetooth/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'tamashii-bluetooth'
|
10
|
+
spec.version = Tamashii::Bluetooth::VERSION
|
11
|
+
spec.authors = ['蒼時弦也']
|
12
|
+
spec.email = ['elct9620@frost.tw']
|
13
|
+
|
14
|
+
spec.summary = 'The bluetooth implement for tamashii'
|
15
|
+
spec.description = 'The bluetooth implement for tamashii'
|
16
|
+
spec.homepage = 'https://tamashii.io'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
spec.bindir = 'exe'
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
spec.extensions = ['ext/bluetooth/extconf.rb']
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
27
|
+
spec.add_development_dependency 'overcommit', '~> 0.44.0'
|
28
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
29
|
+
spec.add_development_dependency 'rake-compiler'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 0.54.0'
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tamashii-bluetooth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0.beta.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- 蒼時弦也
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: overcommit
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.44.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.44.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake-compiler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.54.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.54.0
|
97
|
+
description: The bluetooth implement for tamashii
|
98
|
+
email:
|
99
|
+
- elct9620@frost.tw
|
100
|
+
executables: []
|
101
|
+
extensions:
|
102
|
+
- ext/bluetooth/extconf.rb
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- ".overcommit.yml"
|
107
|
+
- ".rspec"
|
108
|
+
- ".rubocop.yml"
|
109
|
+
- ".travis.yml"
|
110
|
+
- Gemfile
|
111
|
+
- README.md
|
112
|
+
- Rakefile
|
113
|
+
- bin/console
|
114
|
+
- bin/setup
|
115
|
+
- ext/bluetooth/bluetooth.h
|
116
|
+
- ext/bluetooth/extconf.rb
|
117
|
+
- ext/bluetooth/init.h
|
118
|
+
- ext/bluetooth/osx.h
|
119
|
+
- ext/bluetooth/osx/bluetooth.c
|
120
|
+
- ext/bluetooth/osx/event.c
|
121
|
+
- ext/bluetooth/osx/xpc.c
|
122
|
+
- lib/tamashii/bluetooth.rb
|
123
|
+
- lib/tamashii/bluetooth/beacon.rb
|
124
|
+
- lib/tamashii/bluetooth/beacon/line.rb
|
125
|
+
- lib/tamashii/bluetooth/device.rb
|
126
|
+
- lib/tamashii/bluetooth/version.rb
|
127
|
+
- tamashii-bluetooth.gemspec
|
128
|
+
homepage: https://tamashii.io
|
129
|
+
licenses: []
|
130
|
+
metadata: {}
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">"
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 1.3.1
|
145
|
+
requirements: []
|
146
|
+
rubyforge_project:
|
147
|
+
rubygems_version: 2.6.14
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: The bluetooth implement for tamashii
|
151
|
+
test_files: []
|