Split up the code into separate files
* Rename the Hued class to Hued::Engine so that Hued can be the containing module * Split off the version to lib/hued/version.rb * Undo Rule being a nested class of Engine
This commit is contained in:
parent
7d6ae9c093
commit
e2921f9c2e
331
bin/hued
331
bin/hued
|
@ -10,327 +10,7 @@
|
|||
# Free Software Foundation; either version 2 of the License, or (at your
|
||||
# option) any later version.
|
||||
|
||||
require "chronic"
|
||||
require "eventmachine"
|
||||
require "huey"
|
||||
require "logger"
|
||||
require "optparse"
|
||||
require "pp"
|
||||
|
||||
# The main engine class
|
||||
class Hued
|
||||
|
||||
# Scenes
|
||||
Scenes = {}
|
||||
|
||||
# Program version
|
||||
VERSION = "0.0.1"
|
||||
|
||||
# The rule class
|
||||
class Rule
|
||||
|
||||
attr_reader :name, :conditions, :tigger, :priority, :events, :scene
|
||||
|
||||
def initialize(name, log, entry)
|
||||
@name = name
|
||||
@log = log
|
||||
@validity = false
|
||||
@conditions = entry["conditions"] || []
|
||||
@trigger = entry["trigger"].nil? ? true : entry["trigger"]
|
||||
@triggered = false
|
||||
@priority = entry["priority"] || 0
|
||||
@events = []
|
||||
|
||||
if entry["events"]
|
||||
entry["events"].each do |ev_name|
|
||||
event = Huey::Event.find(ev_name)
|
||||
if event.nil?
|
||||
@log.warn "Could not find event \"#{ev_name}\" for rule \"#{name}\", ignoring!"
|
||||
else
|
||||
@events << event
|
||||
end
|
||||
end
|
||||
elsif entry["scene"]
|
||||
@scene = entry["scene"] if Scenes.has_key? entry["scene"]
|
||||
if @scene.nil?
|
||||
@log.warn "Could not find scene \"#{entry["scene"]}\" for rule \"#{name}\", ignoring!"
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "You must supply either an even or scene name"
|
||||
end
|
||||
end
|
||||
|
||||
def valid?
|
||||
# Determine validity
|
||||
prev_validity = @validity
|
||||
@validity = test_conditions
|
||||
|
||||
# Reset the triggered flag if the this is a trigger rule, but it is
|
||||
# no longer valid
|
||||
@triggered = false if @trigger and prev_validity and !@validity
|
||||
|
||||
@validity
|
||||
end
|
||||
|
||||
def triggered?
|
||||
@triggered
|
||||
end
|
||||
|
||||
def trigger?
|
||||
@trigger
|
||||
end
|
||||
|
||||
def execute
|
||||
@triggered = true
|
||||
events = if @scene
|
||||
@log.info "Executing scene: #{@scene}"
|
||||
events = Scenes[@scene]
|
||||
elsif @events
|
||||
@events
|
||||
else
|
||||
@log.info "No scene or events found, skipping execution"
|
||||
[]
|
||||
end
|
||||
|
||||
events.each_with_index do |event, idx|
|
||||
if event.name
|
||||
@log.info "Executing event: #{event.name}!"
|
||||
else
|
||||
@log.info "Executing event #{idx}"
|
||||
end
|
||||
retry_count = 0
|
||||
begin
|
||||
event.execute
|
||||
rescue Huey::Errors::BulbOff
|
||||
if retry_count > 4
|
||||
@log.warn "One of the lights is still off, ignoring event"
|
||||
else
|
||||
@log.warn "One of the lights was off, retrying..."
|
||||
event.group.bulbs.each { |bulb| bulb.on = false }
|
||||
retry_count += 1
|
||||
retry
|
||||
end
|
||||
rescue Huey::Errors::Error => e
|
||||
@log.error "Error while executing event (#{e.class}): #{e.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
|
||||
def test_conditions
|
||||
# If there are no conditions, the rule is always valid
|
||||
return true if @conditions.empty?
|
||||
|
||||
@conditions.map do |cond|
|
||||
cond_negate = false
|
||||
res = if cond.is_a? Hash
|
||||
cond_name, cond_value = cond.to_a.first
|
||||
if cond_name[0] == "^"
|
||||
cond_negate = true
|
||||
cond_name = cond_name[1..-1]
|
||||
end
|
||||
case cond_name
|
||||
when "from"
|
||||
Time.now >= Chronic.parse(cond_value)
|
||||
when "until"
|
||||
Time.now <= Chronic.parse(cond_value)
|
||||
when "found host"
|
||||
system("ping -W3 -c1 -q #{cond_value} > /dev/null")
|
||||
end
|
||||
else
|
||||
@log.warn "Unknown condition type/form #{cond.inspect}"
|
||||
end
|
||||
cond_negate ? !res : res
|
||||
end.all?
|
||||
end
|
||||
|
||||
end # class Hued::Rule
|
||||
|
||||
def initialize(options = {})
|
||||
@options = options
|
||||
@ctime = Hash.new(Time.now)
|
||||
|
||||
# Set up the logger
|
||||
@log = Logger.new($stdout)
|
||||
@log.progname = "hued"
|
||||
@log.level = options[:debug] ? Logger::DEBUG : Logger::INFO
|
||||
@log.formatter = proc do |severity, datetime, progname, msg|
|
||||
"#{datetime.strftime('%b %d %X')} #{progname}[#{$$}]: #{msg}\n"
|
||||
end
|
||||
|
||||
configure
|
||||
discover
|
||||
load
|
||||
@log.info "Started successfully!"
|
||||
end
|
||||
|
||||
def configure
|
||||
@log.info "Starting..."
|
||||
bridge_cfg = File.open("bridge.yml") { |file| YAML.load(file) }
|
||||
Huey.configure do |cfg|
|
||||
cfg.hue_ip = bridge_cfg["ip"]
|
||||
cfg.uuid = bridge_cfg["user"]
|
||||
if @options[:hue_debug]
|
||||
cfg.logger = @log
|
||||
else
|
||||
# Use the default logger and make it shut up
|
||||
cfg.logger.level = Logger::FATAL
|
||||
end
|
||||
end
|
||||
@log.info "Configured bridge connection"
|
||||
end
|
||||
|
||||
def discover
|
||||
@log.info "Discovering lights..."
|
||||
@lights = Huey::Bulb.all
|
||||
@lights.each do |light|
|
||||
@log.info "* Found light #{light.id}: #{light.name}"
|
||||
light.alert! if @options[:blink]
|
||||
end
|
||||
@log.info "Found #{@lights.count} light#{"s" unless @lights.count == 1}"
|
||||
|
||||
@log.info "Discovering groups..."
|
||||
@groups = Huey::Group.all
|
||||
@groups.each do |group|
|
||||
@log.info "* Found group #{group.id}: #{group.name} with " \
|
||||
"lights #{group.bulbs.map(&:id).join(", ")}"
|
||||
end
|
||||
@log.info "Found #{@groups.count} group#{@groups.count != 1 || "s"}"
|
||||
end
|
||||
|
||||
def refresh!
|
||||
@log.debug "Refreshing lights..."
|
||||
@lights.each { |light| light.reload }
|
||||
@log.debug "Refreshed #{@lights.count} light#{"s" unless @lights.count == 1}"
|
||||
end
|
||||
|
||||
def load
|
||||
[:events, :scenes].each do |items|
|
||||
if File.exist? "#{items}.yml"
|
||||
@log.info "Loading #{items}..."
|
||||
send("load_#{items}")
|
||||
end
|
||||
end
|
||||
|
||||
# Treat rules separately, we cannot start without it
|
||||
if File.exist? "rules.yml"
|
||||
@log.info "Loading rules"
|
||||
load_rules
|
||||
else
|
||||
@log.error "Cannot find required file: rules.yml, aborting!"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
def reload
|
||||
@log.debug "Checking if events/scenes/rules need to be reloaded..."
|
||||
@reload_rules = false
|
||||
[:events, :scenes].each do |items|
|
||||
if File.exist?("#{items}.yml") and
|
||||
File.ctime("#{items}.yml") > @ctime[items]
|
||||
@log.info "Reloading events..."
|
||||
send("load_#{items}")
|
||||
# Rules may depend on events/scenes, reload the rules too!
|
||||
@reload_rules = true
|
||||
end
|
||||
end
|
||||
|
||||
if File.exist?("rules.yml") and
|
||||
(@reload_rules or File.ctime("rules.yml") > @ctime[:rules])
|
||||
@log.info "Reloading rules..."
|
||||
send("load_rules")
|
||||
end
|
||||
end
|
||||
|
||||
def execute
|
||||
@log.debug "Looking for active (and valid) rules..."
|
||||
valid_rules = @rules.select(&:valid?)
|
||||
if valid_rules.empty?
|
||||
@log.debug "No valid rules found"
|
||||
return
|
||||
else
|
||||
@log.debug "There #{valid_rules.count == 1 ? "is" : "are"} " \
|
||||
"#{valid_rules.count} valid " \
|
||||
"rule#{"s" unless valid_rules.count == 1}"
|
||||
end
|
||||
|
||||
prio_map = valid_rules.group_by(&:priority)
|
||||
prios = prio_map.keys.sort
|
||||
prios.each do |prio|
|
||||
prio_rules = prio_map[prio]
|
||||
@log.debug "* Rule#{"s" unless prio_rules.count == 1} with prioity #{prio}: " +
|
||||
prio_rules.map(&:name).join(", ")
|
||||
end
|
||||
active_rules = prio_map[prios.last]
|
||||
if valid_rules != active_rules
|
||||
@log.debug "There #{active_rules.count == 1 ? "is" : "are"} " \
|
||||
"only #{active_rules.count} active " \
|
||||
"rule#{"s" unless active_rules.count == 1}"
|
||||
"(i.e. with priority #{prios.last})"
|
||||
end
|
||||
active_rules.each do |rule|
|
||||
if rule.trigger?
|
||||
if rule.triggered?
|
||||
@log.info "Rule \"#{rule.name}\" is active, but has already been triggered"
|
||||
else
|
||||
@log.info "Rule \"#{rule.name}\" is active and should be triggered"
|
||||
rule.execute
|
||||
end
|
||||
else
|
||||
@log.info "Rule \"#{rule.name}\" is active and should be triggered (again)"
|
||||
rule.execute
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def shutdown
|
||||
@log.info "Shutting down..."
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
|
||||
def load_events
|
||||
@ctime[:events] = File.ctime("events.yml")
|
||||
@events = Huey::Event.import("events.yml")
|
||||
@events.each do |event|
|
||||
event.actions["on"] = true if event.actions["on"].nil?
|
||||
@log.info "* Loaded event: #{event.name}"
|
||||
end
|
||||
@log.info "Loaded #{@events.count} event#{"s" unless @events.count == 1}"
|
||||
end
|
||||
|
||||
def load_scenes
|
||||
@ctime[:scenes] = File.ctime "scenes.yml"
|
||||
@scenes = {}
|
||||
YAML.load_file("scenes.yml").each do |name, entry|
|
||||
@scenes[name] = entry.map do |ev_options|
|
||||
# Keys should be symbols
|
||||
options = ev_options.inject({}) { |opts, (k, v)| opts[k.to_sym] = v; opts }
|
||||
event = Huey::Event.new(options)
|
||||
event.actions["on"] = true if event.actions["on"].nil?
|
||||
event
|
||||
end
|
||||
Scenes[name] = @scenes[name]
|
||||
@log.info "* Loaded scene: #{name}"
|
||||
end
|
||||
@log.info "Loaded #{@scenes.count} scene#{"s" unless @scenes.count == 1}"
|
||||
end
|
||||
|
||||
def load_rules
|
||||
@ctime[:rules] = File.ctime "rules.yml"
|
||||
@rules = YAML.load_file("rules.yml").map do |name, entry|
|
||||
Rule.new(name, @log, entry)
|
||||
end
|
||||
@rules.each do |rule|
|
||||
@log.info "* Loaded rule: #{rule.name}"
|
||||
end
|
||||
@log.info "Loaded #{@rules.count} rule#{"s" unless @rules.count == 1}"
|
||||
end
|
||||
|
||||
end # class Hued
|
||||
require "hued"
|
||||
|
||||
# Option parsing
|
||||
options = {blink: true}
|
||||
|
@ -369,7 +49,8 @@ rescue OptionParser::InvalidOption => e
|
|||
end
|
||||
|
||||
# Create the main engine
|
||||
hued = Hued.new(options)
|
||||
Hued.configure(options)
|
||||
engine = Hued::Engine.new
|
||||
|
||||
# Handle signals
|
||||
Signal.trap("INT") { EM.stop }
|
||||
|
@ -377,7 +58,7 @@ Signal.trap("TERM") { EM.stop }
|
|||
|
||||
# Trigger rule execution and light status refreshing periodically
|
||||
EM.run do
|
||||
EM.add_periodic_timer(10) { hued.reload; hued.execute }
|
||||
EM.add_periodic_timer(300) { hued.refresh! }
|
||||
EM.add_periodic_timer(10) { engine.reload; engine.execute }
|
||||
EM.add_periodic_timer(300) { engine.refresh! }
|
||||
end
|
||||
hued.shutdown
|
||||
engine.shutdown
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require "chronic"
|
||||
require "eventmachine"
|
||||
require "huey"
|
||||
require "logger"
|
||||
require "optparse"
|
||||
require "pp"
|
||||
|
||||
require "hued/version"
|
||||
|
||||
require "hued/engine"
|
||||
require "hued/rule"
|
||||
|
||||
module Hued
|
||||
extend self
|
||||
|
||||
# Daemon configuration
|
||||
attr_reader :config
|
||||
|
||||
# Daemon log
|
||||
attr_reader :log
|
||||
|
||||
# Loaded scenes
|
||||
# FIXME: load scenes as Hued::Scene classes
|
||||
Scenes = {}
|
||||
|
||||
def configure(options)
|
||||
@config = options
|
||||
|
||||
# Set up the logger
|
||||
@log = Logger.new($stdout)
|
||||
@log.progname = "hued"
|
||||
@log.level = options[:debug] ? Logger::DEBUG : Logger::INFO
|
||||
@log.formatter = proc do |severity, datetime, progname, msg|
|
||||
"#{datetime.strftime('%b %d %X')} #{progname}[#{$$}]: #{msg}\n"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,192 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module Hued
|
||||
|
||||
# The main engine class
|
||||
class Engine
|
||||
|
||||
def initialize(options = {})
|
||||
@options = Hued.config
|
||||
@log = Hued.log
|
||||
@ctime = Hash.new(Time.now)
|
||||
|
||||
configure
|
||||
discover
|
||||
load
|
||||
@log.info "Started successfully!"
|
||||
end
|
||||
|
||||
def configure
|
||||
@log.info "Starting..."
|
||||
bridge_cfg = File.open("bridge.yml") { |file| YAML.load(file) }
|
||||
Huey.configure do |cfg|
|
||||
cfg.hue_ip = bridge_cfg["ip"]
|
||||
cfg.uuid = bridge_cfg["user"]
|
||||
if @options[:hue_debug]
|
||||
cfg.logger = @log
|
||||
else
|
||||
# Use the default logger and make it shut up
|
||||
cfg.logger.level = Logger::FATAL
|
||||
end
|
||||
end
|
||||
@log.info "Configured bridge connection"
|
||||
end
|
||||
|
||||
def discover
|
||||
@log.info "Discovering lights..."
|
||||
@lights = Huey::Bulb.all
|
||||
@lights.each do |light|
|
||||
@log.info "* Found light #{light.id}: #{light.name}"
|
||||
light.alert! if @options[:blink]
|
||||
end
|
||||
@log.info "Found #{@lights.count} light#{"s" unless @lights.count == 1}"
|
||||
|
||||
@log.info "Discovering groups..."
|
||||
@groups = Huey::Group.all
|
||||
@groups.each do |group|
|
||||
@log.info "* Found group #{group.id}: #{group.name} with " \
|
||||
"lights #{group.bulbs.map(&:id).join(", ")}"
|
||||
end
|
||||
@log.info "Found #{@groups.count} group#{@groups.count != 1 || "s"}"
|
||||
rescue
|
||||
@lights = []
|
||||
@groups = []
|
||||
end
|
||||
|
||||
def refresh!
|
||||
@log.debug "Refreshing lights..."
|
||||
@lights.each { |light| light.reload }
|
||||
@log.debug "Refreshed #{@lights.count} light#{"s" unless @lights.count == 1}"
|
||||
end
|
||||
|
||||
def load
|
||||
[:events, :scenes].each do |items|
|
||||
if File.exist? "#{items}.yml"
|
||||
@log.info "Loading #{items}..."
|
||||
send("load_#{items}")
|
||||
end
|
||||
end
|
||||
|
||||
# Treat rules separately, we cannot start without it
|
||||
if File.exist? "rules.yml"
|
||||
@log.info "Loading rules"
|
||||
load_rules
|
||||
else
|
||||
@log.error "Cannot find required file: rules.yml, aborting!"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
def reload
|
||||
@log.debug "Checking if events/scenes/rules need to be reloaded..."
|
||||
@reload_rules = false
|
||||
[:events, :scenes].each do |items|
|
||||
if File.exist?("#{items}.yml") and
|
||||
File.ctime("#{items}.yml") > @ctime[items]
|
||||
@log.info "Reloading events..."
|
||||
send("load_#{items}")
|
||||
# Rules may depend on events/scenes, reload the rules too!
|
||||
@reload_rules = true
|
||||
end
|
||||
end
|
||||
|
||||
if File.exist?("rules.yml") and
|
||||
(@reload_rules or File.ctime("rules.yml") > @ctime[:rules])
|
||||
@log.info "Reloading rules..."
|
||||
send("load_rules")
|
||||
end
|
||||
end
|
||||
|
||||
def execute
|
||||
@log.debug "Looking for active (and valid) rules..."
|
||||
valid_rules = @rules.select(&:valid?)
|
||||
if valid_rules.empty?
|
||||
@log.debug "No valid rules found"
|
||||
return
|
||||
else
|
||||
@log.debug "There #{valid_rules.count == 1 ? "is" : "are"} " \
|
||||
"#{valid_rules.count} valid " \
|
||||
"rule#{"s" unless valid_rules.count == 1}"
|
||||
end
|
||||
|
||||
prio_map = valid_rules.group_by(&:priority)
|
||||
prios = prio_map.keys.sort
|
||||
prios.each do |prio|
|
||||
prio_rules = prio_map[prio]
|
||||
@log.debug "* Rule#{"s" unless prio_rules.count == 1} with prioity #{prio}: " +
|
||||
prio_rules.map(&:name).join(", ")
|
||||
end
|
||||
active_rules = prio_map[prios.last]
|
||||
if valid_rules != active_rules
|
||||
@log.debug "There #{active_rules.count == 1 ? "is" : "are"} " \
|
||||
"only #{active_rules.count} active " \
|
||||
"rule#{"s" unless active_rules.count == 1}"
|
||||
"(i.e. with priority #{prios.last})"
|
||||
end
|
||||
active_rules.each do |rule|
|
||||
if rule.trigger?
|
||||
if rule.triggered?
|
||||
@log.info "Rule \"#{rule.name}\" is active, but has already been triggered"
|
||||
else
|
||||
@log.info "Rule \"#{rule.name}\" is active and should be triggered"
|
||||
rule.execute
|
||||
end
|
||||
else
|
||||
@log.info "Rule \"#{rule.name}\" is active and should be triggered (again)"
|
||||
rule.execute
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def shutdown
|
||||
@log.info "Shutting down..."
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
|
||||
def load_events
|
||||
@ctime[:events] = File.ctime("events.yml")
|
||||
@events = Huey::Event.import("events.yml")
|
||||
@events.each do |event|
|
||||
event.actions["on"] = true if event.actions["on"].nil?
|
||||
@log.info "* Loaded event: #{event.name}"
|
||||
end
|
||||
@log.info "Loaded #{@events.count} event#{"s" unless @events.count == 1}"
|
||||
rescue
|
||||
@events= []
|
||||
end
|
||||
|
||||
def load_scenes
|
||||
@ctime[:scenes] = File.ctime "scenes.yml"
|
||||
@scenes = {}
|
||||
YAML.load_file("scenes.yml").each do |name, entry|
|
||||
@scenes[name] = entry.map do |ev_options|
|
||||
# Keys should be symbols
|
||||
options = ev_options.inject({}) { |opts, (k, v)| opts[k.to_sym] = v; opts }
|
||||
event = Huey::Event.new(options)
|
||||
event.actions["on"] = true if event.actions["on"].nil?
|
||||
event
|
||||
end
|
||||
Scenes[name] = @scenes[name]
|
||||
@log.info "* Loaded scene: #{name}"
|
||||
end
|
||||
@log.info "Loaded #{@scenes.count} scene#{"s" unless @scenes.count == 1}"
|
||||
rescue
|
||||
@scenes = {}
|
||||
end
|
||||
|
||||
def load_rules
|
||||
@ctime[:rules] = File.ctime "rules.yml"
|
||||
@rules = YAML.load_file("rules.yml").map do |name, entry|
|
||||
Rule.new(name, @log, entry)
|
||||
end
|
||||
@rules.each do |rule|
|
||||
@log.info "* Loaded rule: #{rule.name}"
|
||||
end
|
||||
@log.info "Loaded #{@rules.count} rule#{"s" unless @rules.count == 1}"
|
||||
end
|
||||
|
||||
end # class Hued::Engine
|
||||
|
||||
end # module Hued
|
|
@ -0,0 +1,127 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module Hued
|
||||
|
||||
# The rule class
|
||||
class Rule
|
||||
|
||||
attr_reader :name, :conditions, :tigger, :priority, :events, :scene
|
||||
|
||||
def initialize(name, log, entry)
|
||||
@name = name
|
||||
@log = log
|
||||
@validity = false
|
||||
@conditions = entry["conditions"] || []
|
||||
@trigger = entry["trigger"].nil? ? true : entry["trigger"]
|
||||
@triggered = false
|
||||
@priority = entry["priority"] || 0
|
||||
@events = []
|
||||
|
||||
if entry["events"]
|
||||
entry["events"].each do |ev_name|
|
||||
event = Huey::Event.find(ev_name)
|
||||
if event.nil?
|
||||
@log.warn "Could not find event \"#{ev_name}\" for rule \"#{name}\", ignoring!"
|
||||
else
|
||||
@events << event
|
||||
end
|
||||
end
|
||||
elsif entry["scene"]
|
||||
@scene = entry["scene"] if Scenes.has_key? entry["scene"]
|
||||
if @scene.nil?
|
||||
@log.warn "Could not find scene \"#{entry["scene"]}\" for rule \"#{name}\", ignoring!"
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "You must supply either an even or scene name"
|
||||
end
|
||||
end
|
||||
|
||||
def valid?
|
||||
# Determine validity
|
||||
prev_validity = @validity
|
||||
@validity = test_conditions
|
||||
|
||||
# Reset the triggered flag if the this is a trigger rule, but it is
|
||||
# no longer valid
|
||||
@triggered = false if @trigger and prev_validity and !@validity
|
||||
|
||||
@validity
|
||||
end
|
||||
|
||||
def triggered?
|
||||
@triggered
|
||||
end
|
||||
|
||||
def trigger?
|
||||
@trigger
|
||||
end
|
||||
|
||||
def execute
|
||||
@triggered = true
|
||||
events = if @scene
|
||||
@log.info "Executing scene: #{@scene}"
|
||||
events = Scenes[@scene]
|
||||
elsif @events
|
||||
@events
|
||||
else
|
||||
@log.info "No scene or events found, skipping execution"
|
||||
[]
|
||||
end
|
||||
|
||||
events.each_with_index do |event, idx|
|
||||
if event.name
|
||||
@log.info "Executing event: #{event.name}!"
|
||||
else
|
||||
@log.info "Executing event #{idx}"
|
||||
end
|
||||
retry_count = 0
|
||||
begin
|
||||
event.execute
|
||||
rescue Huey::Errors::BulbOff
|
||||
if retry_count > 4
|
||||
@log.warn "One of the lights is still off, ignoring event"
|
||||
else
|
||||
@log.warn "One of the lights was off, retrying..."
|
||||
event.group.bulbs.each { |bulb| bulb.on = false }
|
||||
retry_count += 1
|
||||
retry
|
||||
end
|
||||
rescue Huey::Errors::Error => e
|
||||
@log.error "Error while executing event (#{e.class}): #{e.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
|
||||
def test_conditions
|
||||
# If there are no conditions, the rule is always valid
|
||||
return true if @conditions.empty?
|
||||
|
||||
@conditions.map do |cond|
|
||||
cond_negate = false
|
||||
res = if cond.is_a? Hash
|
||||
cond_name, cond_value = cond.to_a.first
|
||||
if cond_name[0] == "^"
|
||||
cond_negate = true
|
||||
cond_name = cond_name[1..-1]
|
||||
end
|
||||
case cond_name
|
||||
when "from"
|
||||
Time.now >= Chronic.parse(cond_value)
|
||||
when "until"
|
||||
Time.now <= Chronic.parse(cond_value)
|
||||
when "found host"
|
||||
system("ping -W3 -c1 -q #{cond_value} > /dev/null 2>&1")
|
||||
end
|
||||
else
|
||||
@log.warn "Unknown condition type/form #{cond.inspect}"
|
||||
end
|
||||
cond_negate ? !res : res
|
||||
end.all?
|
||||
end
|
||||
|
||||
end # class Hued::Rule
|
||||
|
||||
end # module Hued
|
|
@ -0,0 +1,6 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module Hued
|
||||
# Daemon version
|
||||
VERSION = "0.0.1"
|
||||
end
|
Reference in New Issue