[0.9.8] - 2026-09-26
Requires Home Assistant 2026.7.0. The live print card needs the companion
app's Live Activity support, added in that release. HACS will not offer this
version to older cores, and a manual install fails setup with an explicit
message. Stay on 0.9.7 if you cannot update.
Added
- Live print notifications for Android and iOS. One self-updating card per printer: countdown to finish, progress bar, current layer, G-code preview as the icon. It ends itself when the print does, replaced by a completion notification carrying a camera snapshot.
- The countdown runs on the phone, so it costs no pushes. State changes push immediately; the rest is rate limited (5 minute refresh, 1% of progress, 30 second floor) because iOS drops Live Activities that update too often.
- Multiple notify targets. Companion-app targets get the full card; every other platform gets title and message only.
- Optional Pause / Resume / Stop buttons, off by default. Stop requires device authentication.
- Tapping opens the camera feed on Android. Set a dashboard path to change that, which is also the only way an iOS tap can deep-link.
- Write your own notification text. Configure → Notifications → Custom text overrides any of the six notifications; clear a field for the built-in wording.
- Placeholders:
{device},{filename},{progress},{layer},{total_layers},{eta},{elapsed},{filament},{nozzle},{bed},{state},{error_code},{error_key},{minutes}. Each field lists the ones it accepts and rejects the rest when you submit. Values come from the frame that triggered the notification; nothing is estimated. [...]marks an optional section that disappears whole while a value inside it is unknown, so{filename}[ -- {eta} left]loses the tail rather than leaving a dangling separator. Double them ([[,]]) for a literal bracket.0counts as a value.- The title is always the printer's name: Android fixes a live card's title when it starts.
- Placeholders:
- Bus events
ha_creality_ws_print_started,_print_finished,_print_errorand_print_stopped, fired whether or not a notify target is configured. They carryentry_id,host,device_name,filename,progress,layer,total_layers,left_secondsanderr_code. This is the supported way to write your own text in a language other than your server's. - A cancelled or aborted print now says so, naming the file and the percentage it stopped at, however the stop was made: Home Assistant, the Creality app, or the printer's own screen. Shares the Notify when a print ends toggle.
- Notification text is translated, Spanish included. It follows the server language (Settings → System → General), not each user's, because an integration is never told which user a notification is for. Use the bus events for per-user text.
snapshot_supportedattribute on camera entities, saying whether a still image is possible at all.- Filament estimate sensors:
expected_material_length(cm),expected_material_weight(g) andfilament_consumption(%, used over expected, uncapped and deliberately not the same as print progress). The figures are the slicer's own, read from the printer when the running file changes; nothing is derived from an assumed diameter or density. Firmware that does not answer the request leaves the three uncreated rather than permanently unknown. Weight is blank for files the printer did not slice itself, and for multi-material jobs, which are not broken down per filament (#122). - Edit filament from the CFS card. Each slot tile gains an edit dialog for material type, name, vendor, colour, temperature range and pressure advance. It writes, then asks the printer to re-report, so the tile only changes once the write lands.
- Names the box and slot it will write to, and says so when it inferred them from the card layout.
- Disabled while the printer is busy, and for cards whose entities span more than one printer.
- Multi-colour spools show their colour read-only; the other fields still save.
- Colour presets: Creality's palette plus your own, kept in your browser.
ha_creality_ws.set_cfs_materialservice for writing slot metadata from automations. Only supplied fields change; omit one to keep the printer's value.- New CFS slot attributes:
box_id,slot_id,min_temp,max_temp,pressure. Not every printer reports the temperatures, so treatnullas unknown rather than zero. spool_keyattribute on every CFS slot (closes #117 part 2). The printer'srfidis a material id, not a tag serial, so spools of the same material shared it and external trackers such as spoolman-sync could not tell them apart.spool_keycombines it with the normalised colour. It is derived, not new telemetry: two genuinely identical spools still collide, and rawrfidis passed through unchanged.color_hex_rawattribute: the printer's original colour string, alongside the correctedcolor_hex.- Third card display mode,
box: a photo of the CFS unit with a spool overlay per bay. Needs a mapped four-slot box, and falls back to the full view. - Optional go2rtc RTSP port under Configure → Camera, for a go2rtc on a non-default RTSP port.
0keeps auto-detection. - Fan entities documented (#114).
fan.*_model_fan/*_case_fan/*_side_fanhave always supported on/off and speed; the README now covers them, with a chamber-too-hot automation example.
Changed
- ⚠️
sensor.<printer>_systemhas been removed. It was a byte-identical duplicate ofsensor.<printer>_modeland is removed from the registry on upgrade. Point any dashboard or automation atsensor.<printer>_model. - ⚠️ The card's
compact_viewoption becameview_mode(full|compact|box). Dashboards migrate automatically; no action needed. - The Notification Device setting became Notification targets and accepts several. Existing configs migrate on load, and the old value stays on disk so a downgrade works. The live card is opt-in.
- The Notify when Completed option is now Notify when a print ends and covers stopped prints too. If you had it on, you will now also hear about cancellations.
- Each options page saves when you submit it. The old Save and apply menu item is gone, so closing the dialog normally no longer discards everything. Done and the window close button both just close.
- Changing only notification settings no longer reloads the integration, so the connection stays up and no entity blinks while you tune text. Every other page still reloads.
- Notification messages name the file, not its full path. Sensor attributes still publish the raw value.
- The built-in notification wording is shorter and leads with the printer:
Print "3DBenchy.gcode" completed successfully!becameK1C finished.Custom text reproduces the old phrasing exactly if you preferred it. - The camera-mode dropdown and the camera and power-switch help text are translatable; they were hardcoded English.
- Notifications are no longer awaited inside the WebSocket receive loop, which could stall the connection and flicker every entity unavailable.
- The card only re-renders when something it displays changed, instead of on every state update.
- Printer status is derived in one place, so the card and the service cannot disagree about whether the printer is busy.
- The bundled CFS image is 17 kB instead of 509 kB (WebP).
www/was 143 kB, so a PNG would have quadrupled every install.
Fixed
- Late-discovered entities were scheduled from the wrong thread. A CFS unit or chamber appearing after setup created entities from an executor thread via the non-thread-safe
hass.loop.call_soon, racing the event loop. - A printer reporting a bare error code took every print-status attribute down with it. Some firmware sends
erras a number rather than{errcode: N}; the attribute builder called.get()on it and loststate_raw,left_time_sand the rest to anAttributeError. - Pause was sent to printers that had already stopped, because the coordinator counted any job with a file name and a progress value as printing. Pause and resume now use the same state derivation as everything else.
- A queued pause or resume outlived its job and fired as the next print started. Queued requests are dropped when the job ends.
- Stopping a print and re-running the same file left notifications dead: the new-job check required a completion, which a stopped print never produces, so the second run got nothing.
- The G-code preview vanished while a print was paused, and stayed hidden for the rest of a print once the printer set an error code it never cleared.
- The diagnostic service was re-registered on every printer setup, and dumps always reported the Home Assistant version as
unknown. Dumps now carry the real version and a UTC timestamp. - The legacy light switch and fan percentage controls could never be created, because the migration removing them ran before platform setup. The dead platform is gone; the cleanup migration stays.
- The camera advertised a
CameraEntityFeature.ON_DEMANDflag that does not exist. It declaresSTREAM, which is what native WebRTC needs. - Every field in the printer card's Style Editor was labelled with its raw config key. The label function sat behind
if (form.computeLabel), whichha-formnever satisfies, so the translated labels were never used. - Saving a colour in the Style Editor changed colours you had not touched. The picker discarded the alpha on the way in and hardcoded
0.9on the way out, so Save rewrote every colour to 90% opacity: the stop button's.95and the light-off grey's.35both became.90. Three-digit hex like#abcalso came back invalid. Both directions now keep the alpha and understand shorthand. - The printer card had branches for
resumingandpausing, states the integration never reports, so a colour and two icon choices were dead. - A failure to serve the dashboard cards was logged at debug level, so the cards 404'd with nothing in the log. It is a warning now.
- A notify target that could not be routed failed silently. The field accepts free text, so a typo simply did nothing; it now logs a warning.
- Notification state stopped tracking when no target was configured, so enabling notifications mid-print could fire a spurious "completed".
- The camera snapshot is skipped where it cannot work, rather than rendering an empty grey box. Applies to K2 direct-WebRTC cameras and to the G-code preview when the printer has none.
- Camera stream API (HLS, recording, casting) failed with
TypeError: 'str' object is not callable(closes #116, thanks @Raymondvb1985). A synchronousstream_sourceproperty returning the go2rtc stream name shadowed core'sasync def stream_source(). It is now an async method returning an RTSP URL, with the port detected automatically (18554built-in,8554stand-alone) and overridable. Frontend WebRTC playback is unchanged. - Wrong CFS spool colour: the leading pad character was kept (closes #113 and #117 part 1, thanks @raf802). Creality tags store seven hex characters, one of padding then
RRGGBB, so0ffffffwas read as#0fffff. The last six digits are used now and normalised to lowercase; non-hex values such asN/Aare left alone. The CFS card already compensated, so its rendering is unchanged. Generic Generic PLAin the filament label (closes #115). The vendor is only prepended when the name does not already start with it.- ⚠️ A vendor the printer never reported is no longer invented. A slot reporting only
PETGwas labelledGeneric PETG; it now falls back to the material name, then the type. Update any template matching the old value. An empty slot reportsUnknownrather than'Generic '.
- ⚠️ A vendor the printer never reported is no longer invented. A slot reporting only
- "Print completed" notification on every Home Assistant restart (closes #112, thanks @chairstacker). The printer reports the finished job indefinitely, so a fresh coordinator read it as new. The first frame carrying print state is now recorded silently, and the same guard covers the error, runout and minutes-to-end notifications.
- Minutes-to-end notification never fired: it read
printTimeLeft, but the printer streamsprintLeftTime. - Chamber-target control missing after a restart while the printer was off.
number.<printer>_chamber_targetwas only created ifmaxBoxTemphad already arrived, and nothing recreated it later. It now uses the capability cached during onboarding, and late-arriving telemetry fires a discovery signal so capabilities create their entities without a restart. - Dynamic CFS discovery relied on a swallowed error:
async_add_entitiesis a synchronous callback, so awaiting it raisedTypeErrorevery time, discarded because nothing held the future. - Completion notification only ever arrived once per file name, so reprinting the same file never notified again. It re-arms below 90% progress, or on a job-clock restart for a print that never got near the end. (Re-arming on any dip below 100% would fire twice per print: the printer rounds up to 100 a second early, then reports 99 once more.)
Internal
- The coordinator holds its
ConfigEntryrather than the entry id, removing fiveasync_get_entryround trips. - The options flow no longer receives the config entry Home Assistant already gives it.
typing.Optional/List/Dictreplaced withX | None,list,dict;Callableand friends fromcollections.abc.KEntity's reads of the onboarding cache are covered by tests; they had none.- Removed the backwards-compatibility code the 2026.7 minimum makes unreachable: six
try/except ImportErrorblocks, the pre-2024.7 static-path API, the pre-dataclass Lovelace resources access, and positional-argument handling infan.turn_on. Two import-time shims stay so an ancient core still gets the friendly message. - Deleted code nothing called: six functions, a write-only parameter, four constants, a no-op
if, and a test asserting"logistics" not in manifest.json. - Modernised deprecated APIs:
FlowResult→ConfigFlowResult,AddEntitiesCallback→AddConfigEntryEntitiesCallback,dt_util.utcnow(),get_running_loop(). - The test suite no longer depends on collection order: shared stubs moved to
conftest.py, so every module runs on its own. LATE_DISCOVERY_FIELDSinconst.pylists the telemetry fields that gate entity creation; the coordinator fires one discovery signal the first time each appears.- New shared CFS helpers in
utils.py(normalize_color_hex,format_filament_label,build_spool_key) replacing duplicated inline logic across three sensors. - Regression tests for all of the above.
Notes
- The live card needs iOS 17.2+ or Android 16+. Older phones still get a notification that replaces itself in place, without the timer and progress bar.
- iOS ends any Live Activity after 8 hours, an Apple limit no app can extend. Past that the card continues as an ordinary notification with the remaining time in the text, and the completion notification still arrives. Android has no such limit.
- The G-code preview does not appear in an iOS Live Activity; that layout has no image slot. It shows on Android and on the plain notifications on both platforms.
Notes for anyone with CFS hardware
Creality does not document the modifyMaterial command. The payload shape comes from @buzato's testing against real hardware and is verified here against the bundled simulator, but two details are unconfirmed: the printer streams colours as seven hex characters yet appears to accept six on write, and the rfid field name is inferred from telemetry. Every write logs the outgoing payload and what the printer reports back. If a material edit does something unexpected, please open an issue with that part of your debug log.
Test server (tools/creality_printer_test_server.py)
Dev tooling only. Several fidelity gaps made the simulator disagree with real hardware.
- Fan telemetry used names the integration never reads (
caseFanrather thancaseFanPctand so on), so fan entities looked stuck at 0. It now emits the real names, honoursM106 P<ch> S<0-255>overgcodeCmd, and leaves a manually driven fan alone. - Video was answered as VP8, which HA's
streamcomponent cannot package into HLS, so the playlist blocked forever. It answers H.264 first now (--prefer-codec). - Keyframes were up to 25 s apart, inherited from libx264's 250-frame default, long after HA's stream worker gives up. Video is pre-encoded with a 1 s GOP (
--video-source auto). - Healthy WebRTC sessions were killed after 60 s by an unconditional sleep-then-close. Teardown follows the connection state.
--deterministicremoves temperature, fan and position randomness so telemetry is reproducible. Progress fields stay time-derived.--cfs-variant edgeadds the awkward payloads: a six-character colour, a vendorless slot, a multi-colour spool, sharedrfidvalues, and an empty external slot.- Test-control endpoints (
POST /test/set,/test/reset,/test/cfs,GET /test/state) pin any telemetry field, so notification scenarios can be driven in seconds. Real printers have no such endpoints. - Log lines carry timestamps, and the offer/answer SDP is dumped under
--debug.