217 lines
6.7 KiB
Ruby
217 lines
6.7 KiB
Ruby
# encoding: utf-8
|
|
|
|
module Hued
|
|
|
|
# The main engine class
|
|
class Engine
|
|
|
|
def initialize(options = {})
|
|
@config = Hued.config
|
|
@log = Hued.log
|
|
@ctime = {}
|
|
@file = {}
|
|
[:bridge, :events, :scenes, :rules].each do |items|
|
|
@ctime[items] = Time.now
|
|
@file[items] = File.join(@config[:config_dir], "#{items}.yml")
|
|
end
|
|
|
|
configure
|
|
discover
|
|
load
|
|
@log.info "Started successfully!"
|
|
end
|
|
|
|
def configure
|
|
@log.info "Starting hued v#{Hued::VERSION}..."
|
|
bridge_cfg = begin
|
|
YAML.load_file(@file[:bridge])
|
|
rescue => e
|
|
@log.info "Cannot find bridge configuration: #{@file[:bridge]}!"
|
|
@log.info "Will trying automatic setup when discovering"
|
|
nil
|
|
end
|
|
Huey.configure do |cfg|
|
|
cfg.hue_ip = bridge_cfg["ip"] if bridge_cfg
|
|
cfg.uuid = bridge_cfg["user"] if bridge_cfg
|
|
if @config[: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 @config[: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#{"s" unless @groups.count == 1}"
|
|
# FIXME: mention bridge.cfg contents if it was done via auto setup
|
|
rescue => e
|
|
@log.error "Could not discover lights/groups: #{e.message}"
|
|
@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}"
|
|
rescue => e
|
|
@log.error "Could not refresh lights: #{e.message}"
|
|
end
|
|
|
|
def load
|
|
[:events, :scenes].each do |items|
|
|
if File.exist? @file[items]
|
|
@log.info "Loading #{items}..."
|
|
send("load_#{items}")
|
|
end
|
|
end
|
|
|
|
# Treat rules separately, we cannot start without it
|
|
if File.exist? @file[:rules]
|
|
@log.info "Loading rules"
|
|
load_rules
|
|
else
|
|
@log.error "Cannot find required file: #{@file[:rules]}, 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?(@file[items]) and
|
|
File.ctime(@file[items]) > @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?(@file[:rules]) and
|
|
(@reload_rules or File.ctime(@file[:rules]) > @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|
|
|
begin
|
|
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
|
|
rescue => e
|
|
@log.error "Could not execute rule: #{e.message}"
|
|
end
|
|
end
|
|
end
|
|
|
|
def shutdown
|
|
@log.info "Shutting down..."
|
|
end
|
|
|
|
#######
|
|
private
|
|
|
|
def load_events
|
|
@ctime[:events] = File.ctime(@file[:events])
|
|
@events = Huey::Event.import(@file[:events])
|
|
@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 => e
|
|
@log.error "Could not load events: #{e.message}"
|
|
@events= []
|
|
end
|
|
|
|
def load_scenes
|
|
@ctime[:scenes] = File.ctime(@file[:scenes])
|
|
@scenes = {}
|
|
YAML.load_file(@file[:scenes]).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 => e
|
|
@log.error "Could not load scenes: #{e.message}"
|
|
@scenes = {}
|
|
end
|
|
|
|
def load_rules
|
|
@ctime[:rules] = File.ctime(@file[:rules])
|
|
@rules =
|
|
YAML.load_file(@file[:rules]).map do |name, entry|
|
|
rule = Rule.new(name, @log, entry)
|
|
@log.info "* Loaded rule: #{rule.name}"
|
|
rule
|
|
end
|
|
@log.info "Loaded #{@rules.count} rule#{"s" unless @rules.count == 1}"
|
|
rescue => e
|
|
@log.error "Could not load rules: #{e.message}"
|
|
@rules = {}
|
|
end
|
|
|
|
end # class Hued::Engine
|
|
|
|
end # module Hued
|