Class: LIFX::LAN::Light

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

Overview

LIFX::LAN::Light represents a Light device

Defined Under Namespace

Classes: LabelTooLong, MessageTimeout

Constant Summary collapse

MAX_LABEL_LENGTH =
32

Constants included from LightTarget

LIFX::LAN::LightTarget::MSEC_PER_SEC, LIFX::LAN::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_multizone_color, #set_power, #set_tile_colors, #sine, #tile_info, #triangle, #turn_off, #turn_on, #zone_count

Methods included from Seen

#last_seen, #seconds_since_seen

Constructor Details

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



27
28
29
30
31
32
33
34
35
36
37
38
# 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

Instance Attribute Details

#contextNetworkContext (readonly)

Returns NetworkContext the Light belongs to.

Returns:



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

def context
  @context
end

#idString (readonly)

Returns Device ID.

Returns:

  • (String)

    Device ID



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

def id
  @id
end

Instance Method Details

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

Compare current Light to another light

Parameters:

Returns:

  • (-1, 0, 1)

    Comparison value

Raises:

  • (ArgumentError)


373
374
375
376
# File 'lib/lifx/lan/light.rb', line 373

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:



318
319
320
321
# 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) ⇒ Object



189
190
191
192
193
194
195
196
# 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) ⇒ 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:



78
79
80
81
82
# 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

#groupObject



347
348
349
350
351
352
353
354
355
# File 'lib/lifx/lan/light.rb', line 347

def group
  send_message!(Protocol::Device::GetGroup.new,
      wait_for: Protocol::Device::StateGroup) do |payload|
    {
      group: payload.group,
      label: payload.label
    }
  end
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



88
89
90
91
92
# 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

#latencyFloat

Pings the device and measures response time.

Returns:

  • (Float)

    Latency from sending a message to receiving a response.



209
210
211
212
213
# 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

#locationObject



337
338
339
340
341
342
343
344
345
# File 'lib/lifx/lan/light.rb', line 337

def location
  send_message!(Protocol::Device::GetLocation.new,
      wait_for: Protocol::Device::StateLocation) do |payload|
    {
      location: payload.location,
      label: payload.label
    }
  end
end

#mcu_firmware(fetch: true) ⇒ Object



227
228
229
230
231
232
233
234
# 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) ⇒ Boolean

Returns true if device is off

Returns:

  • (Boolean)

    Returns true if device is off

See Also:



153
154
155
# 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) ⇒ Boolean

Returns true if device is on

Returns:

  • (Boolean)

    Returns true if device is on

See Also:



147
148
149
# 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) ⇒ :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



160
161
162
163
164
165
166
167
168
169
170
171
# 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) ⇒ Object



198
199
200
201
202
203
204
205
# 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_tag(tag) ⇒ Light

Remove tag from the Light

Parameters:

  • tag (String)

    The tag to remove

Returns:



326
327
328
329
# 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) ⇒ 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



383
384
385
# File 'lib/lifx/lan/light.rb', line 383

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) ⇒ 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: self.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:



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
# File 'lib/lifx/lan/light.rb', line 399

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

Sets the label of the light

Parameters:

  • label (String)

    Desired label

Returns:

Raises:



101
102
103
104
105
106
107
108
109
# 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) ⇒ Light, LightCollection

Set the power state to state synchronously.

Parameters:

  • state (:on, :off)

Returns:



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# 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

#tagsArray<String>

Returns the tags that are associated with the Light

Returns:

  • (Array<String>)

    tags



333
334
335
# File 'lib/lifx/lan/light.rb', line 333

def tags
  context.tags_for_device(self)
end

#temperatureFloat

Returns the temperature of the device

Returns:

  • (Float)

    Temperature in Celcius



238
239
240
241
242
243
# 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

#timeTime

Returns the local time of the light

Returns:

  • (Time)


175
176
177
178
179
# 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_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)


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

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)


365
366
367
# File 'lib/lifx/lan/light.rb', line 365

def to_s
  %Q{#<LIFX::LAN::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:



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

def turn_off!
  set_power!(:off)
end

#turn_on!Light, LightCollection

Turns the light(s) on synchronously

Returns:



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

def turn_on!
  set_power!(:on)
end