class LIFX::LAN::Light

LIFX::LAN::Light represents a Light device

Constants

MAX_LABEL_LENGTH

Attributes

context[R]

@return [NetworkContext] NetworkContext the Light belongs to

id[R]

@return [String] Device ID

Public Class Methods

new(context: required!(:context), id: self.id, site_id: nil, label: nil) click to toggle source

@param context: [NetworkContext] {NetworkContext} the Light belongs to @param id: [String] Device ID of the Light @param site_id: [String] Site ID of the Light. Avoid using when possible. @param label: [String] Label of Light to prepopulate

# File lib/lifx/lan/light.rb, line 27
def initialize(context: required!(:context), id: self.id, site_id: nil, label: nil)
  @context = context
  @id = id
  @site_id = site_id
  @label = label
  @power = nil
  @message_hooks = Hash.new { |h, k| h[k] = [] }
  @context.register_device(self)
  @message_signal = ConditionVariable.new

  add_hooks
end

Public Instance Methods

<=>(other) click to toggle source

Compare current Light to another light @param other [Light] @return [-1, 0, 1] Comparison value

# File lib/lifx/lan/light.rb, line 353
def <=>(other)
  raise ArgumentError.new("Comparison of #{self} with #{other} failed") unless other.is_a?(LIFX::Light)
  [label, id, 0] <=> [other.label, other.id, 0]
end
add_hook(payload_class, hook_arg = nil, &hook_block) click to toggle source

Adds a block to be run when a payload of class `payload_class` is received @param payload_class [Class] Payload type to execute block on @param &hook [Proc] Hook to run @api private @return [void]

# File lib/lifx/lan/light.rb, line 57
def add_hook(payload_class, hook_arg = nil, &hook_block)
  hook = block_given? ? hook_block : hook_arg
  if !hook || !hook.is_a?(Proc)
    raise "Must pass a proc either as an argument or a block"
  end
  @message_hooks[payload_class] << hook
end
add_tag(tag) click to toggle source

Add tag to the Light @param tag [String] The tag to add @return [Light] self

# File lib/lifx/lan/light.rb, line 318
def add_tag(tag)
  context.add_tag_to_device(tag: tag, device: self)
  self
end
ambience(fetch: true) click to toggle source
# File lib/lifx/lan/light.rb, line 189
def ambience(fetch: true)
  @ambience ||= begin
    send_message!(Protocol::Sensor::GetAmbientLight.new,
        wait_for: Protocol::Sensor::StateAmbientLight) do |payload|
      payload.inspect
    end
  end
end
color(refresh: false, fetch: true) click to toggle source

Returns the color of the device. @param refresh: [Boolean] If true, will request for current color @param fetch: [Boolean] If false, it will not request current color if it's not cached @return [Color] Color

# File lib/lifx/lan/light.rb, line 78
def color(refresh: false, fetch: true)
  @color = nil if refresh
  send_message!(Protocol::Light::Get.new, wait_for: Protocol::Light::State) if fetch && !@color
  @color
end
gateway?() click to toggle source

Returns whether the light is a gateway @api private

# File lib/lifx/lan/light.rb, line 339
def gateway?
  context.transport_manager.gateways.include?(self)
end
handle_message(message, ip, transport) click to toggle source

Handles updating the internal state of the Light from incoming protocol messages. @api private

# File lib/lifx/lan/light.rb, line 43
def handle_message(message, ip, transport)
  payload = message.payload

  @message_hooks[payload.class].each do |hook|
    hook.call(payload)
  end
  @message_signal.broadcast
end
inspect()
Alias for: to_s
label(refresh: false, fetch: true) click to toggle source

Returns the label of the light @param refresh: [Boolean] If true, will request for current label @param fetch: [Boolean] If false, it will not request current label if it's not cached @return [String, nil] Label

