brewer 0.0.82 → 0.0.83

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6ca0d81bd3b89653e651668a5c4ba4b54cb29822
4
- data.tar.gz: bf27d0f266b6b90b5e82c7ef87d294ded57ff53e
3
+ metadata.gz: 609b03ad9438c34f412625c145e3ea542aa1ba24
4
+ data.tar.gz: c8c87ac2200e23851190b84c6a6d6cf243c46c53
5
5
  SHA512:
6
- metadata.gz: 547df75710936d8e6e708ca3d18860cc8a7ab671e77e84d1d208688f9e710c74276a7c45c97be1697fd2c2e566943ac623969a702f8047fb5ea9b28ac4b03ee3
7
- data.tar.gz: a74cc250cc8eabc97756cd37c538faaca937a726545ad828545c4e44cc1aa3a97d023a5162643888c8632122564723d8d1e5656c1d4e10e41aa999a3cd49fd5f
6
+ metadata.gz: 361547ccce8b1cb02ea49364554d5c03101d6c922bd1672dbc63a90fce2e2a48ba2b90a473b3520c74dd61f6228e36ef180810b47d6159f123064441e82c43ea
7
+ data.tar.gz: 3a60d9739afa32e0a89166b8c693e9fb539f177a69956e1ed7bcff9c519da66ac23037b0e323f113460238c5d2fb7eb92271841c9cc6603f68c68fb939b8a652
@@ -0,0 +1,45 @@
1
+ require_relative "../brewer"
2
+
3
+ # This is the 'manager' for the adaptibrew repo. It handles cloning and such.
4
+ class Adaptibrew
5
+
6
+ def initialize
7
+ refresh
8
+ end
9
+
10
+ # This will clone adaptibrew into ~/.brewer/adaptibrew/
11
+ def clone
12
+ raise "🛑 Cannot clone, no network connection" unless network?
13
+ if !Dir.exists?(adaptibrew_dir)
14
+ Git.clone('https://github.com/llamicron/adaptibrew.git', 'adaptibrew', :path => brewer_dir)
15
+ end
16
+ self
17
+ end
18
+
19
+ # Danger zone...
20
+ def clear
21
+ # :nocov: since this requires network to be off
22
+ if !network?
23
+ print "Warning: you have no network connection. If you clear, you will not be able to clone again, and you'll be stuck without the adaptibrew source. Are you sure? "
24
+ confirm ? nil : abort
25
+ end
26
+ # :nocov:
27
+ FileUtils.rm_rf(adaptibrew_dir)
28
+ self
29
+ end
30
+
31
+ # This is a good catch-all method
32
+ # If it's not there, it will clone.
33
+ # If it is, it will delete and re-clone
34
+ def refresh
35
+ raise "🛑 Cannot refresh, no network connection" unless network?
36
+ clear
37
+ clone
38
+ self
39
+ end
40
+
41
+ def present?
42
+ return Dir.exists?(adaptibrew_dir) ? true : false
43
+ end
44
+
45
+ end
@@ -0,0 +1,141 @@
1
+ require_relative "../brewer"
2
+
3
+ class Brewer
4
+
5
+ attr_reader :base_path
6
+ attr_accessor :temps
7
+
8
+ def initialize
9
+ @base_path = Dir.home + '/.brewer'
10
+ Settings.new
11
+ @temps = {}
12
+ end
13
+
14
+ public
15
+
16
+ # Brewer methods ------------------------------------------------------
17
+ # general utilities for the brewer class
18
+
19
+ def wait(time=30)
20
+ sleep(time.to_f)
21
+ true
22
+ end
23
+
24
+ # Runs an adaptibrew script
25
+ # Output will be stored in @out
26
+ # you may see `echo` quite a bit. This will almost always be directly after calling a script
27
+ # It will be set to the output of the last script. I can't just return the output because i need to return self
28
+ def script(script, params=nil)
29
+ `python #{@base_path}/adaptibrew/#{script}.py #{params}`.chomp
30
+ end
31
+
32
+ # Adaptibrew methods ----------------------------------------------
33
+ # for working with the rig
34
+
35
+ def pump(state="status")
36
+ if state == "status"
37
+ return relay_status($settings['pumpRelay'])
38
+ end
39
+
40
+ if state == 1
41
+ return script("set_pump_on")
42
+ else
43
+ if pid['pid_running'].to_b
44
+ pid(0)
45
+ end
46
+ return script("set_pump_off")
47
+ end
48
+ end
49
+
50
+ # Turns PID on or off, or gets state if no arg is provided
51
+ def pid(state="status")
52
+ if state == "status"
53
+ return {
54
+ 'pid_running' => script("is_pid_running"),
55
+ 'sv_temp' => sv,
56
+ 'pv_temp' => pv
57
+ }
58
+ end
59
+
60
+ if state == 1
61
+ script('set_pid_on')
62
+ pump(1)
63
+ return "Pump and PID are now on"
64
+ else
65
+ return script("set_pid_off")
66
+ end
67
+ end
68
+
69
+ def sv(temp=nil)
70
+ if temp
71
+ return script('set_sv', temp).to_f
72
+ end
73
+ script('get_sv').to_f
74
+ end
75
+
76
+ def pv
77
+ script('get_pv').to_f
78
+ end
79
+
80
+ def relay(relay, state)
81
+ # If you try to turn the relay to a state that it is already in, this skips the wait
82
+ if relay_status(relay).to_b == state.to_b
83
+ return true
84
+ end
85
+ script("set_relay", "#{relay} #{state}")
86
+ wait(10)
87
+ true
88
+ end
89
+
90
+ def all_relays_status
91
+ output = script("get_relay_status_test")
92
+ puts output.split('\n')
93
+ true
94
+ end
95
+
96
+ def relay_status(relay)
97
+ if script("get_relay_status", "#{relay}").include? "on"
98
+ return "on"
99
+ else
100
+ return "off"
101
+ end
102
+ end
103
+
104
+ # :nocov:
105
+ def watch
106
+ until pv >= sv do
107
+ wait(2)
108
+ end
109
+ self
110
+ end
111
+ # :nocov:
112
+
113
+ def rims_to(location)
114
+ if location == "mash"
115
+ # we ended up swapping this relay, so the name is backwards
116
+ relay($settings['rimsToMashRelay'], 0)
117
+ elsif location == "boil"
118
+ relay($settings['rimsToMashRelay'], 1)
119
+ end
120
+ self
121
+ end
122
+
123
+ def hlt_to(location)
124
+ if location == "mash"
125
+ relay($settings['spargeToMashRelay'], 0)
126
+ elsif location == "boil"
127
+ relay($settings['spargeToMashRelay'], 1)
128
+ end
129
+ self
130
+ end
131
+
132
+ def hlt(state)
133
+ if state.to_b
134
+ relay($settings['spargeRelay'], 1)
135
+ elsif !state.to_b
136
+ relay($settings['spargeRelay'], 0)
137
+ end
138
+ self
139
+ end
140
+
141
+ end
@@ -0,0 +1,67 @@
1
+ require_relative "../brewer"
2
+
3
+ class Communicator
4
+
5
+ attr_accessor :slack, :brewer
6
+
7
+ def initialize
8
+ @settings = Settings.new
9
+ @brewer = Brewer.new
10
+ @slack = configure_slack
11
+ end
12
+
13
+ def configure_slack
14
+ unless @settings.settings['webhook_url']
15
+ print "Slack Webhook URL: "
16
+ webhook_url = gets.chomp
17
+ @settings.add({
18
+ 'webhook_url' => webhook_url
19
+ })
20
+ end
21
+ return Slack::Notifier.new @settings.settings['webhook_url']
22
+ end
23
+
24
+ def ping(message="ping at #{Time.now}")
25
+ if message.is_a? Array
26
+ final = message.join("\n")
27
+ @slack.ping(final)
28
+ end
29
+ @slack.ping(message)
30
+ end
31
+
32
+ # TODO: test these methods
33
+ def slack_monitor(delay=10)
34
+ while true do
35
+ before_temp = @brewer.pv
36
+ @brewer.wait(to_seconds(delay))
37
+ diff = @brewer.pv - before_temp
38
+
39
+ ping([
40
+ "Current Temperature: #{@brewer.pid['pv_temp']} F",
41
+ "Set Value Temperature: #{@brewer.pid['sv_temp']} F",
42
+ "Current temperature has climed #{diff} F since #{delay} minute(s) ago",
43
+ "Sent at #{Time.now.strftime("%H:%M")}",
44
+ ""
45
+ ])
46
+ end
47
+ true
48
+ end
49
+
50
+ def monitor
51
+ while true do
52
+ status_table_rows = [
53
+ ["Current Temp", @brewer.pv],
54
+ ["Set Value Temp", @brewer.sv],
55
+ ["PID is: ", @brewer.pid['pid_running'].to_b ? "on" : "off"],
56
+ ["Pump is: ", @brewer.pump]
57
+ ]
58
+
59
+ status_table = Terminal::Table.new :headings ["Item", "Status"], :rows => status_table_rows
60
+
61
+ clear_screen
62
+ puts status_table
63
+ sleep(1)
64
+ end
65
+ end
66
+
67
+ end
@@ -0,0 +1,42 @@
1
+ require_relative "../brewer"
2
+
3
+ module Helpers
4
+
5
+ # Formatted as: 03/07/2017 14:26
6
+ def time
7
+ Time.now.strftime("%m/%d/%Y %H:%M")
8
+ end
9
+
10
+ def network?
11
+ connection = Net::Ping::TCP.new('google.com', 80, 5)
12
+ connection.ping?
13
+ end
14
+
15
+ def confirm(input=gets.chomp)
16
+ if input.to_b
17
+ return true
18
+ end
19
+ false
20
+ end
21
+
22
+ def to_minutes(seconds)
23
+ seconds.to_f / 60
24
+ end
25
+
26
+ def to_seconds(minutes)
27
+ minutes.to_f * 60
28
+ end
29
+
30
+ def clear_screen
31
+ Gem.win_platform? ? (system "cls") : (system "clear")
32
+ end
33
+
34
+ def brewer_dir(path="")
35
+ return Dir.home + "/.brewer/#{path}"
36
+ end
37
+
38
+ def adaptibrew_dir(path="")
39
+ return brewer_dir + "adaptibrew/#{path}"
40
+ end
41
+
42
+ end
@@ -0,0 +1,202 @@
1
+ require_relative "../brewer"
2
+
3
+ class Procedures
4
+
5
+ attr_accessor :com, :brewer, :recipe
6
+
7
+ def initialize
8
+ @brewer = Brewer.new
9
+ @com = Communicator.new
10
+ @recipe = Recipe.new(@brewer)
11
+ end
12
+
13
+ def master
14
+ @recipe.get_recipe_vars
15
+ boot
16
+ heat_strike_water
17
+ dough_in
18
+ mash
19
+ mashout
20
+ sparge
21
+ top_off
22
+ boil
23
+ end
24
+
25
+ def boot
26
+ puts Rainbow("booting...").yellow
27
+ @brewer.pid(0)
28
+ @brewer.pump(0)
29
+ @brewer.rims_to('mash')
30
+ @brewer.hlt_to('mash')
31
+ @brewer.all_relays_status
32
+ puts @brewer.pid
33
+
34
+ puts Rainbow("Boot finished!").green
35
+ @com.ping("🍺 boot finished 🍺")
36
+ true
37
+ end
38
+
39
+ # :nocov:
40
+ def heat_strike_water
41
+ puts "heat-strike-water procedure started"
42
+
43
+ # Confirm strike water is in the mash tun
44
+ print Rainbow("Is the strike water in the mash tun? ").yellow
45
+ # -> response
46
+ confirm ? nil : abort
47
+
48
+ # confirm return manifold is in the mash tun
49
+ print Rainbow("Is the return manifold in the mash tun? ").yellow
50
+ # -> response
51
+ confirm ? nil : abort
52
+
53
+ print Rainbow("Is the mash tun valve open? ").yellow
54
+ confirm ? nil : abort
55
+
56
+ # confirm RIMS relay is on
57
+ @brewer.rims_to('mash')
58
+ puts "RIMS-to-mash relay is now on"
59
+
60
+ # turn on pump
61
+ @brewer.pump(1)
62
+ puts "Pump is now on"
63
+
64
+ puts Rainbow("Is the pump running properly? ").yellow
65
+ # TODO: Test this
66
+ until confirm
67
+ puts "restarting pump"
68
+ @brewer.pump(0)
69
+ @brewer.wait(2)
70
+ @brewer.pump(1)
71
+ end
72
+
73
+ # confirm that strike water is circulating well
74
+ print Rainbow("Is the strike water circulating well? ").yellow
75
+ # -> response
76
+ confirm ? nil : abort
77
+
78
+
79
+ # calculate strike temp & set PID to strike temp
80
+ # this sets PID SV to calculated strike temp automagically
81
+ @brewer.sv(@recipe.strike_water_temp)
82
+ puts "SV has been set to calculated strike water temp"
83
+ # turn on RIMS heater
84
+ @brewer.pid(1)
85
+
86
+ # measure current strike water temp and save
87
+ @recipe.starting_strike_temp = @brewer.pv
88
+ puts "current strike water temp is #{@brewer.pv}. Saved."
89
+ puts "Heating to #{@brewer.sv}"
90
+
91
+ @com.ping("Strike water beginning to heat. This may take a few minutes.")
92
+
93
+ # when strike temp is reached, @com.ping slack
94
+ @brewer.watch
95
+ @com.ping("Strike water heated to #{@brewer.pv}. Maintaining temperature.")
96
+ puts Rainbow("Strike water heated. Maintaining temp.").green
97
+ true
98
+ end
99
+ # :nocov:
100
+
101
+ def dough_in
102
+ # turn pump off
103
+ @brewer.pump(0)
104
+ # turn PID off
105
+ @brewer.pid(0)
106
+ @brewer.wait(3)
107
+ @com.ping("Ready to dough in")
108
+ puts Rainbow("Ready to dough in").green
109
+
110
+ # pour in grain
111
+
112
+ print Rainbow("Confirm when you're done with dough-in (y): ").yellow
113
+ confirm ? nil : abort
114
+ true
115
+ end
116
+
117
+ def mash
118
+ @brewer.sv(@recipe.mash_temp)
119
+
120
+ puts Rainbow("mash stated. This will take a while.").green
121
+ @com.ping("Mash started. This will take a while.")
122
+
123
+ @brewer.rims_to('mash')
124
+
125
+ @brewer.pump(1)
126
+ @brewer.pid(1)
127
+
128
+ @brewer.watch
129
+ @com.ping("Mash temp (#{@brewer.pv} F) reached. Starting timer for #{@recipe.mash_time} minutes.")
130
+ @brewer.wait(@recipe.mash_time)
131
+ @com.ping("🍺 Mash complete 🍺. Check for starch conversion.")
132
+ puts Rainbow("Mash complete").green
133
+ puts "Check for starch conversion"
134
+ end
135
+
136
+ def mashout
137
+ @com.ping("Start heating sparge water")
138
+
139
+ @brewer.sv(@recipe.mashout_temp)
140
+
141
+ @brewer.pump(1)
142
+ @brewer.pid(1)
143
+
144
+ @com.ping("Heating to #{@brewer.sv}... this could take a few minutes.")
145
+ @brewer.watch
146
+ @com.ping("Mashout temperature (#{@brewer.pv}) reached. Mashout complete.")
147
+ end
148
+
149
+ def sparge
150
+ print Rainbow("Is the sparge water heated to the correct temperature? ").yellow
151
+ confirm ? nil : abort
152
+
153
+ @brewer.hlt_to('mash')
154
+ @brewer.hlt(1)
155
+
156
+ print "Waiting for 10 seconds. "
157
+ puts Rainbow("Regulate sparge balance.").yellow
158
+ puts "(ctrl-c to abort proccess)"
159
+ @brewer.wait(30)
160
+
161
+ @brewer.rims_to('boil')
162
+ @brewer.pump(1)
163
+
164
+ @com.ping("Please check the sparge balance and ignite boil tun burner")
165
+
166
+ puts Rainbow("Waiting until intervention to turn off pump (y): ").yellow
167
+ confirm ? nil : abort
168
+
169
+ @brewer.pid(0)
170
+ @brewer.pump(0)
171
+
172
+ @brewer.hlt(0)
173
+ end
174
+
175
+ def top_off
176
+ @brewer.hlt_to('boil')
177
+
178
+ @brewer.hlt(1)
179
+
180
+ print Rainbow("waiting for intervention to turn off hlt (y): ").yellow
181
+ confirm ? nil : abort
182
+
183
+ @brewer.hlt(0)
184
+
185
+ @com.ping('Topping off complete')
186
+ puts Rainbow("Topping off complete").green
187
+ end
188
+
189
+ def boil
190
+ @com.ping("starting boil procedure")
191
+ @brewer.wait(to_seconds(5))
192
+ @com.ping("Add boil hops")
193
+ @brewer.wait(to_seconds(40))
194
+ @com.ping("Add flovering hops")
195
+ @brewer.wait(to_seconds(13))
196
+ @com.ping("Add finishing hops")
197
+ @brewer.wait(30)
198
+ @com.ping("Done.")
199
+ puts Rainbow("Done.").green
200
+ end
201
+
202
+ end
@@ -0,0 +1,56 @@
1
+ require_relative "../brewer"
2
+
3
+ class Recipe
4
+
5
+ attr_accessor :mash_temp, :mash_time, :mashout_temp, :water, :grain, :grain_temp, :desired_mash_temp, :strike_water_temp, :starting_strike_temp
6
+
7
+ def initialize(brewer)
8
+ @brewer = brewer
9
+ end
10
+
11
+ def get_recipe_vars(vars=false)
12
+ if vars
13
+ raise "Vars must be a hash" unless vars.is_a? Hash
14
+ vars.each do |recipe_key, value|
15
+ instance_variable_set("@" + recipe_key, value)
16
+ end
17
+ end
18
+
19
+ puts "Variables for heating strike water"
20
+ get_strike_temp
21
+
22
+ puts "Variables for mash ---"
23
+ print "Enter mash temperature: "
24
+ @mash_temp = gets.chomp.to_f
25
+ print "Enter mash time in minutes: "
26
+ @mash_time = to_seconds(gets.chomp.to_f)
27
+
28
+ puts "Variables for mashout ---"
29
+ print "Enter mashout temp: "
30
+ @mashout_temp = gets.chomp.to_f
31
+ end
32
+
33
+ def get_strike_temp
34
+ print "Input amount of water in quarts: "
35
+ @water = gets.chomp.to_f
36
+
37
+ print "Input amount of grain in lbs: "
38
+ @grain = gets.chomp.to_f
39
+
40
+ print "Input current grain temp (#{@brewer.pv.to_s} F): "
41
+ @grain_temp = gets.chomp.to_f
42
+ if @grain_temp == ""
43
+ @grain_temp = @brewer.pv
44
+ end
45
+
46
+ print "Input desired mash temp (150 F): "
47
+ @desired_mash_temp = gets.chomp
48
+ if @desired_mash_temp == ""
49
+ @desired_mash_temp = 150
50
+ end
51
+ @desired_mash_temp
52
+
53
+ @strike_water_temp = script('get_strike_temp', "#{@water} #{@grain} #{@grain_temp} #{@desired_mash_temp}").to_f
54
+ end
55
+
56
+ end
@@ -0,0 +1,137 @@
1
+ require_relative "../brewer"
2
+
3
+ class Settings
4
+
5
+ attr_accessor :settings
6
+ attr_reader :cache_file, :source
7
+
8
+ def initialize(testing=false)
9
+
10
+ @source = adaptibrew_dir('settings.py')
11
+ @cache_file = brewer_dir('settings.yml')
12
+
13
+ @settings = Hash.new
14
+
15
+ if !testing
16
+ Adaptibrew.new.clone
17
+
18
+ unless cache?
19
+ parse_and_cache
20
+ else
21
+ load_cached_settings
22
+ end
23
+
24
+ load_global
25
+ end
26
+ end
27
+
28
+ # This will create the cache and populate
29
+ # it with settings from settings.py
30
+ def parse_and_cache
31
+ parse
32
+ cache
33
+ end
34
+
35
+ # Loads cached settings
36
+ # If no cached settings, it returns false
37
+ def load_cached_settings
38
+ if cache?
39
+ @settings = YAML.load(File.open(@cache_file))
40
+ return true
41
+ end
42
+ false
43
+ end
44
+
45
+ # Checks if there are settings in the cache
46
+ def cache?
47
+ if File.exists?(@cache_file) and File.readlines(@cache_file).grep(/DEBUG/).size > 0
48
+ return true
49
+ end
50
+ false
51
+ end
52
+
53
+ # Parse the settings from the source file into @settings
54
+ def parse
55
+ File.open(@source, 'r') do |file|
56
+ file.each_line do |line|
57
+ if line.include? "=" and line[0] != "#"
58
+ key, value = line.match(/(.+)=(.+)/).captures
59
+ @settings[key.strip.chomp] = value.strip.chomp
60
+ end
61
+ end
62
+ type_cast
63
+ return true
64
+ end
65
+ false
66
+ end
67
+
68
+ # Creates the cache if there isn't one already
69
+ def create_cache
70
+ unless File.exists?(@cache_file)
71
+ File.open(@cache_file, 'w')
72
+ end
73
+ true
74
+ end
75
+
76
+ # This will add a new element to the @settings hash
77
+ # AND re-cache the settings
78
+ def add(setting)
79
+ raise "Setting needs to be a hash" unless setting.is_a? Hash
80
+ setting.each do |key, value|
81
+ @settings[key] = value
82
+ end
83
+ cache
84
+ end
85
+
86
+ # Stores the currents @settings in settings.yml
87
+ def cache
88
+ create_cache
89
+ store = YAML::Store.new @cache_file
90
+ store.transaction {
91
+ @settings.each do |k, v|
92
+ store[k] = v
93
+ end
94
+ }
95
+ true
96
+ end
97
+
98
+ # This deletes the cache file
99
+ def clear_cache
100
+ if File.exists?(@cache_file)
101
+ FileUtils.rm_f @cache_file
102
+ end
103
+ true
104
+ end
105
+
106
+ # This is so that the settings are easier to use in my code
107
+ # and for backwards compatability purposes
108
+ def load_global
109
+ parse_and_cache
110
+ $settings = @settings
111
+ end
112
+
113
+ def change(values)
114
+ raise "Values to change must be a hash" unless values.is_a? Hash
115
+ values.each do |k, v|
116
+ @settings[k] = v
117
+ end
118
+ return true
119
+ end
120
+
121
+ # This method is r/badcode, i know
122
+ def type_cast
123
+ # Super janky
124
+ change({
125
+ 'rimsaddressint' => @settings['rimsaddressint'].to_i,
126
+ 'switchaddressint' => @settings['switchaddressint'].to_i,
127
+ 'baudrate' => @settings['baudrate'].to_i,
128
+ 'timeout' => @settings['timeout'].to_i,
129
+ 'spargeToMashRelay' => @settings['spargeToMashRelay'].to_i,
130
+ 'spargeRelay' => @settings['spargeRelay'].to_i,
131
+ 'rimsToMashRelay' => @settings['rimsToMashRelay'].to_i,
132
+ 'pumpRelay' => @settings['pumpRelay'].to_i,
133
+ 'DEBUG' => @settings['DEBUG'].to_b,
134
+ })
135
+ end
136
+
137
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brewer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.82
4
+ version: 0.0.83
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luke Sweeney
@@ -249,6 +249,13 @@ files:
249
249
  - Rakefile
250
250
  - bin/brewer
251
251
  - lib/brewer.rb
252
+ - lib/brewer/adaptibrew.rb
253
+ - lib/brewer/brewer.rb
254
+ - lib/brewer/communicator.rb
255
+ - lib/brewer/helpers.rb
256
+ - lib/brewer/procedures.rb
257
+ - lib/brewer/recipe.rb
258
+ - lib/brewer/settings.rb
252
259
  - spec/adaptibrew_spec.rb
253
260
  - spec/brewer_spec.rb
254
261
  - spec/hardware_spec.rb