lifx 0.0.1 → 0.4.0
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 +4 -4
- data/.yardopts +1 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +71 -13
- data/Rakefile +12 -0
- data/bin/lifx-console +15 -0
- data/bin/lifx-snoop +50 -0
- data/examples/auto-off/Gemfile +3 -0
- data/examples/auto-off/auto-off.rb +35 -0
- data/examples/identify/Gemfile +3 -0
- data/examples/identify/identify.rb +70 -0
- data/examples/travis-build-light/Gemfile +4 -0
- data/examples/travis-build-light/build-light.rb +57 -0
- data/lib/bindata_ext/bool.rb +29 -0
- data/lib/bindata_ext/record.rb +11 -0
- data/lib/lifx/client.rb +136 -0
- data/lib/lifx/color.rb +190 -0
- data/lib/lifx/config.rb +12 -0
- data/lib/lifx/firmware.rb +55 -0
- data/lib/lifx/gateway_connection.rb +177 -0
- data/lib/lifx/light.rb +406 -0
- data/lib/lifx/light_collection.rb +105 -0
- data/lib/lifx/light_target.rb +189 -0
- data/lib/lifx/logging.rb +11 -0
- data/lib/lifx/message.rb +166 -0
- data/lib/lifx/network_context.rb +200 -0
- data/lib/lifx/observable.rb +46 -0
- data/lib/lifx/protocol/address.rb +21 -0
- data/lib/lifx/protocol/device.rb +225 -0
- data/lib/lifx/protocol/header.rb +24 -0
- data/lib/lifx/protocol/light.rb +110 -0
- data/lib/lifx/protocol/message.rb +17 -0
- data/lib/lifx/protocol/metadata.rb +21 -0
- data/lib/lifx/protocol/payload.rb +7 -0
- data/lib/lifx/protocol/sensor.rb +29 -0
- data/lib/lifx/protocol/type.rb +134 -0
- data/lib/lifx/protocol/wan.rb +50 -0
- data/lib/lifx/protocol/wifi.rb +76 -0
- data/lib/lifx/protocol_path.rb +84 -0
- data/lib/lifx/routing_manager.rb +110 -0
- data/lib/lifx/routing_table.rb +33 -0
- data/lib/lifx/seen.rb +15 -0
- data/lib/lifx/site.rb +89 -0
- data/lib/lifx/tag_manager.rb +105 -0
- data/lib/lifx/tag_table.rb +47 -0
- data/lib/lifx/target.rb +23 -0
- data/lib/lifx/timers.rb +18 -0
- data/lib/lifx/transport/tcp.rb +81 -0
- data/lib/lifx/transport/udp.rb +67 -0
- data/lib/lifx/transport.rb +41 -0
- data/lib/lifx/transport_manager/lan.rb +140 -0
- data/lib/lifx/transport_manager.rb +34 -0
- data/lib/lifx/utilities.rb +33 -0
- data/lib/lifx/version.rb +1 -1
- data/lib/lifx.rb +15 -1
- data/lifx.gemspec +11 -7
- data/spec/color_spec.rb +45 -0
- data/spec/gateway_connection_spec.rb +32 -0
- data/spec/integration/client_spec.rb +40 -0
- data/spec/integration/light_spec.rb +43 -0
- data/spec/integration/tags_spec.rb +31 -0
- data/spec/message_spec.rb +163 -0
- data/spec/protocol_path_spec.rb +109 -0
- data/spec/routing_manager_spec.rb +22 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/transport/udp_spec.rb +38 -0
- data/spec/transport_spec.rb +14 -0
- metadata +143 -26
| @@ -0,0 +1,110 @@ | |
| 1 | 
            +
            # Generated code ahoy!
         | 
| 2 | 
            +
            module LIFX
         | 
| 3 | 
            +
              module Protocol
         | 
| 4 | 
            +
                module Light
         | 
| 5 | 
            +
                  module Waveform
         | 
| 6 | 
            +
                    SAW = 0
         | 
| 7 | 
            +
                    SINE = 1
         | 
| 8 | 
            +
                    HALF_SINE = 2
         | 
| 9 | 
            +
                    TRIANGLE = 3
         | 
| 10 | 
            +
                    PULSE = 4
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  class Hsbk < Payload
         | 
