This has been update. Please see the update here
The following demonstrates the ability to use the Smarthome Smartlinc network controller, similar to a dedicated PLM.
I wrote this as my first Ruby program. It allows me expand the use of the 2412n to include motion sensor and more advanced timed events.
Insteon.rb Main program
require 'rubygems'
require 'thread'
require 'ice_cube'
require "rexml/document"
require "socket"
include Socket::Constants
require 'Sunrise'
require 'pp'
#Create the location
$long = "36.76"
$lat = "-80.73"
$timezone = 'America/New_York'
$host = "ha-controller.home.local"
$port = 80
$username = "htrn"
$password = "foofoodd"
$buffer = ""
$queue = Queue.new
producer = Thread.new {
def check_controller()
path = "/buffstatus.xml" # The file we want
# This is the HTTP request we send to fetch a file
request = "POST #{path} HTTP/1.1\r\n\r\n"
mr = 2
tbs = ''
while mr > 0
socket = TCPSocket.new($host,$port) # Connect to server
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
socket.write(request[0..0]) # Send request
socket.flush
socket.write(request[1..-1]) # Send request
socket.flush
response = socket.read # Read complete response
# Split response at first blank line into headers and body
socket.close
header,body = response.split("\r\n\r\n", 2)
if header != nil
m = header.scan(/\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/min)
retval = {'status' => m[0][1].to_i,'content' => body}
else
retval = {'status' => 404,'content' => ''}
end
if retval['status'] == 200
bs = get_buffstatus(retval['content'])
if bs != nil
if tbs != bs
tbs = bs
else
mr = mr - 1
if mr == 0
clear_buffstatus()
queue_messages(bs)
end
end
end
else
puts "Buffstatus error #{retval['status']}"
end
end
return true
end
def queue_messages(buff)
buff = buff.gsub("0250", ",0250")
buff = buff.gsub("0251", ",0251")
buff = buff.gsub("0252", ",0252")
buff = buff.gsub("0253", ",0253")
buff = buff.gsub("0254", ",0254")
buff = buff.gsub("0255", ",0255")
buff = buff.gsub("0256", ",0256")
buff = buff.gsub("0257", ",0257")
buff = buff.gsub("0258", ",0258")
buff = buff.gsub("0260", ",0260")
buff = buff.gsub("0261", ",0261")
buff = buff.gsub("0262", ",0262")
buff = buff.gsub("0263", ",0263")
buff = buff.gsub("0264", ",0264")
buff = buff.gsub("0265", ",0265")
buff = buff.gsub("0266", ",0266")
buff = buff.gsub("0267", ",0267")
buff = buff.gsub("0268", ",0268")
buff = buff.gsub("0269", ",0269")
buff = buff.gsub("026A", ",026A")
buff = buff.gsub("026B", ",026B")
buff = buff.gsub("026C", ",026C")
buff = buff.gsub("026D", ",026D")
buff = buff.gsub("026E", ",026E")
buff = buff.gsub("026F", ",026F")
buff = buff.gsub("0270", ",0270")
buff = buff.gsub("0271", ",0271")
buff = buff.gsub("0272", ",0272")
buff = buff.gsub("0273", ",0273")
time = Time.new
e = buff.split(',')
e.each_index {|i|
if !e[i].empty?
message = Hash.new
message['Time'] = time
message['Event'] = ''
message['Description'] = ''
message['From'] = ''
message['To'] = ''
message['Flags'] = ''
message['Command'] = ''
message['Extended'] = ''
message['Category'] = ''
message['Subcateory'] = ''
message['Version'] = ''
message['Status'] = ''
message['Linkcode'] = ''
event = e[i][0..3]
case event
when '0250' # received standard message
arr = e[i].scan(/(....)(......)(......)(..)(....)/)
if arr.length == 1 and arr[0].length == 5
message['Event'] = arr[0][0]
message['Description'] = 'Std Message'
message['From'] = arr[0][1]
message['To'] = arr[0][2]
message['Flags'] = arr[0][3]
message['Command'] = arr[0][4]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '0251' # received extended message
arr = e[i].scan(/(....)(......)(......)(..)(....)(............................)/)
if arr.length == 1 and arr[0].length == 6
message['Event'] = arr[0][0]
message['Description'] = 'Ext Message'
message['From'] = arr[0][1]
message['To'] = arr[0][2]
message['Flags'] = arr[0][3]
message['Command'] = arr[0][4]
message['Extended'] = arr[0][5]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '0253' # All Linking Completed
arr = e[i].scan(/(....)(..)(..)(......)(..)(..)(..)/)
if arr.length == 1 and arr[0].length == 6
message['Event'] = arr[0][0]
message['Description'] = 'All Linking Completed'
message['Linkcode'] = arr[0][1]
message['To'] = arr[0][2]
message['From'] = arr[0][3]
message['Category'] = arr[0][4]
message['Subcateory'] = arr[0][5]
message['Status'] = arr[0][6]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '0254' # Button Event Report
arr = e[i].scan(/(....)(..)/)
if arr.length == 1 and arr[0].length == 2
message['Event'] = arr[0][0]
message['Description'] = 'Button Event Report'
message['Status'] = arr[0][1]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '0257' # All-Link Report
arr = e[i].scan(/(....)(..)(..)(......)(......)/)
if arr.length == 1 and arr[0].length == 5
message['Event'] = arr[0][0]
message['Description'] = 'All-Link Report'
message['From'] = arr[0][3]
message['To'] = arr[0][2]
message['Flags'] = arr[0][1]
message['Command'] = arr[0][4]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '0258' # ALL-Link Cleanup Status Report
arr = e[i].scan(/(....)(.*)/)
if arr.length == 1 and arr[0].length == 2
message['Event'] = arr[0][0]
message['Description'] = 'All-Link Cleanup'
message['Status'] = arr[0][1]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '0260' # Get IM Info
arr = e[i].scan(/(....)(......)(..)(..)(..)(..)/)
if arr.length == 1 and arr[0].length == 6
message['Event'] = arr[0][0]
message['Description'] = 'Get IM Info'
message['From'] = arr[0][1]
message['Category'] = arr[0][2]
message['Subcateory'] = arr[0][3]
message['Version'] = arr[0][4]
message['Status'] = arr[0][5]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '0261' # Scene Command
arr = e[i].scan(/(....)(..)(..)(.*)/)
if arr.length == 1 and arr[0].length == 4
message['Event'] = arr[0][0]
message['Description'] = 'Scene Message'
message['From'] = arr[0][1]
message['Command'] = arr[0][2]
message['Status'] = arr[0][3]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '0262' # Send Message
arr = e[i].scan(/(....)(......)(..)(....)(.*)/)
if arr.length == 1 and arr[0].length == 5
message['Event'] = arr[0][0]
message['Description'] = 'Send Message'
message['To'] = arr[0][1]
message['Flags'] = arr[0][2]
message['Command'] = arr[0][3]
message['Status'] = arr[0][4][-2..-1]
if arr[0][2].hex & 16 == 16
message['Extended'] = arr[0][4][0..-3]
end
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '0265' # Cancel All-linking
arr = e[i].scan(/(....)(..)/)
if arr.length == 1 and arr[0].length == 2
message['Event'] = arr[0][0]
message['Description'] = 'Cancel All-linking'
message['Status'] = arr[0][1]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '0269' # Get First ALL-Link
arr = e[i].scan(/(....)(..)/)
if arr.length == 1 and arr[0].length == 2
message['Event'] = arr[0][0]
message['Description'] = 'Get First ALL-Link'
message['Status'] = arr[0][1]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
when '026A' # Get Next ALL-Link
arr = e[i].scan(/(....)(..)/)
if arr.length == 1 and arr[0].length == 2
message['Event'] = arr[0][0]
message['Description'] = 'Get Next ALL-Link'
message['Status'] = arr[0][1]
else
message['Event'] = "FF#{event}"
message['Description'] = 'Malformed Message'
message['Command'] = e[i]
end
else
message['Event'] = 'FFFF'
message['Description'] = 'Unknown'
message['Command'] = e[i]
end
$queue.push(message)
end
}
end
def clear_buffstatus()
path = "/1?XB=M=1" # The file we want
# This is the HTTP request we send to fetch a file
request = "POST #{path} HTTP/1.1\r\n\r\n"
socket = TCPSocket.new($host,$port) # Connect to server
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
socket.write(request[0..0]) # Send request
socket.flush
socket.write(request[1..-1]) # Send request
socket.flush
response = socket.read # Read complete response
socket.close
# Split response at first blank line into headers and body
header,body = response.split("\r\n\r\n", 2)
m = header.scan(/\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/min)
retval = {'status' => m[0][1].to_i,'content' => body}
return retval
rescue Errno::ECONNRESET => e
puts "Clear Buff Timeout error " + e
end
def get_buffstatus(xml)
doc = REXML::Document.new(xml)
doc.elements.each("/response/BS") do |elem|
return elem.text
end
end
while true
check_controller()
Thread.pass
end
}
def setclock()
now = Date.today.strftime("%H:%M")
path = "1?TD=${now}=1=falsefalse"
# This is the HTTP request we send to fetch a file
request = "POST #{path} HTTP/1.1\r\n\r\n"
socket = TCPSocket.new($host,$port) # Connect to server
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
socket.write(request[0..0]) # Send request
socket.flush
socket.write(request[1..-1]) # Send request
socket.flush
response = socket.read # Read complete response
socket.close
# Split response at first blank line into headers and body
header,body = response.split("\r\n\r\n", 2)
m = header.scan(/\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/min)
retval = {'status' => m[0][1].to_i,'content' => body}
return retval
rescue Errno::ECONNRESET => e
puts "Set Clock Timeout error " + e
end
def sendcommand(device,cmd)
path = "/3?0262#{device}0F#{cmd}=I=3"
# This is the HTTP request we send to fetch a file
request = "POST #{path} HTTP/1.1\r\n\r\n"
socket = TCPSocket.new($host,$port) # Connect to server
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
socket.write(request[0..0]) # Send request
socket.flush
socket.write(request[1..-1]) # Send request
socket.flush
response = socket.read # Read complete response
socket.close
# Split response at first blank line into headers and body
header,body = response.split("\r\n\r\n", 2)
m = header.scan(/\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/min)
retval = {'status' => m[0][1].to_i,'content' => body}
return retval
rescue Errno::ECONNRESET => e
puts "Send Command Timeout error " + e
end
def parse_buffstatus(e)
print e['Time'].strftime("%Y-%m-%d %H:%M:%S") + " "
print "Event: #{e['Description']}(#{e['Event']}) "
case e['Event']
when '0250' # received standard message
print "From: #{e['From']} "
print "To: #{e['To']} "
print "Flags:[" + parse_flag(e['Flags']) + '] '
print "Command: #{e['Command']} "
when '0251' # received extended message
print "From: #{e['From']} "
print "To: #{e['To']} "
print "Flags:[" + parse_flag(e['Flags']) + '] '
print "Command: #{e['Command']} "
print "Extended: #{e['Extended']} "
when '0253' # ALL-Linking Completed
print "From: #{e['From']} "
print "Group: #{e['To']} "
print "Link Code: "
case e['Linkcode']
when '00'
print "IM is a Responder"
when '01'
print "IM is a Controller"
when 'FF'
print "ALL-Link to the device was deleted"
else
print "Unknown"
end
print "Cat: #{e['Category']} "
print "Sub Cat: #{e['Subcategory']} "
print "Status: #{e['Status']}"
when '0254' # Button Event Report
print "Status: "
case e['Status']
when '02'
print "IM SET Button tapped"
when '03'
print "IM SET Button held"
when '04'
print "IM SET Button released after hold"
when '12'
print "IM Button 2 tapped"
when '13'
print "IM Button 2 held"
when '14'
print "IM Button 2 released after hold"
when '22'
print "IM Button 3 tapped"
when '23'
print "IM Button 3 held"
when '24'
print "IM Button 3 released after hold"
else
print "Unknown"
end
when '0257' # All-Link Report
print "Flags:[" + parse_flag(e['Flags']) + '] '
print "Group: #{e['To']} "
print "Device: #{e['From']} "
print "Data: #{e['Command']} "
when '0258' # ALL-Link Cleanup Status Report
print "Status: #{e['Status']}"
when '0260' # Get IM Info
print "Device: #{e['From']} "
print "Cat: #{e['Category']} "
print "Sub Cat: #{e['Subcategory']} "
print "Version: #{e['Version']} "
print "Status: #{e['Status']}"
when '0261' # Scene Command
print "Scene: #{e['From']} "
print "Command: "
case e['Command']
when '11'
print "On"
when '12'
print "Fast On"
when '13'
print "Off"
when '14'
print "Fast Off"
else
print "Unknown"
end
print " Result: #{e['Status']}"
when '0262' # Send Message
print "To: #{e['To']} "
print "Flags:[" + parse_flag(e['Flags']) + '] '
print "Command: #{e['Command']} "
if e['Flags'].hex & 16 == 16
print e['Extended'] + ' '
end
print "Status: "
case e['Status']
when '06'
print "Ack"
when '15'
print "Nack"
when 'FF'
print "Fail"
else
print "Unknown"
end
when '0265' # Cancel All-linking
print "Status: #{e['Status']} "
when '0269' # Get First ALL-Link
print "Status: #{e['Status']} "
when '026A' # Get Next ALL-Link
print "Status: #{e['Status']} "
else
print "Command: #{e['Command']} "
end
print "\n"
end
def parse_flag(flag)
retval = flag.hex
f = ''
if retval & 128 == 128
f = f + 'B'
else
f = f + 'b'
end
if retval & 64 == 64
f = f + 'G'
else
f = f + 'g'
end
if retval & 32 == 32
f = f + 'A'
else
f = f + 'a'
end
if retval & 16 == 16
f = f + 'E'
else
f = f + 'e'
end
f = f + ' HL:'
if retval & 8 == 8
f = f + '1'
else
f = f + '0'
end
if retval & 4 == 4
f = f + '1'
else
f = f + '0'
end
f = f + ' MH:'
if retval & 2 == 2
f = f + '1'
else
f = f + '0'
end
if retval & 1 == 1
f = f + '1'
else
f = f + '0'
end
return f
end
def daytime()
date = Date.today
calc = SolarEventCalculator.new(date, BigDecimal.new($long), BigDecimal.new($lat))
localSunrise = calc.compute_official_sunrise($timezone)
localSunset = calc.compute_official_sunset($timezone)
now = DateTime.now
now = DateTime.new(now.year, now.month, now.day, now.hour, now.min, now.sec, now.sec_fraction, now.offset)
if now >= localSunrise and now <= localSunset
retval = true
else
retval = false
end
return retval
end
def execute_message(m)
if m['From'] == '141120' and m['To'] == '000001'
print 'Command from Motion Sensor ' + m['Command'] + ' ' + (daytime()?'Day':'Night') + ' '
if m['Command'] = '1101' and daytime() == false
print "Send on to 154AE3"
sendcommand('154AE3', '11FF')
sendcommand('154DE4', '11FF')
end
print "\n"
end
if m['From'] == '13DED1' and m['To'] == '000001'
print 'Command from RemoteLinc ' + m['Command'] + ' '
if m['Command'] == '1100'
print 'Send On to 154AE3'
sendcommand('154AE3', '11FF')
end
if m['Command'] == '1300'
print 'Send Off to 154AE3'
sendcommand('154AE3', '13FF')
end
print "\n"
end
if m['From'] == '13DED1' and m['To'] == '000002'
print 'Command from RemoteLinc ' + m['Command'] + ' '
if m['Command'] == '1100'
print 'Send On to 154DE4'
sendcommand('154DE4', '11FF')
end
if m['Command'] == '1300'
print 'Send Off to 154DE4'
sendcommand('154DE4', '13FF')
end
print "\n"
end
end
producer.abort_on_exception = true
setclockrule = IceCube::Rule.daily
setclockschedule = IceCube::Schedule.new(Time.parse("01:00"), :duration => 2)
setclockschedule.add_recurrence_rule setclockrule
alloffrule = IceCube::Rule.daily
alloffschedule = IceCube::Schedule.new(Time.parse("11:00"), :duration => 2)
alloffschedule.add_recurrence_rule alloffrule
scheduletime = Time.now
while true
#puts "Producer #{producer.status} Timers #{timers.status} Main #{Thread.main.status}"
#puts $queue.length()
if $queue.length() > 0
message = $queue.pop()
#PP.singleline_pp(message)
#print ' '
execute_message(message)
parse_buffstatus(message)
end
if scheduletime + 1 <= Time.now
scheduletime = Time.now
if setclockschedule.occurring_at?(Time.now)
print "Setting clock\n"
setclock()
end
if alloffschedule.occurring_at?(Time.now)
print "All off\n"
sendcommand('154AE3', '13FF')
sendcommand('154DE4', '13FF')
end
end
Thread.pass
end
Sunrise.rb for daytime function
require 'bigdecimal'
require 'date'
require 'tzinfo'
class SolarEventCalculator
@date
@latitude
@longitude
def initialize(date, latitude, longitude)
@date = date
@latitude = latitude
@longitude = longitude
end
def compute_lnghour
lngHour = @longitude / BigDecimal.new("15")
lngHour.round(4)
end
def compute_longitude_hour(isSunrise)
minuend = (isSunrise) ? BigDecimal.new("6") : BigDecimal.new("18")
longHour = @date.yday + ((minuend - compute_lnghour) / BigDecimal.new("24"))
longHour.round(4)
end
def compute_sun_mean_anomaly(longHour)
constant = BigDecimal.new("0.9856")
((longHour * constant) - BigDecimal.new("3.289")).round(4)
end
def compute_sun_true_longitude(meanAnomaly)
mAsRads = degrees_as_rads(meanAnomaly)
sinM = BigDecimal.new(Math.sin(mAsRads.to_f).to_s)
sinTwoM = BigDecimal.new(Math.sin((2 * mAsRads).to_f).to_s)
firstParens = BigDecimal.new("1.916") * sinM
secondParens = BigDecimal.new("0.020") * sinTwoM
trueLong = meanAnomaly + firstParens + secondParens + BigDecimal.new("282.634")
trueLong = put_in_range(trueLong, 0, 360, 360)
trueLong.round(4)
end
def compute_right_ascension(sunTrueLong)
tanL = BigDecimal.new(Math.tan(degrees_as_rads(sunTrueLong).to_f).to_s)
ra = rads_as_degrees(BigDecimal.new(Math.atan(BigDecimal.new("0.91764") * tanL).to_s))
ra = put_in_range(ra, 0, 360, 360)
ra.round(4)
end
def put_ra_in_correct_quadrant(sunTrueLong)
lQuadrant = BigDecimal.new("90") * (sunTrueLong / BigDecimal.new("90")).floor
raQuadrant = BigDecimal.new("90") * (compute_right_ascension(sunTrueLong) / BigDecimal.new("90")).floor
ra = compute_right_ascension(sunTrueLong) + (lQuadrant - raQuadrant)
ra = ra / BigDecimal.new("15")
ra.round(4)
end
def compute_sin_sun_declination(sunTrueLong)
sinL = BigDecimal.new(Math.sin(degrees_as_rads(sunTrueLong).to_f).to_s)
sinDec = sinL * BigDecimal.new("0.39782")
sinDec.round(4)
end
def compute_cosine_sun_declination(sinSunDeclination)
cosDec = BigDecimal.new(Math.cos(Math.asin(sinSunDeclination)).to_s)
cosDec.round(4)
end
def compute_cosine_sun_local_hour(sunTrueLong, zenith)
cosZenith = BigDecimal.new(Math.cos(degrees_as_rads(BigDecimal.new(zenith.to_s))).to_s)
sinLatitude = BigDecimal.new(Math.sin(degrees_as_rads(@latitude)).to_s)
cosLatitude = BigDecimal.new(Math.cos(degrees_as_rads(@latitude)).to_s)
sinSunDeclination = compute_sin_sun_declination(sunTrueLong)
top = cosZenith - (sinSunDeclination * sinLatitude)
bottom = compute_cosine_sun_declination(sinSunDeclination) * cosLatitude
cosLocalHour = top / bottom
cosLocalHour.round(4)
end
def compute_local_hour_angle(cosSunLocalHour, isSunrise)
acosH = BigDecimal.new(Math.acos(cosSunLocalHour).to_s)
acosHDegrees = rads_as_degrees(acosH)
localHourAngle = (isSunrise) ? BigDecimal.new("360") - acosHDegrees : acosHDegrees
localHourAngle = localHourAngle / BigDecimal.new("15")
localHourAngle.round(4)
end
def compute_local_mean_time(sunTrueLong, longHour, t, sunLocalHour)
h = sunLocalHour
ra = put_ra_in_correct_quadrant(sunTrueLong)
parens = BigDecimal.new("0.06571") * t
time = h + ra - parens - BigDecimal.new("6.622")
utcTime = time - longHour
utcTime = put_in_range(utcTime.round(4), 0, 24, 24)
end
def compute_utc_solar_event(zenith, isSunrise)
longHour = compute_lnghour
eventLongHour = compute_longitude_hour(isSunrise)
meanAnomaly = compute_sun_mean_anomaly(eventLongHour)
sunTrueLong = compute_sun_true_longitude(meanAnomaly)
cosineSunLocalHour = compute_cosine_sun_local_hour(sunTrueLong, zenith)
if(cosineSunLocalHour > BigDecimal.new("1") || cosineSunLocalHour < BigDecimal.new("-1"))
return nil
end
sunLocalHour = compute_local_hour_angle(cosineSunLocalHour, isSunrise)
localMeanTime = compute_local_mean_time(sunTrueLong, longHour, eventLongHour, sunLocalHour)
timeParts = localMeanTime.to_s('F').split('.')
mins = BigDecimal.new("." + timeParts[1]) * BigDecimal.new("60")
mins = mins.truncate()
mins = pad_minutes(mins.to_i)
hours = timeParts[0]
Time.utc(@date.year, @date.mon, @date.mday, hours, pad_minutes(mins.to_i))
end
def compute_utc_civil_sunrise
convert_to_datetime(compute_utc_solar_event(96, true))
end
def compute_utc_civil_sunset
convert_to_datetime(compute_utc_solar_event(96, false))
end
def compute_utc_official_sunrise
convert_to_datetime(compute_utc_solar_event(90.8333, true))
end
def compute_utc_official_sunset
convert_to_datetime(compute_utc_solar_event(90.8333, false))
end
def compute_utc_nautical_sunrise
convert_to_datetime(compute_utc_solar_event(102, true))
end
def compute_utc_nautical_sunset
convert_to_datetime(compute_utc_solar_event(102, false))
end
def compute_utc_astronomical_sunrise
convert_to_datetime(compute_utc_solar_event(108, true))
end
def compute_utc_astronomical_sunset
convert_to_datetime(compute_utc_solar_event(108, false))
end
def convert_to_datetime(time)
DateTime.parse("#{@date.strftime}T#{time.hour}:#{time.min}:00+0000") unless time == nil
end
def compute_civil_sunrise(timezone)
put_in_timezone(compute_utc_solar_event(96, true), timezone)
end
def compute_civil_sunset(timezone)
put_in_timezone(compute_utc_solar_event(96, false), timezone)
end
def compute_official_sunrise(timezone)
put_in_timezone(compute_utc_solar_event(90.8333, true), timezone)
end
def compute_official_sunset(timezone)
put_in_timezone(compute_utc_solar_event(90.8333, false), timezone)
end
def compute_nautical_sunrise(timezone)
put_in_timezone(compute_utc_solar_event(102, true), timezone)
end
def compute_nautical_sunset(timezone)
put_in_timezone(compute_utc_solar_event(102, false), timezone)
end
def compute_astronomical_sunrise(timezone)
put_in_timezone(compute_utc_solar_event(108, true), timezone)
end
def compute_astronomical_sunset(timezone)
put_in_timezone(compute_utc_solar_event(108, false), timezone)
end
def put_in_timezone(utcTime, timezone)
tz = TZInfo::Timezone.get(timezone)
# puts "UTCTime #{utcTime}"
local = utcTime + get_utc_offset(timezone)
# puts "LocalTime #{local}"
offset = (get_utc_offset(timezone) / 60 / 60).to_i
offset = (offset > 0) ? "+" + offset.to_s : offset.to_s
timeInZone = DateTime.parse("#{@date.strftime}T#{local.strftime('%H:%M:%S')}#{offset}")
# puts "CALC:timeInZone #{timeInZone}"
timeInZone
end
def get_utc_offset(timezone)
tz = TZInfo::Timezone.get(timezone)
noonUTC = Time.gm(@date.year, @date.mon, @date.mday, 12, 0)
tz.utc_to_local(noonUTC) - noonUTC
end
def pad_minutes(minutes)
if(minutes < 10)
"0" + minutes.to_s
else
minutes
end
end
def put_in_range(number, lower, upper, adjuster)
if number > upper then
number -= adjuster
elsif number < lower then
number += adjuster
else
number
end
end
def degrees_as_rads(degrees)
pi = BigDecimal(Math::PI.to_s)
radian = pi / BigDecimal.new("180")
degrees * radian
end
def rads_as_degrees(radians)
pi = BigDecimal(Math::PI.to_s)
degree = BigDecimal.new("180") / pi
radians * degree
end
end
| < Prev | Next > |
|---|


