Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1016665
CYBER: Skywalker V2 (Cyberroaster) package
neuralldev Jun 28, 2026
05d9699
CYBER: core hooks for Skywalker devices 207/208
neuralldev Jun 28, 2026
41fd04d
CYBER: fork tooling ignores
neuralldev Jun 28, 2026
3fcfda5
CYBER: index guard-rail script
neuralldev Jun 28, 2026
cbe015f
CYBER: ignore REBASE.md working doc
neuralldev Jun 28, 2026
5febbb3
CYBER: AppVeyor CI for the fork
neuralldev Jun 28, 2026
964f4c3
CYBER: Skywalker V2 (Cyberroaster) package
neuralldev Jun 28, 2026
6e57b50
CYBER: core hooks for Skywalker devices 207/208
neuralldev Jun 28, 2026
198cd22
Merge branch 'artisan-roaster-scope:master' into master
neuralldev Jul 2, 2026
8243596
Merge branch 'artisan-roaster-scope:master' into master
neuralldev Jul 12, 2026
46597d4
Skycommand BLE support for Artisan Roaster Scope
neuralldev Jul 13, 2026
55c808d
Merge branch 'artisan-roaster-scope:master' into master
neuralldev Jul 13, 2026
526e080
PR Skycommand
neuralldev Jul 13, 2026
7e1cb19
Fix Ruff error message
neuralldev Jul 13, 2026
cd8ffa7
updates for artisan checks
neuralldev Jul 13, 2026
8e7cd2a
Merge branch 'feature/skywalker-cyberroaster'
neuralldev Jul 13, 2026
b7ddaec
forgotten commits
neuralldev Jul 13, 2026
ad517a4
codespell & pytest
neuralldev Jul 13, 2026
38905ca
Merge branch 'feature/skywalker-cyberroaster'
neuralldev Jul 13, 2026
93af980
changed method to be compatible wiht artisan syntax check
neuralldev Jul 13, 2026
59c8c38
yntax erge branch 'feature/skywalker-cyberroaster'
neuralldev Jul 13, 2026
d841802
Skycommand logs for debug purpose
neuralldev Jul 13, 2026
9163929
Merge branch 'feature/skywalker-cyberroaster'
neuralldev Jul 13, 2026
f65bb80
skycommand telemetry syntax update
neuralldev Jul 14, 2026
5dbe37e
Merge branch 'feature/skywalker-cyberroaster'
neuralldev Jul 14, 2026
9626bf7
skycommand debug
neuralldev Jul 14, 2026
341deb6
Merge branch 'feature/skywalker-cyberroaster'
neuralldev Jul 14, 2026
747a0e0
debug
neuralldev Jul 14, 2026
5be2bc8
Merge branch 'feature/skywalker-cyberroaster'
neuralldev Jul 14, 2026
94d6cd5
change send command
neuralldev Jul 14, 2026
2bbecc5
Merge branch 'feature/skywalker-cyberroaster'
neuralldev Jul 14, 2026
c7e2785
changed logging
neuralldev Jul 21, 2026
e4e53f1
Merge branch 'feature/skywalker-cyberroaster'
neuralldev Jul 21, 2026
c0a754e
git commit
neuralldev Jul 27, 2026
04ce361
Merge branch 'feature/skywalker-cyberroaster'
neuralldev Jul 27, 2026
f40c448
Merge remote-tracking branch 'upstream/master'
neuralldev Jul 27, 2026
67c68eb
bad merge from artisan main branch
neuralldev Jul 28, 2026
3165fd3
Merge remote-tracking branch 'upstream/master'
neuralldev Jul 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ environment:
PYUPGRADE_WIN_VER: 3.14.6
#PYUPGRADE_MACOS_V: 3.12.1 # when upgrading see the notes in the deploy phase

ARTISAN_KEY:
secure: 4BswEsF7OdFwgn3gMbnjkmCyAfJtmnak+3rmXzggbpitK0txN22SERo/sIh3wxse1L3rFDIPc+NDEEagMTFsODcmM8/gsclDbeJA4S5UTIY=
## CYBER ## ARTISAN_KEY and GITHUB_TOKEN are set as secure variables in this
## CYBER ## fork's AppVeyor *project settings* (Settings -> Environment), not
## CYBER ## here. The upstream `secure:` blob is account-bound to Marko and
## CYBER ## cannot decrypt on the fork account. See CI-SETUP.md (local doc).

