brewby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ require 'brewby/inputs/ds18b20'
2
+ require 'brewby/inputs/test'
3
+
4
+ module Brewby
5
+ module Inputs
6
+ def self.adapter_class adapter
7
+ case adapter
8
+ when :test
9
+ Brewby::Inputs::Test
10
+ else
11
+ Brewby::Inputs::DS18B20
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,51 @@
1
+ module Brewby
2
+ module Inputs
3
+ class DS18B20
4
+ attr_accessor :name, :hardware_id, :device_path
5
+ def initialize options = {}
6
+ @name = options[:name]
7
+ @device_path = options[:device_path] || "/sys/bus/w1/devices"
8
+ @hardware_id = options[:hardware_id] || find_hardware_id
9
+ end
10
+
11
+ def find_hardware_id
12
+ w1_devices[0].gsub("#{device_path}/",'')
13
+ end
14
+
15
+ def w1_devices
16
+ Dir["#{device_path}/28*"]
17
+ end
18
+
19
+ def read
20
+ raw = read_raw
21
+
22
+ if tempC = parse(raw)
23
+ tempF = to_fahrenheit tempC
24
+ else
25
+ tempC
26
+ end
27
+ end
28
+
29
+ def parse raw_data
30
+ if temp_data = raw_data.match(/t=([0-9]+)/)
31
+ temp_data[1].to_f / 1000
32
+ else
33
+ nil
34
+ end
35
+ end
36
+
37
+ def to_fahrenheit temp
38
+ ((temp * 1.8) + 32).round(3)
39
+ end
40
+
41
+ def read_raw
42
+ File.read device_file
43
+ end
44
+
45
+ def device_file
46
+ "/sys/bus/w1/devices/#{@hardware_id}/w1_slave"
47
+ end
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,14 @@
1
+ module Brewby
2
+ module Inputs
3
+ class Test
4
+ attr_accessor :name
5
+ def initialize options = {}
6
+ @name = options[:name]
7
+ end
8
+
9
+ def read
10
+ (75 + rand(100)).to_f
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ require 'brewby/outputs/test'
2
+ require 'brewby/outputs/gpio'
3
+
4
+ module Brewby
5
+ module Outputs
6
+ def self.adapter_class adapter
7
+ case adapter
8
+ when :test
9
+ Brewby::Outputs::Test
10
+ else
11
+ Brewby::Outputs::GPIO
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,40 @@
1
+ module Brewby
2
+ module Outputs
3
+ class GPIO
4
+ attr_reader :gpio_path, :pin
5
+ def initialize options = {}
6
+ @pin = options[:pin]
7
+ @gpio_path = options[:gpio_path] || '/sys/class/gpio'
8
+
9
+ initialize_gpio_pin
10
+ initialize_gpio_direction
11
+ end
12
+
13
+ def initialize_gpio_pin
14
+ unless File.exists? File.join(gpio_path, "gpio#{pin}", "value")
15
+ File.write File.join(gpio_path, "export"), pin
16
+ end
17
+ end
18
+
19
+ def initialize_gpio_direction
20
+ File.write File.join(gpio_path, "gpio#{pin}", "direction"), 'out'
21
+ end
22
+
23
+ def on
24
+ write(1)
25
+ end
26
+
27
+ def off
28
+ write(0)
29
+ end
30
+
31
+ def on?
32
+ '1' == File.read(File.join(gpio_path, "gpio#{pin}", "value"))
33
+ end
34
+
35
+ def write value
36
+ File.write File.join(gpio_path, "gpio#{pin}", "value"), value
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ module Brewby
2
+ module Outputs
3
+ class Test
4
+ attr_accessor :name
5
+ def initialize options = {}
6
+ @on = false
7
+ @name = options[:name]
8
+ end
9
+
10
+ def on
11
+ @on = true
12
+ end
13
+
14
+ def off
15
+ @on = false
16
+ end
17
+
18
+ def on?
19
+ @on
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module Brewby
2
+ class StepLoader
3
+ def initialize application
4
+ @application = application
5
+ end
6
+
7
+ def load_file file
8
+ instance_eval File.read(file), file
9
+ end
10
+
11
+ def recipe name, &block
12
+ @application.name = name
13
+ yield self
14
+ end
15
+
16
+ def step name, &block
17
+ step = generate_step name, &block
18
+ @application.steps.push step
19
+ end
20
+
21
+ def generate_step name, &block
22
+ step_generator = Brewby::Steps::DSL::Step.new name, @application
23
+ step_generator.instance_eval &block
24
+ step_generator.create!
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ require 'brewby/steps/temp_control'
2
+ require 'brewby/steps/dsl/step'
3
+ require 'brewby/step_loader'
4
+
5
+ module Brewby
6
+ module Steps
7
+ end
8
+ end
@@ -0,0 +1,48 @@
1
+ module Brewby
2
+ module Steps
3
+ module DSL
4
+ class Step
5
+ attr_reader :step_class, :options
6
+ def initialize name, application
7
+ @application = application
8
+ @options = {}
9
+ @options[:name] = name
10
+ end
11
+
12
+ def type symbol, options = {}
13
+ @step_class = case symbol
14
+ when :temp_control
15
+ Brewby::Steps::TempControl
16
+ end
17
+ @options.merge!(options)
18
+ end
19
+
20
+ def mode m
21
+ @options[:mode] = m
22
+ end
23
+
24
+ def target t
25
+ @options[:target] = t
26
+ end
27
+
28
+ def hold_duration d
29
+ @options[:duration] = d
30
+ end
31
+
32
+ def input i
33
+ @options[:input] = i
34
+ end
35
+
36
+ def output o
37
+ @options[:output] = o
38
+ end
39
+
40
+ def create!
41
+ @options[:input] = @application.inputs.find { |i| i.name.to_sym == @options[:input] }
42
+ @options[:output] = @application.outputs.find { |o| o.name.to_sym == @options[:output] }
43
+ @step_class.new @options
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,111 @@
1
+ require 'brewby/temp_sensor'
2
+ require 'brewby/heating_element'
3
+ require 'brewby/timed'
4
+ require 'temper'
5
+
6
+ module Brewby
7
+ module Steps
8
+ class TempControl
9
+ attr_reader :input, :output, :pid, :target, :duration, :mode, :last_reading, :threshold_reached, :name
10
+
11
+ include Brewby::Timed
12
+
13
+ def initialize options = {}
14
+ @mode = options[:mode] || :manual
15
+ @output = 0
16
+ @pulse_range = options[:pulse_range] || 5000
17
+
18
+ @input = options[:input]
19
+ @output = options[:output]
20
+ @threshold_reached = false
21
+ @name = options[:name]
22
+ @last_reading = 0.0
23
+
24
+ if automatic_control?
25
+ configure_automatic_control options
26
+ else
27
+ set_power_level options[:power_level] || 1.0
28
+ end
29
+ end
30
+
31
+ def configure_automatic_control options
32
+ @target = options[:target]
33
+ @duration = options[:duration] || 1
34
+
35
+ @pid = Temper::PID.new maximum: @pulse_range
36
+ @pid.tune 44, 165, 4
37
+ @pid.setpoint = @target
38
+ end
39
+
40
+ def manual_control?
41
+ !automatic_control?
42
+ end
43
+
44
+ def automatic_control?
45
+ @mode == :auto
46
+ end
47
+
48
+ def read_input
49
+ reading = input.read
50
+ @last_reading = reading if reading
51
+ end
52
+
53
+ def set_power_level level
54
+ set_pulse_width (level * @pulse_range).to_i
55
+ end
56
+
57
+ def calculate_power_level
58
+ if read_input
59
+ set_pulse_width pid.control @last_reading
60
+ end
61
+ end
62
+
63
+ def set_pulse_width width
64
+ if width
65
+ output.pulse_width = width
66
+ end
67
+ end
68
+
69
+ def power_level
70
+ output.pulse_width / @pulse_range.to_f
71
+ end
72
+
73
+ def step_iteration
74
+ if automatic_control?
75
+ calculate_power_level
76
+ output.pulse
77
+ check_temp_threshold unless threshold_reached
78
+ check_step_completion
79
+ else
80
+ read_input if input
81
+ output.pulse
82
+ end
83
+ end
84
+
85
+ def check_step_completion
86
+ if threshold_reached && Time.now.to_i > @step_finishes_at
87
+ stop_timer
88
+ end
89
+ end
90
+
91
+ def time_remaining
92
+ if @step_finishes_at
93
+ @step_finishes_at - Time.now.to_i
94
+ else
95
+ duration_in_seconds
96
+ end
97
+ end
98
+
99
+ def check_temp_threshold
100
+ if last_reading >= target
101
+ @threshold_reached = true
102
+ @step_finishes_at = Time.now.to_i + duration_in_seconds
103
+ end
104
+ end
105
+
106
+ def duration_in_seconds
107
+ @duration * 60
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,13 @@
1
+ module Brewby
2
+ class TempSensor
3
+ attr_reader :input_pin
4
+
5
+ def initialize input_pin
6
+ @input_pin = input_pin
7
+ end
8
+
9
+ def read
10
+ 0.0
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,50 @@
1
+ module Brewby
2
+ module Timed
3
+ attr_reader :start_time, :end_time
4
+
5
+ def started?
6
+ !@start_time.nil?
7
+ end
8
+
9
+ def ended?
10
+ !@end_time.nil?
11
+ end
12
+
13
+ def in_progress?
14
+ started? && !ended?
15
+ end
16
+
17
+ def start_timer
18
+ @start_time = Time.now
19
+ end
20
+
21
+ def stop_timer
22
+ @end_time = Time.now
23
+ end
24
+
25
+ def elapsed
26
+ started? ? (@end_time || Time.now) - @start_time : 0
27
+ end
28
+
29
+ def timer_for seconds
30
+ time = seconds > 0 ? time_from_seconds(seconds) : [0, 0, 0]
31
+
32
+ "%0.2d:%0.2d:%0.2d" % time
33
+ end
34
+
35
+ def countdown_for seconds
36
+ sign = seconds > 0 ? "" : "+"
37
+
38
+ sign + timer_for(seconds.abs)
39
+ end
40
+
41
+ def time_from_seconds seconds
42
+ hours = seconds / 3600
43
+ seconds -= (hours * 3600)
44
+ minutes = seconds / 60
45
+ seconds -= minutes * 60
46
+
47
+ [hours, minutes, seconds]
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module Brewby
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe Brewby::Application do
4
+ before do
5
+ @output = {
6
+ pin: 1,
7
+ pulse_range: 5000
8
+ }
9
+
10
+ Brewby::Application.any_instance.stub(:configure_view)
11
+ @application = Brewby::Application.new adapter: :test, outputs: [@output], inputs: [{}, {hardware_id: '28-ba1c9d2e48'}]
12
+ end
13
+
14
+ it 'should have one output' do
15
+ @application.outputs.size.should == 1
16
+ @application.outputs.first.should be_instance_of Brewby::HeatingElement
17
+ @application.outputs.first.adapter.should be_instance_of Brewby::Outputs::Test
18
+ end
19
+
20
+ it 'should have two inputs' do
21
+ @application.inputs.size.should == 2
22
+ @application.inputs.each do |input|
23
+ input.should be_instance_of Brewby::Inputs::Test
24
+ end
25
+ end
26
+
27
+
28
+ context 'adding steps' do
29
+ before do
30
+ @application.add_step :temp_control, mode: :auto, mode: :auto, target: 155.0, duration: 15
31
+ @step = @application.steps.first
32
+ end
33
+
34
+ it 'creates a step object with passed configuration options' do
35
+ @step.should be_instance_of Brewby::Steps::TempControl
36
+ end
37
+
38
+ it 'passes an input and an output to the step' do
39
+ @step.input.should == @application.inputs.first
40
+ @step.output.should == @application.outputs.first
41
+ end
42
+
43
+ it 'allows the step to specify the input/output objects to use' do
44
+ @application.add_step :temp_control, mode: :auto, mode: :auto, target: 155.0, duration: 15, input: @application.inputs.last
45
+ @step = @application.steps.last
46
+ @step.input.should == @application.inputs.last
47
+ end
48
+ end
49
+ end