| 14 | 
            +
                    endian :little
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    uint16 :hue # 0..65_535 scaled to 0° - 360°.
         | 
| 17 | 
            +
                    uint16 :saturation # 0..65_535 scaled to 0% - 100%.
         | 
| 18 | 
            +
                    uint16 :brightness # 0..65_535 scaled to 0% - 100%.
         | 
| 19 | 
            +
                    uint16 :kelvin # Explicit 2_400..10_000.
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  class Get < Payload
         | 
| 23 | 
            +
                    endian :little
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  class Set < Payload
         | 
| 28 | 
            +
                    endian :little
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    uint8 :stream # 0 is no stream.
         | 
| 31 | 
            +
                    hsbk :color
         | 
| 32 | 
            +
                    uint32 :duration # Milliseconds.
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  class SetWaveform < Payload
         | 
| 36 | 
            +
                    endian :little
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    uint8 :stream # 0 is no stream.
         | 
| 39 | 
            +
                    bool :transient
         | 
| 40 | 
            +
                    hsbk :color
         | 
| 41 | 
            +
                    uint32 :period # Milliseconds per cycle.
         | 
| 42 | 
            +
                    float :cycles
         | 
| 43 | 
            +
                    int16 :duty_cycle
         | 
| 44 | 
            +
                    uint8 :waveform
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  class SetDimAbsolute < Payload
         | 
| 48 | 
            +
                    endian :little
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    int16 :brightness # 0 is no change.
         | 
| 51 | 
            +
                    uint32 :duration # Milliseconds.
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  class SetDimRelative < Payload
         | 
| 55 | 
            +
                    endian :little
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    int32 :brightness # 0 is no change.
         | 
| 58 | 
            +
                    uint32 :duration # Milliseconds.
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  class Rgbw < Payload
         | 
| 62 | 
            +
                    endian :little
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    uint16 :red
         | 
| 65 | 
            +
                    uint16 :green
         | 
| 66 | 
            +
                    uint16 :blue
         | 
| 67 | 
            +
                    uint16 :white
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  class SetRgbw < Payload
         | 
| 71 | 
            +
                    endian :little
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                    rgbw :color
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  class State < Payload
         | 
| 77 | 
            +
                    endian :little
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                    hsbk :color
         | 
| 80 | 
            +
                    int16 :dim
         | 
| 81 | 
            +
                    uint16 :power
         | 
| 82 | 
            +
                    string :label, length: 32, trim_padding: true
         | 
| 83 | 
            +
                    uint64 :tags
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  class GetRailVoltage < Payload
         | 
| 87 | 
            +
                    endian :little
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  class StateRailVoltage < Payload
         | 
| 92 | 
            +
                    endian :little
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    uint32 :voltage
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  class GetTemperature < Payload
         | 
| 98 | 
            +
                    endian :little
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  class StateTemperature < Payload
         | 
| 103 | 
            +
                    endian :little
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    int16 :temperature # Deci-celsius. 25.45 celsius is 2545
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            require 'lifx/protocol/header'
         | 
| 2 | 
            +
            require 'lifx/protocol/address'
         | 
| 3 | 
            +
            require 'lifx/protocol/metadata'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module LIFX
         | 
| 6 | 
            +
              module Protocol
         | 
| 7 | 
            +
                class Message < BinData::Record
         | 
| 8 | 
            +
                  endian :little
         | 
| 9 | 
            +
                  
         | 
| 10 | 
            +
                  include HeaderFields
         | 
| 11 | 
            +
                  include AddressFields
         | 
| 12 | 
            +
                  include MetadataFields
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  rest :payload
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            module LIFX
         | 
| 2 | 
            +
              module Protocol
         | 
| 3 | 
            +
                module MetadataFields
         | 
| 4 | 
            +
                  def MetadataFields.included(mod)
         | 
| 5 | 
            +
                    mod.instance_eval do
         | 
| 6 | 
            +
                      hide :_reserved3
         | 
| 7 | 
            +
                      
         | 
| 8 | 
            +
                      uint64 :at_time
         | 
| 9 | 
            +
                      uint16 :type
         | 
| 10 | 
            +
                      uint16 :_reserved3
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                class Metadata < BinData::Record
         | 
