Class: Picobrew::Api

Inherits:
Object
  • Object
show all
Includes:
HTTParty
Defined in:
lib/picobrew/api.rb

Constant Summary collapse

HOST =
'picobrew.com'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(username, password, cookies: nil, enableLogging: false) ⇒ Api

Returns a new instance of Api.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/picobrew/api.rb', line 17

def initialize(username, password, cookies: nil, enableLogging: false)
    @username = username
    @password = password
    @enableLogging = enableLogging
    @http = Net::HTTP.new(HOST, 443)
    @http.use_ssl = true
    @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    @cached_sessions = []

    log "Created Picobrew object for #{username}"
    if !cookies.nil?
        log "Using provided cookies instead of logging in"
        @cookies = cookie_from_hash(cookies)
    else
        ()
    end
end

Instance Attribute Details

#cookiesObject (readonly)

debug_output



15
16
17
# File 'lib/picobrew/api.rb', line 15

def cookies
  @cookies
end

Instance Method Details

#cache_sessionsObject



200
201
202
203
204
# File 'lib/picobrew/api.rb', line 200

def cache_sessions()
    log "Caching sesions"
    @cached_sessions = get_all_sessions()
    @cached_at = Time.now
end

#check_active(session_id) ⇒ Object

don’t really understand this one, not sure if id is required



219
220
221
222
223
224
225
226
227
228
# File 'lib/picobrew/api.rb', line 219

def check_active(session_id)
    log "Check if session is active: #{session_id}"
    begin
        options = options({:body => {'option' => 'checkActive', 'sessionId' => session_id}})
        response = self.class.post('/JSONAPI/Zymatic/ZymaticSession.cshtml', options)
        response.body
    rescue Exception => e
        log "Error: #{e}"
    end
end


265
266
267
268
269
# File 'lib/picobrew/api.rb', line 265

def cookie_from_hash(hsh)
    cookie_hash = CookieHash.new
    cookie_hash.add_cookies(hsh)
    cookie_hash
end

#find_session(session_guid) ⇒ Object



193
194
195
196
197
198
# File 'lib/picobrew/api.rb', line 193

def find_session(session_guid)
    log "Looking up short session id for #{session_guid}"
    # quick and dirty cache expiration
    cache_sessions if @cached_sessions.empty? || @cached_at.to_i + 5 * 60 < Time.now.to_i
    return @cached_sessions.find {|session| session['GUID'] == session_guid}
end

#get_active_sessionObject



206
207
208
209
210
211
212
213
214
215
216
# File 'lib/picobrew/api.rb', line 206

def get_active_session()
    log "Get Active Session"
    begin
    	options = options({:body => {'option' => 'getZymaticsForUser', 'getActiveSession' => 'true'}})
    	response = self.class.post('/JSONAPI/Zymatic/ZymaticSession.cshtml', options)
        # TODO: return json?
        response.body
    rescue Exception => e
    	log "Error: #{e}"
    end
end

#get_all_recipesObject



56
57
58
59
60
61
62
63
64
65
# File 'lib/picobrew/api.rb', line 56

def get_all_recipes()
    log "Get All Recipes"
    begin
    	options = options({:body => {'option' => 'getAllRecipesForUser'}})
    	response = self.class.post('/JSONAPI/Zymatic/ZymaticRecipe.cshtml', options)
    	body = JSON.parse(response.body)
    rescue Exception => e
    	log "Error: #{e}"
    end
end

#get_all_sessionsObject



138
139
140
141
142
143
144
145
146
147
# File 'lib/picobrew/api.rb', line 138

def get_all_sessions()
    log "Get All Sessions"
    begin
        options = options({:body => {'option' => 'getAllSessionsForUser'}})
        response = self.class.post('/JSONAPI/Zymatic/ZymaticSession.cshtml', options)
        body = JSON.parse(response.body)
    rescue Exception => e
        log "Error: #{e}"
    end
end

#get_recipe(recipe_id) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/picobrew/api.rb', line 67

def get_recipe(recipe_id)
    log "Scrape Recipe #{recipe_id}"
    begin
        options = options({})
        response = self.class.get("/Members/Recipes/ParseRecipe.cshtml?id=#{recipe_id}", options)
        page = Nokogiri::HTML(response.body)
        recipe = {'specs' => {}}
        page.css('#user-specs-table tr').each do |element|
            recipe['specs'][element.css('td')[0].text] = element.css('td')[1].text
        end
        page.css('#editForm input').each do |element|
            if is_json? element['value']
                recipe[element['name']] = JSON.parse(element['value'])
            else
                recipe[element['name']] = element['value']
            end
        end
        recipe
    rescue Exception => e
        log "Error: #{e}"
    end
end

#get_recipe_control_program(recipe_id) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/picobrew/api.rb', line 90

def get_recipe_control_program(recipe_id)
    log "Scrape Recipe Advanced Editor"
    begin
        options = options({})
        response = self.class.get("/members/recipes/editctlprogram?id=#{recipe_id}", options)
        page = Nokogiri::HTML(response.body)
        program = {'steps' => []}
        page.css('#stepTable tr').each do |row|
            next if row.at_css('input').nil?

            step = {
                'index' => row.at_css('input')['data-index'].to_i,
                'name' => row.at_css('input[name*=Name]')['value'],
                'location' => row.at_css('select option[@selected=selected]').text,
                'targetTemp' => row.at_css('input[name*=Temp]')['value'].to_i,
                'time' => row.at_css('input[name*=Time]')['value'].to_i,
                'drain' => row.at_css('input[name*=Drain]')['value'].to_i
            }

            program['steps'].push(step) if !step['name'].nil?
        end
        program
    rescue Exception => e
        log "Error: #{e}"
    end
