brewby 0.1.0

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.
@@ -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