| 16 | 
            +
                  endian :little
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  include MetadataFields
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            # Generated code ahoy!
         | 
| 2 | 
            +
            module LIFX
         | 
| 3 | 
            +
              module Protocol
         | 
| 4 | 
            +
                module Sensor
         | 
| 5 | 
            +
                  class GetAmbientLight < Payload
         | 
| 6 | 
            +
                    endian :little
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  class StateAmbientLight < Payload
         | 
| 11 | 
            +
                    endian :little
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    float :lux
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  class GetDimmerVoltage < Payload
         | 
| 17 | 
            +
                    endian :little
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  class StateDimmerVoltage < Payload
         | 
| 22 | 
            +
                    endian :little
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    uint32 :voltage
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| @@ -0,0 +1,134 @@ | |
| 1 | 
            +
            # Generated code ahoy!
         | 
| 2 | 
            +
            module LIFX
         | 
| 3 | 
            +
              module Protocol
         | 
| 4 | 
            +
                TYPE_ID_TO_CLASS = {
         | 
| 5 | 
            +
                  1 => Device::SetSite,
         | 
| 6 | 
            +
                  2 => Device::GetPanGateway,
         | 
| 7 | 
            +
                  3 => Device::StatePanGateway,
         | 
| 8 | 
            +
                  4 => Device::GetTime,
         | 
| 9 | 
            +
                  5 => Device::SetTime,
         | 
| 10 | 
            +
                  6 => Device::StateTime,
         | 
| 11 | 
            +
                  7 => Device::GetResetSwitch,
         | 
| 12 | 
            +
                  8 => Device::StateResetSwitch,
         | 
| 13 | 
            +
                  12 => Device::GetMeshInfo,
         | 
| 14 | 
            +
                  13 => Device::StateMeshInfo,
         | 
| 15 | 
            +
                  14 => Device::GetMeshFirmware,
         | 
| 16 | 
            +
                  15 => Device::StateMeshFirmware,
         | 
| 17 | 
            +
                  16 => Device::GetWifiInfo,
         | 
| 18 | 
            +
                  17 => Device::StateWifiInfo,
         | 
| 19 | 
            +
                  18 => Device::GetWifiFirmware,
         | 
| 20 | 
            +
                  19 => Device::StateWifiFirmware,
         | 
| 21 | 
            +
                  20 => Device::GetPower,
         | 
| 22 | 
            +
                  21 => Device::SetPower,
         | 
| 23 | 
            +
                  22 => Device::StatePower,
         | 
| 24 | 
            +
                  23 => Device::GetLabel,
         | 
| 25 | 
            +
                  24 => Device::SetLabel,
         | 
| 26 | 
            +
                  25 => Device::StateLabel,
         | 
| 27 | 
            +
                  26 => Device::GetTags,
         | 
| 28 | 
            +
                  27 => Device::SetTags,
         | 
| 29 | 
            +
                  28 => Device::StateTags,
         | 
| 30 | 
            +
                  29 => Device::GetTagLabels,
         | 
| 31 | 
            +
                  30 => Device::SetTagLabels,
         | 
| 32 | 
            +
                  31 => Device::StateTagLabels,
         | 
| 33 | 
            +
                  32 => Device::GetVersion,
         | 
| 34 | 
            +
                  33 => Device::StateVersion,
         | 
| 35 | 
            +
                  34 => Device::GetInfo,
         | 
| 36 | 
            +
                  35 => Device::StateInfo,
         | 
| 37 | 
            +
                  36 => Device::GetMcuRailVoltage,
         | 
| 38 | 
            +
                  37 => Device::StateMcuRailVoltage,
         | 
| 39 | 
            +
                  38 => Device::Reboot,
         | 
| 40 | 
            +
                  101 => Light::Get,
         | 
| 41 | 
            +
                  102 => Light::Set,
         | 
| 42 | 
            +
                  103 => Light::SetWaveform,
         | 
| 43 | 
            +
                  104 => Light::SetDimAbsolute,
         | 
| 44 | 
            +
                  105 => Light::SetDimRelative,
         | 
| 45 | 
            +
                  106 => Light::SetRgbw,
         | 
| 46 | 
            +
                  107 => Light::State,
         | 
| 47 | 
            +
                  108 => Light::GetRailVoltage,
         | 
| 48 | 
            +
                  109 => Light::StateRailVoltage,
         | 
| 49 | 
            +
                  110 => Light::GetTemperature,
         | 
| 50 | 
            +
                  111 => Light::StateTemperature,
         | 
| 51 | 
            +
                  201 => Wan::ConnectPlain,
         | 
| 52 | 
            +
                  202 => Wan::ConnectKey,
         | 
| 53 | 
            +
                  203 => Wan::StateConnect,
         | 
| 54 | 
            +
                  204 => Wan::Sub,
         | 
| 55 | 
            +
                  205 => Wan::Unsub,
         | 
| 56 | 
            +
                  206 => Wan::StateSub,
         | 
| 57 | 
            +
                  301 => Wifi::Get,
         | 
| 58 | 
            +
                  302 => Wifi::Set,
         | 
| 59 | 
            +
                  303 => Wifi::State,
         | 
| 60 | 
            +
                  304 => Wifi::GetAccessPoint,
         | 
| 61 | 
            +
                  305 => Wifi::SetAccessPoint,
         | 
| 62 | 
            +
                  306 => Wifi::StateAccessPoint,
         | 
| 63 | 
            +
                  401 => Sensor::GetAmbientLight,
         | 
| 64 | 
            +
                  402 => Sensor::StateAmbientLight,
         | 
| 65 | 
            +
                  403 => Sensor::GetDimmerVoltage,
         | 
| 66 | 
            +
                  404 => Sensor::StateDimmerVoltage,
         | 
| 67 | 
            +
                }
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                CLASS_TO_TYPE_ID = {
         | 
| 70 | 
            +
                  Device::SetSite => 1,
         | 
| 71 | 
            +
                  Device::GetPanGateway => 2,
         | 
| 72 | 
            +
                  Device::StatePanGateway => 3,
         | 
| 73 | 
            +
                  Device::GetTime => 4,
         | 
| 74 | 
            +
                  Device::SetTime => 5,
         | 
| 75 | 
            +
                  Device::StateTime => 6,
         | 
| 76 | 
            +
                  Device::GetResetSwitch => 7,
         | 
| 77 | 
            +
                  Device::StateResetSwitch => 8,
         | 
| 78 | 
            +
                  Device::GetMeshInfo => 12,
         | 
| 79 | 
            +
                  Device::StateMeshInfo => 13,
         | 
| 80 | 
            +
                  Device::GetMeshFirmware => 14,
         | 
| 81 | 
            +
                  Device::StateMeshFirmware => 15,
         | 
| 82 | 
            +
                  Device::GetWifiInfo => 16,
         | 
| 83 | 
            +
                  Device::StateWifiInfo => 17,
         | 
| 84 | 
            +
                  Device::GetWifiFirmware => 18,
         | 
| 85 | 
            +
                  Device::StateWifiFirmware => 19,
         | 
| 86 | 
            +
                  Device::GetPower => 20,
         | 
| 87 | 
            +
                  Device::SetPower => 21,
         | 
| 88 | 
            +
                  Device::StatePower => 22,
         | 
| 89 | 
            +
                  Device::GetLabel => 23,
         | 
| 90 | 
            +
                  Device::SetLabel => 24,
         | 
| 91 | 
            +
                  Device::StateLabel => 25,
         | 
| 92 | 
            +
                  Device::GetTags => 26,
         | 
| 93 | 
            +
                  Device::SetTags => 27,
         | 
| 94 | 
            +
                  Device::StateTags => 28,
         | 
| 95 | 
            +
                  Device::GetTagLabels => 29,
         | 
| 96 | 
            +
                  Device::SetTagLabels => 30,
         | 
| 97 | 
            +
                  Device::StateTagLabels => 31,
         | 
| 98 | 
            +
                  Device::GetVersion => 32,
         | 
| 99 | 
            +
                  Device::StateVersion => 33,
         | 
| 100 | 
            +
                  Device::GetInfo => 34,
         | 
| 101 | 
            +
                  Device::StateInfo => 35,
         | 
| 102 | 
            +
                  Device::GetMcuRailVoltage => 36,
         | 
| 103 | 
            +
                  Device::StateMcuRailVoltage => 37,
         | 
| 104 | 
            +
                  Device::Reboot => 38,
         | 
| 105 | 
            +
                  Light::Get => 101,
         | 
| 106 | 
            +
                  Light::Set => 102,
         | 
| 107 | 
            +
                  Light::SetWaveform => 103,
         | 
| 108 | 
            +
                  Light::SetDimAbsolute => 104,
         | 
| 109 | 
            +
                  Light::SetDimRelative => 105,
         | 
| 110 | 
            +
                  Light::SetRgbw => 106,
         | 
| 111 | 
            +
                  Light::State => 107,
         | 
| 112 | 
            +
                  Light::GetRailVoltage => 108,
         | 
| 113 | 
            +
                  Light::StateRailVoltage => 109,
         | 
| 114 | 
            +
                  Light::GetTemperature => 110,
         | 
| 115 | 
            +
                  Light::StateTemperature => 111,
         | 
| 116 | 
            +
                  Wan::ConnectPlain => 201,
         | 
| 117 | 
            +
                  Wan::ConnectKey => 202,
         | 
| 118 | 
            +
                  Wan::StateConnect => 203,
         | 
| 119 | 
            +
                  Wan::Sub => 204,
         | 
| 120 | 
            +
                  Wan::Unsub => 205,
         | 
| 121 | 
            +
                  Wan::StateSub => 206,
         | 
| 122 | 
            +
                  Wifi::Get => 301,
         | 
| 123 | 
            +
                  Wifi::Set => 302,
         | 
| 124 | 
            +
                  Wifi::State => 303,
         | 
| 125 | 
            +
                  Wifi::GetAccessPoint => 304,
         | 
| 126 | 
            +
                  Wifi::SetAccessPoint => 305,
         | 
| 127 | 
            +
                  Wifi::StateAccessPoint => 306,
         | 
| 128 | 
            +
                  Sensor::GetAmbientLight => 401,
         | 
| 129 | 
            +
                  Sensor::StateAmbientLight => 402,
         | 
| 130 | 
            +
                  Sensor::GetDimmerVoltage => 403,
         | 
| 131 | 
            +
                  Sensor::StateDimmerVoltage => 404,
         | 
| 132 | 
            +
                }
         | 
