# Printer monitoring

> Connect a local Bambu LAN bridge for private, read-only telemetry. Setup, TLS trust, namespace access, and honest freshness states.

Source: http://tinyro.bot/docs/printer

tinyrobot can retain the **current** normalized printer state in a private
namespace. Open [Printers](/printers) from [your account](/app) to connect and
inspect it. A computer or NAS on the printer's LAN runs an outbound connector.
No inbound port forwarding is needed.

**Hardware support is unverified.** The exact printer model and firmware have
not been established or physically tested. This is not a hardware compatibility
certification. Confirm LAN status availability and authenticated TLS trust for
your printer before relying on the connector. Never use this page as a safety
monitor or leave a printer unattended because a status appears healthy.

## Current capabilities

| Capability                             | This slice                                                                               |
| -------------------------------------- | ---------------------------------------------------------------------------------------- |
| Status                                 | Idle, preparing, printing, paused, completed, or failed when reported; otherwise unknown |
| Progress                               | Reported percentage only; never inferred from status                                     |
| ETA                                    | Reported remaining duration only; not a guaranteed finish time                           |
| Temperatures                           | Nozzle and bed when valid readings are present                                           |
| Freshness                              | Separate connector heartbeat and printer observation ages                                |
| Camera                                 | Unsupported; no images, streaming, or camera credentials                                 |
| Printer controls / G-code              | Unsupported; subscribe-only MQTT, no command publication                                 |
| History / notifications / AI detection | Not implemented                                                                          |
| Automatic intervention                 | Not implemented; no pause, cancel, or safety action                                      |

Values can be partial. Each supported MQTT update replaces the current sample;
fields absent from that update become **unknown**, not silently carried forward.
Unknown status names are not guessed. Reconnects and detected print changes clear
the previous sample. There is no print history or raw MQTT payload archive.

## Hosted and self-hosted topology

```text
Printer on LAN -- authenticated TLS / status subscription --> LAN computer or NAS
                                                             |
                                                    outbound HTTPS only
                                                             |
                                                  tinyrobot server
                                                             |
                                                  private /printers page
```

This diagram is illustrative, not live telemetry or evidence of tested hardware.
Hosted tinyro.bot cannot connect directly to a private LAN. Keep the connector
running on a trusted computer or NAS on that LAN. A local self-hosted tinyrobot
uses the **same connector**; it may run on the same machine as the server, but is
still a separate process. The public documentation lives on the cloud site and
is not bundled into the self-hosted application.

## Before you start

