anycubic-lan 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ build/
6
+ dist/
7
+ *.egg-info/
8
+ .coverage
9
+ coverage.xml
10
+ htmlcov/
11
+ .pytest_cache/
12
+ .mypy_cache/
13
+ .ruff_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nino Bondonno
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.5
2
+ Name: anycubic-lan
3
+ Version: 0.1.0
4
+ Summary: Async client for Anycubic 3D printers in LAN Mode (local MQTT, no cloud).
5
+ Project-URL: Source, https://github.com/Nino6689/anycubic-lan
6
+ Project-URL: Issues, https://github.com/Nino6689/anycubic-lan/issues
7
+ Author: Nino Bondonno
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: 3d-printer,anycubic,asyncio,home-assistant,mqtt
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Home Automation
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.13
21
+ Requires-Dist: aiohttp>=3.10
22
+ Requires-Dist: cryptography>=43
23
+ Requires-Dist: paho-mqtt>=2.1
24
+ Provides-Extra: test
25
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'test'
26
+ Requires-Dist: pytest-cov>=5; extra == 'test'
27
+ Requires-Dist: pytest>=8; extra == 'test'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # anycubic-lan
31
+
32
+ Talk to an Anycubic 3D printer in **LAN Mode**, directly on your network: no
33
+ Anycubic account, no cloud.
34
+
35
+ A printer with LAN Mode switched on runs its own MQTT broker. This library
36
+ performs the printer's handshake to get the broker credentials, connects,
37
+ asks for state, parses the reports into typed objects, and sends the commands
38
+ the printer accepts locally. It is fully async and has no cloud code and no
39
+ bundled certificates or keys.
40
+
41
+ Tested on a Kobra S1 (firmware 2.7.2.7). Reports from a Kobra X (firmware
42
+ 2.0.2.2) informed the parser.
43
+
44
+ ## Install
45
+
46
+ ```sh
47
+ pip install anycubic-lan
48
+ ```
49
+
50
+ Python 3.13 or later. Dependencies: `aiohttp`, `paho-mqtt`, `cryptography`.
51
+
52
+ Switch LAN Mode on at the printer first: **Settings → Network → LAN Mode**.
53
+ While LAN Mode is on the printer is not connected to the Anycubic cloud.
54
+
55
+ ## Usage
56
+
57
+ ```python
58
+ import asyncio
59
+
60
+ import aiohttp
61
+
62
+ from anycubic_lan import AnycubicLanClient, AnycubicLanError, handshake
63
+
64
+
65
+ async def main() -> None:
66
+ async with aiohttp.ClientSession() as session:
67
+ info = await handshake(session, "192.168.1.50") # the printer's IP
68
+ print(info.model_name, info.device_id)
69
+
70
+ client = AnycubicLanClient(info)
71
+ client.add_state_listener(
72
+ lambda state: print(state.status, state.temperatures.nozzle, state.job)
73
+ )
74
+ # Fires on changes after connect(): False when lost, True when restored.
75
+ client.add_connection_listener(lambda up: print("restored" if up else "lost"))
76
+
77
+ await client.connect() # subscribes and sends every query
78
+ try:
79
+ for _ in range(4):
80
+ await asyncio.sleep(15)
81
+ await client.query_all() # the printer answers queries; poll them
82
+ await client.light_on()
83
+ except AnycubicLanError as err:
84
+ print("printer error:", err)
85
+ finally:
86
+ await client.disconnect()
87
+
88
+
89
+ asyncio.run(main())
90
+ ```
91
+
92
+ - `handshake()` raises `PrinterUnreachableError`, `LanModeDisabledError`,
93
+ `UnsupportedPrinterError`, `RequestRejectedError` or `InvalidResponseError`
94
+ (all subclasses of `AnycubicLanError`).
95
+ - `client.state` is an immutable `PrinterState` merged from every report:
96
+ status, temperatures, fans, speed mode, firmware, the current job (progress,
97
+ layers, times, task id), ACE boxes and slots, lights, head position,
98
+ capability flags, camera URL and the last printer error code.
99
+ - Commands: `pause()`, `resume()`, `stop()` (for the current job),
100
+ `light_on()`, `light_off()`, `set_light_brightness()`.
101
+ - The broker credentials rotate when the printer restarts. They are held in
102
+ memory only; if the connection does not come back, run the handshake again.
103
+
104
+ ## Supported printers
105
+
106
+ | Model | Model id | Status |
107
+ |---|---|---|
108
+ | Kobra S1 | 20025 | Tested (firmware 2.7.2.7, with ACE Pro) |
109
+ | Kobra X | 20030 | Parser informed by user logs (firmware 2.0.2.2) |
110
+
111
+ Other models that offer LAN Mode with the same handshake should work and are
112
+ reported as "Anycubic printer (model <id>)". Older models (Kobra 2 and
113
+ earlier) are detected and refused as unsupported.
114
+
115
+ ## Status
116
+
117
+ Version 0.1.0, alpha. The specification in [`docs/`](docs/) comes first; the
118
+ implementation follows it. Questions to the specification team and their
119
+ answers are in [`docs/QUESTIONS.md`](docs/QUESTIONS.md). See
120
+ [`docs/CLEAN-ROOM.md`](docs/CLEAN-ROOM.md) for how this library was written
121
+ and by whom.
122
+
123
+ ## Development
124
+
125
+ ```sh
126
+ pip install -e ".[test]" ruff mypy
127
+ ruff check . && ruff format --check .
128
+ mypy --strict src
129
+ pytest
130
+ ```
131
+
132
+ ## Licence
133
+
134
+ MIT.
@@ -0,0 +1,105 @@
1
+ # anycubic-lan
2
+
3
+ Talk to an Anycubic 3D printer in **LAN Mode**, directly on your network: no
4
+ Anycubic account, no cloud.
5
+
6
+ A printer with LAN Mode switched on runs its own MQTT broker. This library
7
+ performs the printer's handshake to get the broker credentials, connects,
8
+ asks for state, parses the reports into typed objects, and sends the commands
9
+ the printer accepts locally. It is fully async and has no cloud code and no
10
+ bundled certificates or keys.
11
+
12
+ Tested on a Kobra S1 (firmware 2.7.2.7). Reports from a Kobra X (firmware
13
+ 2.0.2.2) informed the parser.
14
+
15
+ ## Install
16
+
17
+ ```sh
18
+ pip install anycubic-lan
19
+ ```
20
+
21
+ Python 3.13 or later. Dependencies: `aiohttp`, `paho-mqtt`, `cryptography`.
22
+
23
+ Switch LAN Mode on at the printer first: **Settings → Network → LAN Mode**.
24
+ While LAN Mode is on the printer is not connected to the Anycubic cloud.
25
+
26
+ ## Usage
27
+
28
+ ```python
29
+ import asyncio
30
+
31
+ import aiohttp
32
+
33
+ from anycubic_lan import AnycubicLanClient, AnycubicLanError, handshake
34
+
35
+
36
+ async def main() -> None:
37
+ async with aiohttp.ClientSession() as session:
38
+ info = await handshake(session, "192.168.1.50") # the printer's IP
39
+ print(info.model_name, info.device_id)
40
+
41
+ client = AnycubicLanClient(info)
42
+ client.add_state_listener(
43
+ lambda state: print(state.status, state.temperatures.nozzle, state.job)
44
+ )
45
+ # Fires on changes after connect(): False when lost, True when restored.
46
+ client.add_connection_listener(lambda up: print("restored" if up else "lost"))
47
+
48
+ await client.connect() # subscribes and sends every query
49
+ try:
50
+ for _ in range(4):
51
+ await asyncio.sleep(15)
52
+ await client.query_all() # the printer answers queries; poll them
53
+ await client.light_on()
54
+ except AnycubicLanError as err:
55
+ print("printer error:", err)
56
+ finally:
57
+ await client.disconnect()
58
+
59
+
60
+ asyncio.run(main())
61
+ ```
62
+
63
+ - `handshake()` raises `PrinterUnreachableError`, `LanModeDisabledError`,
64
+ `UnsupportedPrinterError`, `RequestRejectedError` or `InvalidResponseError`
65
+ (all subclasses of `AnycubicLanError`).
66
+ - `client.state` is an immutable `PrinterState` merged from every report:
67
+ status, temperatures, fans, speed mode, firmware, the current job (progress,
68
+ layers, times, task id), ACE boxes and slots, lights, head position,
69
+ capability flags, camera URL and the last printer error code.
70
+ - Commands: `pause()`, `resume()`, `stop()` (for the current job),
71
+ `light_on()`, `light_off()`, `set_light_brightness()`.
72
+ - The broker credentials rotate when the printer restarts. They are held in
73
+ memory only; if the connection does not come back, run the handshake again.
74
+
75
+ ## Supported printers
76
+
77
+ | Model | Model id | Status |
78
+ |---|---|---|
79
+ | Kobra S1 | 20025 | Tested (firmware 2.7.2.7, with ACE Pro) |
80
+ | Kobra X | 20030 | Parser informed by user logs (firmware 2.0.2.2) |
81
+
82
+ Other models that offer LAN Mode with the same handshake should work and are
83
+ reported as "Anycubic printer (model <id>)". Older models (Kobra 2 and
84
+ earlier) are detected and refused as unsupported.
85
+
86
+ ## Status
87
+
88
+ Version 0.1.0, alpha. The specification in [`docs/`](docs/) comes first; the
89
+ implementation follows it. Questions to the specification team and their
90
+ answers are in [`docs/QUESTIONS.md`](docs/QUESTIONS.md). See
91
+ [`docs/CLEAN-ROOM.md`](docs/CLEAN-ROOM.md) for how this library was written
92
+ and by whom.
93
+
94
+ ## Development
95
+
96
+ ```sh
97
+ pip install -e ".[test]" ruff mypy
98
+ ruff check . && ruff format --check .
99
+ mypy --strict src
100
+ pytest
101
+ ```
102
+
103
+ ## Licence
104
+
105
+ MIT.
@@ -0,0 +1,61 @@
1
+ # Clean-room record
2
+
3
+ This library and the Home Assistant core integration built on it are written
4
+ so that they contain **no code or expression** from two GPL-3.0 projects:
5
+
6
+ - `WaresWichall/hass-anycubic_cloud` (the original Anycubic Cloud integration)
7
+ - `Nino6689/hass-anycubic` and `Nino6689/anycubic-cloud-api` (its fork and
8
+ the fork's API library)
9
+
10
+ The fork's maintainer (Nino Bondonno) has worked on those projects, so the
11
+ work is split into two teams.
12
+
13
+ ## Roles
14
+
15
+ | Role | Who | May read | Must not read |
16
+ |---|---|---|---|
17
+ | **Specification team** ("dirty") | Nino Bondonno, and Claude sessions on his machine that have read the GPL projects | Everything | — |
18
+ | **Implementation team** ("clean") | A separate Claude cloud session per phase, with no access to Nino's machine | This repository's `docs/`, the `anycubic-lan` package, Python and dependency documentation, the Home Assistant developer docs and `home-assistant/core` | The three repositories above, and any other Anycubic Home Assistant integration (e.g. adamoutler, chrisfore) |
19
+
20
+ Rules:
21
+
22
+ 1. The specification team writes **facts and requirements only** — protocol
23
+ behaviour, captured payloads, entity lists — in `docs/`. It never writes or
24
+ edits code in this repository or in the core integration.
25
+ 2. The implementation team writes **all code**, from `docs/` and public
26
+ documentation. When a fact is missing it asks in `docs/QUESTIONS.md`; it
27
+ does not look elsewhere.
28
+ 3. Feedback from hardware testing reaches the implementation team only as
29
+ functional bug reports (observed behaviour vs expected), never as code or
30
+ diffs.
31
+ 4. Before publication, the finished code is compared mechanically against the
32
+ GPL projects and the result recorded below.
33
+
34
+ ## Log
35
+
36
+ | Date | Team | Session | Inputs given | Outputs | Did not read the excluded repos |
37
+ |---|---|---|---|---|---|
38
+ | 2026-09-26 | Specification | Claude (local, Nino's machine) | Captured payloads from Nino's Kobra S1; Kobra X logs from a user; the public `anycubic_ha_local` protocol description; notes from testing the fork | `PROTOCOL.md`, `INTEGRATION-SPEC.md`, this file, `LICENSE`, `README.md`, CI workflows | n/a — specification team |
39
+ | 2026-09-26 | Implementation | Claude cloud session (routine) | `docs/CLEAN-ROOM.md`, `docs/PROTOCOL.md`, `docs/INTEGRATION-SPEC.md`, `docs/QUESTIONS.md`, `README.md`, `LICENSE`, CI workflows; general knowledge of the Python, aiohttp, paho-mqtt, cryptography and pytest APIs and the installed packages themselves (no web searches, no Anycubic code) | `src/anycubic_lan/` (v0.1.0), `tests/`, `pyproject.toml`, `.gitignore`, README sections, questions Q1–Q9 in `QUESTIONS.md` | Yes |
40
+ | 2026-09-26 | Specification | Claude (local) | The v0.1.0 implementation (for acceptance only); the GPL projects (for the similarity check) | Answers to Q1–Q9 in `QUESTIONS.md`; `PROTOCOL.md` §6.7 corrected; similarity checks below | n/a — specification team |
41
+ | 2026-09-26 | Implementation | Claude cloud session (routine, round 2) | `docs/QUESTIONS.md` (answers to Q1–Q9), `docs/PROTOCOL.md` (incl. corrected §6.7), `docs/INTEGRATION-SPEC.md`, `docs/CLEAN-ROOM.md`, `README.md`, the CI workflow, this branch's own code and tests; the installed Python, paho-mqtt, pytest, mypy and ruff (no web searches, no Anycubic code) | `src/anycubic_lan/` and `tests/` updated for Q1–Q4 and Q9, comments for Q5–Q8; "Applied in round 2" marks in `QUESTIONS.md`; README status line; this row. Merged the concurrent commits `ce0ee48` and `ca86d53` pushed to this branch during the session, keeping this session's Q1–Q4/Q9 code where both overlapped | Yes |
42
+
43
+ | 2026-09-28 | Specification | Claude (local) | Nino's Kobra S1 in LAN Mode; the v0.1.0 branch's public API (black-box script, not committed) | Hardware report HW1–HW3 in `QUESTIONS.md`; corrections to `PROTOCOL.md` §2, §6.8, §6.9 and the Q5 answer | n/a — specification team |
44
+ | 2026-09-28 | Implementation | Claude cloud session (routine, round 3) | `docs/QUESTIONS.md` (HW1–HW3), `docs/PROTOCOL.md` (corrected §2, §6.8, §6.9), this branch's code and tests (no web searches, no Anycubic code) | Fixes for HW1–HW3 in `reports.py`, `client.py` docstring and `README.md`; tests from the live captures; "Fixed" notes in `QUESTIONS.md`; this row | Yes |
45
+
46
+ | 2026-09-28 | Specification | Claude (local) | The core branch run against the Kobra S1 in a throwaway Home Assistant; the library re-run after round 3 | HW1–HW2 confirmed fixed on hardware; HW4 reported; `PROTOCOL.md` §2 discovery fields added | n/a — specification team |
47
+ | 2026-09-28 | Implementation | Claude cloud session (core integration, round 2) | `docs/QUESTIONS.md` (HW4), `docs/PROTOCOL.md` §2 and §6.1, this library's source at `3f8ac33` (`DiscoveryInfo.as_redacted_dict`, `parse_discovery`); core's own code and dev tooling for house style (no web searches, no Anycubic code) | Core branch `anycubic-integration` `68757a01` (diagnostics redact `fileUploadurl` by key, use the library's redacted discovery copy, scrub any string containing `gcode_upload?s=`) and `cfb7317c` (discovery fixture refreshed with the live fields and a string `modelId`, regression test, snapshot); "Fixed on core branch" note under HW4; this row | Yes |
48
+
49
+ | 2026-09-28 | Specification | Claude (local) | Core branch `cfb7317` and library `5b8b406`, both run against the Kobra S1 | HW1, HW2 and HW4 confirmed fixed on hardware (HW3 settled by documentation); similarity rechecked | n/a — specification team |
50
+
51
+ ## Similarity checks
52
+
53
+ | Date | Artefact | Compared against | Longest identical run (non-blank, non-comment lines) | Result |
54
+ |---|---|---|---|---|
55
+ | 2026-09-26 | `src/anycubic_lan/` @ `b7c0042` | `hass-anycubic` integration, `anycubic-cloud-api` library, `WaresWichall/hass-anycubic_cloud` | 6 — alphabetised stdlib imports (`hashlib, json, re, secrets, string, time`); next 4 — the standard `ssl` no-verification idiom | Clean: both are dictated by the language, not copied expression |
56
+ | 2026-09-26 | `tests/` @ `b7c0042` | the same, plus `anycubic-cloud-api/tests` | 15 — the captured `info.project` payload | Expected: data captured from a printer, copied from `PROTOCOL.md` §6.2 |
57
+ | 2026-09-26 | `src/anycubic_lan/` @ `b87f11e` (after round 2) | same three projects | 6 — the same stdlib import block; next 4 — the same `ssl` idiom | Clean; unchanged by round 2 |
58
+ | 2026-09-28 | `src/anycubic_lan/` @ `5b8b406` (after hardware fixes) | same three projects | 6 — the same stdlib import block | Clean; unchanged by rounds 3–4 |
59
+ | 2026-09-28 | core `homeassistant/components/anycubic/` @ `cfb7317` (after HW4) | same three projects | 5 — HA's sensor import block | Clean; unchanged by the HW4 fix |
60
+ | 2026-09-28 | core `tests/components/anycubic/` @ `cfb7317` | the same, plus both GPL test suites | 2 | Clean |
61
+
@@ -0,0 +1,121 @@
1
+ # Home Assistant core integration `anycubic` — functional specification
2
+
3
+ What the core integration must do. How it is built is up to the implementer,
4
+ following the Home Assistant developer documentation and house style. All
5
+ wire behaviour comes from `anycubic-lan` (see `PROTOCOL.md`).
6
+
7
+ ## Identity
8
+
9
+ | manifest field | value |
10
+ |---|---|
11
+ | `domain` | `anycubic` |
12
+ | `name` | `Anycubic` |
13
+ | `integration_type` | `device` |
14
+ | `iot_class` | `local_push` |
15
+ | `config_flow` | `true` |
16
+ | `requirements` | `["anycubic-lan==<released version>"]` |
17
+ | `codeowners` | `["@Nino6689"]` |
18
+ | `dhcp` | `[{"macaddress": "A4E88D*"}, {"hostname": "anycubic*"}, {"hostname": "kobra*"}]` |
19
+ | `quality_scale` | `bronze` in the first PR |
20
+
21
+ ## Library API the integration needs (the library must provide this)
22
+
23
+ - An async **handshake**: given an `aiohttp.ClientSession` and a host, return
24
+ broker credentials plus printer identity (model id, model name, serial,
25
+ MAC, device id). Distinct exceptions for: unreachable, LAN Mode off,
26
+ unsupported printer, bad/rejected response.
27
+ - An async **client**: connect with those credentials; register a callback
28
+ that receives parsed state updates; `query_all()`; `disconnect()`;
29
+ `is_connected`; a connection-lost / connection-restored signal.
30
+ - A **state model**: one object per printer with typed fields for everything
31
+ in `PROTOCOL.md` §6 (temperatures, fans, state, speed mode, firmware,
32
+ job with progress/layers/times/task id, ACE boxes and slots, light,
33
+ position, last error code/message, capability flags, camera URL).
34
+ - **Commands**: pause, resume, stop (using the current job's task id), light
35
+ on/off.
36
+
37
+ ## Config flow
38
+
39
+ - **user** step: one field, `host`. Run the handshake.
40
+ - Errors: `cannot_connect` (unreachable), `not_in_lan_mode`,
41
+ `unsupported_printer`, `unknown`.
42
+ - `unique_id` = the printer's `deviceId` from the handshake.
43
+ Abort `already_configured` if it exists (and update the host).
44
+ - Entry title = model name (e.g. "Anycubic Kobra S1").
45
+ - `data_description` text for `host` explaining where to find the IP and
46
+ that LAN Mode must be switched on at the printer (Settings → Network →
47
+ LAN Mode).
48
+ - **dhcp** step: from the manifest matchers. Probe the host's discovery
49
+ document; if it is an Anycubic printer in LAN Mode, set `unique_id` and
50
+ abort `already_configured` with the new host if known
51
+ (`discovery-update-info`); otherwise confirm with the user
52
+ (`discovery_confirm`). If LAN Mode is off, abort `not_in_lan_mode`.
53
+ - **reconfigure** step: change the host; must be the same printer
54
+ (`unique_id` match) or abort `wrong_device`.
55
+ - No credentials are stored (the printer rotates them) → reauthentication is
56
+ exempt.
57
+
58
+ ## Runtime
59
+
60
+ - Setup: handshake + connect; on failure raise `ConfigEntryNotReady`. Store
61
+ the client and coordinator in `entry.runtime_data`.
62
+ - Coordinator: push-based. Apply each state update as it arrives; send
63
+ `query_all()` on connect and every **15 seconds**.
64
+ - Connection lost: entities become **unavailable once**, stay unavailable, log
65
+ a single warning; on return log a single info line and re-run the handshake
66
+ if needed. Never alternate between stale values and unavailable.
67
+ - Unload: disconnect cleanly.
68
+ - Diagnostics: the latest parsed state and the discovery document, with
69
+ `token`, credentials, serial and MAC redacted.
70
+
71
+ ## Device
72
+
73
+ One device per entry: manufacturer `Anycubic`, model from the model id
74
+ (`PROTOCOL.md` §9), name from the printer, `sw_version` = firmware,
75
+ `serial_number` = `cn`, connection = MAC, `configuration_url` optional.
76
+
77
+ ## Entities — first PR: `sensor` only
78
+
79
+ All with `has_entity_name`, translated names (`entity-translations`), icons
80
+ via `icons.json`, unique id = `<deviceId>_<key>`.
81
+
82
+ | key | what | device class / unit | state class | category / default |
83
+ |---|---|---|---|---|
84
+ | `status` | printer state: `idle`, `printing`, `paused`, `busy`, `unknown` | enum | — | — |
85
+ | `nozzle_temperature` | current nozzle °C | temperature, °C | measurement | — |
86
+ | `nozzle_target_temperature` | target nozzle °C | temperature, °C | measurement | — |
87
+ | `bed_temperature` | current bed °C | temperature, °C | measurement | — |
88
+ | `bed_target_temperature` | target bed °C | temperature, °C | measurement | — |
89
+ | `chamber_temperature` | only created if the printer reports a non-zero chamber reading | temperature, °C | measurement | — |
90
+ | `fan_speed` | part fan % | %, | measurement | disabled by default |
91
+ | `auxiliary_fan_speed` | aux fan % | % | measurement | disabled by default |
92
+ | `job_progress` | % | % | measurement | — |
93
+ | `job_current_layer` | layer | — | measurement | — |
94
+ | `job_total_layers` | layers | — | — | — |
95
+ | `job_elapsed` | minutes | duration, min | — | — |
96
+ | `job_remaining` | minutes | duration, min | — | — |
97
+ | `job_end_time` | now + remaining | timestamp | — | — |
98
+ | `job_name` | file name without path/extension | — | — | — |
99
+ | `job_status` | enum from `print_status`: `printing`, `complete`, `cancelled`, `downloading`, `checking`, `preheating`, `slicing`, `levelling`; `idle` when no job | enum | — | — |
100
+ | `speed_mode` | enum: `silent`, `standard`, `sport` (raw otherwise) | enum | — | — |
101
+ | `last_error_code` | printer error code; `none` when 200 | — | — | diagnostic |
102
+
103
+ Job sensors must read `idle`/unknown sensibly when there is no job — never
104
+ `unavailable` while the printer is connected.
105
+
106
+ ## Later PRs (one each, after the first merges)
107
+
108
+ 1. `binary_sensor`: printing, paused, ACE drying, runout-refill.
109
+ 2. `button`: pause, resume, stop (available only when applicable).
110
+ 3. `light`: printer light (type 2), on/off + brightness.
111
+ 4. ACE sensors: per-slot material/colour/status, loaded slot, box temperature,
112
+ drying remaining.
113
+ 5. `number` + `select`: target temperatures, speed mode.
114
+ 6. `camera`: the FLV stream URL from `info.urls.rtspUrl` via ffmpeg.
115
+ 7. `update`: firmware version (read-only unless a local update path exists).
116
+
117
+ ## Non-goals
118
+
119
+ Anycubic cloud accounts, file browsers, printing files, a custom panel or
120
+ card, filament cost ledgers, custom actions. Those belong to the separate
121
+ HACS integration.