| 133 | 
            +
              end
         | 
| 134 | 
            +
            end
         | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            # Generated code ahoy!
         | 
| 2 | 
            +
            module LIFX
         | 
| 3 | 
            +
              module Protocol
         | 
| 4 | 
            +
                module Wan
         | 
| 5 | 
            +
                  class ConnectPlain < Payload
         | 
| 6 | 
            +
                    endian :little
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    string :user, length: 32, trim_padding: true
         | 
| 9 | 
            +
                    string :pass, length: 32, trim_padding: true
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  class ConnectKey < Payload
         | 
| 13 | 
            +
                    endian :little
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    string :auth_key, length: 32
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  class StateConnect < Payload
         | 
| 19 | 
            +
                    endian :little
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    string :auth_key, length: 32
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  class Sub < Payload
         | 
| 25 | 
            +
                    endian :little
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    string :target, length: 8
         | 
| 28 | 
            +
                    string :site, length: 6
         | 
| 29 | 
            +
                    bool :device # 0 - Targets a device. 1 - Targets a tag.
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  class Unsub < Payload
         | 
| 33 | 
            +
                    endian :little
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    string :target, length: 8
         | 
| 36 | 
            +
                    string :site, length: 6
         | 
| 37 | 
            +
                    bool :device # 0 - Targets a device. 1 - Targets a tag.
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  class StateSub < Payload
         | 