1. Install the repository's pinned Node.js and pnpm versions and run
   `./scripts/setup.sh web` from a checked-out repository. The connector is a
   source workspace package, not a promised published SDK or hosted relay.
   With [nvm](https://github.com/nvm-sh/nvm) already installed, run `nvm install`,
   `nvm use`, and `corepack enable` from the repository root before setup.
   No Python, Docker, or local PostgreSQL is needed for the connector alone.
   Run the `pnpm --filter` commands below from that same repository root.
2. Know your own printer's LAN address, serial number, and LAN access code. Read
   them from your printer's supported local settings or vendor documentation for
   its model and firmware. Do not use or scrape Bambu Cloud account credentials.
3. Establish certificate trust as described below. If you cannot authenticate
   the printer, **stop**. Do not bypass TLS checks to complete setup.
4. Sign into your tinyrobot deployment and select the intended namespace on
   [Printers](/printers). Members can read; owners and admins can create,
   re-pair, and revoke. The server checks current membership on every read and
   checks owner/admin rights on every mutation.

## Configure the local connector

Create a private directory on the connector machine, owned by the account that
will run it. On Linux/macOS, use `chmod 700` for the directory and `chmod 600`
for the config file. Windows and network filesystems are unverified and
unsupported in this slice; do not bypass the connector's permission checks.
Keep config, certificate material, and state out of source
control, shared folders, backups accessible to others, and support logs.

Example local config (replace every placeholder locally; these are not working
credentials). All file paths must be absolute:

```json
{
  "origin": "https://tinyro.bot",
  "stateDirectory": "/absolute/private/state",
  "printer": {
    "host": "192.168.1.50",
    "port": 8883,
    "serial": "PRINTER_SERIAL",
    "accessCode": "LAN_CODE",
    "tls": {
      "certificateFile": "/absolute/private/printer.pem",
      "fingerprint256": "REPLACE_WITH_64_LOWERCASE_HEX_SHA256_DIGEST"
    }
  }
}
```

The LAN access code is the printer's eight-character alphanumeric code. It and
the serial stay on the LAN machine; tinyrobot receives neither. Use a plain
server origin, with no path, query string, userinfo, or token. Non-loopback
connections require HTTPS. HTTP is accepted only for `localhost`, `127.0.0.1`,
or `[::1]` on the connector machine, not an arbitrary LAN server. For a
self-hosted server on another machine, configure HTTPS. Credential-bearing
requests reject redirects rather than forwarding credentials to another URL.

### Authenticate the printer with TLS

Choose one of the two supported trust configurations:

- **Exact certificate pin:** `certificateFile` is a PEM certificate from a
  trusted source; `fingerprint256` is its independently verified SHA-256 digest
  of the DER certificate, exactly 64 lowercase hexadecimal characters without
  colons. The connector checks the live peer against that pin before sending
  MQTT credentials. Certificate validity and chain checks remain enabled.
- **Trusted CA:** replace the entire `tls` object with the following. Supply a
  trusted CA PEM and the printer certificate's DNS identity, not an arbitrary
  display name:

```json
{
  "caFile": "/absolute/private/trusted-ca.pem",
  "serverName": "printer-certificate-dns-name"
}
```

For a PEM already obtained from a trusted source, a local fingerprint can be
computed without printing credentials:

```sh
openssl x509 -in /absolute/private/printer.pem -outform DER | openssl dgst -sha256
```

Use only the final hexadecimal digest, lowercased. Computing a hash does **not**
establish trust. A certificate retrieved from the network, even on an isolated or
direct LAN, is only a candidate until compared with an independent trusted
reference. There is no automatic trust-on-first-use, no silent acceptance of a
self-signed certificate, and no global TLS-verification disable switch. We do
not yet have model-specific validated certificate provisioning instructions. If
your hardware cannot provide a trusted reference or valid certificate, setup is
blocked rather than weakened. Firmware or certificate changes may require
re-establishing trust; do not blindly replace a mismatched pin.

## Create, claim, and start

1. On [Printers](/printers), explicitly select your namespace. An owner/admin
   enters a human-readable printer name and chooses **Create pairing code**.
   Do not enter the printer password, access code, serial, or IP in the label.
2. The one-use pairing code is shown only at issuance and expires in ten minutes.
   It is a tinyrobot pairing secret, **not** the printer access code. Keep it out
   of URLs, command arguments, shell history, screenshots, logs, and chats.
3. From the repository root on the LAN machine, claim it:

```sh
pnpm --filter @tinyrobot/printer-bridge claim --config /absolute/private/config.json
```

Paste the code into the **hidden interactive prompt**. A successful claim
consumes it and stores a narrow device credential bound to that printer and
namespace, never a user's login session. The server retains secret hashes; the
credential and next sequence are stored atomically in the private local state
directory. Only one process may own that state directory. Do not copy the state
to a second running connector or reset its sequence to replay old observations.

4. Start the connector:

```sh
pnpm --filter @tinyrobot/printer-bridge start --config /absolute/private/config.json
```

Keep the process running. It subscribes to printer status, never publishes
printer control/request messages, and opens no inbound service. It sends bounded
normalized samples no more frequently than every five seconds and independent
heartbeats every fifteen seconds. Shutdown releases the local connection and
state lock; stopping the process does not itself revoke the device credential.
Transient HTTP failures stop after five attempts. MQTT permits the initial
session plus four reconnects; trust failures stop immediately. Investigate the
cause before restarting rather than configuring an infinite restart loop.

5. Return to the selected namespace in the app. It refreshes every fifteen
   seconds while visible. Requests and timers stop when the page is left or
   hidden; private state and pairing codes are cleared on namespace changes,
   sign-out, and access loss. Reopen or refresh to check current authorization.

### Re-pair or revoke

**New pairing code** requires confirmation: it immediately revokes the previous
device credential, replaces any pending code, and clears retained readings.
Stop the old local connector and claim the new code using the same commands.
An expired or consumed code cannot be recovered; generate a new one.

**Revoke connector** invalidates both the active device credential and pending
pairing code, and clears current readings. Stop the process on the LAN computer
too. Revocation does not change your printer's LAN access code or account
settings. Repeated revocation is safe. Printer records are retained, with limits
of 25 per namespace and 1,000 per deployment; this slice has no deletion UI.

## Read the two clocks separately

| State   | Meaning                                                                             |
| ------- | ----------------------------------------------------------------------------------- |
| Unknown | Not paired, no heartbeat yet, or a live connector without a retained printer sample |
| Fresh   | Live connector with valid printer telemetry no older than 90 seconds                |
| Stale   | Live connector, but retained printer data older than 90 seconds                     |
| Offline | Revoked, explicitly disconnected, or heartbeat older than 90 seconds                |

**Bridge heartbeat** measures server acceptance of connector messages.
**Printer telemetry** measures when the connector actually received a supported
printer update. A heartbeat, reconnect, or disconnect cannot make old printer
data fresh. Ages use the server's time reference, not the browser's wall clock.
Stale/offline readings, if retained, are explicitly last known values. Unknown
is not zero, idle, complete, or safe. ETA is the last reported remaining duration,
not a countdown inferred by tinyrobot.

## Troubleshooting and limits

- **No namespaces or access denied:** select a namespace you currently belong
  to. Only an owner/admin can manage connectors. Membership removal hides
  telemetry; a device credential cannot log into the browser app.
- **Pairing expired/used or interrupted claim:** request a new pairing code.
  The old code cannot be reused. Do not paste either secret into an issue.
- **State directory locked:** stop the existing connector before claiming or
  re-pairing. After an unclean crash, verify that no process uses the state
  directory, then remove only its empty `lock` directory. Never restore old
  sequence snapshots, delete credential state to force a retry, or clone it to
  another process.
- **Diagnostics refused:** the CLI refuses `DEBUG`, `NODE_DEBUG`, and
  `NODE_DEBUG_NATIVE` diagnostics to avoid leaking credentials. Do not enable
  verbose protocol logging or send private config/state files to support.
- **TLS rejected:** verify the trusted reference, hostname (CA mode), expiry,
  certificate chain, and independently verified pin. Never disable verification.
- **Connected but unknown:** status pushes may omit supported fields or have
  not arrived. The bridge does not request a full snapshot or invent values.
- **Stale with recent heartbeats:** the LAN process is alive, but no fresh
  supported printer observation has arrived. Check the printer and LAN path.
- **Offline:** check process power, network reachability, MQTT connection, and
  whether the connector was revoked. Cloud reachability does not prove LAN health.
- **Rejected observations:** keep the connector machine's clock synchronized.
  Samples older than 30 seconds or more than five seconds in the future are
  rejected; retries do not re-date old data. Replay/regressing sequences fail.
- **Too many requests:** respect the displayed retry delay. Repeatedly creating
  codes or refreshing does not speed up telemetry. Storage/unavailable responses
  hide data rather than presenting a successful update.
- **Changed print or reconnect:** previous values are cleared. Missing fields
  remain unknown until a supported update includes them.

## Bambu integration facts and protocol references

[Bambu's official third-party integration explanation](https://wiki.bambulab.com/en/software/third-party-integration)
states that MQTT **status pushes** are exempt from its authorization changes.
Camera access is separate and is not part of this connector. Developer Mode is
**not a blanket prerequisite** for status monitoring; it disables Bambu Cloud,
so do not turn it on as a default setup step. Availability still depends on the
actual model, firmware, and network settings.

Bambu Handy is free. Paid OctoEverywhere service is optional and is not required
for this local connector. tinyrobot makes no promise of access to, or rights to
redistribute, Bambu's SDK.

Undocumented MQTT mapping was checked against the established ha-bambulab
integration at commit `0e027ff135a6d9265cb756d3e246747954c76722` as a protocol
reference, not copied implementation code:

- [bambu_client.py](https://github.com/greghesp/ha-bambulab/blob/0e027ff135a6d9265cb756d3e246747954c76722/custom_components/bambu_lab/pybambu/bambu_client.py): MQTT user `bblp`, port 8883, and report subscription.
- [models.py](https://github.com/greghesp/ha-bambulab/blob/0e027ff135a6d9265cb756d3e246747954c76722/custom_components/bambu_lab/pybambu/models.py): `mc_percent`, `mc_remaining_time` in minutes, `nozzle_temper`, `bed_temper`, `gcode_state`, and local print identity handling.
- [const.py](https://github.com/greghesp/ha-bambulab/blob/0e027ff135a6d9265cb756d3e246747954c76722/custom_components/bambu_lab/pybambu/const.py): reported status strings.

These references are not physical testing or a supported-model list. Camera,
controls, history, alerts, and AI detection are outside this implementation,
not advertised as working or promised future features.

