Skip to content

Commit 71b4d9c

Browse files
committed
fixes typo in new event replay
1 parent 8486915 commit 71b4d9c

File tree

11 files changed

+94
-45
lines changed

11 files changed

+94
-45
lines changed

src/artisanlib/acaia.py

+16-14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
if TYPE_CHECKING:
2424
from bleak.backends.characteristic import BleakGATTCharacteristic # pylint: disable=unused-import
2525

26+
try:
27+
from PyQt6.QtCore import QObject # @UnusedImport @Reimport @UnresolvedImport
28+
except ImportError:
29+
from PyQt5.QtCore import QObject # type: ignore # @UnusedImport @Reimport @UnresolvedImport
2630

2731
from artisanlib.ble_port import ClientBLE
2832
from artisanlib.async_comm import AsyncIterable, IteratorReader
@@ -121,8 +125,7 @@ class ACAIA_TIMER(IntEnum):
121125

122126

123127

124-
class AcaiaBLE(ClientBLE):
125-
128+
class AcaiaBLE(QObject, ClientBLE): # pyright: ignore [reportGeneralTypeIssues] # Argument to class must be a base class
126129

127130

128131
# Acaia message constants
@@ -177,6 +180,12 @@ def __init__(self, connected_handler:Optional[Callable[[], None]] = None,
177180
self.add_notify(ACAIA_NOTIFY_UUID, self.notify_callback)
178181
self.add_write(ACAIA_SERVICE_UUID, ACAIA_WRITE_UUID)
179182

183+
def set_connected_handler(self, connected_handler:Optional[Callable[[], None]]) -> None:
184+
self._connected_handler = connected_handler
185+
186+
def set_disconnected_handler(self, disconnected_handler:Optional[Callable[[], None]]) -> None:
187+
self._disconnected_handler = disconnected_handler
188+
180189

181190
# protocol parser
182191

@@ -517,23 +526,16 @@ def battery_changed(self, new_value:int) -> None: # pylint: disable=no-self-use
517526

518527

519528

520-
# QObject needs to go first in this mixing and AcaiaBLE and its super class are not allowed to hold __slots__
521-
class Acaia(Scale, AcaiaBLE): # pyright: ignore [reportGeneralTypeIssues] # Argument to class must be a base class
529+
# AcaiaBLE and its super class are not allowed to hold __slots__
530+
class Acaia(AcaiaBLE, Scale): # pyright: ignore [reportGeneralTypeIssues] # Argument to class must be a base class
522531

523-
def __init__(self, connected_handler:Optional[Callable[[], None]] = None,
532+
def __init__(self, model:int, ident:Optional[str], name:Optional[str], connected_handler:Optional[Callable[[], None]] = None,
524533
disconnected_handler:Optional[Callable[[], None]] = None):
525-
Scale.__init__(self)
534+
Scale.__init__(self, model, ident, name)
526535
AcaiaBLE.__init__(self, connected_handler = connected_handler, disconnected_handler=disconnected_handler)
527-
self.address:Optional[str] = None # if set, connects are restricted to the device with this BLE address
528-
529-
def set_address(self, address:Optional[str]) -> None:
530-
self.address = address
531-
532-
def get_address(self) -> Optional[str]:
533-
return self.address
534536

535537
def connect(self) -> None:
536-
self.start(address=self.address)
538+
self.start(address=self.ident)
537539

538540
def disconnect(self) -> None:
539541
self.stop()

src/artisanlib/background.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ def readChecks(self) -> None:
765765
self.aw.qmc.backgroundETcurve = bool(self.backgroundETflag.isChecked())
766766
self.aw.qmc.backgroundBTcurve = bool(self.backgroundBTflag.isChecked())
767767
self.aw.qmc.backgroundShowFullflag = bool(self.backgroundFullflag.isChecked())
768-
self.aw.qmc.redraw(recomputeAllDeltas=True)
768+
self.aw.qmc.redraw_keep_view(recomputeAllDeltas=True)
769769

770770
@pyqtSlot(int)
771771
def changeAlignEventidx(self, i:int) -> None:

src/artisanlib/canvas.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ class tgraphcanvas(FigureCanvas):
186186
showEventsSignal = pyqtSignal(int, bool)
187187
showBackgroundEventsSignal = pyqtSignal(bool)
188188
redrawSignal = pyqtSignal(bool,bool,bool,bool,bool)
189+
redrawKeepViewSignal = pyqtSignal(bool,bool,bool,bool,bool)
189190

190191
umlaute_dict : Final[Dict[str, str]] = {
191192
uchr(228): 'ae', # U+00E4 \xc3\xa4
@@ -2417,6 +2418,7 @@ def __init__(self, parent:QWidget, dpi:int, locale:str, aw:'ApplicationWindow')
24172418
self.showEventsSignal.connect(self.showEvents)
24182419
self.showBackgroundEventsSignal.connect(self.showBackgroundEvents)
24192420
self.redrawSignal.connect(self.redraw, type=Qt.ConnectionType.QueuedConnection) # type: ignore
2421+
self.redrawKeepViewSignal.connect(self.redraw_keep_view, type=Qt.ConnectionType.QueuedConnection) # type: ignore
24202422

24212423
#NOTE: empty Figure is initially drawn at the end of self.awsettingsload()
24222424
################################# FUNCTIONS ###################################
@@ -5896,34 +5898,32 @@ def playbackevent(self) -> None:
58965898
# if last registered event of event_type has higher BT as next to be replayed one, we
58975899
# expect a temperature decrease instead of an increase
58985900
last_registered_event_index = len(self.specialeventstype) - 1 - self.specialeventstype[::-1].index(event_type)
5899-
if self.stemp2B[self.specialevents[last_registered_event_index]] > self.stemp2B[bge]:
5901+
if self.ctemp2[self.specialevents[last_registered_event_index]] > self.stemp2B[bge]:
59005902
delta = self.ctemp2[-1] - self.stemp2B[bge]
59015903
increasing = False
59025904
except Exception: # pylint: disable=broad-except
59035905
# a previous event of that type might not yet exist
59045906
pass
5905-
next_byTemp_checked[event_type] = True
59065907
else: # before TP we switch back to time-based
59075908
delta = timed
5908-
next_byTemp_checked[event_type] = True
5909+
next_byTemp_checked[event_type] = True
59095910
elif not next_byTemp_checked[event_type] and self.replayType == 2: # replay by ET (after TP)
59105911
if self.TPalarmtimeindex is not None:
5911-
if len(self.ctemp1)> 0 and self.ctemp1[-1] is not None and len(self.stemp1)>bge:
5912+
if len(self.ctemp1)> 0 and self.ctemp1[-1] is not None and len(self.stemp1B)>bge:
59125913
delta = self.stemp1B[bge] - self.ctemp1[-1]
59135914
try:
59145915
# if last registered event of event_type has higher BT as next to be replayed one, we
59155916
# expect a temperature decrease instead of an increase
59165917
last_registered_event_index = len(self.specialeventstype) - 1 - self.specialeventstype[::-1].index(event_type)
5917-
if self.stemp1B[self.specialevents[last_registered_event_index]] > self.stemp1B[bge]:
5918+
if self.ctemp1[self.specialevents[last_registered_event_index]] > self.stemp1B[bge]:
59185919
delta = self.ctemp1[-1] - self.stemp1B[bge]
59195920
increasing = False
59205921
except Exception: # pylint: disable=broad-except
59215922
# a previous event of that type might not yet exist
59225923
pass
5923-
next_byTemp_checked[event_type] = True
59245924
else: # before TP we switch back to time-based
59255925
delta = timed
5926-
next_byTemp_checked[event_type] = True
5926+
next_byTemp_checked[event_type] = True
59275927
else:
59285928
delta = 99999 # don't trigger this one
59295929

@@ -6097,7 +6097,6 @@ def playbackevent(self) -> None:
60976097
coefficients = numpy.polyfit([last_time, next_time], [last_event_value, next_event_value], 1)
60986098
ramps[event_type] = numpy.poly1d(coefficients)(now)
60996099

6100-
61016100
# now move the sliders to the new values (if any)
61026101
for k,v in slider_events.items():
61036102
self.aw.moveslider(k,v)
@@ -8853,6 +8852,7 @@ def twoAxisMode(self) -> bool:
88538852
any(self.aw.extraDelta1[:len(self.extratimex)]) or
88548853
any(self.aw.extraDelta2[:len(self.extratimex)])))
88558854

8855+
@pyqtSlot(bool,bool,bool,bool,bool)
88568856
def redraw_keep_view(self, *args:bool, **kwargs:bool) -> None:
88578857
xlimit_min: Optional[float] = None
88588858
xlimit: Optional[float] = None

src/artisanlib/devices.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,7 @@ def scale1ModelChanged(self, i:int) -> None:
16981698
self.scale1NameComboBox.setEnabled(False)
16991699
self.scale1ScanButton.setEnabled(True)
17001700
else:
1701+
self.aw.scale1_name = None
17011702
self.aw.scale1_model = None
17021703
self.scale1NameComboBox.clear()
17031704
self.scale1NameComboBox.setEnabled(False)
@@ -1708,9 +1709,11 @@ def scale1NameChanged(self, i:int) -> None:
17081709
if 0 <= i < len(self.scale1_devices) and self.aw.scale1_model is not None:
17091710
self.aw.scale1_name = self.scale1_devices[i][0]
17101711
self.aw.scale1_id = self.scale1_devices[i][1]
1711-
scale = self.aw.scale_manager.get_scale(self.aw.scale1_model, self.scale1_devices[i][1])
1712+
scale = self.aw.scale_manager.get_scale(self.aw.scale1_model, self.aw.scale1_id, self.aw.scale1_name)
17121713
self.aw.scale_manager.set_scale1(scale)
17131714
if scale is not None:
1715+
scale.set_connected_handler(lambda : self.aw.sendmessageSignal.emit(QApplication.translate('Message', '{} connected').format(self.aw.scale1_name),True,None))
1716+
scale.set_disconnected_handler(lambda : self.aw.sendmessageSignal.emit(QApplication.translate('Message', '{} disconnected').format(self.aw.scale1_name),True,None))
17141717
scale.connect()
17151718
# i == -1 if self.scale1NameComboBox is empty!
17161719
else:
@@ -1743,6 +1746,7 @@ def scale2ModelChanged(self, i:int) -> None:
17431746
self.scale2NameComboBox.setEnabled(False)
17441747
self.scale2ScanButton.setEnabled(True)
17451748
else:
1749+
self.aw.scale2_name = None
17461750
self.aw.scale2_model = None
17471751
self.scale2NameComboBox.clear()
17481752
self.scale2NameComboBox.setEnabled(False)
@@ -1753,7 +1757,7 @@ def scale2NameChanged(self, i:int) -> None:
17531757
if 0 <= i < len(self.scale2_devices) and self.aw.scale2_model is not None:
17541758
self.aw.scale2_name = self.scale2_devices[i][0]
17551759
self.aw.scale2_id = self.scale2_devices[i][1]
1756-
scale = self.aw.scale_manager.get_scale(self.aw.scale2_model, self.scale2_devices[i][1])
1760+
scale = self.aw.scale_manager.get_scale(self.aw.scale2_model, self.aw.scale2_id, self.aw.scale2_name)
17571761
self.aw.scale_manager.set_scale2(scale)
17581762
if scale is not None:
17591763
scale.connect()

src/artisanlib/main.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -9953,15 +9953,29 @@ def eventaction_internal(self, action:int, cmd:str, eventtype:Optional[int]) ->
99539953
if len(args) == 2:
99549954
event_type = int(args[0])
99559955
if 0 < event_type < 5:
9956+
updated:bool = False
99569957
try:
99579958
state = toBool(eval(args[1])) # pylint: disable=eval-used
9958-
self.qmc.specialeventplaybackramp[event_type - 1] = state
9959+
if self.qmc.specialeventplaybackramp[event_type - 1] != state:
9960+
self.qmc.specialeventplaybackramp[event_type - 1] = state
9961+
updated = True
99599962
except Exception: # pylint: disable=broad-except
99609963
value_str = args[1].strip()
99619964
if value_str.lower() in {'yes', 'true', 't', '1'}:
9962-
self.qmc.specialeventplaybackramp[event_type - 1] = True
9963-
else:
9965+
if not self.qmc.specialeventplaybackramp[event_type - 1]:
9966+
self.qmc.specialeventplaybackramp[event_type - 1] = True
9967+
updated = True
9968+
elif self.qmc.specialeventplaybackramp[event_type - 1]:
99649969
self.qmc.specialeventplaybackramp[event_type - 1] = False
9970+
updated = True
9971+
if updated:
9972+
self.qmc.redrawKeepViewSignal.emit(
9973+
False, # recomputeAllDeltas (default: True)
9974+
False, # re_smooth_foreground (default: True)
9975+
True, # takelock (default: True)
9976+
False, # forceRenewAxis (default: False)
9977+
False, # re_smooth_background (default: False)
9978+
)
99659979
except Exception as e: # pylint: disable=broad-except
99669980
_log.exception(e)
99679981
# playback(<int>, <bool>) with <int> from {1,2,3,4} selecting one of the four event types

src/artisanlib/roast_properties.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,9 @@ def __init__(self, parent:QWidget, aw:'ApplicationWindow', activeTab:int = 0) ->
13741374
try:
13751375
from artisanlib.acaia import Acaia
13761376
self.acaia = Acaia(
1377+
model = 1,
1378+
ident = None,
1379+
name = 'Acaia',
13771380
connected_handler=lambda : self.aw.sendmessageSignal.emit(QApplication.translate('Message', '{} connected').format('Acaia'),True,None),
13781381
disconnected_handler=lambda : self.aw.sendmessageSignal.emit(QApplication.translate('Message', '{} disconnected').format('Acaia'),True,None))
13791382
self.acaia.weight_changed_signal.connect(self.ble_weight_changed)
@@ -2569,7 +2572,7 @@ def closeEvent(self, _:Optional['QCloseEvent'] = None) -> None:
25692572
self.aw.qmc.clear_last_picked_event_selection()
25702573
self.aw.eNumberSpinBox.setValue(0)
25712574

2572-
self.aw.qmc.redraw(recomputeAllDeltas=False)
2575+
self.aw.qmc.redraw_keep_view(recomputeAllDeltas=False)
25732576

25742577
self.clean_up()
25752578
super().reject()
@@ -5282,7 +5285,7 @@ def accept(self) -> None:
52825285
try:
52835286
self.aw.qmc.background = not self.aw.qmc.hideBgafterprofileload
52845287
self.aw.qmc.timealign(redraw=False)
5285-
self.aw.qmc.redraw()
5288+
self.aw.qmc.redraw_keep_view()
52865289
except Exception: # pylint: disable=broad-except
52875290
pass
52885291
elif ((not self.aw.qmc.flagon) or

src/artisanlib/scale.py

+35-11
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
# Marko Luther, 2025
1717

1818
import logging
19-
from typing import Final, List, Tuple, Optional
19+
from typing import Final, List, Tuple, Optional, Callable
2020

2121
try:
22-
from PyQt6.QtCore import QObject, pyqtSignal # @UnusedImport @Reimport @UnresolvedImport
22+
from PyQt6.QtCore import pyqtSignal # @UnusedImport @Reimport @UnresolvedImport
2323
except ImportError:
24-
from PyQt5.QtCore import QObject, pyqtSignal # type: ignore # @UnusedImport @Reimport @UnresolvedImport
24+
from PyQt5.QtCore import pyqtSignal # type: ignore # @UnusedImport @Reimport @UnresolvedImport
2525

2626

2727
_log: Final[logging.Logger] = logging.getLogger(__name__)
@@ -38,15 +38,42 @@
3838

3939

4040
# NOTE: this class and all subclasses are not allowed to hold __slots__
41-
class Scale(QObject): # pyright: ignore [reportGeneralTypeIssues] # Argument to class must be a base class
41+
class Scale:
4242

4343
weight_changed_signal = pyqtSignal(int) # delivers new weight in g
4444
battery_changed_signal = pyqtSignal(int) # delivers new batter level in %
4545
disconnected_signal = pyqtSignal() # issued on disconnect
4646

47-
def __init__(self) -> None:
48-
QObject.__init__(self)
47+
def __init__(self, model:int, ident:Optional[str], name:Optional[str]):
48+
self.model = model
49+
self.ident = ident
50+
self.name = name
51+
52+
def set_model(self, model:int) -> None:
53+
self.model = model
54+
55+
def get_model(self) -> int:
56+
return self.model
57+
58+
def set_ident(self, ident:Optional[str]) -> None:
59+
self.ident = ident
60+
61+
def get_ident(self) -> Optional[str]:
62+
return self.ident
63+
64+
def set_name(self, name:Optional[str]) -> None:
65+
self.name = name
4966

67+
def get_name(self) -> Optional[str]:
68+
return self.name
69+
70+
#---
71+
72+
def set_connected_handler(self, connected_handler:Optional[Callable[[], None]]) -> None:
73+
pass
74+
75+
def set_disconnected_handler(self, disconnected_handler:Optional[Callable[[], None]]) -> None:
76+
pass
5077

5178
def connect(self) -> None:
5279
pass
@@ -56,7 +83,6 @@ def disconnect(self) -> None:
5683

5784

5885

59-
6086
class ScaleManager:
6187

6288
__slots__ = [ 'scale1', 'scale2' ]
@@ -85,12 +111,10 @@ def scan_for_scales(model:int) -> ScaleSpecs:
85111
return []
86112

87113
@staticmethod
88-
def get_scale(model:int, address:str) -> Optional[Scale]:
114+
def get_scale(model:int, ident:str, name:str) -> Optional[Scale]:
89115
if model == 0:
90116
from artisanlib.acaia import Acaia
91-
scale = Acaia()
92-
scale.set_address(address)
93-
return scale
117+
return Acaia(model, ident, name)
94118
return None
95119

96120
def set_scale1(self, scale:Optional[Scale]) -> None:

src/includes/Machines/Santoker/Cube_Bluetooth_PID.aset

+1-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ positiveTargetMin=0
156156
positiveTargetRangeLimit=false
157157
svLookahead=5
158158
svMode=0
159-
svSlider=true
159+
svSlider=false
160160
svSliderMax=230
161161
svSliderMin=0
162162
svValue=0

src/requirements-dev.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pytest-cov==6.0.0
2626
#pytest-benchmark==4.0.0
2727
#pytest-mock==3.11.1
2828
hypothesis>=6.129.3
29-
coverage>=7.6.12
29+
coverage>=7.7.0
3030
coverage-badge==1.1.2
3131
codespell==2.4.1
3232
# the following 2 packages are not installed along aiohttp on Python3.12 and make mypy complain

src/requirements.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ psutil==7.0.0
5252
typing-extensions==4.10.0; python_version < '3.8' # required for supporting Final and TypeDict on Python <3.8
5353
protobuf==5.29.3 # v5.30.x drops support for Python 3.8!
5454
numpy==1.24.3; python_version < '3.9' # last Python 3.8 release
55-
numpy==2.2.3; python_version >= '3.9'
55+
numpy==2.2.4; python_version >= '3.9'
5656
scipy==1.10.1; python_version < '3.9' # last Python 3.8 release
5757
scipy==1.15.2; python_version >= '3.9'
5858
wquantiles==0.6
@@ -67,7 +67,7 @@ matplotlib==3.7.3; python_version < '3.9' # last Python 3.8 release
6767
matplotlib==3.10.0; python_version >= '3.9'
6868
jinja2==3.1.5
6969
aiohttp==3.10.11; python_version < '3.9' # last Python 3.8 release
70-
aiohttp==3.11.11; python_version >= '3.9'
70+
aiohttp==3.11.14; python_version >= '3.9'
7171
aiohttp_jinja2==1.6
7272
python-bidi==0.4.2; python_version < '3.9' # last Python 3.8 release
7373
python-bidi==0.6.6; python_version >= '3.9'

wiki/ReleaseHistory.md

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ v3.1.1
5252
- the quick custom event entry using the q, w, e and e key followed by number keys now requires the ENTER/RETURN key to establish the new value. The last entered digit can be removed by using the backspace key. ESC cancels the action.
5353
- improved accuracy on rendering [artisan.plus](https://artisan.plus) blend component weights
5454
- improved Cropster importer
55+
- event replay not at any time ensures that only future events are replayed. As the set of future events may change on moving the background profile, an event can be replayed again. In previous Artisan versions, events did replay only once.
5556

5657
* FIXES
5758
- ensure complete reset to defaults in energy tab loads tab
@@ -63,6 +64,7 @@ v3.1.1
6364
- fixed an issue in event replay where certain events failed to be replayed by temperature
6465
- fixes an issue where the PID Input for external MODBUS/SV PIDs was not correctly persisted
6566
- fixes broken `button` Modbus Command
67+
- fixes communication with some Santoker R Master Series machines ([Issue #1811](../../../issues/1811))
6668

6769
* REMOVALS
6870
- support for the non-standard MODBUS little-endian byte order has been removed

0 commit comments

Comments
 (0)