flic 0.0.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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +30 -0
  8. data/Rakefile +12 -0
  9. data/flic.gemspec +25 -0
  10. data/lib/flic.rb +7 -0
  11. data/lib/flic/client.rb +247 -0
  12. data/lib/flic/client/connection.rb +87 -0
  13. data/lib/flic/event_bus.rb +81 -0
  14. data/lib/flic/event_bus/driver.rb +23 -0
  15. data/lib/flic/event_bus/subscription.rb +66 -0
  16. data/lib/flic/protocol.rb +64 -0
  17. data/lib/flic/protocol/commands.rb +44 -0
  18. data/lib/flic/protocol/commands/cancel_scan_wizard.rb +15 -0
  19. data/lib/flic/protocol/commands/change_mode_parameters.rb +17 -0
  20. data/lib/flic/protocol/commands/command.rb +21 -0
  21. data/lib/flic/protocol/commands/create_connection_channel.rb +20 -0
  22. data/lib/flic/protocol/commands/create_scan_wizard.rb +15 -0
  23. data/lib/flic/protocol/commands/create_scanner.rb +14 -0
  24. data/lib/flic/protocol/commands/force_disconnect.rb +15 -0
  25. data/lib/flic/protocol/commands/get_button_uuid.rb +15 -0
  26. data/lib/flic/protocol/commands/get_info.rb +12 -0
  27. data/lib/flic/protocol/commands/ping.rb +14 -0
  28. data/lib/flic/protocol/commands/remove_connection_channel.rb +14 -0
  29. data/lib/flic/protocol/commands/remove_scanner.rb +14 -0
  30. data/lib/flic/protocol/events.rb +60 -0
  31. data/lib/flic/protocol/events/advertisement_packet.rb +25 -0
  32. data/lib/flic/protocol/events/bluetooth_controller_state_change.rb +15 -0
  33. data/lib/flic/protocol/events/button_click_or_hold.rb +19 -0
  34. data/lib/flic/protocol/events/button_single_or_double_click.rb +19 -0
  35. data/lib/flic/protocol/events/button_single_or_double_click_or_hold.rb +19 -0
  36. data/lib/flic/protocol/events/button_up_or_down.rb +19 -0
  37. data/lib/flic/protocol/events/connection_channel_removed.rb +16 -0
  38. data/lib/flic/protocol/events/connection_status_changed.rb +19 -0
  39. data/lib/flic/protocol/events/create_connection_channel_response.rb +18 -0
  40. data/lib/flic/protocol/events/event.rb +21 -0
  41. data/lib/flic/protocol/events/get_button_uuid_response.rb +17 -0
  42. data/lib/flic/protocol/events/get_info_response.rb +27 -0
  43. data/lib/flic/protocol/events/got_space_for_new_connection.rb +14 -0
  44. data/lib/flic/protocol/events/new_verified_button.rb +15 -0
  45. data/lib/flic/protocol/events/no_space_for_new_connection.rb +14 -0
  46. data/lib/flic/protocol/events/ping_response.rb +14 -0
  47. data/lib/flic/protocol/events/scan_wizard_button_connected.rb +14 -0
  48. data/lib/flic/protocol/events/scan_wizard_completed.rb +16 -0
  49. data/lib/flic/protocol/events/scan_wizard_found_private_button.rb +14 -0
  50. data/lib/flic/protocol/events/scan_wizard_found_public_button.rb +19 -0
  51. data/lib/flic/protocol/packet_header.rb +12 -0
  52. data/lib/flic/protocol/primitives.rb +22 -0
  53. data/lib/flic/protocol/primitives/bluetooth_address.rb +35 -0
  54. data/lib/flic/protocol/primitives/bluetooth_address_type.rb +13 -0
  55. data/lib/flic/protocol/primitives/bluetooth_controller_state.rb +14 -0
  56. data/lib/flic/protocol/primitives/boolean.rb +19 -0
  57. data/lib/flic/protocol/primitives/click_type.rb +17 -0
  58. data/lib/flic/protocol/primitives/connection_status.rb +14 -0
  59. data/lib/flic/protocol/primitives/create_connection_channel_error.rb +13 -0
  60. data/lib/flic/protocol/primitives/device_name.rb +44 -0
  61. data/lib/flic/protocol/primitives/disconnect_reason.rb +15 -0
  62. data/lib/flic/protocol/primitives/enum.rb +85 -0
  63. data/lib/flic/protocol/primitives/latency_mode.rb +14 -0
  64. data/lib/flic/protocol/primitives/removed_reason.rb +18 -0
  65. data/lib/flic/protocol/primitives/scan_wizard_result.rb +18 -0
  66. data/lib/flic/protocol/primitives/uuid.rb +23 -0
  67. data/lib/flic/version.rb +3 -0
  68. metadata +180 -0