| 41 | 
            +
                    endian :little
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    string :target, length: 8
         | 
| 44 | 
            +
                    string :site, length: 6
         | 
| 45 | 
            +
                    bool :device # 0 - Targets a device. 1 - Targets a tag.
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
| @@ -0,0 +1,76 @@ | |
| 1 | 
            +
            # Generated code ahoy!
         | 
| 2 | 
            +
            module LIFX
         | 
| 3 | 
            +
              module Protocol
         | 
| 4 | 
            +
                module Wifi
         | 
| 5 | 
            +
                  module Interface
         | 
| 6 | 
            +
                    SOFT_AP = 1
         | 
| 7 | 
            +
                    STATION = 2
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  module Security
         | 
| 11 | 
            +
                    UNKNOWN = 0
         | 
| 12 | 
            +
                    OPEN = 1
         | 
| 13 | 
            +
                    WEP_PSK = 2
         | 
| 14 | 
            +
                    WPA_TKIP_PSK = 3
         | 
| 15 | 
            +
                    WPA_AES_PSK = 4
         | 
| 16 | 
            +
                    WPA2_AES_PSK = 5
         | 
| 17 | 
            +
                    WPA2_TKIP_PSK = 6
         | 
| 18 | 
            +
                    WPA2_MIXED_PSK = 7
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  module Status
         | 
