-
Notifications
You must be signed in to change notification settings - Fork 4
/
updateFirmware.py
executable file
·448 lines (392 loc) · 17.9 KB
/
updateFirmware.py
1
2
3
4
5
6
7
8
9
10
11
12
13
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
85
86
87
88
89
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
#!/usr/bin/env python3
# Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy)
# This file is part of LBussy's BrewPi Script Remix (BrewPi-Script-RMX).
#
# BrewPi Script RMX is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# BrewPi Script RMX is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BrewPi Script RMX. If not, see <https://www.gnu.org/licenses/>.
# These scripts were originally a part of brewpi-script, a part of
# the BrewPi project. Legacy support (for the very popular Arduino
# controller) seems to have been discontinued in favor of new hardware.
# All credit for the original brewpi-script goes to @elcojacobs,
# @m-mcgowan, @rbrady, @steersbob, @glibersat, @Niels-R and I'm sure
# many more contributors around the world. My apologies if I have
# missed anyone; those were the names listed as contributors on the
# Legacy branch.
# See: 'original-license.md' for notes about the original project's
# license and credits.
import sys
import os
import subprocess
import psutil
# sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..") # append parent directory to be able to import files
import autoSerial
from BrewPiUtil import createDontRunFile, removeDontRunFile, stopThisChamber, readCfgWithDefaults, addSlash, setupSerial, scriptPath
from gitHubReleases import gitHubReleases
import brewpiVersion
import programController as programmer
from ConvertBrewPiDevice import ConvertBrewPiDevice
# import sentry_sdk
# sentry_sdk.init("https://[email protected]/1803681")
# Globals
firmRepo = "https://api.github.com/repos/brewpi-remix/brewpi-firmware-rmx"
userInput = True
# Replacement for raw_input which works when piped through shell
def pipeInput(prompt=""):
saved_stdin = sys.stdin
sys.stdin = open('/dev/tty', 'r')
result = input(prompt)
sys.stdin = saved_stdin
return (result)
# Return "a" or "an" depending on first letter of argument (yes this is
# a grammar function)
def article(word):
if not word:
return "a" # in case word is not valid
firstLetter = word[0]
if firstLetter.lower() in 'aeiouh':
return "an"
else:
return "a"
# Log to stderr.txt
def printStdErr(*objs):
if userInput:
print(*objs, file=sys.stderr)
# Log to stdout.txt
def printStdOut(*objs):
if userInput:
print(*objs, file=sys.stdout)
# Quits all running instances of BrewPi
# def quitBrewPi(webPath):
# import BrewPiProcess
# allProcesses = BrewPiProcess.BrewPiProcesses()
# allProcesses.stopAll(webPath + "/do_not_run_brewpi")
# See if the version we got back from the board is valid
def goodVersion(versn):
try:
lst = versn.toString().split(".")
count = len(lst)
if count == 3:
M,m,p = lst
if M.isdigit() and m.isdigit() and p.isdigit():
return True
except:
pass
return False
def updateFromGitHub(beta = False, doShield = False, usePinput = True, restoreSettings = True, restoreDevices = True, ):
configFile = '{0}settings/config.cfg'.format(addSlash(scriptPath()))
config = readCfgWithDefaults(configFile)
stopResult = stopThisChamber(config['scriptPath'], config['wwwPath'])
if stopResult is True:
# BrewPi was running and stopped. Start after update.
startAfterUpdate = True
pass
elif stopResult is False:
# Unable to stop BrewPi
return False
elif stopResult is None:
# BrewPi was not probably not running, don't start after update.
startAfterUpdate = False
pass
hwVersion = None
shield = None
board = None
family = None
ser = None
### Get version number
printStdErr("\nChecking current firmware version.\n")
try:
ser = setupSerial(config, 57600, 1.0, 1.0, True)
hwVersion = brewpiVersion.getVersionFromSerial(ser)
family = hwVersion.family
shield = hwVersion.shield
board = hwVersion.board
printStdErr("\nFound the following controller:")
printStdErr("\tVersion:" + hwVersion.toString())
printStdErr("\tBuild:\t" + hwVersion.build)
printStdErr("\tBoard:\t" + hwVersion.board)
printStdErr("\tShield:\t" + hwVersion.shield)
printStdErr("\tPort:\t" + ser.name)
except:
if hwVersion is None:
choice = pipeInput("\nUnable to receive version from controller. If your controller is" +
"\nunresponsive, or if this is a new controller you can choose to proceed" +
"\nand flash the firmware. Would you like to do this? [y/N]: ").lower()
if not choice.startswith('y'):
printStdErr("\nPlease make sure your controller is connected properly and try again.")
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started. If it does not start after this you',
'\nmay have to investigate.')
return True
# Be sure to check the configured port
if config['port'] == 'auto':
printStdErr("\nUsing auto port configuration.")
port, name = autoSerial.detect_port()
else:
# Convert udev rule based port to /dev/ttyA*
if not config['port'].startswith("/dev/ttyA"):
oldport = config['port']
convert = ConvertBrewPiDevice()
config['port'] = convert.get_device_from_brewpidev(config['port'])
printStdErr("\nUsing port {0} translated from port {1}.".format(config['port'], oldport))
else:
printStdErr("\nUsing port {0} according to configuration settings.".format(config['port']))
port, name = autoSerial.detect_port(my_port = config['port'])
if not port:
printStdErr("\nCould not find compatible device in available serial ports.")
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started. If it does not start after this you',
'\nmay have to investigate.')
return False
if "Arduino" in name:
family = "Arduino"
if "Uno" in name:
board = 'uno'
if board is None:
printStdErr("\nUnable to connect to an Arduino Uno, perhaps it is disconnected or otherwise"
"\nunavailable.")
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started. If it does not start after this you',
'\nmay have to investigate.')
return False
else:
printStdErr("\nProcessing a firmware flash for your blank %s." % name)
if ser:
ser.close() # Close serial port so we can flash it
ser = None
if hwVersion and hwVersion is not None:
# Make sure we didn't get half a string (happens when the BrewPi process
# does not shut down or restarts)
if not goodVersion(hwVersion):
printStdErr("\nInvalid version returned from controller. Make sure you are running as root" +
"\nand the script is able to shut down correctly.")
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started. If it does not start after this you',
'\nmay have to investigate.')
return False
else:
restoreDevices = False
restoreSettings = False
printStdErr("\nChecking GitHub for available release(s).")
releases = gitHubReleases(firmRepo)
availableTags = releases.getTags(beta)
stableTags = releases.getTags(False)
compatibleTags = []
# Allow reflashing the shield type
if doShield is True:
shield = None
# Allow selecting the desired shield type
if shield is None:
shields = releases.getShields()
printStdErr("\nPlease select the shield type you would like to use. Available shields:")
for i in range(len(shields)):
printStdErr("[{0}] {1}".format(i, shields[i]))
# Give chance to exit
printStdErr("[{0}] {1}".format(i + 1, "Cancel firmware update"))
while 1:
try:
choice = pipeInput("\nEnter the number [0-{0}] of the shield you would like to use.\n"
"[default = {0} ({1})]: ".format(len(shields) - 1, shields[len(shields) - 1]))
if choice == "":
selection = len(shields) - 1
elif int(choice) == len(shields):
printStdErr("\nExiting without making any changes.")
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started. If it does not start after this you',
'\nmay have to investigate.')
return True
else:
selection = int(choice)
except ValueError:
printStdErr("\nNot a valid choice. Try again.")
continue
try:
shield = shields[selection]
printStdErr("\nReflashing controller with {0} shield.".format(shield))
except IndexError:
printStdErr("\nNot a valid choice. Try again.")
continue
break
for tag in availableTags:
url = None
if family == "Arduino":
url = releases.getBinUrl(tag, [board, shield, ".hex"])
if url is not None:
compatibleTags.append(tag)
if len(compatibleTags) == 0:
printStdErr("\nNo compatible releases found for {0} {1} {2} with {3} {4} shield.".format(article(family), family.capitalize(), board.capitalize(), article(shield), str(shield).upper()))
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started. If it does not start after this you',
'\nmay have to investigate.')
return False
# Default tag is latest stable tag, or latest unstable tag if no stable tag is found
for i, t in enumerate(compatibleTags):
if t in stableTags:
default_choice = i
break
elif t in compatibleTags:
default_choice = i
break
tag = compatibleTags[default_choice]
if userInput:
printStdErr("\nAvailable releases:")
for i, menu_tag in enumerate(compatibleTags):
printStdErr("\t[%d] %s" % (i, menu_tag))
printStdErr("\t[" + str(len(compatibleTags)) + "] Cancel firmware update")
num_choices = len(compatibleTags)
while 1:
try:
choice = pipeInput("\nEnter the number [0-%d] of the version you want to program\n"
"[default = %d (%s)]: " % (num_choices, default_choice, tag))
if choice == "":
break
else:
selection = int(choice)
except ValueError:
printStdErr("Select by the number corresponding to your choice [0-%d]" % num_choices)
continue
if selection == num_choices:
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started. If it does not start after this you',
'\nmay have to investigate.')
return True # choice = skip updating
try:
tag = compatibleTags[selection]
except IndexError:
printStdErr("\nNot a valid choice. Try again.")
continue
break
else:
printStdErr("\nLatest version on GitHub: " + tag)
if doShield is False:
if hwVersion is not None and not hwVersion.isNewer(tag):
if hwVersion.isEqual(tag):
printStdErr("\nYou are already running version %s." % tag)
else:
printStdErr("\nYour current version is newer than %s." % tag)
if userInput:
choice = pipeInput("\nIf you are encountering problems, you can reprogram anyway. Would you like" +
"\nto do this? [y/N]: ").lower()
if not choice.startswith('y'):
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
return True
else:
printStdErr("\nNo update needed. Exiting.")
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started. If it does not start after this you',
'\nmay have to investigate.')
return True
if hwVersion is not None and userInput:
choice = pipeInput("\nWould you like to restore your settings after programming? [Y/n]: ").lower()
if choice == "":
choice = "y"
if not choice.startswith('y'):
restoreSettings = False
choice = pipeInput("\nWould you like to restore your configured devices after programming? [Y/n]: ").lower()
if choice == "":
choice = "y"
if not choice.startswith('y'):
restoreDevices = False
localFileName = None
system1 = None
system2 = None
if family == "Arduino":
localFileName = releases.getBin(tag, [board, shield, ".hex"])
else:
printStdErr("\nError: Device family {0} not recognized".format(family))
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started. If it does not start after this you',
'\nmay have to investigate.')
return False
if localFileName:
printStdErr("\nLatest firmware downloaded to:\n" + localFileName)
else:
printStdErr("\nDownloading firmware failed.")
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started. If it does not start after this you',
'\nmay have to investigate.')
return False
printStdErr("\nUpdating firmware.")
result = programmer.programController(config, board, localFileName, {
'settings': restoreSettings,
'devices': restoreDevices,
'versionNew': tag,
'versionOld': hwVersion,
})
if startAfterUpdate:
# Only restart if it was running when we started
removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath'])))
else:
printStdErr('\nBrewPi was not running when we started, leaving \'do_not_run_brewpi\' in:\n{0}.'.format(addSlash(config['wwwPath'])))
return result
def main():
import getopt
# Read in command line arguments
try:
opts, args = getopt.getopt(sys.argv[1:], "sbhg", ['beta', 'silent', 'shield', 'glycol'])
except getopt.GetoptError:
# print help message for command line options
print ("Unknown parameter, available options: \n" +
"\t--silent or -s\t Use default options, do not ask for user input\n" +
"\t--beta or -b\t Include unstable (prerelease) releases\n" +
"\t--shield or -h\t Allow flashing a different shield\n")
return True
beta = False
doShield = False
userInput = True
for o, a in opts:
if o in ('-s', '--silent'):
userInput = False
if o in ('-b', '--beta'):
beta = True
if o in ('-h', '--shield'):
doShield = True
if o in ('-g', '--glycol'):
doShield = True
beta = True
result = updateFromGitHub(beta, doShield, userInput)
return result
if __name__ == '__main__':
result = main()
exit(result)