@@ -0,0 +1,81 @@
1
+ require 'flic'
2
+
3
+ require 'thread'
4
+
5
+ module Flic
6
+ class EventBus
7
+ class Error < StandardError; end
8
+ class EventBusShutdown < Error; end
9
+
10
+ autoload :Driver, 'flic/event_bus/driver'
11
+ autoload :Subscription, 'flic/event_bus/subscription'
12
+
13
+ def initialize
14
+ @semaphore = Mutex.new
15
+ @subscriptions = []
16
+ @is_shutdown = false
17
+ end
18
+
19
+ def subscribe(subscription = Subscription.new)
20
+ @semaphore.synchronize do
21
+ raise EventBusShutdown if shutdown?
22
+
23
+ @subscriptions << subscription
24
+ end
25
+
26
+ if block_given?
27
+ begin
28
+ yield subscription
29
+ ensure
30
+ unsubscribe subscription
31
+ end
32
+ end
33
+ end
34
+
35
+ def unsubscribe(subscription)
36
+ @semaphore.synchronize do
37
+ raise EventBusShutdown if shutdown?
38
+
39
+ @subscriptions.delete subscription
40
+ end
41
+
42
+ subscription.destroy
43
+ end
44
+
45
+ def listen
46
+ subscribe do |subscription|
47
+ subscription.listen do
48
+ yield
49
+ end
50
+ end
51
+ end
52
+
53
+ def broadcast(*args)
54
+ @semaphore.synchronize do
55
+ raise EventBusShutdown if shutdown?
56
+
57
+ @subscriptions.each do |subscription|
58
+ subscription.publish(*args)
59
+ end
60
+ end
61
+ end
62
+
63
+ def shutdown?
64
+ @is_shutdown
65
+ end
66
+
67
+ def shutdown
68
+ @semaphore.synchronize do
69
+ unless shutdown?
70
+ @is_shutdown = true
71
+
72
+ @subscriptions.each do |subscription|
73
+ subscription.destroy
74
+ end
75
+
76
+ @subscriptions.clear
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,23 @@
1
+ require 'flic/event_bus'
2
+
3
+ require 'thread'
4
+
5
+ module Flic
6
+ class EventBus
7
+ class Driver < Thread
8
+ attr_reader :event_bus
9
+
10
+ def initialize
11
+ @event_bus = EventBus.new
12
+
13
+ super() do
14
+ begin
15
+ yield(event_bus)
16
+ ensure
17
+ event_bus.shutdown
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,66 @@
1
+ require 'flic/event_bus'
2
+
3
+ require 'thread'
4
+
5
+ module Flic
6
+ class EventBus
7
+ class Subscription
8
+ class SubscriptionDestroyed < Error
9
+ end
10
+
11
+ def initialize
12
+ @queue = Queue.new
13
+ @semaphore = Mutex.new
14
+ @is_destroyed = false
15
+ end
16
+
17
+ def listen
18
+ loop do
19
+ yield *next_event!
20
+ end
21
+ end
22
+
23
+ def next_event_nonblock
24
+ next_event unless @queue.empty?
25
+ end
26
+
27
+ def next_event
28
+ @semaphore.synchronize do
29
+ unless destroyed?
30
+ control, event = @queue.pop
31
+
32
+ case control
33
+ when :event
34
+ event
35
+ when :destroy
36
+ @is_destroyed = true
37
+
38
+ nil
39
+ else
40
+ raise NotImplementedError
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def next_event!
47
+ event = next_event
48
+ raise SubscriptionDestroyed if destroyed?
49
+ event
50
+ end
51
+
52
+ def publish(*args)
53
+ raise SubscriptionDestroyed if destroyed?
54
+ @queue.push [:event, *args]
55
+ end
56
+
57
+ def destroyed?
58
+ @is_destroyed
59
+ end
60
+
61
+ def destroy
62
+ @queue.push [:destroy] unless destroyed?
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,64 @@
1
+ require 'flic'
2
+
3
+ module Flic
4
+ module Protocol
5
+ class Error < StandardError; end
6
+
7
+ autoload :Commands, 'flic/protocol/commands'
8
+ autoload :Events, 'flic/protocol/events'
9
+ autoload :PacketHeader, 'flic/protocol/packet_header'
10
+ autoload :Primitives, 'flic/protocol/primitives'
11
+
12
+ INVALID_BUTTON_UUID = '00000000-0000-0000-0000-000000000000'.freeze
13
+
14
+ def self.serialize_command(command)
15
+ case command
16
+ when Commands::Command
17
+ command.to_binary_s
18
+ else
19
+ raise NotImplementedError
20
+ end
21
+ rescue
22
+ raise Error, "Cannot serialize command `#{command.inspect}`"
23
+ end
24
+
25
+ def self.parse_command(serialized_command)
26
+ command = Commands::Command.read(serialized_command)
27
+ opcode = command.opcode
28
+ command_class = Commands::Command.command_class_for_opcode(opcode)
29
+
30
+ if command_class
31
+ command_class.read(serialized_command)
32
+ else
33
+ raise NotImplementedError, "Unknown command opcode #{opcode}"
34
+ end
35
+ rescue
36
+ raise Error, "Cannot parse event `#{serialized_command.inspect}`"
37
+ end
38
+
39
+ def self.serialize_event(event)
40
+ case event
41
+ when Commands::Event
42
+ event.to_binary_s
43
+ else
44
+ raise NotImplementedError
45
+ end
46
+ rescue
47
+ raise Error, "Cannot serialize event `#{event.inspect}`"
48
+ end
49
+
50
+ def self.parse_event(serialized_event)
51
+ event = Events::Event.read(serialized_event)
52
+ opcode = event.opcode
53
+ event_class = Events.event_class_for_opcode(opcode)
54
+
55
+ if event_class
56
+ event_class.read(serialized_event)
57
+ else
58
+ raise NotImplementedError, "Unknown event opcode #{opcode}"
59
+ end
60
+ rescue
61
+ raise Error, "Cannot parse event `#{serialized_event.inspect}`"
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,44 @@
1
+ require 'flic/protocol'
2
+
3
+ module Flic
4
+ module Protocol
5
+ module Commands
6
+ autoload :CancelScanWizard, 'flic/protocol/commands/cancel_scan_wizard'
7
+ autoload :ChangeModeParameters, 'flic/protocol/commands/change_mode_parameters'
8
+ autoload :Command, 'flic/protocol/commands/command'
9
+ autoload :CreateConnectionChannel, 'flic/protocol/commands/create_connection_channel'
10
+ autoload :CreateScanWizard, 'flic/protocol/commands/create_scan_wizard'
11
+ autoload :CreateScanner, 'flic/protocol/commands/create_scanner'
12
+ autoload :ForceDisconnect, 'flic/protocol/commands/force_disconnect'
13
+ autoload :GetButtonUuid, 'flic/protocol/commands/get_button_uuid'
14
+ autoload :GetInfo, 'flic/protocol/commands/get_info'
15
+ autoload :Ping, 'flic/protocol/commands/ping'
16
+ autoload :RemoveConnectionChannel, 'flic/protocol/commands/remove_connection_channel'
17
+ autoload :RemoveScanner, 'flic/protocol/commands/remove_scanner'
18
+
19
+ COMMAND_CLASS_OPCODE = {
20
+ Commands::GetInfo => 0x00,
21
+ Commands::CreateScanner => 0x01,
22
+ Commands::RemoveScanner => 0x02,
23
+ Commands::CreateConnectionChannel => 0x03,
24
+ Commands::RemoveConnectionChannel => 0x04,
25
+ Commands::ForceDisconnect => 0x05,
26
+ Commands::ChangeModeParameters => 0x06,
27
+ Commands::Ping => 0x07,
28
+ Commands::GetButtonUuid => 0x08,
29
+ Commands::CreateScanWizard => 0x09,
30
+ Commands::CancelScanWizard => 0x0A
31
+ }.freeze
32
+
33
+ OPCODE_COMMAND_CLASS = COMMAND_CLASS_OPCODE.invert.freeze
34
+
35
+ def self.command_class_for_opcode(opcode)
36
+ OPCODE_COMMAND_CLASS[opcode]
37
+ end
38
+
39
+ def self.opcode_for_command_class(command_class)
40
+ COMMAND_CLASS_OPCODE[command_class]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ require 'flic/protocol/commands'
2
+ require 'flic/protocol/commands/command'
3
+ require 'flic/protocol/primitives/bluetooth_address'
4
+
5
+ module Flic
6
+ module Protocol
7
+ module Commands
8
+ class CancelScanWizard < Command
9
+ endian :little
10
+
11
+ uint32 :scan_wizard_id
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ require 'flic/protocol/commands'
2
+ require 'flic/protocol/commands/command'
3
+ require 'flic/protocol/primitives/latency_mode'
4
+
5
+ module Flic
6
+ module Protocol
7
+ module Commands
8
+ class ChangeModeParameters < Command
9
+ endian :little
10
+
11
+ uint32 :connection_id
12
+ latency_mode :latency_mode
13
+ uint16 :auto_disconnect_time
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ require 'flic/protocol/commands'
2
+
3
+ require 'bindata'
4
+
5
+ module Flic
6
+ module Protocol
7
+ module Commands
8
+ class Command < BinData::Record
9
+ endian :little
10
+
11
+ uint8 :opcode, initial_value: -> { init_opcode }
12
+
13
+ private
14
+
15
+ def init_opcode
16
+ Commands.opcode_for_command_class(self.class)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ require 'flic/protocol/commands'
2
+ require 'flic/protocol/commands/command'
3
+ require 'flic/protocol/primitives/bluetooth_address'
4
+ require 'flic/protocol/primitives/latency_mode'
5
+
6
+ module Flic
7
+ module Protocol
8
+ module Commands
9
+ class CreateConnectionChannel < Command
10
+ endian :little
11
+
12
+ uint32 :connection_id
13
+ bluetooth_address :bluetooth_address
14
+
15
+ latency_mode :latency_mode
16
+ uint16 :auto_disconnect_time, initial_value: 512 # 512 means disabled
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ require 'flic/protocol/commands'
2
+ require 'flic/protocol/commands/command'
3
+ require 'flic/protocol/primitives/bluetooth_address'
4
+
5
+ module Flic
6
+ module Protocol
7
+ module Commands
8
+ class CreateScanWizard < Command
9
+ endian :little
10
+
11
+ uint32 :scan_wizard_id
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require 'flic/protocol/commands'
2
+ require 'flic/protocol/commands/command'
3
+
4
+ module Flic
5
+ module Protocol
6
+ module Commands
7
+ class CreateScanner < Command
8
+ endian :little
9
+
10
+ uint32 :scan_id
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ require 'flic/protocol/commands'
2
+ require 'flic/protocol/commands/command'
3
+ require 'flic/protocol/primitives/bluetooth_address'
4
+
5
+ module Flic
6
+ module Protocol
7
+ module Commands
8
+ class ForceDisconnect < Command
9
+ endian :little
10
+
11
+ bluetooth_address :bluetooth_address
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'flic/protocol/commands'
2
+ require 'flic/protocol/commands/command'
3
+ require 'flic/protocol/primitives/bluetooth_address'
4
+
5
+ module Flic
6
+ module Protocol
7
+ module Commands
8
+ class GetButtonUuid < Command
9
+ endian :little
10
+
11
+ bluetooth_address :bluetooth_address
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ require 'flic/protocol/commands'
2
+ require 'flic/protocol/commands/command'
3
+
4
+ module Flic
5
+ module Protocol
6
+ module Commands
7
+ class GetInfo < Command
8
+ endian :little
9
+ end
10
+ end
11
+ end
12
+ end