| 22 | 
            +
                    CONNECTING = 0
         | 
| 23 | 
            +
                    CONNECTED = 1
         | 
| 24 | 
            +
                    FAILED = 2
         | 
| 25 | 
            +
                    OFF = 3
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  class Get < Payload
         | 
| 29 | 
            +
                    endian :little
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    uint8 :interface
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  class Set < Payload
         | 
| 35 | 
            +
                    endian :little
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    uint8 :interface
         | 
| 38 | 
            +
                    bool :active
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  class State < Payload
         | 
| 42 | 
            +
                    endian :little
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    uint8 :interface
         | 
| 45 | 
            +
                    uint8 :status
         | 
| 46 | 
            +
                    uint32 :ipv4
         | 
| 47 | 
            +
                    string :ipv6, length: 16
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  class GetAccessPoint < Payload
         | 
| 51 | 
            +
                    endian :little
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  class SetAccessPoint < Payload
         | 
| 56 | 
            +
                    endian :little
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    uint8 :interface
         | 
| 59 | 
            +
                    string :ssid, length: 32, trim_padding: true
         | 
| 60 | 
            +
                    string :pass, length: 64, trim_padding: true
         | 
| 61 | 
            +
                    uint8 :security
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  class StateAccessPoint < Payload
         | 
| 65 | 
            +
                    endian :little
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    uint8 :interface
         | 
| 68 | 
            +
                    string :ssid, length: 32, trim_padding: true
         | 
| 69 | 
            +
                    uint8 :security
         | 
| 70 | 
            +
                    int16 :strength
         | 
| 71 | 
            +
                    uint16 :channel
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
            end
         | 
| @@ -0,0 +1,84 @@ | |
| 1 | 
            +
            require 'lifx/utilities'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module LIFX
         | 
| 4 | 
            +
              class ProtocolPath
         | 
| 5 | 
            +
                # ProtocolPath contains all the addressable information that is required
         | 
| 6 | 
            +
                # for the protocol.
         | 
| 7 | 
            +
                # It handles the conversion between raw binary and hex strings
         | 
| 8 | 
            +
                # as well as raw tags_field to tags array
         | 
| 9 | 
            +
                include Utilities
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                attr_accessor :raw_site, :raw_target, :tagged
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def initialize(raw_site: "\x00".b * 6, raw_target: "\x00".b * 8, tagged: false,
         | 
| 14 | 
            +
                               site_id: nil, device_id: nil, tag_ids: nil)
         | 
| 15 | 
            +
                  self.raw_site = raw_site
         | 
| 16 | 
            +
                  self.raw_target = raw_target
         | 
| 17 | 
            +
                  self.tagged = tagged
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  self.site_id = site_id if site_id
         | 
| 20 | 
            +
                  self.device_id = device_id if device_id
         | 
| 21 | 
            +
                  self.tag_ids = tag_ids if tag_ids
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def site_id
         | 
| 25 | 
            +
                  raw_site.unpack('H*').join
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def site_id=(value)
         | 
| 29 | 
            +
                  self.raw_site = [value].pack('H12').b
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def device_id
         | 
| 33 | 
            +
                  if !tagged?
         | 
| 34 | 
            +
                    raw_target[0...6].unpack('H*').join
         | 
| 35 | 
            +
                  else
         | 
| 36 | 
            +
                    nil
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def device_id=(value)
         | 
| 41 | 
            +
                  self.raw_target = [value].pack('H16').b
         | 
