Skip to content

Commit c70d1c0

Browse files
committed
- adds public key
- hardnes profile loading by adding type checking and further checks - fixes a regression in the software PID calculation of the Dterm (thanks Peter for reporting!) - better handling of empty profiles by Comparator - update libs
1 parent d7b29d8 commit c70d1c0

21 files changed

Lines changed: 293 additions & 540 deletions

src/artisan-mac.spec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ DATA_FILES = [
103103
(r'includes/dijkstra.ttf', '.'),
104104
(r'includes/ComicNeue-Regular.ttf', '.'),
105105
(r'includes/xkcd-script.ttf', '.'),
106+
(r'includes/artisan_public_key.pem', '.'),
106107
(r'includes/Machines', './Machines'),
107108
(r'includes/Themes', './Themes'),
108109
(r'includes/Icons', './Icons'),

src/artisan-win.spec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ for fn in [
294294
r'includes\safari-pinned-tab.svg',
295295
r'includes\site.webmanifest',
296296
r'includes\logging.yaml',
297+
r'includes\artisan_public_key.pem',
297298
]:
298299
copy_file(fn, TARGET)
299300

src/artisanlib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
__version__ = '4.0.3'
22
__revision__ = ''
33
__build__ = '0'
4+
__signature__ = ''
45

56
__release_sponsor_name__ = 'artisan.plus'
67
__release_sponsor_domain__ = 'artisan.plus'

src/artisanlib/alarms.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ def deletealarm(self, _:bool = False) -> None:
633633
def importalarms(self, _:bool = False) -> None:
634634
self.aw.fileImport(QApplication.translate('Message', 'Load Alarms'),self.importalarmsJSON,ext='*.alrm *.alog')
635635

636-
def importalarmsJSON(self, filename:str) -> None:
636+
def importalarmsJSON(self, filename:str) -> bool:
637637
try:
638638
_,ext = os.path.splitext(filename)
639639
if ext == '.alrm':
@@ -662,19 +662,22 @@ def importalarmsJSON(self, filename:str) -> None:
662662
elif ext == '.alog':
663663
obj = deserialize(filename)
664664
self.aw.plusAddPath(obj, filename)
665-
self.aw.loadAlarmsFromProfile(filename, cast('ProfileData', obj))
665+
profile:ProfileData = self.aw.validateProfileDict(obj)
666+
self.aw.loadAlarmsFromProfile(filename, profile)
666667
self.alarmsfile.setText(self.aw.qmc.alarmsfile)
667668
self.aw.qmc.alarmstate = [-1]*len(self.aw.qmc.alarmflag)
668669
aitems = self.buildAlarmSourceList()
669670
for i, _ in enumerate(self.aw.qmc.alarmsource):
670671
if self.aw.qmc.alarmsource[i] + 3 >= len(aitems):
671672
self.aw.qmc.alarmsource[i] = 1 # BT
672673
self.createalarmtable()
674+
return True
673675
except Exception as ex: # pylint: disable=broad-except
674676
_log.exception(ex)
675677
_, _, exc_tb = sys.exc_info()
676678
self.aw.sendmessage(QApplication.translate('Message','Error loading alarm file'))
677679
self.aw.qmc.adderror((QApplication.translate('Error Message','Exception:') + ' importalarmsJSON() {0}').format(str(ex)),getattr(exc_tb, 'tb_lineno', '?'))
680+
return False
678681

679682
@pyqtSlot(bool)
680683
def exportalarms(self, _:bool = False) -> None:

src/artisanlib/async_comm.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import logging
2222
import serial as pyserial
2323
import asyncio
24-
import platform
2524

2625
from contextlib import suppress
2726
from threading import Thread
@@ -215,7 +214,7 @@ async def create_serial_connection(
215214
if clear_HUPCL:
216215
# the transport serial port is not open yet in this case
217216
try:
218-
if platform.system() == 'Linux': # seems not to resolve the issue on macOS
217+
if sys.platform.startswith('linux'): # seems not to resolve the issue on macOS
219218
import termios # pylint: disable=C0415,E0401
220219
# the following might hang on macOS for non-callup devices
221220
#url = url.replace('/dev/tty.','/dev/cu.')

src/artisanlib/atypes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ class ProfileData(TypedDict, total=False):
132132
version: str
133133
revision: str
134134
build: str
135+
signature: str
135136
artisan_os: str
136137
artisan_os_version: str
137138
artisan_os_arch: str

src/artisanlib/canvas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7934,7 +7934,7 @@ def resetButtonAction(self,_:bool=False) -> None:
79347934
# if keepProperties=True (a call from OnMonitor()), we keep all the pre-set roast properties
79357935
# onMonitor is set if called from onMonitor
79367936
def reset(self,redraw:bool = True, soundOn:bool = True, keepProperties:bool = False, fireResetAction:bool = True, onMonitor:bool = False) -> bool:
7937-
_log.debug('PRINT reset(keepProperties=%s,onMonitor=%s)',keepProperties,onMonitor)
7937+
# _log.debug('PRINT reset(keepProperties=%s,onMonitor=%s)',keepProperties,onMonitor)
79387938
try:
79397939
focused_widget = QApplication.focusWidget()
79407940
if focused_widget and focused_widget != self.aw.centralWidget():

src/artisanlib/comm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2890,7 +2890,7 @@ def DT301temperature(self, retry:int=2) -> tuple[float, float]:
28902890
libtime.sleep(0.01) # this may not be necessary but works well
28912891
r = self.SP.read(11)
28922892
if len(r)==11:
2893-
data = bytearray(r)
2893+
data = bytes(r)
28942894
if len(data)==11 and data[0] == 0xfc and data[1] == 0x13 and data[10] == 0xf3:
28952895
for i in range(2,6):
28962896
temp = (temp << 4) | (data[i] & 0xf)

src/artisanlib/comparator.py

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ def __init__(self, aw:'ApplicationWindow', profile:'ProfileData', filepath:str,
331331
self.metadata['ambient_humidity'] = f"{float2float(profile['ambient_humidity']):g}%"
332332
if 'ambient_pressure' in profile and profile['ambient_pressure'] != 0:
333333
self.metadata['ambient_pressure'] = f"{float2float(profile['ambient_pressure']):g}hPa"
334-
if 'computed' in profile and 'weight_loss' in profile['computed']:
334+
if 'computed' in profile and 'weight_loss' in profile['computed'] and 'weight' in profile and profile['weight'][1] != 0.0:
335335
self.metadata['weight_loss'] = f"-{profile['computed']['weight_loss']:g}%"
336336
if 'ground_color' in profile and profile['ground_color'] != 0:
337337
self.metadata['ground_color'] = f"#{float2str(profile['ground_color'])}"
@@ -420,16 +420,17 @@ def recompute(self) -> None:
420420
self.events_timex = []
421421
if self.stemp1 is not None and self.stemp2 is not None: # type:ignore[redundant-expr]
422422
for ti in self.timeindex[:-1]:
423-
temp1:float|None = (self.stemp1[ti] if len(self.stemp1)>ti else None)
424-
temp2:float|None = (self.stemp2[ti] if len(self.stemp2)>ti else None)
425-
if ((len(self.events1) == 0 and ti != -1) or ti > 0):
426-
self.events1.append(temp1)
427-
self.events2.append(temp2)
428-
self.events_timex.append(self.timex[ti])
429-
else:
430-
self.events1.append(None)
431-
self.events2.append(None)
432-
self.events_timex.append(None)
423+
if ti >= 0 and len(self.timex)>0:
424+
temp1:float|None = (self.stemp1[ti] if len(self.stemp1)>ti else None)
425+
temp2:float|None = (self.stemp2[ti] if len(self.stemp2)>ti else None)
426+
if ((len(self.events1) == 0 and ti != -1) or ti > 0):
427+
self.events1.append(temp1)
428+
self.events2.append(temp2)
429+
self.events_timex.append(self.timex[ti])
430+
else:
431+
self.events1.append(None)
432+
self.events2.append(None)
433+
self.events_timex.append(None)
433434
# update special events
434435
if self.specialevents is not None and self.specialeventstype is not None and self.specialeventsvalue is not None:
435436
# calculated bot and top corresponding to the temperature positions of the event values 0 and 100
@@ -497,19 +498,21 @@ def recompute(self) -> None:
497498
except Exception as ex: # pylint: disable=broad-except
498499
_log.exception(ex)
499500
# add a last event at DROP/END to extend the lines to the end of roast
500-
if not self.aw.qmc.compareRoast and self.aw.qmc.compareBBP:
501-
# BBP-only mode
502-
end = (self.timex[-1] if self.timeindex[0] == -1 else self.timex[self.timeindex[0]])
503-
else:
504-
end = (self.timex[-1] if self.timeindex[6] == 0 else self.timex[self.timeindex[6]])
505-
if self.E1:
506-
self.E1.append((end,self.E1[-1][1]))
507-
if self.E2:
508-
self.E2.append((end,self.E2[-1][1]))
509-
if self.E3:
510-
self.E3.append((end,self.E3[-1][1]))
511-
if self.E4:
512-
self.E4.append((end,self.E4[-1][1]))
501+
# (only for non-empty profiles)
502+
if len(self.timex)>0:
503+
if not self.aw.qmc.compareRoast and self.aw.qmc.compareBBP:
504+
# BBP-only mode
505+
end = (self.timex[-1] if self.timeindex[0] == -1 else self.timex[self.timeindex[0]])
506+
else:
507+
end = (self.timex[-1] if self.timeindex[6] == 0 else self.timex[self.timeindex[6]])
508+
if self.E1:
509+
self.E1.append((end,self.E1[-1][1]))
510+
if self.E2:
511+
self.E2.append((end,self.E2[-1][1]))
512+
if self.E3:
513+
self.E3.append((end,self.E3[-1][1]))
514+
if self.E4:
515+
self.E4.append((end,self.E4[-1][1]))
513516

514517
def firstTime(self) -> float:
515518
try:
@@ -527,7 +530,7 @@ def endTime(self) -> float:
527530
try:
528531
return self.timex[self.endTimeIdx]
529532
except Exception: # pylint: disable=broad-except
530-
return self.timex[-1]
533+
return (self.timex[-1] if len(self.timex)>0 else 0)
531534

532535
def setVisible(self, b:bool) -> None:
533536
self.visible = b
@@ -2034,21 +2037,24 @@ def addProfileFromFile(self, filename:str) -> None:
20342037
if firstChar == '{':
20352038
f.close()
20362039
obj = deserialize(filename)
2040+
profile_data = self.aw.validateProfileDict(obj)
20372041
self.aw.plusAddPath(obj, filename)
2038-
self.addProfile(filename,cast('ProfileData', obj))
2042+
self.addProfile(filename,profile_data)
20392043
except Exception as ex: # pylint: disable=broad-except
2044+
self.aw.sendmessage(f"{filename}: {QApplication.translate('Message','invalid artisan file')}")
20402045
_log.exception(ex)
20412046

20422047
def addProfiles(self, filenames:list[str]) -> None:
20432048
if len(filenames) > 0:
20442049
for filename in filenames:
20452050
self.addProfileFromFile(filename)
2046-
self.updateMenus()
2047-
self.realign()
2048-
self.updateZorders()
2049-
self.redrawOnDeltaAxisVisibilityChanged()
2050-
if self.aw.qpc is not None:
2051-
self.aw.qpc.update_phases(self.getPhasesData())
2051+
if len(self.profiles)>0:
2052+
self.updateMenus()
2053+
self.realign()
2054+
self.updateZorders()
2055+
self.redrawOnDeltaAxisVisibilityChanged()
2056+
if self.aw.qpc is not None:
2057+
self.aw.qpc.update_phases(self.getPhasesData())
20522058

20532059
def deleteProfile(self, i:int) -> None:
20542060
self.profileTable.removeRow(i)
@@ -2084,17 +2090,17 @@ def getPhasesData(self) -> list[tuple[str, float, tuple[float,float,float], bool
20842090
for p in reversed(profiles):
20852091
if p.visible:
20862092
start_idx = p.timeindex[0] if p.timeindex[0] != -1 else 0
2087-
start:float = p.timex[start_idx]
2088-
total:float = p.timex[p.timeindex[6]] - start if p.timeindex[6] != 0 else p.timex[-1]
2089-
dry:float = p.timex[p.timeindex[1]] - start if p.timeindex[1] != 0 else 0
2090-
fcs:float = p.timex[p.timeindex[2]] - start if p.timeindex[2] != 0 else 0
2093+
start:float = (p.timex[start_idx] if len(p.timex)>start_idx else 0)
2094+
total:float = (p.timex[p.timeindex[6]] - start if p.timeindex[6] != 0 else p.timex[-1] if len(p.timex) > p.timeindex[6] else 0)
2095+
dry:float = p.timex[p.timeindex[1]] - start if len(p.timex) > p.timeindex[1] > 0 else 0
2096+
fcs:float = p.timex[p.timeindex[2]] - start if len(p.timex) > p.timeindex[2] > 0 else 0
20912097
p1:float = dry
20922098
p3:float = total - fcs if fcs != 0 else 0
20932099
p2:float = total - p1 - p3 if p1 != 0 and p3 != 0 else 0
20942100
c:QColor = QColor.fromRgbF(*p.color)
2095-
t1:float = p.temp2[p.timeindex[1]] if p.timeindex[1] != 0 and len(p.temp2) > p.timeindex[1] else -1
2096-
t2:float = p.temp2[p.timeindex[2]] if p.timeindex[2] != 0 and len(p.temp2) > p.timeindex[2] else -1
2097-
t3:float = p.temp2[p.timeindex[6]] if p.timeindex[6] != 0 and len(p.temp2) > p.timeindex[6] else (p.temp2[-1] if len(p.temp2)>0 else -1)
2101+
t1:float = p.temp2[p.timeindex[1]] if len(p.temp2) > p.timeindex[1] > 0 else -1
2102+
t2:float = p.temp2[p.timeindex[2]] if len(p.temp2) > p.timeindex[2] > 0 else -1
2103+
t3:float = p.temp2[p.timeindex[6]] if len(p.temp2) > p.timeindex[6] > 0 else (p.temp2[-1] if len(p.temp2)>0 else -1)
20982104
r1:float = -1
20992105
r2:float = -1
21002106
r3:float = -1

0 commit comments

Comments
 (0)