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


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)


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

def context
  @context
end

#idString (readonly)


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

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


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.


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


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.


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

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

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


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


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


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

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

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.


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


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


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


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.


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


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


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


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

def turn_on!
  set_power!(:on)
end