# File lib/lifx/lan/light.rb, line 88
def label(refresh: false, fetch: true)
  @label = nil if refresh
  send_message!(Protocol::Light::Get.new, wait_for: Protocol::Light::State) if fetch && !@label
  @label
end
last_downtime() click to toggle source

Return device last downtime @api private @return [Float] Device's last downtime in secodns

# File lib/lifx/lan/light.rb, line 286
def last_downtime
  send_message!(Protocol::Device::GetInfo.new,
     wait_for: Protocol::Device::StateInfo) do |payload|
    payload.downtime.to_f / NSEC_IN_SEC
  end
end
latency() click to toggle source

Pings the device and measures response time. @return [Float] Latency from sending a message to receiving a response.

# File lib/lifx/lan/light.rb, line 209
def latency
  start = Time.now.to_f
  send_message!(Protocol::Device::GetTime.new, wait_for: Protocol::Device::StateTime)
  Time.now.to_f - start
end
mcu_firmware(fetch: true) click to toggle source
# File lib/lifx/lan/light.rb, line 227
def mcu_firmware(fetch: true)
  @mcu_firmware ||= begin
    send_message!(Protocol::Device::GetHostFirmware.new,
      wait_for: Protocol::Device::StateHostFirmware) do |payload|
      Firmware.new(payload)
    end if fetch
  end
end
off?(refresh: false, fetch: true) click to toggle source

@see power @return [Boolean] Returns true if device is off

# File lib/lifx/lan/light.rb, line 153
def off?(refresh: false, fetch: true)
  power(refresh: refresh, fetch: fetch) == :off
end
on?(refresh: false, fetch: true) click to toggle source

@see power @return [Boolean] Returns true if device is on

# File lib/lifx/lan/light.rb, line 147
def on?(refresh: false, fetch: true)
  power(refresh: refresh, fetch: fetch) == :on
end
power(refresh: false, fetch: true) click to toggle source

@param refresh: see label @param fetch: see label @return [:unknown, :off, :on] Light power state

# File lib/lifx/lan/light.rb, line 160
def power(refresh: false, fetch: true)
  @power = nil if refresh
  send_message!(Protocol::Light::Get.new, wait_for: Protocol::Light::State) if !@power && fetch
  case @power
  when nil
    :unknown
  when 0
    :off
  else
    :on
  end
end
power_level(fetch: true) click to toggle source
# File lib/lifx/lan/light.rb, line 198
def power_level(fetch: true)
  @power_level ||= begin
    send_message!(Protocol::Device::GetPower.new,
        wait_for: Protocol::Device::StatePower) do |payload|
      payload.inspect
    end
  end
end
remove_hook(payload_class, hook) click to toggle source

