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 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
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,6 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+
4
+ Style/GlobalVars:
5
+ Exclude:
6
+ - '**/extconf.rb'
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.3
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in tamashii_bluetooth.gemspec
8
+ gemspec
9
+
10
+ gem 'pry'
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'tamashii/bluetooth'
6
+
7
+ require 'pry'
8
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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
+ }
@@ -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,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Bluetooth
5
+ # Beacon Support
6
+ module Beacon
7
+ autoload :LINE, 'tamashii/bluetooth/beacon/line'
8
+ end
9
+ end
10
+ 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Bluetooth
5
+ VERSION = '0.1.0.beta.1'
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tamashii/bluetooth/version'
4
+ require 'tamashii/bluetooth/bluetooth'
5
+ require 'tamashii/bluetooth/device'
6
+
7
+ module Tamashii
8
+ # Tamashii Bluetooth
9
+ module Bluetooth
10
+ # TODO
11
+ end
12
+ 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: []