Class: LIFX::Light

Inherits:
Object
  • Object
show all
Includes:
LightTarget, RequiredKeywordArguments, Seen
Defined in:
lib/lifx/light.rb

Overview

LIFX::Light represents a Light device

Defined Under Namespace

Classes: LabelTooLong, MessageTimeout

Constant Summary collapse

MAX_LABEL_LENGTH =
32

Constants included from LightTarget

LIFX::LightTarget::MSEC_PER_SEC, LIFX::LightTarget::NSEC_IN_SEC

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from RequiredKeywordArguments

#required!

Methods included from LightTarget

#half_sine, #pulse, #refresh, #saw, #set_color, #set_power, #sine, #triangle, #turn_off, #turn_on

Methods included from Seen

#last_seen, #seconds_since_seen

Constructor Details

#initialize(context: required!(:context), id: id, site_id: nil, label: nil) ⇒ Light

Returns a new instance of Light

Parameters:

  • context: (NetworkContext) (defaults to: required!(:context))

    NetworkContext the Light belongs to

  • id: (String) (defaults to: id)

    Device ID of the Light

  • site_id: (String) (defaults to: nil)

    Site ID of the Light. Avoid using when possible.

  • label: (String) (defaults to: nil)

    Label of Light to prepopulate



26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/lifx/light.rb', line 26

def initialize(context: required!(:context), id: 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

Instance Attribute Details

#contextNetworkContext (readonly)

Returns NetworkContext the Light belongs to

Returns:



17
18
19
# File 'lib/lifx/light.rb', line 17

def context
  @context
end

#idString (readonly)

Returns Device ID

Returns:

  • (String)

    Device ID



20
21
22
# File 'lib/lifx/light.rb', line 20

def id
  @id
end

Instance Method Details

#<=>(other) ⇒ -1, ...

Compare current Light to another light

Parameters:

Returns:

  • (-1, 0, 1)

    Comparison value

Raises:

  • (ArgumentError)


351
352
353
354
# File 'lib/lifx/light.rb', line 351

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_tag(tag) ⇒ Light

Add tag to the Light

Parameters:

  • tag (String)

    The tag to add

Returns:



316
317
318
319
# File 'lib/lifx/light.rb', line 316

def add_tag(tag)
  context.add_tag_to_device(tag: tag, device: self)
  self
end

#color(refresh: false, fetch: true) ⇒ Color

Returns the color of the device.

Parameters:

  • refresh: (Boolean) (defaults to: false)

    If true, will request for current color

  • fetch: (Boolean) (defaults to: true)

    If false, it will not request current color if it's not cached

Returns:



77
78
79
80
81
# File 'lib/lifx/light.rb', line 77

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

#label(refresh: false, fetch: true) ⇒ String?

Returns the label of the light

Parameters:

  • refresh: (Boolean) (defaults to: false)

    If true, will request for current label

  • fetch: (Boolean) (defaults to: true)

    If false, it will not request current label if it's not cached

Returns:

  • (String, nil)

    Label



87
88
89
90
91
# File 'lib/lifx/light.rb', line 87

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

#latencyFloat

Pings the device and measures response time.

Returns:

  • (Float)

    Latency from sending a message to receiving a response.



190
191
192
193
194
# File 'lib/lifx/light.rb', line 190

def latency
  start = Time.now.to_f
  send_message!(Protocol::Device::GetTime.new, wait_for: Protocol::Device::StateTime)
  Time.now.to_f - start
end

#off?(refresh: false, fetch: true) ⇒ Boolean

Returns true if device is off

Returns:

  • (Boolean)

    Returns true if device is off

See Also:



152
153
154
# File 'lib/lifx/light.rb', line 152

def off?(refresh: false, fetch: true)
  power(refresh: refresh, fetch: fetch) == :off
end

#on?(refresh: false, fetch: true) ⇒ Boolean

Returns true if device is on

Returns:

  • (Boolean)

    Returns true if device is on

See Also:



146
147
148
# File 'lib/lifx/light.rb', line 146

def on?(refresh: false, fetch: true)
  power(refresh: refresh, fetch: fetch) == :on
end

#power(refresh: false, fetch: true) ⇒ :unknown, ...

Returns Light power state

Parameters:

  • refresh: (defaults to: false)

    see #label

  • fetch: (defaults to: true)

    see #label

Returns:

  • (:unknown, :off, :on)

    Light power state



159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/lifx/light.rb', line 159

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

#remove_tag(tag) ⇒ Light

Remove tag from the Light

Parameters:

  • tag (String)

    The tag to remove

Returns:



324
325
326
327
# File 'lib/lifx/light.rb', line 324

def remove_tag(tag)
  context.remove_tag_from_device(tag: tag, device: self)
  self
end

#send_message(payload, acknowledge: true, at_time: nil) ⇒ Light

Queues a message to be sent the Light

Parameters:

  • payload (Protocol::Payload)

    the payload to send

  • acknowledge: (Boolean) (defaults to: true)

    whether the device should respond

  • at_time: (Integer) (defaults to: nil)

    Unix epoch in milliseconds to run the payload. Only applicable to certain payload types.

Returns:

  • (Light)

    returns self for chaining



361
362
363
# File 'lib/lifx/light.rb', line 361

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: wait_for, wait_timeout: Config.message_wait_timeout, retry_interval: Config.message_retry_interval, &block) ⇒ Object

