flic 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +30 -0
- data/Rakefile +12 -0
- data/flic.gemspec +25 -0
- data/lib/flic.rb +7 -0
- data/lib/flic/client.rb +247 -0
- data/lib/flic/client/connection.rb +87 -0
- data/lib/flic/event_bus.rb +81 -0
- data/lib/flic/event_bus/driver.rb +23 -0
- data/lib/flic/event_bus/subscription.rb +66 -0
- data/lib/flic/protocol.rb +64 -0
- data/lib/flic/protocol/commands.rb +44 -0
- data/lib/flic/protocol/commands/cancel_scan_wizard.rb +15 -0
- data/lib/flic/protocol/commands/change_mode_parameters.rb +17 -0
- data/lib/flic/protocol/commands/command.rb +21 -0
- data/lib/flic/protocol/commands/create_connection_channel.rb +20 -0
- data/lib/flic/protocol/commands/create_scan_wizard.rb +15 -0
- data/lib/flic/protocol/commands/create_scanner.rb +14 -0
- data/lib/flic/protocol/commands/force_disconnect.rb +15 -0
- data/lib/flic/protocol/commands/get_button_uuid.rb +15 -0
- data/lib/flic/protocol/commands/get_info.rb +12 -0
- data/lib/flic/protocol/commands/ping.rb +14 -0
- data/lib/flic/protocol/commands/remove_connection_channel.rb +14 -0
- data/lib/flic/protocol/commands/remove_scanner.rb +14 -0
- data/lib/flic/protocol/events.rb +60 -0
- data/lib/flic/protocol/events/advertisement_packet.rb +25 -0
- data/lib/flic/protocol/events/bluetooth_controller_state_change.rb +15 -0
- data/lib/flic/protocol/events/button_click_or_hold.rb +19 -0
- data/lib/flic/protocol/events/button_single_or_double_click.rb +19 -0
- data/lib/flic/protocol/events/button_single_or_double_click_or_hold.rb +19 -0
- data/lib/flic/protocol/events/button_up_or_down.rb +19 -0
- data/lib/flic/protocol/events/connection_channel_removed.rb +16 -0
- data/lib/flic/protocol/events/connection_status_changed.rb +19 -0
- data/lib/flic/protocol/events/create_connection_channel_response.rb +18 -0
- data/lib/flic/protocol/events/event.rb +21 -0
- data/lib/flic/protocol/events/get_button_uuid_response.rb +17 -0
- data/lib/flic/protocol/events/get_info_response.rb +27 -0
- data/lib/flic/protocol/events/got_space_for_new_connection.rb +14 -0
- data/lib/flic/protocol/events/new_verified_button.rb +15 -0
- data/lib/flic/protocol/events/no_space_for_new_connection.rb +14 -0
- data/lib/flic/protocol/events/ping_response.rb +14 -0
- data/lib/flic/protocol/events/scan_wizard_button_connected.rb +14 -0
- data/lib/flic/protocol/events/scan_wizard_completed.rb +16 -0
- data/lib/flic/protocol/events/scan_wizard_found_private_button.rb +14 -0
- data/lib/flic/protocol/events/scan_wizard_found_public_button.rb +19 -0
- data/lib/flic/protocol/packet_header.rb +12 -0
- data/lib/flic/protocol/primitives.rb +22 -0
- data/lib/flic/protocol/primitives/bluetooth_address.rb +35 -0
- data/lib/flic/protocol/primitives/bluetooth_address_type.rb +13 -0
- data/lib/flic/protocol/primitives/bluetooth_controller_state.rb +14 -0
- data/lib/flic/protocol/primitives/boolean.rb +19 -0
- data/lib/flic/protocol/primitives/click_type.rb +17 -0
- data/lib/flic/protocol/primitives/connection_status.rb +14 -0
- data/lib/flic/protocol/primitives/create_connection_channel_error.rb +13 -0
- data/lib/flic/protocol/primitives/device_name.rb +44 -0
- data/lib/flic/protocol/primitives/disconnect_reason.rb +15 -0
- data/lib/flic/protocol/primitives/enum.rb +85 -0
- data/lib/flic/protocol/primitives/latency_mode.rb +14 -0
- data/lib/flic/protocol/primitives/removed_reason.rb +18 -0
- data/lib/flic/protocol/primitives/scan_wizard_result.rb +18 -0
- data/lib/flic/protocol/primitives/uuid.rb +23 -0
- data/lib/flic/version.rb +3 -0
- 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,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
|