saal 0.2.2 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/TODO +7 -1
- data/bin/saal_chart +72 -98
- data/bin/saal_daemon +14 -6
- data/lib/chart.rb +50 -0
- data/lib/chart_data.rb +106 -22
- data/lib/charts.rb +23 -0
- data/lib/daemon.rb +11 -6
- data/lib/dbstore.rb +2 -2
- data/lib/dinrelay.rb +19 -10
- data/lib/outliercache.rb +1 -1
- data/lib/owsensor.rb +4 -1
- data/lib/saal.rb +4 -1
- data/lib/sensor.rb +22 -7
- data/lib/sensors.rb +3 -2
- data/saal.gemspec +7 -2
- data/test/chart_data_test.rb +106 -13
- data/test/chart_test.rb +55 -0
- data/test/charts_test.rb +31 -0
- data/test/dinrelay_test.rb +26 -1
- data/test/sensor_test.rb +37 -1
- data/test/test_charts.yml +27 -0
- data/test/test_dinrelay_sensors.yml +9 -1
- data/test/test_helper.rb +1 -0
- data/test/test_sensor_cleanups.yml +16 -0
- metadata +11 -4
    
        data/TODO
    CHANGED
    
    | @@ -1,11 +1,15 @@ | |
| 1 1 | 
             
            TODO
         | 
| 2 | 
            +
            !-Index the value column of the sensor reads for minimum and maximum
         | 
| 3 | 
            +
            - Change the filtering operations (e.g., outliercache) so that the raw value is always stored in the database
         | 
| 4 | 
            +
            - Make the outliercache filter based on the expected sensor range (e.g. -20-50 in temperature and 800-1200 in pressure) so as to not be overly sensitive when around 0)
         | 
| 2 5 | 
             
            - Add logging to the daemon
         | 
| 3 6 | 
             
            ?- Change the sensor configuration to be a ruby DSL and make it a daemon config
         | 
| 4 7 | 
             
            - Split classes into one per file with corresponding test (rails style)
         | 
| 5 8 | 
             
            - Verify inputs on the server to make sure it never crashes
         | 
| 6 9 | 
             
            - Remove the chart configuration options from the saal_chart script
         | 
| 10 | 
            +
            ?- Remove Sensors and Charts and move their functionality to Sensor and Chart
         | 
| 7 11 | 
             
            - Add an init.d file to the package (and possibly an installer script for ubuntu/debian)
         | 
| 8 | 
            -
            - Add  | 
| 12 | 
            +
            - Add interface that does retries for reading as well as writing (e.g., dinrelay confirm state change)
         | 
| 9 13 | 
             
            DONE
         | 
| 10 14 | 
             
            X- Make the server bind only to a certain interface (not applicable)
         | 
| 11 15 | 
             
            X- Override OWNet::Connection with a mock object so that owserver is not needed
         | 