matrix:
- job_name: windows
Expand Down
17 changes: 17 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"permissions": {
"allow": [
"Bash(python3 -c ' *)",
"Bash(.venv/bin/python --version)",
"Bash(.venv/bin/python -c \"import sys; print\\(sys.executable\\); print\\(sys.base_prefix\\)\")",
"Bash(../.venv/bin/python -c ' *)",
"Bash(./.venv/bin/python -c ' *)",
"Bash(python3 -m py_compile help/eventbuttons_help.py help/eventsliders_help.py)",
"Bash(git commit *)",
"Bash(git rebase *)",
"Bash(python3 tools/check_cyber_indices.py)",
"Bash(echo \"exit=$?\")",
"Bash(git fetch *)"
]
}
}
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,8 @@ Thumbs.db
#src/translations/*.qm
#src/help/[!_]*.py
#doc/help_dialogs/Output_html/[!_]*.html
CI-SETUP.md
CLAUDE.md
HANDOFF.md
PR_DESCRIPTION.md
REBASE.md
48 changes: 48 additions & 0 deletions PR_DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
## Summary

Adds support for the **Skywalker V2 "Cyberroaster"** (ITOP FIR/NIR radiant electric drum
roaster) as a new Artisan device, over a **TC4-over-BLE** transport.

The change is **strictly additive and guarded**: no existing Artisan behavior changes when
the device is not selected. All device-specific logic lives in a new self-contained module
`src/artisanlib/skywalker.py`; the edits to core files are append-only and confined to
wiring the new device into Artisan's hardcoded device list.

## What's added

Two device entries are registered (ids must match their position in `devicefunctionlist`):

- **207 — `Skywalker BT/ET`**: main device, reads Bean Temp / Env Temp over BLE.
- **208 — `+Skywalker Burner/Air`**: extra device, echoes the OT1/OT2 duty values
(burner / airflow) as percentages.

Both are `nonserial` (BLE, no serial-port plumbing). The `skywalker(OTn, val)` alarm command
lets roast events drive burner/airflow.

## Files touched

| File | Change |
|------|--------|
| `src/artisanlib/skywalker.py` | **New** — self-contained TC4-over-BLE device class |
| `src/artisanlib/comm.py` | `SKYWALKER_BTET` (207) + `SKYWALKER_PF` (208), appended to `devicefunctionlist` |
| `src/artisanlib/canvas.py` | Device-list entries, `nonserial` flag, BLE connect/disconnect |
| `src/artisanlib/devices.py` | Main-device selection for device 207 |
| `src/artisanlib/main.py` | `aw.skywalker` attribute, settings load/save, `skywalker(OTn,val)` alarm command |
| `src/help/eventbuttons_help.py`, `src/help/eventsliders_help.py` | `skywalker()` command documentation |
| `src/artisan.pro` | Register the new source file |

## Design notes / things to review

- **Device indices (207/208):** these *must* equal their position in
`serialport.devicefunctionlist`. They are correct against the current `master` (last native
entry being `+MQTT 1112` at 206). If other devices have landed since, the indices and the
matching entries in `canvas.py` / `devices.py` / `comm.py` need to be shifted together.
- **Temperature units:** the Skywalker reports Celsius; values are converted with
`fromCtoFstrict` only when `qmc.mode == 'F'`.
- **Hardware behavior (FIR/NIR specifics):** unlike classical roasters, **ET < BT is normal**
on this machine (BT rises faster than ET right after the turning point). RoR_BT/RoR_ET
ratios are not meaningful here. AirWave airflow above ~30% cools the drum.
- **Transport choice:** Bluetooth SPP is not viable on this firmware (it forces a PIN re-pair
after every disconnect), so TC4-over-BLE is used as the reliable transport.
- Every fork-specific line in core files is marked with a `## CYBER ##` comment for easy

2 changes: 2 additions & 0 deletions src/artisan.pro
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ SOURCES = \
artisanlib/rubasse.py \
artisanlib/s7port.py \
artisanlib/sampling.py \
artisanlib/skywalker.py \
artisanlib/syyble.py \
artisanlib/statistics.py \
artisanlib/transposer.py \
artisanlib/wheels.py \
Expand Down
41 changes: 38 additions & 3 deletions src/artisanlib/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,10 @@ def __init__(self, parent:QWidget, dpi:int, locale:str, aw:'ApplicationWindow')
'+MQTT 78', #204
'+MQTT 910', #205
'+MQTT 1112', #206
'Skywalker BT/ET', #207 ## CYBER ## Cyberroaster (Skywalker V2)
'+Skywalker Burner/Air', #208 ## CYBER ## OT1/OT2 duty echoes
'Skycommand BT/ET', #209 ## SKYBLE ## Skywalker V2 through SkyBLE dongle
'+Skycommand Burner/Air', #210 ## SKYBLE ## OT1/OT2 duty echoes
]

