tamashii-bluetooth 0.1.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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: []