aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-10 10:42:37 +0100
committerDanilo M. <danix@danix.xyz>2026-11-10 10:42:37 +0100
commitb4893a1d51763a81380d8cff35c393236c379ec5 (patch)
tree41df161211e4857146d3d934e2eea8638b26ad31
parente3583dca51ce4b1e5d045147d939a0544622b194 (diff)
downloaddeck-it-b4893a1d51763a81380d8cff35c393236c379ec5.tar.gz
deck-it-b4893a1d51763a81380d8cff35c393236c379ec5.zip
fix(k1c): read the part fan from Creality's output_pin fan0
Creality's K1C firmware drives the part cooling fan as `output_pin fan0` and reports the standard `fan` object with speed null, so the tab showed no fan speed. The standard object still wins when it has a value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--deckit/modules/k1c.py11
-rw-r--r--tests/test_k1c.py7
2 files changed, 16 insertions, 2 deletions
diff --git a/deckit/modules/k1c.py b/deckit/modules/k1c.py
index bc7c439..8481e91 100644
--- a/deckit/modules/k1c.py
+++ b/deckit/modules/k1c.py
@@ -31,7 +31,7 @@ from ..module import Module, ModuleError
QUERY = ("/printer/objects/query?webhooks&print_stats&virtual_sdcard"
"&extruder=temperature,target,power&heater_bed=temperature,target,power"
- "&fan=speed&toolhead=position,homed_axes&gcode_move=speed_factor")
+ "&fan=speed&output_pin%20fan0=value&toolhead=position,homed_axes&gcode_move=speed_factor")
BACKOFF = 10 # seconds between tries while the printer is off
@@ -60,6 +60,10 @@ def _thumbnail(base: str, filename: str, meta: dict) -> str | None:
return f"{base}/server/files/gcodes/{urllib.parse.quote(path)}"
+def _first(*values):
+ return next((v for v in values if v is not None), None)
+
+
def summarize(status: dict, snapshot: str | None = None) -> dict:
"""The panel's state from Moonraker's object status."""
ps = status.get("print_stats") or {}
@@ -89,7 +93,10 @@ def summarize(status: dict, snapshot: str | None = None) -> dict:
"filament": ps.get("filament_used"),
"extruder": heater("extruder"),
"bed": heater("heater_bed"),
- "fan": (status.get("fan") or {}).get("speed"),
+ # Creality's firmware drives the part fan as `output_pin fan0` and
+ # leaves the standard `fan` object empty.
+ "fan": _first((status.get("fan") or {}).get("speed"),
+ (status.get("output_pin fan0") or {}).get("value")),
"position": ((status.get("toolhead") or {}).get("position") or [None] * 3)[:3],
"speed_factor": (status.get("gcode_move") or {}).get("speed_factor"),
"snapshot": snapshot,
diff --git a/tests/test_k1c.py b/tests/test_k1c.py
index b82869e..47c25ec 100644
--- a/tests/test_k1c.py
+++ b/tests/test_k1c.py
@@ -80,6 +80,13 @@ class K1C(unittest.TestCase):
m.state(), m.state()
self.assertEqual(sum("/server/files/metadata" in u for u, _ in fake.calls), 1)
+ def test_part_fan_falls_back_to_crealitys_output_pin(self):
+ st = k1c.summarize({"fan": {}, "output_pin fan0": {"value": 0.5}})
+ self.assertEqual(st["fan"], 0.5)
+ # What the K1C actually reports: the key present, the value null.
+ self.assertEqual(k1c.summarize({"fan": {"speed": None}, "output_pin fan0": {"value": 1.0}})["fan"], 1.0)
+ self.assertEqual(k1c.summarize({"fan": {"speed": 0.3}, "output_pin fan0": {"value": 1}})["fan"], 0.3)
+
def test_off_backs_off_instead_of_polling_every_tick(self):
fake = Fake(down=True)
m = k1c.make({"k1c": {"url": URL}, "http": fake})