# ADD DEVICE:
Expand Down Expand Up @@ -1109,7 +1113,11 @@ def __init__(self, parent:QWidget, dpi:int, locale:str, aw:'ApplicationWindow')
195: (True, True), # +Yocto Meteo Pressure
198: (True, True), # +Orbiter Sound/Drum
199: (True, True), # +Orbiter Damper/Heater
200: (True, True) # +Orbiter Air/RoR
200: (True, True), # +Orbiter Air/RoR
207: (False, False), # Skywalker BT/ET ## CYBER ## Cyberroaster is BLE (nonserial)
208: (True, True), # +Skywalker Burner/Air ## CYBER ## OT1/OT2 duty echoes
209: (False, False), # SkyBLE BT/ET ## SKYBLE ## Skywalker V2 through SkyBLE dongle
210: (True, True), # +Skycommand Burner/Air ## SKYBLE
}

# ADD DEVICE:
Expand Down Expand Up @@ -1183,10 +1191,11 @@ def __init__(self, parent:QWidget, dpi:int, locale:str, aw:'ApplicationWindow')
175, # Thermoworks BlueDOT
176, # Aillio Bullet R2
194, # +Yocto Meteo Hum/Temp
195 # +Yocto Meteo Pressure
195, # +Yocto Meteo Pressure
207, # Skywalker BT/ET ## CYBER ## Cyberroaster is BLE (nonserial)
209, # SkyBLE BT/ET ## SKYBLE ## Skywalker V2 through SkyBLE dongle
]


# ADD DEVICE:
# ids of special devices certain input filters should not be applied
self.specialDevices : Final[list[int]] = [
Expand Down Expand Up @@ -13242,6 +13251,22 @@ def OnMonitor(self) -> None:
disconnected_handler=lambda : self.aw.sendmessageSignal.emit(QApplication.translate('Message', '{} disconnected').format('Santoker R'),True,None))
self.aw.santokerR.setLogging(self.device_logging)
self.aw.santokerR.start(case_sensitive=False)
elif self.device == 207:
## CYBER ## connect Skywalker V2 (Cyberroaster)
from artisanlib.skywalker import Skywalker
self.aw.skywalker = Skywalker(
connected_handler=lambda : self.aw.sendmessageSignal.emit(QApplication.translate('Message', '{} connected').format('Skywalker'),True,None),
disconnected_handler=lambda : self.aw.sendmessageSignal.emit(QApplication.translate('Message', '{} disconnected').format('Skywalker'),True,None))
self.aw.skywalker.setLogging(self.device_logging)
self.aw.skywalker.start(case_sensitive=False)
elif self.device == 209:
## SKYBLE ## connect Skywalker V2 through BLE (SkyBLE)
from artisanlib.skyble import SkyBLE
self.aw.skyble = SkyBLE(
connected_handler=lambda : self.aw.sendmessageSignal.emit(QApplication.translate('Message', '{} connected').format('SkyBLE'),True,None),
disconnected_handler=lambda : self.aw.sendmessageSignal.emit(QApplication.translate('Message', '{} disconnected').format('SkyBLE'),True,None))
self.aw.skyble.setLogging(self.device_logging)
self.aw.skyble.start(case_sensitive=False)
elif self.device == 175:
# connect Thermoworks BlueDOT
from artisanlib.bluedot import BlueDOT
Expand Down Expand Up @@ -13451,6 +13476,16 @@ def OffMonitorCloseDown(self, respectAlwaysON:bool, wasRecording:bool) -> None:
self.aw.santokerR.stop()
self.aw.santokerR = None

## CYBER ## disconnect Skywalker V2 (Cyberroaster)
if not bool(self.aw.simulator) and self.device == 207 and self.aw.skywalker is not None:
self.aw.skywalker.stop()
self.aw.skywalker = None

## SKYBLE ## disconnect Skywalker V2 through BLE (SkyBLE)
if not bool(self.aw.simulator) and self.device == 209 and self.aw.skyble is not None:
self.aw.skyble.stop()
self.aw.skyble = None

if self.aw.lebrew_roastseeNEXT is not None:
self.aw.lebrew_roastseeNEXT.stop()
self.aw.lebrew_roastseeNEXT = None
Expand Down
52 changes: 51 additions & 1 deletion src/artisanlib/comm.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,11 @@ def __init__(self, aw:'ApplicationWindow') -> None:
self.MQTT_56, #203
self.MQTT_78, #204
self.MQTT_910, #205
self.MQTT_1112 #206
self.MQTT_1112, #206
self.SKYWALKER_BTET, #207 ## CYBER ## index must == device id
self.SKYWALKER_PF, #208 ## CYBER ## burner/airflow echoes
self.SKYBLE_BTET, #209 ## SKYBLE ## index must == device id
self.SKYBLE_PF, #210 ## SKYBLE ## burner/airflow echoes
]
#string with the name of the program for device #27
self.externalprogram:str = 'test.py'
Expand Down Expand Up @@ -2017,6 +2021,52 @@ def MQTT_1112(self) -> tuple[float,float,float]:
tx,t2,t1 = self.MQTTread(4)
return tx,t2,t1