| @@ -22,3 +26,5 @@ X- Make connections persistant | |
| 22 26 | 
             
            - Make the date returned by GET the date of the last read (GET doesn't return
         | 
| 23 27 | 
             
              a date now
         | 
| 24 28 | 
             
            - Implement monthly, yearly and 10-day average charts
         | 
| 29 | 
            +
            - Add outlier detection and removal
         | 
| 30 | 
            +
            - Add filter support to sensor reads (e.g., altitude compensation for pressure)
         | 
    
        data/bin/saal_chart
    CHANGED
    
    | @@ -1,110 +1,84 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            NUM_VALUES = 500 # Number of datapoints per series in the chart
         | 
| 2 3 |  | 
| 3 4 | 
             
            require File.dirname(__FILE__)+'/../lib/saal.rb'
         | 
| 4 5 |  | 
| 5 6 | 
             
            def usage
         | 
| 6 | 
            -
              $stderr.puts("Usage: saal_chart < | 
| 7 | 
            +
              $stderr.puts("Usage: saal_chart <chart dir>")
         | 
| 7 8 | 
             
            end
         | 
| 8 9 |  | 
| 9 | 
            -
            if ARGV.size !=  | 
| 10 | 
            +
            if ARGV.size != 1
         | 
| 10 11 | 
             
              usage
         | 
| 11 12 | 
             
              exit (2)
         | 
| 12 13 | 
             
            end
         | 
| 13 14 |  | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
               | 
| 21 | 
            -
             | 
| 22 | 
            -
               | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
               | 
| 26 | 
            -
               | 
| 27 | 
            -
               | 
| 28 | 
            -
               | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
               | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
               | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
               | 
| 48 | 
            -
             | 
| 49 | 
            -
               | 
| 50 | 
            -
               | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
               | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
            @ | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
               | 
| 73 | 
            -
               | 
| 74 | 
            -
               | 
| 75 | 
            -
              @ | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
            @ | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
            @dataurl = @data.map {|values| values.join(",")}.join('|')
         | 
| 85 | 
            -
             | 
| 86 | 
            -
            r = {}
         | 
| 87 | 
            -
            case ALIGNNAMES
         | 
| 88 | 
            -
            when :center
         | 
| 89 | 
            -
              @periodnamesurl = "||"+PERIODNAMES.join('||')+"||"
         | 
| 90 | 
            -
            when :left
         | 
| 91 | 
            -
              @periodnamesurl = "|"+PERIODNAMES.join('|')+"||"
         | 
| 92 | 
            -
              r[:chxs] = "0,555555,11,-1,lt"
         | 
| 15 | 
            +
            SENSORS = [[:temp_exterior, [-15, 45]], [:temp_estufa, [-15, 45]],
         | 
| 16 | 
            +
                       [:hum_exterior, [0,100]], 
         | 
| 17 | 
            +
                       [:pressao, [950,1050]]]
         | 
| 18 | 
            +
             | 
| 19 | 
            +
             | 
| 20 | 
            +
            SAAL::Charts.new.each do |chart|
         | 
| 21 | 
            +
              $stderr.puts "Generating chart #{chart.name}"
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              pngfile = ARGV[0]+'/chart-'+chart.name.to_s+'.png'
         | 
| 24 | 
            +
              ymlfile = ARGV[0]+'/chart-'+chart.name.to_s+'.yml'
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              @mins = chart.minimum
         | 
| 27 | 
            +
              @maxs = chart.maximum
         | 
| 28 | 
            +
              @avgs = chart.average
         | 
| 29 | 
            +
              @minmax = {}
         | 
| 30 | 
            +
              SENSORS.each do |s,r| 
         | 
| 31 | 
            +
                @minmax[s] = {:maximum => @maxs[s], :minimum => @mins[s], :average => @avgs[s]}
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              File.open(ymlfile, 'w').write(YAML::dump(@minmax))
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              def normalize_data(data, min, max, offset=0)
         | 
| 37 | 
            +
                data.map do |i|
         | 
| 38 | 
            +
                  if i.nil?
         | 
| 39 | 
            +
                    -1.0
         | 
| 40 | 
            +
                  elsif i < min
         | 
| 41 | 
            +
                    0.0
         | 
| 42 | 
            +
                  elsif i > max
         | 
| 43 | 
            +
                    100.0
         | 
| 44 | 
            +
                  else
         | 
| 45 | 
            +
                    (((i-min)/(max-min).to_f)*1000).round/10.0+offset
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              @periodnames = chart.periodnames
         | 
| 51 | 
            +
              @numperiods = @periodnames.size
         | 
| 52 | 
            +
              @averages = chart.average(NUM_VALUES)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              @data = SENSORS.map do |sensor, range|
         | 
| 55 | 
            +
                normalize_data(@averages[sensor], *range)
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              @dataurl = @data.map {|values| values.join(",")}.join('|')
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              r = {}
         | 
| 61 | 
            +
              case chart.alignlabels
         | 
| 62 | 
            +
              when :center
         | 
| 63 | 
            +
                @periodnamesurl = "||"+@periodnames.join('||')+"||"
         | 
| 64 | 
            +
              when :left
         | 
| 65 | 
            +
                @periodnamesurl = "|"+@periodnames.join('|')+"||"
         | 
| 66 | 
            +
                r[:chxs] = "0,555555,11,-1,lt"
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
              @xincr = 100.0/@numperiods.to_f*10000.truncate.to_f/10000
         | 
| 69 | 
            +
             | 
| 70 | 
            +
              r[:chof] = "png"
         | 
| 71 | 
            +
              r[:chs] = "700x300"
         | 
| 72 | 
            +
              r[:cht] = "lc"
         | 
| 73 | 
            +
              r[:chco] = "00ff00,ff0000,0000ff,ffff00"
         | 
| 74 | 
            +
              r[:chxt] = "x,y,y,r"
         | 
| 75 | 
            +
              r[:chxl] = "0:#{@periodnamesurl}1:|-15ºC||0||15||30||45ºC|2:|0%|25|50|75|100%|3:|950||975||1000||1025||1050 hPa"
         | 
| 76 | 
            +
              r[:chg] = "#{@xincr},12.5,1,5"
         | 
| 77 | 
            +
              r[:chd] = "t:#{@dataurl}"
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              @url = "http://chart.apis.google.com/chart?&"
         | 
| 80 | 
            +
              @postdata = r.map{|k,v| k.to_s+"="+v}.join("&")
         | 
| 81 | 
            +
             | 
| 82 | 
            +
             | 
| 83 | 
            +
              system "wget --quiet \"#{@url}\" --post-data=\"#{@postdata}\" -O #{pngfile}"
         | 
| 93 84 | 
             
            end
         | 
| 94 | 
            -
            @xincr = 100.0/NUMPERIODS.to_f*10000.truncate.to_f/10000
         | 
| 95 | 
            -
             | 
| 96 | 
            -
            r[:chof] = "png"
         | 
| 97 | 
            -
            r[:chs] = "700x300"
         | 
| 98 | 
            -
            r[:cht] = "lc"
         | 
| 99 | 
            -
            r[:chco] = "00ff00,ff0000,0000ff,ffff00"
         | 
| 100 | 
            -
            r[:chxt] = "x,y,y,r"
         | 
| 101 | 
            -
            r[:chxl] = "0:#{@periodnamesurl}1:|-15ºC||0||15||30||45ºC|2:|0%|25|50|75|100%|3:|950||975||1000||1025||1050 hPa"
         | 
| 102 | 
            -
            r[:chg] = "#{@xincr},12.5,1,5"
         | 
| 103 | 
            -
            r[:chd] = "t:#{@dataurl}"
         | 
| 104 | 
            -
             | 
| 105 | 
            -
            @url = "http://chart.apis.google.com/chart?&"
         | 
| 106 | 
            -
            @postdata = r.map{|k,v| k.to_s+"="+v}.join("&")
         | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
            system "wget --quiet \"#{@url}\" --post-data=\"#{@postdata}\" -O #{ARGV[1]}"
         | 
| 110 | 
            -
             | 
    
        data/bin/saal_daemon
    CHANGED
    
    | @@ -6,17 +6,25 @@ SENSORSCONF = "/etc/saal/sensors.yml" | |
| 6 6 |  | 
| 7 7 | 
             
            require File.dirname(__FILE__)+'/../lib/saal.rb'
         | 
| 8 8 |  | 
| 9 | 
            +
            def usage
         | 
| 10 | 
            +
              $stderr.puts "Usage: saal_daemon <pidfile|--foreground>"
         | 
| 11 | 
            +
            end
         | 
| 12 | 
            +
             | 
| 9 13 | 
             
            if ARGV.size != 1
         | 
| 10 | 
            -
               | 
| 14 | 
            +
              usage
         | 
| 11 15 | 
             
              exit 2
         | 
| 12 16 | 
             
            else
         | 
| 13 | 
            -
              pidfile = ARGV[0] | 
| 17 | 
            +
              pidfile = ARGV[0]
         | 
| 18 | 
            +
              foreground = (ARGV[0] == '--foreground')
         | 
| 14 19 | 
             
              d = SAAL::Daemon.new(:interval => SENSOR_INTERVAL,
         | 
| 15 20 | 
             
                                   :sensorconf => SENSORSCONF,
         | 
| 16 | 
            -
                                   :dbconf => DBCONF | 
| 21 | 
            +
                                   :dbconf => DBCONF,
         | 
| 22 | 
            +
                                   :foreground => foreground)
         | 
| 17 23 | 
             
              pid = d.run
         | 
| 18 | 
            -
               | 
| 19 | 
            -
                 | 
| 20 | 
            -
             | 
| 24 | 
            +
              if !foreground
         | 
| 25 | 
            +
                File.open(pidfile, 'w') do |f|
         | 
| 26 | 
            +
                  f.write(pid)
         | 
| 27 | 
            +
                  f.close
         | 
| 28 | 
            +
                end
         | 
| 21 29 | 
             
              end
         | 
| 22 30 | 
             
            end
         | 
    
        data/lib/chart.rb
    ADDED
    
    | @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            module SAAL
         | 
| 2 | 
            +
              class Chart
         | 
| 3 | 
            +
                attr_reader :name, :num, :periods, :alt, :description, :sensors, :alignlabels
         | 
| 4 | 
            +
                def initialize(name, defs, sensors, opts={})
         | 
| 5 | 
            +
                  @name = name
         | 
| 6 | 
            +
                  @defs = defs
         | 
| 7 | 
            +
                  @alignlabels = (defs['alignlabels'] || :center).to_sym
         | 
| 8 | 
            +
                  @sensors = defs['sensors'].map{|name| sensors.send(name)} 
         | 
| 9 | 
            +
                  @num = defs['last']
         | 
| 10 | 
            +
                  @periods = defs['periods']
         | 
| 11 | 
            +
                  @alt = defs['alt']
         | 
| 12 | 
            +
                  @description = defs['description']
         | 
| 13 | 
            +
                  @datarange = ChartDataRange.new(defs.merge(:now => opts[:now]))
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def periodnames
         | 
| 17 | 
            +
                  @datarange.periodnames
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def average(num=nil)
         | 
| 21 | 
            +
                  get_data(:average, num)
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def minimum(num=nil)
         | 
| 25 | 
            +
                  get_data(:minimum, num)
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def maximum(num=nil)
         | 
| 29 | 
            +
                  get_data(:maximum, num)
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def from
         | 
| 33 | 
            +
                  @datarange.from
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
                def to
         | 
| 36 | 
            +
                  @datarange.to
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                private
         | 
| 40 | 
            +
                def get_data(method, num)
         | 
| 41 | 
            +
                  n = num || 1
         | 
| 42 | 
            +
                  h = {}
         | 
| 43 | 
            +
                  @sensors.each do |s| 
         | 
| 44 | 
            +
                    data = @datarange.get_data(method,s,n)
         | 
| 45 | 
            +
                    h[s.name.to_sym] = num ? data : data[0]
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                  h
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
    
        data/lib/chart_data.rb
    CHANGED
    
    | @@ -1,30 +1,114 @@ | |
| 1 1 | 
             
            module SAAL
         | 
| 2 | 
            -
              class  | 
| 3 | 
            -
                 | 
| 4 | 
            -
             | 
| 2 | 
            +
              class ChartDataRange
         | 
| 3 | 
            +
                ALIGN = {:years => [12,31,23,59,59],
         | 
| 4 | 
            +
                         :months => [31,23,59,59],
         | 
| 5 | 
            +
                         :days => [23,59,59],
         | 
| 6 | 
            +
                         :weeks => [23,59,59],
         | 
| 7 | 
            +
                         :hours => [59,59]}
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                NUMHOURS = {:hours => 1, :days => 24, :weeks => 24*7}
         | 
| 10 | 
            +
                DAYNAMES = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]
         | 
| 11 | 
            +
                MONTHNAMES = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                attr_reader :num, :periods
         | 
| 14 | 
            +
                def initialize(opts={})
         | 
| 15 | 
            +
                  last = opts[:last] || opts['last'].to_i
         | 
| 16 | 
            +
                  periods = opts[:periods] || (opts['periods'] ? opts['periods'].to_sym : nil)
         | 
| 17 | 
            +
                  @now = opts[:now] || Time.now.utc
         | 
| 18 | 
            +
                  if last && periods
         | 
| 19 | 
            +
                    @num = last
         | 
| 20 | 
            +
                    @periods = periods
         | 
| 21 | 
            +
                    calc_alignment
         | 
| 22 | 
            +
                  else
         | 
| 23 | 
            +
                    @from = opts[:from] || 0
         | 
| 24 | 
            +
                    @to = opts[:to] || @now
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def from
         | 
| 29 | 
            +
                  @from.to_i
         | 
| 5 30 | 
             
                end
         | 
| 6 | 
            -
             | 
| 7 | 
            -
                def  | 
| 8 | 
            -
                   | 
| 31 | 
            +
             | 
| 32 | 
            +
                def to
         | 
| 33 | 
            +
                  @to.to_i
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def get_data(method, sensor, num)
         | 
| 37 | 
            +
                  step = (@to - @from).to_i/num
         | 
| 38 | 
            +
                  t = @from - 1
         | 
| 9 39 | 
             
                  (0..num-2).map do |i|
         | 
| 10 | 
            -
                    f =  | 
| 11 | 
            -
                    t = ( | 
| 12 | 
            -
                     | 
| 13 | 
            -
                  end <<  | 
| 40 | 
            +
                    f = t + 1
         | 
| 41 | 
            +
                    t = (f+step)
         | 
| 42 | 
            +
                    v = sensor.send(method, f.to_i, t.to_i)
         | 
| 43 | 
            +
                  end << sensor.send(method, (t+1).to_i, to.to_i)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def periodnames
         | 
| 47 | 
            +
                  if !@num
         | 
| 48 | 
            +
                    raise RuntimeError, 
         | 
| 49 | 
            +
                          "Trying to get periodnames without a :last & :periods definition" 
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  case @periods
         | 
| 53 | 
            +
                  when :hours
         | 
| 54 | 
            +
                    (0...@num).map{|i| ((@now.hour - i)%24).to_s}.reverse
         | 
| 55 | 
            +
                  when :days
         | 
| 56 | 
            +
                    (1..@num).map{|i| (@now.wday - i)%7}.map{|w| DAYNAMES[w]}.reverse
         | 
| 57 | 
            +
                  when :weeks
         | 
| 58 | 
            +
                    initial = @now - (@now.wday-1)*24*60*60
         | 
| 59 | 
            +
                    (0...@num).map do |i| 
         | 
| 60 | 
            +
                      time = Time.at(initial - i*24*60*60*7)
         | 
| 61 | 
            +
                      time.day.to_s+" "+ MONTHNAMES[time.month-1]
         | 
| 62 | 
            +
                    end.reverse
         | 
| 63 | 
            +
                  when :months
         | 
| 64 | 
            +
                    (1..@num).map{|i| (@now.month - i)%12}.map{|m| MONTHNAMES[m]}.reverse
         | 
| 65 | 
            +
                  when :years
         | 
| 66 | 
            +
                    (0...@num).map{|i| (@now.year - i).to_s}.reverse
         | 
| 67 | 
            +
                  else
         | 
| 68 | 
            +
                    raise RuntimeError, "No such period type #{@periods}" 
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                private
         | 
| 73 | 
            +
                def calc_alignment
         | 
| 74 | 
            +
                  if [:years, :year].include? periods
         | 
| 75 | 
            +
                    # Calculate by date manipulation
         | 
| 76 | 
            +
                    from = Time.utc(@now.year - num + 1, 1, 1, 0, 0, 0)
         | 
| 77 | 
            +
                    to = Time.utc(@now.year, 12, 31, 23, 59, 59)
         | 
| 78 | 
            +
                  elsif [:months, :month].include? periods
         | 
| 79 | 
            +
                    # advance to the 1st of the next month
         | 
| 80 | 
            +
                    newm = @now.month%12 + 1
         | 
| 81 | 
            +
                    newy = @now.year + (@now.month == 12 ? 1 : 0)
         | 
| 82 | 
            +
                    to = Time.utc(newy, newm, 1, 0, 0, 0)
         | 
| 83 | 
            +
                    # Go back num months for from
         | 
| 84 | 
            +
                    from = dec_months(num, to)
         | 
| 85 | 
            +
                    # subtract 1 second from two to get the end of current month
         | 
| 86 | 
            +
                    to -= 1
         | 
| 87 | 
            +
                  else
         | 
| 88 | 
            +
                    # Calculate by elasped time
         | 
| 89 | 
            +
                    args = [@now.year, @now.month, @now.day, @now.hour, @now.min, @now.sec]
         | 
| 90 | 
            +
                    args = args[0..-(ALIGN[periods].size+1)]
         | 
| 91 | 
            +
                    args += ALIGN[periods]
         | 
| 92 | 
            +
                    to = Time.utc(*args)
         | 
| 93 | 
            +
                    to += (7-@now.wday)*60*60*24 if [:weeks,:week].include?(periods)
         | 
| 94 | 
            +
                    from = to - NUMHOURS[periods]*60*60*num+1
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                  @from = from
         | 
| 97 | 
            +
                  @to = to
         | 
| 14 98 | 
             
                end
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                 | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
                     | 
| 25 | 
            -
             | 
| 26 | 
            -
                    end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                # Subtract num months from a given Time
         | 
| 101 | 
            +
                def dec_months(num, time)
         | 
| 102 | 
            +
                  # Go back any 12 month intervals (aka years)
         | 
| 103 | 
            +
                  newy = time.year - num/12
         | 
| 104 | 
            +
                  num = num%12
         | 
| 105 | 
            +
                  # Go back the remainder months
         | 
| 106 | 
            +
                  newm = time.month - num
         | 
| 107 | 
            +
                  if newm < 1
         | 
| 108 | 
            +
                    newm = 12 - (-newm)
         | 
| 109 | 
            +
                    newy -= 1
         | 
| 27 110 | 
             
                  end
         | 
| 111 | 
            +
                  from = Time.utc(newy, newm, time.day, time.hour, time.min, time.sec)
         | 
| 28 112 | 
             
                end
         | 
| 29 113 | 
             
              end
         | 
| 30 114 | 
             
            end
         | 
    
        data/lib/charts.rb
    ADDED
    
    | @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            module SAAL
         | 
| 2 | 
            +
              class Charts
         | 
| 3 | 
            +
                include Enumerable
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(conffile=SAAL::CHARTSCONF, opts={})
         | 
| 6 | 
            +
                  @defs = YAML::load(File.new(conffile))
         | 
| 7 | 
            +
                  @sensors = opts[:sensors] || Sensors.new
         | 
| 8 | 
            +
                  @charts = {}
         | 
| 9 | 
            +
                  @defs.each do |name, defs|
         | 
| 10 | 
            +
                    @charts[name.to_sym] = Chart.new(name, defs, @sensors, opts)
         | 
| 11 | 
            +
                  end  
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                # Fetch a specific chart by name
         | 
| 15 | 
            +
                def find(name)
         | 
| 16 | 
            +
                  @charts[name.to_sym]
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def each
         | 
| 20 | 
            +
                  @charts.each{|name, chart| yield chart}
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
    
        data/lib/daemon.rb
    CHANGED
    
    | @@ -1,13 +1,18 @@ | |
| 1 1 | 
             
            module SAAL
         | 
| 2 2 | 
             
              class ForkedRunner
         | 
| 3 3 | 
             
                def self.run_as_fork(opts={})
         | 
| 4 | 
            -
                   | 
| 5 | 
            -
                     | 
| 6 | 
            -
                      $stderr.reopen "/dev/null", "a"
         | 
| 7 | 
            -
                      $stdin.reopen "/dev/null", "a"
         | 
| 8 | 
            -
                      $stdout.reopen "/dev/null", "a"
         | 
| 9 | 
            -
                    end
         | 
| 4 | 
            +
                  if opts[:foreground]
         | 
| 5 | 
            +
                    $stderr.puts "Running saal_daemon #{SAAL::VERSION} in foreground..."
         | 
| 10 6 | 
             
                    yield ForkedRunner.new
         | 
| 7 | 
            +
                  else
         | 
| 8 | 
            +
                    fork do
         | 
| 9 | 
            +
                      if not opts[:keep_stdin]
         | 
| 10 | 
            +
                        $stderr.reopen "/dev/null", "a"
         | 
| 11 | 
            +
                        $stdin.reopen "/dev/null", "a"
         | 
| 12 | 
            +
                        $stdout.reopen "/dev/null", "a"
         | 
| 13 | 
            +
                      end
         | 
| 14 | 
            +
                      yield ForkedRunner.new
         | 
| 15 | 
            +
                    end
         | 
| 11 16 | 
             
                  end
         | 
| 12 17 | 
             
                end
         | 
| 13 18 |  | 
    
        data/lib/dbstore.rb
    CHANGED
    
    | @@ -67,7 +67,7 @@ module SAAL | |
| 67 67 | 
             
                  Mysql.quote(text)
         | 
| 68 68 | 
             
                end
         | 
| 69 69 |  | 
| 70 | 
            -
                def db_query(query)
         | 
| 70 | 
            +
                def db_query(query, opts={})
         | 
| 71 71 | 
             
                  db = nil
         | 
| 72 72 | 
             
                  begin
         | 
| 73 73 | 
             
                    # connect to the MySQL server
         | 
| @@ -77,7 +77,7 @@ module SAAL | |
| 77 77 | 
             
                    res = db.query(query)
         | 
| 78 78 | 
             
                    yield res if block_given?
         | 
| 79 79 | 
             
                  rescue Mysql::Error => e
         | 
| 80 | 
            -
                    $stderr.puts "MySQL Error  | 
| 80 | 
            +
                    $stderr.puts "MySQL Error #{e.errno}: #{e.error}" if !(e.errno == opts[:ignoreerr])
         | 
| 81 81 | 
             
                  ensure
         | 
| 82 82 | 
             
                    db.close if db
         | 
| 83 83 | 
             
                  end
         | 
    
        data/lib/dinrelay.rb
    CHANGED
    
    | @@ -10,6 +10,10 @@ module SAAL | |
| 10 10 | 
             
                    @og = outletgroup
         | 
| 11 11 | 
             
                  end
         | 
| 12 12 |  | 
| 13 | 
            +
                  def sensor_type
         | 
| 14 | 
            +
                    :onoff
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 13 17 | 
             
                  def read(uncached = false)
         | 
| 14 18 | 
             
                    {'ON' => 1.0, 'OFF' => 0.0}[@og.state(@num)]
         | 
| 15 19 | 
             
                  end
         | 
| @@ -33,25 +37,30 @@ module SAAL | |
| 33 37 |  | 
| 34 38 | 
             
                  def state(num)
         | 
| 35 39 | 
             
                    response = do_get('/index.htm')
         | 
| 36 | 
            -
                    return parse_index_html(response.body)[num]
         | 
| 40 | 
            +
                    return response ? parse_index_html(response.body)[num] : nil
         | 
| 37 41 | 
             
                  end
         | 
| 38 42 |  | 
| 39 43 | 
             
                  def set_state(num, state)
         | 
| 40 44 | 
             
                    response = do_get("/outlet?#{num}=#{state}")
         | 
| 41 | 
            -
                    response | 
| 45 | 
            +
                    response != nil
         | 
| 42 46 | 
             
                  end
         | 
| 43 47 |  | 
| 44 48 | 
             
                  private
         | 
| 45 49 | 
             
                  def do_get(path)
         | 
| 46 | 
            -
                     | 
| 47 | 
            -
                       | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
                         | 
| 52 | 
            -
             | 
| 50 | 
            +
                    begin
         | 
| 51 | 
            +
                      Net::HTTP.start(@host,@port) do |http|
         | 
| 52 | 
            +
                        req = Net::HTTP::Get.new(path)
         | 
| 53 | 
            +
                        req.basic_auth @user, @pass
         | 
| 54 | 
            +
                        response = http.request(req)
         | 
| 55 | 
            +
                        if response.code != "200"
         | 
| 56 | 
            +
                          #$stderr.puts "ERROR: Code #{response.code}"
         | 
| 57 | 
            +
                          #$stderr.puts response.body
         | 
| 58 | 
            +
                          return nil
         | 
| 59 | 
            +
                        end
         | 
| 60 | 
            +
                        return response
         | 
| 53 61 | 
             
                      end
         | 
| 54 | 
            -
             | 
| 62 | 
            +
                    rescue Exception
         | 
| 63 | 
            +
                      return nil
         | 
| 55 64 | 
             
                    end
         | 
| 56 65 | 
             
                  end
         | 
| 57 66 |  | 
    
        data/lib/outliercache.rb
    CHANGED
    
    | @@ -13,7 +13,7 @@ module SAAL | |
| 13 13 | 
             
                # Sets how close the central values have to be for the cache to be "live"
         | 
| 14 14 | 
             
                MAX_CACHE_DEVIATION = 0.05
         | 
| 15 15 | 
             
                # Sets how off the read value can be from the cache median to be accepted
         | 
| 16 | 
            -
                MAX_VALUE_DEVIATION = 0. | 
| 16 | 
            +
                MAX_VALUE_DEVIATION = 0.15
         | 
| 17 17 |  | 
| 18 18 | 
             
                def initialize
         | 
| 19 19 | 
             
                  @compcache = []
         | 
    
        data/lib/owsensor.rb
    CHANGED
    
    | @@ -1,12 +1,15 @@ | |
| 1 1 | 
             
            module SAAL
         | 
| 2 2 | 
             
              class OWSensor < SensorUnderlying
         | 
| 3 | 
            -
                attr_reader :serial
         | 
| 3 | 
            +
                attr_reader :serial, :sensor_type
         | 
| 4 4 | 
             
                def initialize(defs, opts={})
         | 
| 5 5 | 
             
                  @serial = defs['serial']
         | 
| 6 6 | 
             
                  @connect_opts = {}
         | 
| 7 7 | 
             
                  @connect_opts[:server] = defs['server'] if defs['server']
         | 
| 8 8 | 
             
                  @connect_opts[:port] = defs['port'] if defs['port']
         | 
| 9 9 | 
             
                  @owconn = opts[:owconn]
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  basename = File.basename(@serial)
         | 
| 12 | 
            +
                  @sensor_type = basename.to_sym if ['pressure','temperature','humidity'].include?(basename)
         | 
| 10 13 | 
             
                end
         | 
| 11 14 |  | 
| 12 15 | 
             
                def read(uncached = false)
         | 
    
        data/lib/saal.rb
    CHANGED
    
    | @@ -8,8 +8,9 @@ module SAAL | |
| 8 8 | 
             
                CONFDIR = "/etc/saal/"
         | 
| 9 9 | 
             
                SENSORSCONF = CONFDIR+"sensors.yml"
         | 
| 10 10 | 
             
                DBCONF = CONFDIR+"database.yml"
         | 
| 11 | 
            +
                CHARTSCONF = CONFDIR+"charts.yml"
         | 
| 11 12 |  | 
| 12 | 
            -
                VERSION = '0.2. | 
| 13 | 
            +
                VERSION = '0.2.11'
         | 
| 13 14 | 
             
            end
         | 
| 14 15 |  | 
| 15 16 | 
             
            require File.dirname(__FILE__)+'/dbstore.rb'
         | 
| @@ -17,6 +18,8 @@ require File.dirname(__FILE__)+'/sensors.rb' | |
| 17 18 | 
             
            require File.dirname(__FILE__)+'/sensor.rb'
         | 
| 18 19 | 
             
            require File.dirname(__FILE__)+'/owsensor.rb'
         | 
| 19 20 | 
             
            require File.dirname(__FILE__)+'/daemon.rb'
         | 
| 21 | 
            +
            require File.dirname(__FILE__)+'/charts.rb'
         | 
| 22 | 
            +
            require File.dirname(__FILE__)+'/chart.rb'
         | 
| 20 23 | 
             
            require File.dirname(__FILE__)+'/chart_data.rb'
         | 
| 21 24 | 
             
            require File.dirname(__FILE__)+'/outliercache.rb'
         | 
| 22 25 | 
             
            require File.dirname(__FILE__)+'/dinrelay.rb'
         | 
    
        data/lib/sensor.rb
    CHANGED
    
    | @@ -3,6 +3,7 @@ module SAAL | |
| 3 3 | 
             
              end
         | 
| 4 4 |  | 
| 5 5 | 
             
              class SensorUnderlying
         | 
| 6 | 
            +
                def sensor_type; nil; end
         | 
| 6 7 | 
             
                def writeable?; false; end
         | 
| 7 8 | 
             
                def self.writeable! 
         | 
| 8 9 | 
             
                  define_method(:writeable?){true}
         | 
| @@ -26,14 +27,24 @@ module SAAL | |
| 26 27 | 
             
                  @min_value = defs['min_value']
         | 
| 27 28 | 
             
                  @min_correctable = defs['min_correctable']
         | 
| 28 29 |  | 
| 30 | 
            +
                  @read_offset = if defs['altitude'] && @underlying.sensor_type == :pressure
         | 
| 31 | 
            +
                    defs['altitude'].to_f/9.2
         | 
| 32 | 
            +
                  else
         | 
| 33 | 
            +
                    0.0
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 29 36 | 
             
                  # Outliercache
         | 
| 30 37 | 
             
                  @outliercache = opts[:no_outliercache] ? nil : OutlierCache.new
         | 
| 31 | 
            -
                end | 
| 38 | 
            +
                end
         | 
| 32 39 |  | 
| 33 40 | 
             
                def writeable?
         | 
| 34 41 | 
             
                  @underlying.writeable?
         | 
| 35 42 | 
             
                end
         | 
| 36 43 |  | 
| 44 | 
            +
                def sensor_type
         | 
| 45 | 
            +
                  @underlying.sensor_type
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 37 48 | 
             
                def read
         | 
| 38 49 | 
             
                  outlier_proof_read(false)
         | 
| 39 50 | 
             
                end
         | 
| @@ -52,22 +63,22 @@ module SAAL | |
| 52 63 |  | 
| 53 64 | 
             
                def average(from, to)
         | 
| 54 65 | 
             
                  return @mock_opts[:average] if @mock_opts[:average]
         | 
| 55 | 
            -
                  @dbstore.average(@name, from, to)
         | 
| 66 | 
            +
                  apply_offset @dbstore.average(@name, from, to)
         | 
| 56 67 | 
             
                end
         | 
| 57 68 |  | 
| 58 69 | 
             
                def minimum(from, to)
         | 
| 59 70 | 
             
                  return @mock_opts[:minimum] if @mock_opts[:minimum]
         | 
| 60 | 
            -
                  @dbstore.minimum(@name, from, to)
         | 
| 71 | 
            +
                  apply_offset @dbstore.minimum(@name, from, to)
         | 
| 61 72 | 
             
                end
         | 
| 62 73 |  | 
| 63 74 | 
             
                def maximum(from, to)
         | 
| 64 75 | 
             
                  return @mock_opts[:maximum] if @mock_opts[:maximum]
         | 
| 65 | 
            -
                  @dbstore.maximum(@name, from, to)
         | 
| 76 | 
            +
                  apply_offset @dbstore.maximum(@name, from, to)
         | 
| 66 77 | 
             
                end
         | 
| 67 78 |  | 
| 68 79 | 
             
                def store_value
         | 
| 69 80 | 
             
                  value = read_uncached
         | 
| 70 | 
            -
                  @dbstore.write(@name, Time.now.utc.to_i, value) if value
         | 
| 81 | 
            +
                  @dbstore.write(@name, Time.now.utc.to_i, value-@read_offset) if value
         | 
| 71 82 | 
             
                end
         | 
| 72 83 |  | 
| 73 84 | 
             
                def mock_set(opts)
         | 
| @@ -87,14 +98,18 @@ module SAAL | |
| 87 98 | 
             
                  normalize(value)
         | 
| 88 99 | 
             
                end
         | 
| 89 100 |  | 
| 101 | 
            +
                def apply_offset(v)
         | 
| 102 | 
            +
                  v ? v+@read_offset : v
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 90 105 | 
             
                def normalize(value)
         | 
| 91 | 
            -
                  if @max_value and value > @max_value
         | 
| 106 | 
            +
                  apply_offset(if @max_value and value > @max_value
         | 
| 92 107 | 
             
                    (@max_correctable and value <= @max_correctable) ? @max_value : nil
         | 
| 93 108 | 
             
                  elsif @min_value and value < @min_value
         | 
| 94 109 | 
             
                    (@min_correctable and value >= @min_correctable) ? @min_value : nil
         | 
| 95 110 | 
             
                  else
         | 
| 96 111 | 
             
                    value
         | 
| 97 | 
            -
                  end
         | 
| 112 | 
            +
                  end)
         | 
| 98 113 | 
             
                end
         | 
| 99 114 | 
             
              end
         | 
| 100 115 | 
             
            end
         | 
    
        data/lib/sensors.rb
    CHANGED
    
    | @@ -11,8 +11,7 @@ module SAAL | |
| 11 11 | 
             
                  @sensors =  {}
         | 
| 12 12 | 
             
                  @defs.each do |name, defs|
         | 
| 13 13 | 
             
                    self.class.sensors_from_defs(@dbstore, name, defs).each{|s| @sensors[s.name] = s}
         | 
| 14 | 
            -
                  end | 
| 15 | 
            -
                  
         | 
| 14 | 
            +
                  end   
         | 
| 16 15 | 
             
                end
         | 
| 17 16 |  | 
| 18 17 | 
             
                # Implements the get methods to fetch a specific sensor
         | 
| @@ -36,7 +35,9 @@ module SAAL | |
| 36 35 | 
             
                  elsif defs['dinrelay']
         | 
| 37 36 | 
             
                    og = DINRelay::OutletGroup.new(defs['dinrelay'])
         | 
| 38 37 | 
             
                    outlet_names = defs['dinrelay']['outlets'] || []
         | 
| 38 | 
            +
                    outlet_descriptions = defs['dinrelay']['descriptions'] || []
         | 
| 39 39 | 
             
                    return outlet_names.map do |num, oname|
         | 
| 40 | 
            +
                      defs.merge!('name' => outlet_descriptions[num])
         | 
| 40 41 | 
             
                      Sensor.new(dbstore, oname, DINRelay::Outlet.new(num.to_i, og), defs, opts)
         | 
| 41 42 | 
             
                    end
         | 
| 42 43 | 
             
                  else
         | 
    
        data/saal.gemspec
    CHANGED
    
    | @@ -6,8 +6,8 @@ Gem::Specification.new do |s| | |
| 6 6 | 
             
              s.platform = Gem::Platform::RUBY
         | 
| 7 7 |  | 
| 8 8 | 
             
              s.name              = 'saal'
         | 
| 9 | 
            -
              s.version           = '0.2. | 
| 10 | 
            -
              s.date              = ' | 
| 9 | 
            +
              s.version           = '0.2.11'
         | 
| 10 | 
            +
              s.date              = '2011-05-26'
         | 
| 11 11 |  | 
| 12 12 | 
             
              s.summary     = "Thin abstraction layer for interfacing and recording sensors (currently onewire) and actuators (currently dinrelay)"
         | 
| 13 13 | 
             
              s.description = <<EOF
         | 
| @@ -46,7 +46,9 @@ EOF | |
| 46 46 | 
             
                bin/saal_dump_database
         | 
| 47 47 | 
             
                bin/saal_import_mysql
         | 
| 48 48 | 
             
                bin/saal_readall
         | 
| 49 | 
            +
                lib/chart.rb
         | 
| 49 50 | 
             
                lib/chart_data.rb
         | 
| 51 | 
            +
                lib/charts.rb
         | 
| 50 52 | 
             
                lib/daemon.rb
         | 
| 51 53 | 
             
                lib/dbstore.rb
         | 
| 52 54 | 
             
                lib/dinrelay.rb
         | 
| @@ -57,6 +59,8 @@ EOF | |
| 57 59 | 
             
                lib/sensors.rb
         | 
| 58 60 | 
             
                saal.gemspec
         | 
| 59 61 | 
             
                test/chart_data_test.rb
         | 
| 62 | 
            +
                test/chart_test.rb
         | 
| 63 | 
            +
                test/charts_test.rb
         | 
| 60 64 | 
             
                test/daemon_test.rb
         | 
| 61 65 | 
             
                test/dbstore_test.rb
         | 
| 62 66 | 
             
                test/dinrelay.html.erb
         | 
| @@ -65,6 +69,7 @@ EOF | |
| 65 69 | 
             
                test/outliercache_test.rb
         | 
| 66 70 | 
             
                test/sensor_test.rb
         | 
| 67 71 | 
             
                test/sensors_test.rb
         | 
| 72 | 
            +
                test/test_charts.yml
         | 
| 68 73 | 
             
                test/test_db.yml
         | 
| 69 74 | 
             
                test/test_dinrelay_sensors.yml
         | 
| 70 75 | 
             
                test/test_helper.rb
         | 
    
        data/test/chart_data_test.rb
    CHANGED
    
    | @@ -18,22 +18,115 @@ class MockSensor | |
| 18 18 | 
             
              end
         | 
| 19 19 | 
             
            end
         | 
| 20 20 |  | 
| 21 | 
            -
            class TestChartData < Test::Unit::TestCase
         | 
| 22 | 
            -
              def  | 
| 21 | 
            +
            class TestChartData < Test::Unit::TestCase  
         | 
| 22 | 
            +
              def test_basic_range
         | 
| 23 23 | 
             
                sensor = MockSensor.new
         | 
| 24 | 
            -
                 | 
| 25 | 
            -
                assert_equal MOCK_AVERAGES,  | 
| 26 | 
            -
                assert_equal([[ | 
| 24 | 
            +
                range = SAAL::ChartDataRange.new(:from => 1, :to => 1000)
         | 
| 25 | 
            +
                assert_equal MOCK_AVERAGES, range.get_data(:average, sensor, 5)
         | 
| 26 | 
            +
                assert_equal([[1,200],[201,400],[401,600],[601,800],[801,1000]],
         | 
| 27 27 | 
             
                             sensor.asked_averages)
         | 
| 28 28 | 
             
              end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
              def  | 
| 29 | 
            +
             | 
| 30 | 
            +
              def test_interval_range
         | 
| 31 | 
            +
                sensor = MockSensor.new
         | 
| 32 | 
            +
                now = Time.utc(2010, 12, 30, 15, 38, 19)
         | 
| 33 | 
            +
                ranges = [[1293638400,1293655679],[1293655680,1293672959],
         | 
| 34 | 
            +
                          [1293672960,1293690239],[1293690240,1293707519],
         | 
| 35 | 
            +
                          [1293707520,1293724799]]
         | 
| 36 | 
            +
                range = SAAL::ChartDataRange.new(:last => 24, :periods => :hours, :now => now)
         | 
| 37 | 
            +
                assert_equal MOCK_AVERAGES, range.get_data(:average, sensor, 5)
         | 
| 38 | 
            +
                assert_equal ranges, sensor.asked_averages
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              def test_correct_time_use
         | 
| 31 42 | 
             
                sensor = MockSensor.new
         | 
| 32 | 
            -
                 | 
| 33 | 
            -
                 | 
| 34 | 
            -
                 | 
| 35 | 
            -
             | 
| 36 | 
            -
                assert_equal | 
| 37 | 
            -
             | 
| 43 | 
            +
                range = SAAL::ChartDataRange.new(:last => 24, :periods => :hours)
         | 
| 44 | 
            +
                now = Time.now.utc
         | 
| 45 | 
            +
                to = Time.utc(now.year,now.month,now.day,now.hour,59,59).to_i
         | 
| 46 | 
            +
                from = to - 24*60*60 + 1
         | 
| 47 | 
            +
                assert_equal MOCK_AVERAGES[0..0], range.get_data(:average, sensor, 1)
         | 
| 48 | 
            +
                assert_equal [[from,to]], sensor.asked_averages
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
              
         | 
| 51 | 
            +
              # Test all the alignment functions underlying :last, :periods
         | 
| 52 | 
            +
              def self.assert_alignment_interval(num,periods,from,to, periodnames=nil, 
         | 
| 53 | 
            +
                                                 now = nil, extra=nil)
         | 
| 54 | 
            +
                define_method("test_alignment_#{num}#{periods}#{extra.to_s}") do
         | 
| 55 | 
            +
                  now = now || Time.utc(2010, 12, 30, 15, 38, 19)
         | 
| 56 | 
            +
                  o = SAAL::ChartDataRange.new(:last => num, :periods => periods, :now => now)
         | 
| 57 | 
            +
                  assert_equal [from.to_i, to.to_i], [o.from, o.to],
         | 
| 58 | 
            +
                               "Expecting #{from.utc} - #{to.utc}\n"+
         | 
| 59 | 
            +
                               "Got #{Time.at(o.from).utc} - #{Time.at(o.to).utc}"
         | 
| 60 | 
            +
                  assert_equal periodnames, o.periodnames if periodnames
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
              assert_alignment_interval(24, :hours, Time.utc(2010, 12, 29, 16, 0, 0),
         | 
| 64 | 
            +
                                                    Time.utc(2010, 12, 30, 15, 59, 59),
         | 
| 65 | 
            +
                                        (16..23).map{|s| s.to_s}+(0..15).map{|s| s.to_s})
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              assert_alignment_interval(22, :hours, Time.utc(2010, 12, 29, 18, 0, 0),
         | 
| 68 | 
            +
                                                    Time.utc(2010, 12, 30, 15, 59, 59),
         | 
| 69 | 
            +
                                        (18..23).map{|s| s.to_s}+(0..15).map{|s| s.to_s})
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              assert_alignment_interval(1, :days, Time.utc(2010, 12, 30, 0, 0, 0),
         | 
| 72 | 
            +
                                                  Time.utc(2010, 12, 30, 23, 59, 59),
         | 
| 73 | 
            +
                                        ["Thu"])
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              assert_alignment_interval(12, :hours, Time.utc(2010, 12, 30, 4, 0, 0),
         | 
| 76 | 
            +
                                                    Time.utc(2010, 12, 30, 15, 59, 59),
         | 
| 77 | 
            +
                                        (4..15).map{|s| s.to_s})
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              assert_alignment_interval(1, :weeks,Time.utc(2010, 12, 27, 0, 0, 0),
         | 
| 80 | 
            +
                                                  Time.utc(2011, 1, 2, 23, 59, 59),
         | 
| 81 | 
            +
                                        ["27 Dec"])
         | 
| 82 | 
            +
             | 
| 83 | 
            +
              assert_alignment_interval(2, :weeks,Time.utc(2010, 12, 20, 0, 0, 0),
         | 
| 84 | 
            +
                                                  Time.utc(2011, 1, 2, 23, 59, 59),
         | 
| 85 | 
            +
                                        ["20 Dec","27 Dec"])
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              assert_alignment_interval(1, :years, Time.utc(2010, 1, 1, 0, 0, 0),
         | 
| 88 | 
            +
                                                    Time.utc(2010, 12, 31, 23, 59, 59),
         | 
| 89 | 
            +
                                        ["2010"])
         | 
| 90 | 
            +
             | 
| 91 | 
            +
              assert_alignment_interval(2, :years, Time.utc(2009, 1, 1, 0, 0, 0),
         | 
| 92 | 
            +
                                                    Time.utc(2010, 12, 31, 23, 59, 59),
         | 
| 93 | 
            +
                                        ["2009","2010"])
         | 
| 94 | 
            +
             | 
| 95 | 
            +
              assert_alignment_interval(1, :months, Time.utc(2010, 12, 1, 0, 0, 0),
         | 
| 96 | 
            +
                                                    Time.utc(2010, 12, 31, 23, 59, 59),
         | 
| 97 | 
            +
                                        ["Dec"])
         | 
| 98 | 
            +
              assert_alignment_interval(1, :months, Time.utc(2010, 4, 1, 0, 0, 0),
         | 
| 99 | 
            +
                                                    Time.utc(2010, 4, 30, 23, 59, 59),
         | 
| 100 | 
            +
                                        ["Apr"],    Time.utc(2010, 4, 30, 12, 50, 30), 
         | 
| 101 | 
            +
                                                   "_30day_month")
         | 
| 102 | 
            +
             | 
| 103 | 
            +
              assert_alignment_interval(12, :months, Time.utc(2010, 1, 1, 0, 0, 0),
         | 
| 104 | 
            +
                                                    Time.utc(2010, 12, 31, 23, 59, 59),
         | 
| 105 | 
            +
                                        ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug",
         | 
| 106 | 
            +
                                         "Sep","Oct","Nov","Dec"])
         | 
| 107 | 
            +
             | 
| 108 | 
            +
              assert_alignment_interval(13, :months, Time.utc(2009, 12, 1, 0, 0, 0),
         | 
| 109 | 
            +
                                                    Time.utc(2010, 12, 31, 23, 59, 59))
         | 
| 110 | 
            +
              assert_alignment_interval(24, :months, Time.utc(2009, 1, 1, 0, 0, 0),
         | 
| 111 | 
            +
                                                    Time.utc(2010, 12, 31, 23, 59, 59))
         | 
| 112 | 
            +
              assert_alignment_interval(12, :months, Time.utc(2010, 2, 1, 0, 0, 0),
         | 
| 113 | 
            +
                                                   Time.utc(2011, 1, 31, 23, 59, 59),
         | 
| 114 | 
            +
                                        ["Feb","Mar","Apr","May","Jun","Jul","Aug",
         | 
| 115 | 
            +
                                         "Sep","Oct","Nov","Dec","Jan"],
         | 
| 116 | 
            +
                                                   Time.utc(2011, 1, 1, 14, 15, 10),
         | 
| 117 | 
            +
                                        "_midyear")
         | 
| 118 | 
            +
             | 
| 119 | 
            +
              def self.assert_dec_months(num, from, to)
         | 
| 120 | 
            +
                define_method("test_dec_#{num}months_from#{from.join('-')}_to#{to.join('-')}") do
         | 
| 121 | 
            +
                  o = SAAL::ChartDataRange.new
         | 
| 122 | 
            +
                  assert_equal Time.utc(to[0], to[1], 1, 0, 0, 0),
         | 
| 123 | 
            +
                               o.send(:dec_months, num, Time.utc(from[0],from[1],1,0,0,0))
         | 
| 124 | 
            +
                end
         | 
| 38 125 | 
             
              end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
              assert_dec_months 2, [2010,12],[2010,10]
         | 
| 128 | 
            +
              assert_dec_months 12, [2011,1],[2010,1]
         | 
| 129 | 
            +
              assert_dec_months 24, [2011,1],[2009,1]
         | 
| 130 | 
            +
              assert_dec_months 2, [2011,1],[2010,11]
         | 
| 131 | 
            +
              assert_dec_months 14, [2011,1],[2009,11]
         | 
| 39 132 | 
             
            end
         | 
    
        data/test/chart_test.rb
    ADDED
    
    | @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            require File.dirname(__FILE__)+'/test_helper.rb'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class TestChart < Test::Unit::TestCase
         | 
| 4 | 
            +
              def setup
         | 
| 5 | 
            +
                @defs = YAML::load File.new(TEST_CHARTS_FILE)
         | 
| 6 | 
            +
                sensors = SAAL::Sensors.new(TEST_SENSORS_FILE, TEST_DBCONF)
         | 
| 7 | 
            +
                @charts = SAAL::Charts.new(TEST_CHARTS_FILE, 
         | 
| 8 | 
            +
                                           :sensors => sensors, 
         | 
| 9 | 
            +
                                           :now => Time.utc(2010, 12, 30, 15, 38, 19))
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              def test_alignlabels
         | 
| 13 | 
            +
                assert_equal :center, @charts.find('week').alignlabels
         | 
| 14 | 
            +
                assert_equal :left, @charts.find('4week').alignlabels
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              def test_average
         | 
| 18 | 
            +
                name = 'week'
         | 
| 19 | 
            +
                defs = @defs[name]
         | 
| 20 | 
            +
                chart = @charts.find(name)    
         | 
| 21 | 
            +
                assert_equal ['Fri','Sat','Sun','Mon','Tue','Wed','Thu'], chart.periodnames
         | 
| 22 | 
            +
                chart.sensors.each {|s| s.mock_set(:average => 1)}
         | 
| 23 | 
            +
                assert_equal({:fake_temp => [1], :non_existant => [1]}, chart.average(1))
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              def test_from_to
         | 
| 27 | 
            +
                chart = @charts.find('day')
         | 
| 28 | 
            +
                assert_equal Time.utc(2010, 12, 29, 16, 0, 0).to_i, chart.from
         | 
| 29 | 
            +
                assert_equal Time.utc(2010, 12, 30, 15, 59, 59).to_i, chart.to
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def test_min_max_avg_1arity
         | 
| 33 | 
            +
                name = 'week'
         | 
| 34 | 
            +
                defs = @defs[name]
         | 
| 35 | 
            +
                chart = @charts.find(name)    
         | 
| 36 | 
            +
                assert_equal ['Fri','Sat','Sun','Mon','Tue','Wed','Thu'], chart.periodnames
         | 
| 37 | 
            +
                v = {:minimum => 1.0, :maximum => 2.0, :average => 1.5}
         | 
| 38 | 
            +
                [:minimum,:maximum,:average].each do |method|
         | 
| 39 | 
            +
                  chart.sensors.each {|s| s.mock_set(method => v[method])}
         | 
| 40 | 
            +
                  assert_equal({:fake_temp => [v[method]], :non_existant => [v[method]]}, chart.send(method,1))
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
              
         | 
| 44 | 
            +
              def test_min_max_0arity
         | 
| 45 | 
            +
                name = 'week'
         | 
| 46 | 
            +
                defs = @defs[name]
         | 
| 47 | 
            +
                chart = @charts.find(name)    
         | 
| 48 | 
            +
                assert_equal ['Fri','Sat','Sun','Mon','Tue','Wed','Thu'], chart.periodnames
         | 
| 49 | 
            +
                v = {:minimum => 1.0, :maximum => 2.0, :average => 1.5}
         | 
| 50 | 
            +
                [:minimum,:maximum,:average].each do |method|
         | 
| 51 | 
            +
                  chart.sensors.each {|s| s.mock_set(method => v[method])}
         | 
| 52 | 
            +
                  assert_equal({:fake_temp => v[method], :non_existant => v[method]}, chart.send(method))
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
            end
         | 
    
        data/test/charts_test.rb
    ADDED
    
    | @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            require File.dirname(__FILE__)+'/test_helper.rb'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class TestCharts < Test::Unit::TestCase
         | 
| 4 | 
            +
              def setup
         | 
| 5 | 
            +
                @defs = YAML::load File.new(TEST_CHARTS_FILE)
         | 
| 6 | 
            +
                sensors = SAAL::Sensors.new(TEST_SENSORS_FILE, TEST_DBCONF)
         | 
| 7 | 
            +
                @charts = SAAL::Charts.new(TEST_CHARTS_FILE, :sensors => sensors)
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def test_load
         | 
| 11 | 
            +
                @defs.each do |name, defs|
         | 
| 12 | 
            +
                  chart = @charts.find(name)
         | 
| 13 | 
            +
                  assert_instance_of SAAL::Chart, chart
         | 
| 14 | 
            +
                  assert_equal name, chart.name
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def test_each
         | 
| 19 | 
            +
                i = 0
         | 
| 20 | 
            +
                @charts.each do |chart|
         | 
| 21 | 
            +
                  defs = @defs[chart.name]
         | 
| 22 | 
            +
                  assert_equal defs['last'], chart.num
         | 
| 23 | 
            +
                  assert_equal defs['periods'], chart.periods
         | 
| 24 | 
            +
                  assert_equal defs['description'], chart.description
         | 
| 25 | 
            +
                  assert_equal defs['alt'], chart.alt
         | 
| 26 | 
            +
                  assert_equal defs['sensors'], chart.sensors.map{|s| s.name.to_s}
         | 
| 27 | 
            +
                  i += 1
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
                assert_equal @defs.size, i, "Charts.each did not iterate correctly"
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
    
        data/test/dinrelay_test.rb
    CHANGED
    
    | @@ -14,6 +14,7 @@ class TestDINRelay < Test::Unit::TestCase | |
| 14 14 | 
             
                  @html = opts[:html]
         | 
| 15 15 | 
             
                  @user = opts[:user]
         | 
| 16 16 | 
             
                  @pass = opts[:pass]
         | 
| 17 | 
            +
                  @status = opts[:status] || 200
         | 
| 17 18 | 
             
                  @feedback = opts[:feedback] || {}
         | 
| 18 19 | 
             
                end
         | 
| 19 20 | 
             
                def do_GET(req, res)
         | 
| @@ -22,7 +23,8 @@ class TestDINRelay < Test::Unit::TestCase | |
| 22 23 | 
             
                    user == @user && pass == @pass
         | 
| 23 24 | 
             
                  }
         | 
| 24 25 | 
             
                  res.body = @html
         | 
| 25 | 
            -
                  res | 
| 26 | 
            +
                  res.status = @status
         | 
| 27 | 
            +
                  res['Content-Type'] = "text/html"
         | 
| 26 28 | 
             
                end
         | 
| 27 29 | 
             
              end
         | 
| 28 30 |  | 
| @@ -84,6 +86,13 @@ class TestDINRelay < Test::Unit::TestCase | |
| 84 86 | 
             
              def test_enumerate_sensors
         | 
| 85 87 | 
             
                sensors = SAAL::Sensors.new(TEST_SENSORS_DINRELAY_FILE, TEST_DBCONF)
         | 
| 86 88 | 
             
                assert_equal((1..8).map{|i| "name#{i}"}, sensors.map{|s| s.name}.sort)
         | 
| 89 | 
            +
                assert_equal((1..8).map{|i| "description#{i}"}, sensors.map{|s| s.description}.sort)
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
              def test_sensor_type
         | 
| 93 | 
            +
                SAAL::Sensors.new(TEST_SENSORS_DINRELAY_FILE, TEST_DBCONF).each do |s|
         | 
| 94 | 
            +
                  assert_equal :onoff, s.sensor_type
         | 
| 95 | 
            +
                end
         | 
| 87 96 | 
             
              end
         | 
| 88 97 |  | 
| 89 98 | 
             
              def test_read_sensors
         | 
| @@ -109,4 +118,20 @@ class TestDINRelay < Test::Unit::TestCase | |
| 109 118 | 
             
                  end
         | 
| 110 119 | 
             
                end
         | 
| 111 120 | 
             
              end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
              def test_failed_connection
         | 
| 123 | 
            +
                @vals.each do |num, state|
         | 
| 124 | 
            +
                  assert_equal nil, @og.state(num)
         | 
| 125 | 
            +
                  assert !@og.set_state(num,"ON"), "State change working without a server?!"
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
              end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
              def test_failed_request
         | 
| 130 | 
            +
                with_webrick(:html=>create_index_html(@vals),:status=>404) do |feedback|
         | 
| 131 | 
            +
                  @vals.each do |num, state|
         | 
| 132 | 
            +
                    assert_equal nil, @og.state(num)
         | 
| 133 | 
            +
                    assert !@og.set_state(num,"ON"), "State change working without a server?!"
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
              end
         | 
| 112 137 | 
             
            end
         | 
    
        data/test/sensor_test.rb
    CHANGED
    
    | @@ -11,10 +11,19 @@ class MockConnection | |
| 11 11 | 
             
            end
         | 
| 12 12 |  | 
| 13 13 | 
             
            class MockDBStore
         | 
| 14 | 
            -
              attr_accessor :value
         | 
| 14 | 
            +
              attr_accessor :value, :stored_value
         | 
| 15 15 | 
             
              def average(sensor, from, to)
         | 
| 16 16 | 
             
                @value
         | 
| 17 17 | 
             
              end
         | 
| 18 | 
            +
              def minimum(sensor, from, to)
         | 
| 19 | 
            +
                @value
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
              def maximum(sensor, from, to)
         | 
| 22 | 
            +
                @value
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
              def write(sensor,date,value)
         | 
| 25 | 
            +
                @stored_value = value
         | 
| 26 | 
            +
              end
         | 
| 18 27 | 
             
            end
         | 
| 19 28 |  | 
| 20 29 | 
             
            class TestSensor < Test::Unit::TestCase
         | 
| @@ -92,6 +101,33 @@ class TestSensor < Test::Unit::TestCase | |
| 92 101 | 
             
                assert_equal [0]*20+[1000], (1..21).map{@fake3.read_uncached}
         | 
| 93 102 | 
             
              end
         | 
| 94 103 |  | 
| 104 | 
            +
              def test_eliminate_outliers
         | 
| 105 | 
            +
                correctread = 994.422
         | 
| 106 | 
            +
                fakeread = 817.309
         | 
| 107 | 
            +
                @conn.values = [correctread]*20 + [fakeread,correctread]
         | 
| 108 | 
            +
                assert_equal [correctread]*21, (1..21).map{@fake3.read}
         | 
| 109 | 
            +
                @conn.values = [correctread]*20 + [fakeread,correctread]
         | 
| 110 | 
            +
                assert_equal [correctread]*21, (1..21).map{@fake3.read_uncached}
         | 
| 111 | 
            +
              end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
              def test_sealevel_correction
         | 
| 114 | 
            +
                sensor = fake_sensor('pressure')
         | 
| 115 | 
            +
                @conn.value = @dbstore.value = 1000
         | 
| 116 | 
            +
                corrected = 1000+@defs['pressure']['altitude'].to_f/9.2
         | 
| 117 | 
            +
                assert_equal corrected, sensor.read
         | 
| 118 | 
            +
                sensor.store_value
         | 
| 119 | 
            +
                assert_equal 1000, @dbstore.stored_value
         | 
| 120 | 
            +
                assert_equal corrected, sensor.minimum(0,100)
         | 
| 121 | 
            +
                assert_equal corrected, sensor.maximum(0,100)
         | 
| 122 | 
            +
                assert_equal corrected, sensor.average(0,100)
         | 
| 123 | 
            +
              end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
              def test_sensor_type
         | 
| 126 | 
            +
                [:pressure, :humidity, :temperature].each do |type|
         | 
| 127 | 
            +
                  assert_equal type, fake_sensor(type.to_s).sensor_type
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
              end
         | 
| 130 | 
            +
             | 
| 95 131 | 
             
              def test_mocked
         | 
| 96 132 | 
             
                @mockable = fake_sensor('fake3')
         | 
| 97 133 | 
             
                @conn.value = 1.0
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            day:
         | 
| 2 | 
            +
              sensors: [fake_temp, non_existant]
         | 
| 3 | 
            +
              last: 24
         | 
| 4 | 
            +
              periods: hours
         | 
| 5 | 
            +
              alt: Some ALT text
         | 
| 6 | 
            +
              description: some description of the chart
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            week:
         | 
| 9 | 
            +
              sensors: [fake_temp, non_existant]
         | 
| 10 | 
            +
              last: 7
         | 
| 11 | 
            +
              periods: days
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            4week:
         | 
| 14 | 
            +
              sensors: [fake_temp, non_existant]
         | 
| 15 | 
            +
              last: 4
         | 
| 16 | 
            +
              periods: weeks
         | 
| 17 | 
            +
              alignlabels: left
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            year:
         | 
| 20 | 
            +
              sensors: [fake_temp, non_existant]
         | 
| 21 | 
            +
              last: 12
         | 
| 22 | 
            +
              periods: months
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            4year:
         | 
| 25 | 
            +
              sensors: [fake_temp, non_existant]
         | 
| 26 | 
            +
              last: 4
         | 
| 27 | 
            +
              periods: years
         | 
    
        data/test/test_helper.rb
    CHANGED
    
    | @@ -8,6 +8,7 @@ class Test::Unit::TestCase | |
| 8 8 | 
             
              TEST_SENSORS_DINRELAY_FILE = File.dirname(__FILE__)+'/test_dinrelay_sensors.yml'
         | 
| 9 9 | 
             
              TEST_SENSOR_CLEANUPS_FILE = File.dirname(__FILE__)+'/test_sensor_cleanups.yml'
         | 
| 10 10 | 
             
              TEST_NONEXIST_SENSOR_FILE = File.dirname(__FILE__)+'/nonexistant_sensor.yml'
         | 
| 11 | 
            +
              TEST_CHARTS_FILE = File.dirname(__FILE__)+'/test_charts.yml'
         | 
| 11 12 | 
             
              TEST_DBCONF = File.dirname(__FILE__)+'/test_db.yml'
         | 
| 12 13 | 
             
              TEST_DBOPTS = YAML::load(File.new(TEST_DBCONF))
         | 
| 13 14 |  | 
| @@ -19,3 +19,19 @@ fake3: | |
| 19 19 | 
             
              onewire: 
         | 
| 20 20 | 
             
                serial: /10.4AEC29CDBAAB/temperature
         | 
| 21 21 |  | 
| 22 | 
            +
            pressure:
         | 
| 23 | 
            +
              name: "A fake pressure sensor"
         | 
| 24 | 
            +
              altitude: 200
         | 
| 25 | 
            +
              onewire: 
         | 
| 26 | 
            +
                serial: /10.4AEC29CDBAAB/pressure
         | 
| 27 | 
            +
            temperature:
         | 
| 28 | 
            +
              name: "A fake pressure sensor"
         | 
| 29 | 
            +
              onewire: 
         | 
| 30 | 
            +
                serial: /10.4AEC29CDBAAB/temperature
         | 
| 31 | 
            +
            humidity:
         | 
| 32 | 
            +
              name: "A fake pressure sensor"
         | 
| 33 | 
            +
              onewire: 
         | 
| 34 | 
            +
                serial: /10.4AEC29CDBAAB/humidity
         | 
| 35 | 
            +
             | 
| 36 | 
            +
             | 
| 37 | 
            +
             | 
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: saal
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash:  | 
| 4 | 
            +
              hash: 1
         | 
| 5 5 | 
             
              prerelease: false
         | 
| 6 6 | 
             
              segments: 
         | 
| 7 7 | 
             
              - 0
         | 
| 8 8 | 
             
              - 2
         | 
| 9 | 
            -
              -  | 
| 10 | 
            -
              version: 0.2. | 
| 9 | 
            +
              - 11
         | 
| 10 | 
            +
              version: 0.2.11
         | 
| 11 11 | 
             
            platform: ruby
         | 
| 12 12 | 
             
            authors: 
         | 
| 13 13 | 
             
            - "Pedro C\xC3\xB4rte-Real"
         | 
| @@ -15,7 +15,7 @@ autorequire: | |
| 15 15 | 
             
            bindir: bin
         | 
| 16 16 | 
             
            cert_chain: []
         | 
| 17 17 |  | 
| 18 | 
            -
            date:  | 
| 18 | 
            +
            date: 2011-05-26 00:00:00 -07:00
         | 
| 19 19 | 
             
            default_executable: 
         | 
| 20 20 | 
             
            dependencies: 
         | 
| 21 21 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -94,7 +94,9 @@ files: | |
| 94 94 | 
             
            - bin/saal_dump_database
         | 
| 95 95 | 
             
            - bin/saal_import_mysql
         | 
| 96 96 | 
             
            - bin/saal_readall
         | 
| 97 | 
            +
            - lib/chart.rb
         | 
| 97 98 | 
             
            - lib/chart_data.rb
         | 
| 99 | 
            +
            - lib/charts.rb
         | 
| 98 100 | 
             
            - lib/daemon.rb
         | 
| 99 101 | 
             
            - lib/dbstore.rb
         | 
| 100 102 | 
             
            - lib/dinrelay.rb
         | 
| @@ -105,6 +107,8 @@ files: | |
| 105 107 | 
             
            - lib/sensors.rb
         | 
| 106 108 | 
             
            - saal.gemspec
         | 
| 107 109 | 
             
            - test/chart_data_test.rb
         | 
| 110 | 
            +
            - test/chart_test.rb
         | 
| 111 | 
            +
            - test/charts_test.rb
         | 
| 108 112 | 
             
            - test/daemon_test.rb
         | 
| 109 113 | 
             
            - test/dbstore_test.rb
         | 
| 110 114 | 
             
            - test/dinrelay.html.erb
         | 
| @@ -113,6 +117,7 @@ files: | |
| 113 117 | 
             
            - test/outliercache_test.rb
         | 
| 114 118 | 
             
            - test/sensor_test.rb
         | 
| 115 119 | 
             
            - test/sensors_test.rb
         | 
| 120 | 
            +
            - test/test_charts.yml
         | 
| 116 121 | 
             
            - test/test_db.yml
         | 
| 117 122 | 
             
            - test/test_dinrelay_sensors.yml
         | 
| 118 123 | 
             
            - test/test_helper.rb
         | 
| @@ -157,6 +162,8 @@ specification_version: 2 | |
| 157 162 | 
             
            summary: Thin abstraction layer for interfacing and recording sensors (currently onewire) and actuators (currently dinrelay)
         | 
| 158 163 | 
             
            test_files: 
         | 
| 159 164 | 
             
            - test/chart_data_test.rb
         | 
| 165 | 
            +
            - test/chart_test.rb
         | 
| 166 | 
            +
            - test/charts_test.rb
         | 
| 160 167 | 
             
            - test/daemon_test.rb
         | 
| 161 168 | 
             
            - test/dbstore_test.rb
         | 
| 162 169 | 
             
            - test/dinrelay_test.rb
         |