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.
- 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
|