| 42 | 
            +
                  self.tagged = false
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def tag_ids
         | 
| 46 | 
            +
                  if tagged?
         | 
| 47 | 
            +
                    tag_ids_from_field(tags_field)
         | 
| 48 | 
            +
                  else
         | 
| 49 | 
            +
                    nil
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def tag_ids=(values)
         | 
| 54 | 
            +
                  self.tags_field = values.reduce(0) do |value, tag_id|
         | 
| 55 | 
            +
                    value |= 2 ** tag_id
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def tagged?
         | 
| 60 | 
            +
                  !!tagged
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def all_sites?
         | 
| 64 | 
            +
                  site_id == "000000000000"
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                def all_tags?
         | 
| 68 | 
            +
                  tagged? && tag_ids.empty?
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                protected
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def tags_field
         | 
| 74 | 
            +
                  raw_target.unpack('Q').first
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def tags_field=(value)
         | 
| 78 | 
            +
                  self.raw_target = [value].pack('Q').b
         | 
| 79 | 
            +
                  self.tagged = true
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
             | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
            end
         | 
| @@ -0,0 +1,110 @@ | |
| 1 | 
            +
            require 'lifx/routing_table'
         | 
| 2 | 
            +
            require 'lifx/tag_table'
         | 
| 3 | 
            +
            require 'lifx/utilities'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module LIFX
         | 
| 6 | 
            +
              class RoutingManager
         | 
| 7 | 
            +
                include Utilities
         | 
| 8 | 
            +
                # RoutingManager manages a routing table of site <-> device
         | 
| 9 | 
            +
                # It can resolve a target to ProtocolPaths and manages the TagTable
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                attr_reader :context, :tag_table, :routing_table
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def initialize(context:)
         | 
| 14 | 
            +
                  @context = context
         | 
| 15 | 
            +
                  @routing_table = RoutingTable.new
         | 
| 16 | 
            +
                  @tag_table = TagTable.new
         | 
| 17 | 
            +
                  @last_refresh_seen = {}
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def resolve_target(target)
         | 
| 21 | 
            +
                  if target.tag?
         | 
| 22 | 
            +
                    @tag_table.entries_with(label: target.tag).map do |entry|
         | 
| 23 | 
            +
                      ProtocolPath.new(site_id: entry.site_id, tag_ids: [entry.tag_id])
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  elsif target.broadcast?
         | 
| 26 | 
            +
                    if @routing_table.site_ids.empty?
         | 
| 27 | 
            +
                      [ProtocolPath.new(site_id: "\x00".b * 6, tag_ids: [])]
         | 
| 28 | 
            +
                    else
         | 
| 29 | 
            +
                      @routing_table.site_ids.map { |site_id| ProtocolPath.new(site_id: site_id, tag_ids: []) }
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
                  elsif target.site_id && target.device_id.nil?
         | 
| 32 | 
            +
                    [ProtocolPath.new(site_id: target.site_id, tag_ids: [])]
         | 
| 33 | 
            +
                  elsif target.site_id && target.device_id
         | 
| 34 | 
            +
                    [ProtocolPath.new(site_id: target.site_id, device_id: target.device_id)]
         | 
| 35 | 
            +
                  else
         | 
| 36 | 
            +
                    site_id = @routing_table.site_id_for_device_id(target.device_id)
         | 
| 37 | 
            +
                    if site_id
         | 
| 38 | 
            +
                      [ProtocolPath.new(site_id: site_id, device_id: target.device_id)]
         | 
| 39 | 
            +
                    else
         | 
| 40 | 
            +
                      @routing_table.site_ids.map { |site_id| ProtocolPath.new(site_id: site_id, device_id: target.device_id)}
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def tags_for_device_id(device_id)
         | 
| 46 | 
            +
                  entry = @routing_table.entry_for_device_id(device_id)
         | 
| 47 | 
            +
                  entry.tag_ids.map do |tag_id|
         | 
| 48 | 
            +
                    tag = @tag_table.entry_with(site_id: entry.site_id, tag_id: tag_id)
         | 
| 49 | 
            +
                    tag && tag.label
         | 
| 50 | 
            +
                  end.compact
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def update_from_message(message)
         | 
| 54 | 
            +
                  if message.tagged?
         | 
