diff --git a/schemas/bambulab/v1/README.md b/schemas/bambulab/v1/README.md new file mode 100644 index 0000000..ee235db --- /dev/null +++ b/schemas/bambulab/v1/README.md @@ -0,0 +1,67 @@ +# bambulab/v1 JSON Schemas + +Documentation/validation schemas mirroring `src/printer/bambu_commands/` — +not used for codegen (the Rust structs are hand-written, see that module's +doc comment for why), but useful on their own: as language-agnostic +documentation of the wire format, or for validating a captured MQTT +payload without writing Rust to do it. + +## The "inheritance" pattern + +`envelope.schema.json` is the base — `sequence_id` + `command`, the two +fields every command/response shares. Every other schema here *extends* +it with `allOf` + `$ref`: + +```json +{ + "allOf": [ + { "$ref": "envelope.schema.json" }, + { "type": "object", "properties": { "command": { "const": "get_version" } } } + ] +} +``` + +This is genuinely closer to real inheritance than anything else in this +project's schema stack (proto, Rust): the instance must satisfy the base +schema *and* the extension schema simultaneously, and — verified, not just +asserted — the base's `required: ["sequence_id", "command"]` is actually +enforced on every schema that extends it, not just copy-pasted as +documentation. Proto and Rust only ever gave us composition (embed a field +holding the base), never something that reuses a *named, referenced* +schema's constraints automatically like this. + +**One gotcha if you add a new base-style schema**: don't set +`"additionalProperties": false` on something meant to be `allOf`-extended. +`allOf` validates every sub-schema against the *whole* instance +independently — it doesn't merge object schemas — so a closed base schema +would reject every field the extending schema adds. Leave +`additionalProperties` unset (defaults to allowed) on anything used as a +base; enforce closedness only on the top-level schemas +(`request.schema.json`/`report.schema.json` do this correctly — check +their `additionalProperties: false`). + +## Files + +- `envelope.schema.json` — the base +- `module_info.schema.json` — nested object used inside `get_version`'s response +- `get_version.request.schema.json` / `get_version.response.schema.json` +- `pushall.request.schema.json` / `pushall.response.schema.json` +- `request.schema.json` / `report.schema.json` — the top-level `{"info": ...}` / `{"pushing": ...}` dispatch, matching `BambuRequest`/`BambuReport` + +## Validating something + +```python +from referencing import Registry, Resource +from jsonschema import Draft202012Validator +import json, glob, os + +resources = [] +for path in glob.glob("*.schema.json"): + contents = json.load(open(path)) + resources.append((contents["$id"], Resource.from_contents(contents))) + resources.append((os.path.basename(path), Resource.from_contents(contents))) +registry = Registry().with_resources(resources) + +schema = json.load(open("request.schema.json")) +Draft202012Validator(schema, registry=registry).validate({"info": {"sequence_id": "0", "command": "get_version"}}) +``` diff --git a/schemas/bambulab/v1/envelope.schema.json b/schemas/bambulab/v1/envelope.schema.json new file mode 100644 index 0000000..c8d6662 --- /dev/null +++ b/schemas/bambulab/v1/envelope.schema.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://continuum.internal/schemas/bambulab/v1/envelope.schema.json", + "title": "CommandEnvelope", + "description": "Fields every Bambu MQTT command/response shares. This is the 'base class' — other schemas extend it via allOf + $ref. JSON Schema doesn't have inheritance either, strictly speaking (no 'extends' keyword) — allOf is composition: it says the instance must satisfy THIS schema AND whatever else is listed alongside it. It's the closest thing to real inheritance ergonomics of anything we've used across this whole schema stack (proto, Rust), because $ref + allOf lets you name and reuse a shape without re-declaring its fields, which proto/Rust composition can't quite do (there you still write `common: PrinterReportCommon` by hand on every struct).", + "type": "object", + "properties": { + "sequence_id": { "type": "string" }, + "command": { "type": "string" } + }, + "required": ["sequence_id", "command"] +} diff --git a/schemas/bambulab/v1/get_version.request.schema.json b/schemas/bambulab/v1/get_version.request.schema.json new file mode 100644 index 0000000..49d8f9c --- /dev/null +++ b/schemas/bambulab/v1/get_version.request.schema.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://continuum.internal/schemas/bambulab/v1/get_version.request.schema.json", + "title": "GetVersionRequest", + "allOf": [ + { "$ref": "envelope.schema.json" }, + { + "type": "object", + "properties": { + "command": { "const": "get_version" } + } + } + ] +} diff --git a/schemas/bambulab/v1/get_version.response.schema.json b/schemas/bambulab/v1/get_version.response.schema.json new file mode 100644 index 0000000..183315b --- /dev/null +++ b/schemas/bambulab/v1/get_version.response.schema.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://continuum.internal/schemas/bambulab/v1/get_version.response.schema.json", + "title": "GetVersionResponse", + "allOf": [ + { "$ref": "envelope.schema.json" }, + { + "type": "object", + "properties": { + "command": { "const": "get_version" }, + "result": { "type": "string" }, + "reason": { "type": "string" }, + "module": { + "type": "array", + "items": { "$ref": "module_info.schema.json" } + } + }, + "required": ["module"] + } + ] +} diff --git a/schemas/bambulab/v1/module_info.schema.json b/schemas/bambulab/v1/module_info.schema.json new file mode 100644 index 0000000..0dfb237 --- /dev/null +++ b/schemas/bambulab/v1/module_info.schema.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://continuum.internal/schemas/bambulab/v1/module_info.schema.json", + "title": "ModuleInfo", + "type": "object", + "properties": { + "hw_ver": { "type": "string" }, + "name": { "type": "string" }, + "sn": { "type": "string" }, + "sw_ver": { "type": "string" } + }, + "required": ["hw_ver", "name", "sn", "sw_ver"] +} diff --git a/schemas/bambulab/v1/pushall.request.schema.json b/schemas/bambulab/v1/pushall.request.schema.json new file mode 100644 index 0000000..4e6ea03 --- /dev/null +++ b/schemas/bambulab/v1/pushall.request.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://continuum.internal/schemas/bambulab/v1/pushall.request.schema.json", + "title": "PushallRequest", + "allOf": [ + { "$ref": "envelope.schema.json" }, + { + "type": "object", + "properties": { + "command": { "const": "pushall" }, + "version": { "type": "integer" }, + "push_target": { "type": "integer" } + }, + "required": ["version", "push_target"] + } + ] +} diff --git a/schemas/bambulab/v1/pushall.response.schema.json b/schemas/bambulab/v1/pushall.response.schema.json new file mode 100644 index 0000000..a217bf6 --- /dev/null +++ b/schemas/bambulab/v1/pushall.response.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://continuum.internal/schemas/bambulab/v1/pushall.response.schema.json", + "title": "PushallResponse", + "description": "The real pushall/print report (the 'print' category push) has a lot more fields (ams, xcam, tray_exist_bits, ...) — add them under `properties` here the same way you'd add them to src/printer/bambu_commands/pushall.rs. Not required in `required` unless Bambu always sends them.", + "allOf": [ + { "$ref": "envelope.schema.json" }, + { + "type": "object", + "properties": { + "result": { "type": "string" }, + "reason": { "type": "string" } + } + } + ] +} diff --git a/schemas/bambulab/v1/report.schema.json b/schemas/bambulab/v1/report.schema.json new file mode 100644 index 0000000..b1a6c4a --- /dev/null +++ b/schemas/bambulab/v1/report.schema.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://continuum.internal/schemas/bambulab/v1/report.schema.json", + "title": "BambuReport", + "description": "Matches src/printer/bambu_commands::BambuReport.", + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "info": { "$ref": "get_version.response.schema.json" }, + "pushing": { "$ref": "pushall.response.schema.json" } + }, + "additionalProperties": false +} diff --git a/schemas/bambulab/v1/request.schema.json b/schemas/bambulab/v1/request.schema.json new file mode 100644 index 0000000..402265e --- /dev/null +++ b/schemas/bambulab/v1/request.schema.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://continuum.internal/schemas/bambulab/v1/request.schema.json", + "title": "BambuRequest", + "description": "Matches src/printer/bambu_commands::BambuRequest — one JSON key naming the category ('info', 'pushing', ...), the value is that category's request schema.", + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "info": { "$ref": "get_version.request.schema.json" }, + "pushing": { "$ref": "pushall.request.schema.json" } + }, + "additionalProperties": false +}