end

#get_recipe_control_programs(user_id, machine_id) ⇒ Object

API used by Zymatic hardware Does not require auth, but require user id and machine id Move this to a separate class?



233
234
235
236
237
238
239
240
241
# File 'lib/picobrew/api.rb', line 233

def get_recipe_control_programs(user_id, machine_id)
    log "Get Recipe Control Programs"
    begin
        response = self.class.get("/API/SyncUser?user=#{user_id}&machine=#{machine_id}")
        response.body
    rescue Exception => e
        log "Error: #{e}"
    end
end

#get_recipe_id_for_session_id(session_guid) ⇒ Object



183
184
185
186
# File 'lib/picobrew/api.rb', line 183

def get_recipe_id_for_session_id(session_guid)
    session = find_session(session_guid)
    return session['RecipeGUID'] if !session.nil?
end

#get_session_log(session_id) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/picobrew/api.rb', line 149

def get_session_log(session_id)
    log "Get Session Log for #{session_id}"
    # the sessions list contains references for guids, but the log api
    # wants a *different* id, so need to lookup one from the other
    if session_id.length > 6
        session_id = get_short_session_id_for_guid(session_id)
        raise Exception, "No short session id for guid" if session_id.nil?
        log "Get Session Log for #{session_id}"
    end
    begin
        options = options({:body => {'option' => 'getSessionLogs', 'sessionID' => session_id}})
        response = self.class.post('/JSONAPI/Zymatic/ZymaticSession.cshtml', options)
        body = JSON.parse(response.body)
    rescue Exception => e
        log "Error: #{e}"
    end
end

#get_session_notes(session_id) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/picobrew/api.rb', line 167

def get_session_notes(session_id)
    log "Get Session Notes for #{session_id}"
    if session_id.length > 6
        session_id = get_short_session_id_for_guid(session_id)
        raise Exception, "No short session id for guid" if session_id.nil?
        log "Get Session Notes for #{session_id}"
    end
    begin
        options = options({ :body => {'option' => 'getSessionNotes', 'sessionID' => session_id} })
        response = self.class.post('/JSONAPI/Zymatic/ZymaticSession.cshtml', options)
        body = JSON.parse(response.body)
    rescue Exception => e
        log "Error: #{e}"
    end
end

#get_sessions_for_recipe(recipe_id) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/picobrew/api.rb', line 117

def get_sessions_for_recipe(recipe_id)
    log "Get Sessions for Recipe #{recipe_id}"
    begin
        options = options({})
        response = self.class.get("/Members/Logs/brewingsessions.cshtml?id=#{recipe_id}", options)
        page = Nokogiri::HTML(response.body)
        sessions = []
        page.css('#BrewingSessions tbody tr').each do |row|
            sessions.push({
                'name' => row.css('td.name').text,
                'id' => row.css('td.name a')[0]['href'].gsub(/.*id=/, ''),
                'date' => row.css('td.date').text,
                'notes' => row.css('td')[3].text
            } )
        end
        sessions
    rescue Exception => e
        log "Error: #{e}"
    end
end

#get_short_session_id_for_guid(session_guid) ⇒ Object



188
189
190
191
# File 'lib/picobrew/api.rb', line 188

def get_short_session_id_for_guid(session_guid)
    session = find_session(session_guid)
    return session['ID'] if !session.nil?
end

#is_json?(json) ⇒ Boolean

Returns:

  • (Boolean)


243
244
245
246
247
248
249
250
# File 'lib/picobrew/api.rb', line 243

def is_json?(json)
    begin
        JSON.parse(json)
        return true
    rescue JSON::ParserError
        return false
    end
end

#log(msg) ⇒ Object



271
272
273
# File 'lib/picobrew/api.rb', line 271

def log(msg)
	puts msg if @enableLogging
end

#logged_in?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/picobrew/api.rb', line 52

def logged_in?()
    !@cookies.nil?
end

#loginObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/picobrew/api.rb', line 35

def ()
    log "Logging in"
    begin
        options = {
            :body => {'username' => @username, 'Password' => @password},
            :headers => {'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8'},
            :follow_redirects => false
        }
        response = self.class.post('/account/loginAjax.cshtml', options)
        raise "No Set-Cookie in response" if response.get_fields('Set-Cookie').nil?
        @cookies = parse_cookie(response)
    rescue Exception => e
        raise "Authentication error: #{e}"
    end
    log "logged in"
end

#options(params) ⇒ Object



252
253
254
255
256
257
# File 'lib/picobrew/api.rb', line 252

def options(params)
    { :headers => {
        'Content-Type' => 'application/x-www-form-urlencoded',
        'Cookie' => @cookies.to_cookie_string }
    }.merge params
end


259
260
261
262
263
# File 'lib/picobrew/api.rb', line 259

def parse_cookie(resp)
    cookie_hash = CookieHash.new
    resp.get_fields('Set-Cookie').each { |c| cookie_hash.add_cookies(c) }
    cookie_hash
end