| 55 | 
            +
                    case message.payload
         | 
| 56 | 
            +
                    when Protocol::Light::Get
         | 
| 57 | 
            +
                      if message.path.all_tags?
         | 
| 58 | 
            +
                        @last_refresh_seen[message.site_id] = Time.now
         | 
| 59 | 
            +
                      end
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
                    return
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  payload = message.payload
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  if !@routing_table.site_ids.include?(message.site_id)
         | 
| 67 | 
            +
                    # New site detected, fire refresh events
         | 
| 68 | 
            +
                    refresh_site(message.site_id)
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                  case payload
         | 
| 71 | 
            +
                  when Protocol::Device::StateTagLabels
         | 
| 72 | 
            +
                    tag_ids = tag_ids_from_field(payload.tags)
         | 
| 73 | 
            +
                    if payload.label.empty?
         | 
| 74 | 
            +
                      tag_ids.each do |tag_id|
         | 
| 75 | 
            +
                        @tag_table.delete_entries_with(site_id: message.site_id, tag_id: tag_id)
         | 
| 76 | 
            +
                      end
         | 
| 77 | 
            +
                    else
         | 
| 78 | 
            +
                      @tag_table.update_table(site_id: message.site_id, tag_id: tag_ids.first, label: payload.label.to_s)
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                  when Protocol::Device::StateTags, Protocol::Light::State
         | 
| 81 | 
            +
                    @routing_table.update_table(site_id: message.site_id,
         | 
| 82 | 
            +
                                                device_id: message.device_id,
         | 
| 83 | 
            +
                                                tag_ids: tag_ids_from_field(message.payload.tags))
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                  @routing_table.update_table(site_id: message.site_id, device_id: message.device_id)
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                MINIMUM_REFRESH_INTERVAL = 20
         | 
| 89 | 
            +
                def refresh
         | 
| 90 | 
            +
                  @routing_table.site_ids.each do |site_id|
         | 
| 91 | 
            +
                    next if (seen = @last_refresh_seen[site_id]) && Time.now - seen < MINIMUM_REFRESH_INTERVAL
         | 
| 92 | 
            +
                    refresh_site(site_id)
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                def refresh_site(site_id)
         | 
| 97 | 
            +
                  get_lights(site_id)
         | 
| 98 | 
            +
                  get_tag_labels(site_id)
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                def get_lights(site_id)
         | 
| 102 | 
            +
                  context.send_message(target: Target.new(site_id: site_id), payload: Protocol::Light::Get.new)
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                UINT64_MAX = 2 ** 64 - 1
         | 
| 106 | 
            +
                def get_tag_labels(site_id)
         | 
| 107 | 
            +
                  context.send_message(target: Target.new(site_id: site_id), payload: Protocol::Device::GetTagLabels.new(tags: UINT64_MAX))
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module LIFX
         | 
| 2 | 
            +
              class RoutingTable
         | 
| 3 | 
            +
                class Entry < Struct.new(:site_id, :device_id, :tag_ids, :last_seen); end
         | 
| 4 | 
            +
                # RoutingTable stores the device <-> site mapping
         | 
| 5 | 
            +
                def initialize(entries: {})
         | 
| 6 | 
            +
                  @device_site_mapping = entries
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def update_table(site_id:, device_id:, tag_ids: nil)
         | 
| 10 | 
            +
                  device_mapping = @device_site_mapping[device_id] ||= Entry.new(site_id, device_id, [])
         | 
| 11 | 
            +
                  device_mapping.site_id = site_id
         | 
| 12 | 
            +
                  device_mapping.last_seen = Time.now
         | 
| 13 | 
            +
                  device_mapping.tag_ids = tag_ids if tag_ids
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def entry_for_device_id(device_id)
         | 
| 17 | 
            +
                  @device_site_mapping[device_id]
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def site_id_for_device_id(device_id)
         | 
| 21 | 
            +
                  entry = entry_for_device_id(device_id)
         | 
| 22 | 
            +
                  entry ? entry.site_id : nil
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def site_ids
         | 
| 26 | 
            +
                  entries.map(&:site_id).uniq
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def entries
         | 
| 30 | 
            +
                  @device_site_mapping.values
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         |