Removes a hook added by {#add_hook} @param payload_class [Class] Payload type to delete hook from @param hook [Proc] The original hook passed into {#add_hook} @api private @return [void]

# File lib/lifx/lan/light.rb, line 70
def remove_hook(payload_class, hook)
  @message_hooks[payload_class].delete(hook)
end
remove_tag(tag) click to toggle source

Remove tag from the Light @param tag [String] The tag to remove @return [Light] self

# File lib/lifx/lan/light.rb, line 326
def remove_tag(tag)
  context.remove_tag_from_device(tag: tag, device: self)
  self
end
send_message(payload, acknowledge: true, at_time: nil) click to toggle source

Queues a message to be sent the Light @param payload [Protocol::Payload] the payload to send @param acknowledge: [Boolean] whether the device should respond @param at_time: [Integer] Unix epoch in milliseconds to run the payload. Only applicable to certain payload types. @return [Light] returns self for chaining

# File lib/lifx/lan/light.rb, line 363
def send_message(payload, acknowledge: true, at_time: nil)
  context.send_message(target: Target.new(device_id: id), payload: payload, acknowledge: acknowledge, at_time: at_time)
end
send_message!(payload, wait_for: self.wait_for, wait_timeout: Config.message_wait_timeout, retry_interval: Config.message_retry_interval, &block) click to toggle source

Queues a message to be sent to the Light and waits for a response @param payload [Protocol::Payload] the payload to send @param wait_for: [Class] the payload class to wait for @param wait_timeout: [Numeric] wait timeout @param block: [Proc] the block that is executed when the expected `wait_for` payload comes back. If the return value is false or nil, it will try to send the message again. @return [Object] the truthy result of `block` is returned. @raise [MessageTimeout] if the device doesn't respond in time

# File lib/lifx/lan/light.rb, line 379
def send_message!(payload, wait_for: self.wait_for, wait_timeout: Config.message_wait_timeout, retry_interval: Config.message_retry_interval, &block)
  if Thread.current[:sync_enabled]
    raise "Cannot use synchronous methods inside a sync block"
  end

  result = nil
  begin
    block ||= Proc.new { |msg| true }
    proc = -> (payload) {
      result = block.call(payload)
    }
    add_hook(wait_for, proc)
    try_until -> { result }, timeout: wait_timeout, timeout_exception: TimeoutError, action_interval: retry_interval, signal: @message_signal do
      send_message(payload)
    end
    result
  rescue TimeoutError
    backtrace = caller_locations(2).map { |c| c.to_s }
    caller_method = caller_locations(2, 1).first.label
    ex = MessageTimeout.new("#{caller_method}: Timeout exceeded waiting for response from #{self}")
    ex.device = self
    ex.set_backtrace(backtrace)
    raise ex
  ensure
    remove_hook(wait_for, proc)
  end
end
set_label(label) click to toggle source

Sets the label of the light @param label [String] Desired label @raise [LabelTooLong] if label is greater than {MAX_LABEL_LENGTH} @return [Light] self

# File lib/lifx/lan/light.rb, line 101
def set_label(label)
  if label.bytes.length > MAX_LABEL_LENGTH
    raise LabelTooLong.new("Label length in bytes must be below or equal to #{MAX_LABEL_LENGTH}")
  end
  while self.label != label
    send_message!(Protocol::Device::SetLabel.new(label: label.encode('utf-8')), wait_for: Protocol::Device::StateLabel)
  end
  self
end
set_power!(state) click to toggle source

Set the power state to `state` synchronously. @param state [:on, :off] @return [Light, LightCollection] self for chaining

# File lib/lifx/lan/light.rb, line 114
def set_power!(state)
  level = case state
  when :on
    1
  when :off
    0
  else
    raise ArgumentError.new("Must pass in either :on or :off")
  end
  send_message!(Protocol::Device::SetPower.new(level: level), wait_for: Protocol::Device::StatePower) do |payload|
    if level == 0
      payload.level == 0
    else
      payload.level > 0
    end
  end
  self
end
site_id() click to toggle source

Returns the `site_id` the Light belongs to. @api private @return [String]

# File lib/lifx/lan/light.rb, line 296
def site_id
  if @site_id.nil?
    # FIXME: This is ugly.
    context.routing_manager.routing_table.site_id_for_device_id(id)
  else
    @site_id
  end
end
tags() click to toggle source

Returns the tags that are associated with the Light @return [Array<String>] tags

# File lib/lifx/lan/light.rb, line 333
def tags
  context.tags_for_device(self)
end
tags_field() click to toggle source

Returns the tags uint64 bitfield for protocol use. @api private @return [Integer]

# File lib/lifx/lan/light.rb, line 308
def tags_field
  try_until -> { @tags_field } do
    send_message(Protocol::Device::GetTags.new)
  end
  @tags_field
end
temperature() click to toggle source

Returns the temperature of the device @return [Float] Temperature in Celcius

# File lib/lifx/lan/light.rb, line 238
def temperature
  send_message!(Protocol::Light::GetTemperature.new,
      wait_for: Protocol::Light::StateTemperature) do |payload|
    payload.temperature / 100.0
  end
end
time() click to toggle source

Returns the local time of the light @return [Time]

# File lib/lifx/lan/light.rb, line 175
def time
  send_message!(Protocol::Device::GetTime.new, wait_for: Protocol::Device::StateTime) do |payload|
    Time.at(payload.time.to_f / NSEC_IN_SEC)
  end
end
time_delta() click to toggle source

Returns the difference between the device time and time on the current machine Positive values means device time is further in the future. @return [Float]

# File lib/lifx/lan/light.rb, line 184
def time_delta
  device_time = time
  delta = device_time - Time.now
end
to_s() click to toggle source

Returns a nice string representation of the Light @return [String]

# File lib/lifx/lan/light.rb, line 345
def to_s
  %Q{#<LIFX::LAN::Light id=#{id} label=#{label(fetch: false)} power=#{power(fetch: false)}>}.force_encoding('utf-8')
end
Also aliased as: inspect
turn_off!() click to toggle source

Turns the light(s) off synchronously @return [Light, LightCollection]

# File lib/lifx/lan/light.rb, line 141
def turn_off!
  set_power!(:off)
end
turn_on!() click to toggle source

Turns the light(s) on synchronously @return [Light, LightCollection] self for chaining

# File lib/lifx/lan/light.rb, line 135
def turn_on!
  set_power!(:on)
end
uptime() click to toggle source

Return device uptime @api private @return [Float] Device uptime in seconds

# File lib/lifx/lan/light.rb, line 276
def uptime
  send_message!(Protocol::Device::GetInfo.new,
     wait_for: Protocol::Device::StateInfo) do |payload|
    payload.uptime.to_f / NSEC_IN_SEC
  end
end
version() click to toggle source

Returns version info @api private @return [Hash] version info

# File lib/lifx/lan/light.rb, line 262
def version
  send_message!(Protocol::Device::GetVersion.new,
     wait_for: Protocol::Device::StateVersion) do |payload|
    {
      vendor: payload.vendor,
      product: payload.product,
      version: payload.version
    }
  end
end
wifi_firmware(fetch: true) click to toggle source

Returns the wifi firmware details @api private @return [Hash] firmware details

# File lib/lifx/lan/light.rb, line 218
def wifi_firmware(fetch: true)
  @wifi_firmware ||= begin
    send_message!(Protocol::Device::GetWifiFirmware.new,
      wait_for: Protocol::Device::StateWifiFirmware) do |payload|
      Firmware.new(payload)
    end if fetch
  end
end
wifi_info() click to toggle source

Returns wifi network info @api private @return [Hash] wifi network info

# File lib/lifx/lan/light.rb, line 248
def wifi_info
  send_message!(Protocol::Device::GetWifiInfo.new,
      wait_for: Protocol::Device::StateWifiInfo) do |payload|
    {
      signal: payload.signal, # This is in Milliwatts
      tx: payload.tx,
      rx: payload.rx
    }
  end
end

Protected Instance Methods

add_hooks() click to toggle source
# File lib/lifx/lan/light.rb, line 409
def add_hooks
  add_hook(Protocol::Device::StateLabel) do |payload|
    @label = payload.label.to_s.force_encoding('utf-8')
    seen!
  end

  add_hook(Protocol::Light::State) do |payload|
    @label      = payload.label.snapshot.force_encoding('utf-8')
    @color      = Color.from_struct(payload.color.snapshot)
    @power      = payload.power.to_i
    @tags_field = payload.tags
    seen!
  end

  add_hook(Protocol::Device::StateTags) do |payload|
    @tags_field = payload.tags
    seen!
  end

  add_hook(Protocol::Device::StatePower) do |payload|
    @power = payload.level.to_i
    seen!
  end

  add_hook(Protocol::Device::StateWifiFirmware) do |payload|
    @wifi_firmware = Firmware.new(payload)
    seen!
  end
end