flic 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,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,14 @@
1
+ require 'flic/protocol/events'
2
+ require 'flic/protocol/events/event'
3
+
4
+ module Flic
5
+ module Protocol
6
+ module Events
7
+ class ScanWizardFoundPrivateButton < Event
8
+ endian :little
9
+
10
+ uint32 :scan_wizard_id
11
+ end
12
+ end
13
+ end
14
+ 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,12 @@
1
+ require 'flic/protocol'
2
+
3
+ require 'bindata'
4
+
5
+ module Flic
6
+ module Protocol
7
+ class PacketHeader < BinData::Record
8
+ endian :little
9
+ uint16 :byte_length
10
+ end
11
+ end
12
+ 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