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,16 @@
|
|
1
|
+
require 'flic/protocol/events'
|
2
|
+
require 'flic/protocol/events/event'
|
3
|
+
require 'flic/protocol/primitives/scan_wizard_result'
|
4
|
+
|
5
|
+
module Flic
|
6
|
+
module Protocol
|
7
|
+
module Events
|
8
|
+
class ScanWizardCompleted < Event
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
uint32 :scan_wizard_id
|
12
|
+
scan_wizard_result :scan_wizard_result
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'flic/protocol/events'
|
2
|
+
require 'flic/protocol/events/event'
|
3
|
+
require 'flic/protocol/primitives/bluetooth_address'
|
4
|
+
require 'flic/protocol/primitives/device_name'
|
5
|
+
|
6
|
+
module Flic
|
7
|
+
module Protocol
|
8
|
+
module Events
|
9
|
+
class ScanWizardFoundPublicButton < Event
|
10
|
+
endian :little
|
11
|
+
|
12
|
+
uint32 :scan_wizard_id
|
13
|
+
|
14
|
+
bluetooth_address :bluetooth_address
|
15
|
+
device_name :name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'flic/protocol'
|
2
|
+
|
3
|
+
module Flic
|
4
|
+
module Protocol
|
5
|
+
module Primitives
|
6
|
+
autoload :BluetoothAddress, 'flic/protocol/primitives/bluetooth_address'
|
7
|
+
autoload :BluetoothAddressType, 'flic/protocol/primitives/bluetooth_address_type'
|
8
|
+
autoload :BluetoothControllerState, 'flic/protocol/primitives/bluetooth_controller_state'
|
9
|
+
autoload :Boolean, 'flic/protocol/primitives/boolean'
|
10
|
+
autoload :ClickType, 'flic/protocol/primitives/click_type'
|
11
|
+
autoload :ConnectionStatus, 'flic/protocol/primitives/connection_status'
|
12
|
+
autoload :CreateConnectionChannelError, 'flic/protocol/primitives/create_connection_channel_error'
|
13
|
+
autoload :DeviceName, 'flic/protocol/primitives/device_name'
|
14
|
+
autoload :DisconnectReason, 'flic/protocol/primitives/disconnect_reason'
|
15
|
+
autoload :Enum, 'flic/protocol/primitives/enum'
|
16
|
+
autoload :LatencyMode, 'flic/protocol/primitives/latency_mode'
|
17
|
+
autoload :RemovedReason, 'flic/protocol/primitives/removed_reason'
|
18
|
+
autoload :ScanWizardResult, 'flic/protocol/primitives/scan_wizard_result'
|
19
|
+
autoload :Uuid, 'flic/protocol/primitives/uuid'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
|
3
|
+
require 'bindata'
|
4
|
+
require 'scanf'
|
5
|
+
|
6
|
+
module Flic
|
7
|
+
module Protocol
|
8
|
+
module Primitives
|
9
|
+
class BluetoothAddress < BinData::Primitive
|
10
|
+
PRINTF_FORMAT_STRING = '%.2X:%.2X:%.2X:%.2X:%.2X:%.2X'.freeze
|
11
|
+
SCANF_FORMAT_STRING = '%X:%X:%X:%X:%X:%X'.freeze
|
12
|
+
|
13
|
+
array :little_endian_octets, type: :uint8, initial_length: 6
|
14
|
+
|
15
|
+
def get
|
16
|
+
sprintf(PRINTF_FORMAT_STRING, *big_endian_octets)
|
17
|
+
end
|
18
|
+
|
19
|
+
def set(value)
|
20
|
+
self.big_endian_octets = value.scanf(SCANF_FORMAT_STRING)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def big_endian_octets
|
26
|
+
little_endian_octets.to_a.reverse
|
27
|
+
end
|
28
|
+
|
29
|
+
def big_endian_octets=(big_endian_octets)
|
30
|
+
self.little_endian_octets = big_endian_octets.to_a.reverse
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
require 'flic/protocol/primitives/enum'
|
3
|
+
|
4
|
+
module Flic
|
5
|
+
module Protocol
|
6
|
+
module Primitives
|
7
|
+
class BluetoothAddressType < Enum
|
8
|
+
option :public_bluetooth_address_type
|
9
|
+
option :random_bluetooth_address_type
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
require 'flic/protocol/primitives/enum'
|
3
|
+
|
4
|
+
module Flic
|
5
|
+
module Protocol
|
6
|
+
module Primitives
|
7
|
+
class BluetoothControllerState < Enum
|
8
|
+
option :detached # The server software has lost the HCI socket to the bluetooth controller and is trying to reconnect.
|
9
|
+
option :resetting # The server software has just got connected to the HCI socket and initiated a reset of the bluetooth controller.
|
10
|
+
option :attached # The bluetooth controller has done initialization and is up and running.
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
|
3
|
+
module Flic
|
4
|
+
module Protocol
|
5
|
+
module Primitives
|
6
|
+
class Boolean < BinData::Primitive
|
7
|
+
uint8 :byte
|
8
|
+
|
9
|
+
def get
|
10
|
+
byte != 0x00
|
11
|
+
end
|
12
|
+
|
13
|
+
def set(value)
|
14
|
+
self.byte = value ? 0x01 : 0x00
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
require 'flic/protocol/primitives/enum'
|
3
|
+
|
4
|
+
module Flic
|
5
|
+
module Protocol
|
6
|
+
module Primitives
|
7
|
+
class ClickType < Enum
|
8
|
+
option :button_down # The button was pressed.
|
9
|
+
option :button_up # The button was released.
|
10
|
+
option :button_click # The button was clicked, and was held for at most 1 second between press and release.
|
11
|
+
option :button_single_click # The button was clicked once.
|
12
|
+
option :button_double_click # The button was clicked twice. The time between the first and second press must be at most 0.5 seconds.
|
13
|
+
option :button_hold # The button was held for at least 1 second.
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
require 'flic/protocol/primitives/enum'
|
3
|
+
|
4
|
+
module Flic
|
5
|
+
module Protocol
|
6
|
+
module Primitives
|
7
|
+
class ConnectionStatus < Enum
|
8
|
+
option :disconnected # Not currently an established connection, but will connect as soon as the button is pressed and it is in range as long as the connection channel hasn't been removed (and unless maximum number of concurrent connections has been reached or the bluetooth controller has been detached).
|
9
|
+
option :connected # The physical bluetooth connection has just been established and the server and the button are currently verifying each other. As soon as this is done, it will switch to the ready status.
|
10
|
+
option :ready # The verification is done and button events may now arrive.
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
require 'flic/protocol/primitives/enum'
|
3
|
+
|
4
|
+
module Flic
|
5
|
+
module Protocol
|
6
|
+
module Primitives
|
7
|
+
class CreateConnectionChannelError < Enum
|
8
|
+
option :no_error # There were space in the bluetooth controller's white list to accept a physical pending connection for this button
|
9
|
+
option :maximum_pending_connections_reached # There were no space left in the bluetooth controller to allow a new pending connection
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
|
3
|
+
require 'bindata'
|
4
|
+
|
5
|
+
module Flic
|
6
|
+
module Protocol
|
7
|
+
module Primitives
|
8
|
+
class DeviceName < BinData::Primitive
|
9
|
+
BYTE_LENGTH = 16
|
10
|
+
|
11
|
+
uint8 :byte_length
|
12
|
+
array :bytes, type: :int8, initial_length: BYTE_LENGTH
|
13
|
+
|
14
|
+
def get
|
15
|
+
''.tap do |string|
|
16
|
+
byte_length.times do |index|
|
17
|
+
break unless index < BYTE_LENGTH
|
18
|
+
string << bytes[index].to_i
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def set(value)
|
24
|
+
byte_length = 0
|
25
|
+
bytes = []
|
26
|
+
|
27
|
+
BYTE_LENGTH.times do |index|
|
28
|
+
char = value[index].to_s
|
29
|
+
|
30
|
+
if char
|
31
|
+
bytes << char.ord
|
32
|
+
byte_length += 1
|
33
|
+
else
|
34
|
+
bytes << 0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
self.byte_length = byte_length
|
39
|
+
self.bytes = bytes
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
require 'flic/protocol/primitives/enum'
|
3
|
+
|
4
|
+
module Flic
|
5
|
+
module Protocol
|
6
|
+
module Primitives
|
7
|
+
class DisconnectReason < Enum
|
8
|
+
option :unspecified # Unknown reason
|
9
|
+
option :connection_establishment_failed # The bluetooth controller established a connection, but the Flic button didn't answer in time.
|
10
|
+
option :timed_out # The connection to the Flic button was lost due to either being out of range or some radio communication problems.
|
11
|
+
option :bonding_keys_mismatch # The server and the Flic button for some reason don't agree on the previously established bonding keys.
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'flic/protocol'
|
2
|
+
|
3
|
+
require 'bindata'
|
4
|
+
|
5
|
+
module Flic
|
6
|
+
module Protocol
|
7
|
+
module Primitives
|
8
|
+
class Enum < BinData::Primitive
|
9
|
+
class Error < StandardError; end
|
10
|
+
class InvalidOptionError < Error; end
|
11
|
+
class InvalidOctetError < Error; end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def option_octet
|
15
|
+
@option_octet ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def octet_option
|
19
|
+
@octet_option ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def options
|
23
|
+
option_octet.keys
|
24
|
+
end
|
25
|
+
|
26
|
+
def octets
|
27
|
+
octet_option.keys
|
28
|
+
end
|
29
|
+
|
30
|
+
def max_octet
|
31
|
+
octets.max
|
32
|
+
end
|
33
|
+
|
34
|
+
def next_available_octet
|
35
|
+
if max_octet
|
36
|
+
1 + max_octet
|
37
|
+
else
|
38
|
+
0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def option(option, octet = next_available_octet)
|
45
|
+
option_octet[option] = octet
|
46
|
+
octet_option[octet] = option
|
47
|
+
end
|
48
|
+
|
49
|
+
def octet(octet, option)
|
50
|
+
octet_option[octet] = option
|
51
|
+
option_octet[option] = octet
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
uint8 :octet
|
56
|
+
|
57
|
+
def get
|
58
|
+
if octet_option.has_key?(octet)
|
59
|
+
octet_option[octet]
|
60
|
+
else
|
61
|
+
raise InvalidOctetError, "No such octet `#{octet.inspect}` for enum #{inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def set(option)
|
66
|
+
if option_octet.has_key?(option)
|
67
|
+
self.octet = self.class.option_octet[option]
|
68
|
+
else
|
69
|
+
raise InvalidOptionError, "No such option `#{option.inspect}` for enum #{inspect}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def octet_option
|
76
|
+
self.class.octet_option
|
77
|
+
end
|
78
|
+
|
79
|
+
def option_octet
|
80
|
+
self.class.option_octet
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
require 'flic/protocol/primitives/enum'
|
3
|
+
|
4
|
+
module Flic
|
5
|
+
module Protocol
|
6
|
+
module Primitives
|
7
|
+
class LatencyMode < Enum
|
8
|
+
option :normal # Up to 100 ms latency.
|
9
|
+
option :low # Up to 17.5 ms latency.
|
10
|
+
option :high # Up to 275 ms latency.
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
require 'flic/protocol/primitives/enum'
|
3
|
+
|
4
|
+
module Flic
|
5
|
+
module Protocol
|
6
|
+
module Primitives
|
7
|
+
class RemovedReason < Enum
|
8
|
+
option :removed_by_this_client # The connection channel was removed by this client.
|
9
|
+
option :force_disconnected_by_this_client # The connection channel was removed due to a force disconnect by this client.
|
10
|
+
option :force_disconnected_by_other_client # Another client force disconnected the button used in this connection channel.
|
11
|
+
option :button_is_private # The button is not in public mode. Hold it down for 7 seconds while not trying to establish a connection, then try to reconnect by creating a new connection channel.
|
12
|
+
option :verify_timeout # After the connection was established, the bonding procedure didn't complete in time.
|
13
|
+
option :internet_backend_error # The internet request to the Flic backend failed.
|
14
|
+
option :invalid_data # According to the Flic backend, this Flic button supplied invalid identity data.
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
require 'flic/protocol/primitives/enum'
|
3
|
+
|
4
|
+
module Flic
|
5
|
+
module Protocol
|
6
|
+
module Primitives
|
7
|
+
class ScanWizardResult < Enum
|
8
|
+
option :success # Indicates that a button was successfully paired and verified. You may now create a connection channel to that button.
|
9
|
+
option :cancelled_by_user # A CmdCancelScanWizard was sent.
|
10
|
+
option :timeout # The scan wizard did not make any progress for some time. Current timeouts are 20 seconds for finding any button, 20 seconds for finding a public button (in case of a private button was found), 10 seconds for connecting the button, 30 seconds for pairing and verifying the button.
|
11
|
+
option :button_private # First the button was advertising public status, but after connecting it reports private. Probably it switched from public to private just when the connection attempt was started.
|
12
|
+
option :bluetooth_unavailable # The bluetooth controller is not attached.
|
13
|
+
option :internet_backend_error # The internet request to the Flic backend failed.
|
14
|
+
option :invalid_data # According to the Flic backend, this Flic button supplied invalid identity data.
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'flic/protocol/primitives'
|
2
|
+
|
3
|
+
require 'bindata'
|
4
|
+
require 'scanf'
|
5
|
+
|
6
|
+
module Flic
|
7
|
+
module Primitives
|
8
|
+
class Uuid < BinData::Primitive
|
9
|
+
PRINTF_FORMAT_STRING = '%.2X%.2X%.2X%.2X-%.2X%.2X-%.2X%.2X-%.2X%.2X-%.2X%.2X%.2X%.2X%.2X%.2X'.freeze
|
10
|
+
SCANF_FORMAT_STRING = '%X%X%X%X-%X%X-%X%X-%X%X-%X%X%X%X%X%X'.freeze
|
11
|
+
|
12
|
+
array :octets, type: :uint8, initial_length: 16
|
13
|
+
|
14
|
+
def get
|
15
|
+
sprintf(PRINTF_FORMAT_STRING, *octets)
|
16
|
+
end
|
17
|
+
|
18
|
+
def set(value)
|
19
|
+
self.octets = value.scanf(SCANF_FORMAT_STRING)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|