## CYBER ## Skywalker V2 (Cyberroaster) BT/ET reader, device id 207
def SKYWALKER_BTET(self) -> tuple[float,float,float]:
tx = self.aw.qmc.timeclock.elapsedMilli()
if self.aw.skywalker is not None:
t2,t1 = self.aw.skywalker.getBTET() # (BT, ET)
if self.aw.qmc.mode == 'F':
t1 = fromCtoFstrict(t1)
t2 = fromCtoFstrict(t2)
else:
t1 = t2 = -1
return tx,t1,t2 # time, ET (chan2), BT (chan1)

## CYBER ## Skywalker V2 (Cyberroaster) burner/airflow echoes, device id 208
def SKYWALKER_PF(self) -> tuple[float,float,float]:
tx = self.aw.qmc.timeclock.elapsedMilli()
if self.aw.skywalker is not None:
t2,t1 = self.aw.skywalker.getPF() # (burner, airflow)
else:
t1 = t2 = -1
return tx,t1,t2 # time, Airflow (chan2), Burner (chan1)

## SKYBLE ## Skywalker V2 (Cyberroaster) BT/ET reader, device id 207
def SKYBLE_BTET(self) -> tuple[float,float,float]:
tx = self.aw.qmc.timeclock.elapsedMilli()
if self.aw.skyble is not None:
_log.error('SKYBLE_BTET() reading BT/ET from Skycommand device')
t2,t1 = self.aw.skyble.getBTET() # (BT, ET)
_log.error('SKYBLE_BTET() reading BT/ET from Skycommand device: BT=%s, ET=%s', t2, t1)
if self.aw.qmc.mode == 'F':
t1 = fromCtoFstrict(t1)
t2 = fromCtoFstrict(t2)
else:
t1 = t2 = -1
return tx,t1,t2 # time, ET (chan2), BT (chan1)

## SKYBLE ## Skywalker V2 (Cyberroaster) burner/airflow echoes, device id 208
def SKYBLE_PF(self) -> tuple[float,float,float]:
tx = self.aw.qmc.timeclock.elapsedMilli()
if self.aw.skyble is not None:
_log.error('SKYBLE_PF() reading burner/airflow from Skycommand device')
t2,t1 = self.aw.skyble.getPF() # (burner, airflow)
_log.error('SKYBLE_PF() reading burner/airflow from Skycommand device: Burner=%s, Airflow=%s', t2, t1)
else:
t1 = t2 = -1
return tx,t1,t2 # time, Airflow (chan2), Burner (chan1)

#

def YOCTO_thermo(self) -> tuple[float,float,float]:
Expand Down
17 changes: 16 additions & 1 deletion src/artisanlib/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -4630,6 +4630,17 @@ def okEvent(self) -> None: # pyright: ignore [reportGeneralTypeIssues] # Code is
##########################
#### DEVICE 206 is +WebSocket 1112 but +DEVICE cannot be set as main device
##########################
##########################
#### DEVICE 207 is Skywalker BT/ET (Cyberroaster) ## CYBER ##
elif meter == 'Skywalker BT/ET':
self.aw.qmc.device = 207
message = QApplication.translate('Message','Device set to {0}').format(meter)
##########################
#### DEVICE 209 is Skywalker BT/ET ## SKYBLE ##
elif meter == 'Skycommand BT/ET':
self.aw.qmc.device = 209
message = QApplication.translate('Message','Device set to {0}').format(meter)
##########################

# ADD DEVICE:

Expand Down Expand Up @@ -4855,7 +4866,11 @@ def okEvent(self) -> None: # pyright: ignore [reportGeneralTypeIssues] # Code is
1, # 203
1, # 204
1, # 205
1 # 206
1, # 206
1, # 207 ## CYBER ## Skywalker (nonserial, value unused)
1, # 208 ## CYBER ## +Skywalker Burner/Air
1, # 209 ## SKYBLE ## Skycommand (nonserial, value unused)
1, # 210 ## SKYBLE ## +Skycommand Burner/Air
]
#init serial settings of extra devices
for i, _ in enumerate(self.aw.qmc.extradevices):
Expand Down
Loading