Queues a message to be sent to the Light and waits for a response

Parameters:

  • payload (Protocol::Payload)

    the payload to send

  • wait_for: (Class) (defaults to: wait_for)

    the payload class to wait for

  • wait_timeout: (Numeric) (defaults to: Config.message_wait_timeout)

    wait timeout

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

Returns:

  • (Object)

    the truthy result of block is returned.

Raises:



377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/lifx/light.rb', line 377

def send_message!(payload, wait_for: 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) ⇒ Light

Sets the label of the light

Parameters:

  • label (String)

    Desired label

Returns:

Raises:



100
101
102
103
104
105
106
107
108
# File 'lib/lifx/light.rb', line 100

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) ⇒ Light, LightCollection

Set the power state to state synchronously.

Parameters:

  • state (:on, :off)

Returns:



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/lifx/light.rb', line 113

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

#tagsArray<String>

Returns the tags that are associated with the Light

Returns:

  • (Array<String>)

    tags



331
332
333
# File 'lib/lifx/light.rb', line 331

def tags
  context.tags_for_device(self)
end

#temperatureFloat

Returns the temperature of the device

Returns:

  • (Float)

    Temperature in Celcius



222
223
224
225
226
227
# File 'lib/lifx/light.rb', line 222

def temperature
  send_message!(Protocol::Light::GetTemperature.new,
      wait_for: Protocol::Light::StateTemperature) do |payload|
    payload.temperature / 100.0
  end
end

#timeTime

Returns the local time of the light

Returns:

  • (Time)


174
175
176
177
178
# File 'lib/lifx/light.rb', line 174

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_deltaFloat

Returns the difference between the device time and time on the current machine Positive values means device time is further in the future.

Returns:

  • (Float)


183
184
185
186
# File 'lib/lifx/light.rb', line 183

def time_delta
  device_time = time
  delta = device_time - Time.now
end

#to_sString Also known as: inspect

Returns a nice string representation of the Light

Returns:

  • (String)


343
344
345
# File 'lib/lifx/light.rb', line 343

def to_s
  %Q{#<LIFX::Light id=#{id} label=#{label(fetch: false)} power=#{power(fetch: false)}>}.force_encoding('utf-8')
end

#turn_off!Light, LightCollection

Turns the light(s) off synchronously

Returns:



140
141
142
# File 'lib/lifx/light.rb', line 140

def turn_off!
  set_power!(:off)
end

#turn_on!Light, LightCollection

Turns the light(s) on synchronously

Returns:



134
135
136
# File 'lib/lifx/light.rb', line 134

def turn_on!
  set_power!(:on)
end