@@ -993,15 +993,18 @@ def discover_controlstreams(self) -> list[ControlStream]:
993993 ``self.control_channels`` and also returned.
994994
995995 For each discovered control stream we additionally fetch the
996- command schema (``GET /controlstreams/{id}/schema``, which OSH
997- returns as ``application/json`` with a ``parametersSchema``
998- SWE Common component) and cache it on
999- ``_underlying_resource.command_schema``. The CS API listing
1000- endpoint omits the inner schema, so without this step every
1001- discovered control stream would be missing the schema callers
1002- need for command construction or cross-node sync. A failure on
1003- a single control stream's schema fetch is downgraded to a
1004- warning so it doesn't poison the whole call.
996+ command schema (``GET /controlstreams/{id}/schema?f=json``,
997+ which OSH returns as ``application/json`` with a
998+ ``parametersSchema`` SWE Common component) and cache it on
999+ ``_underlying_resource.command_schema`` as a `JSONCommandSchema`.
1000+ ``f=json`` is the OGC API standard format-selector and pins the
1001+ response shape to the JSON variant — without it the server
1002+ default could change. The CS API listing endpoint omits the
1003+ inner schema, so without this step every discovered control
1004+ stream would be missing the schema callers need for command
1005+ construction or cross-node sync. A failure on a single control
1006+ stream's schema fetch is downgraded to a warning so it doesn't
1007+ poison the whole call.
10051008 """
10061009 api = self ._parent_node .get_api_helper ()
10071010 res = api .get_resource (APIResourceTypes .SYSTEM , self ._resource_id ,
@@ -1016,6 +1019,7 @@ def discover_controlstreams(self) -> list[ControlStream]:
10161019 schema_resp = api .get_resource (
10171020 APIResourceTypes .CONTROL_CHANNEL , controlstream_objs .cs_id ,
10181021 APIResourceTypes .SCHEMA ,
1022+ params = {'f' : 'json' },
10191023 )
10201024 schema_resp .raise_for_status ()
10211025 new_cs ._underlying_resource .command_schema = (
@@ -1165,12 +1169,21 @@ def add_insert_controlstream(self, controlstream_resource: ControlStreamResource
11651169 the system's parent node via HTTP POST.
11661170
11671171 Mirrors `add_insert_datastream`: caller assembles the full
1168- `ControlStreamResource` (including the embedded `command_schema`
1169- — a `JSONCommandSchema` for ``application/json`` or a
1170- `SWEJSONCommandSchema` for ``application/swe+json``) and this
1171- method posts it to ``/systems/{id}/controlstreams``, captures
1172- the new resource ID from the ``Location`` header, and returns a
1173- wrapped `ControlStream`.
1172+ `ControlStreamResource` (including the embedded `command_schema`)
1173+ and this method posts it to ``/systems/{id}/controlstreams``,
1174+ captures the new resource ID from the ``Location`` header, and
1175+ returns a wrapped `ControlStream`.
1176+
1177+ For the embedded `command_schema`, prefer
1178+ `JSONCommandSchema` (`commandFormat: application/json` with a
1179+ ``parametersSchema``). It matches what OSH returns from
1180+ ``GET /controlstreams/{id}/schema?f=json`` (the form
1181+ ``discover_controlstreams`` parses), keeps round-trip sync
1182+ symmetric, and avoids the SWE+JSON ``encoding``-omission
1183+ deviation documented in ``docs/osh_spec_deviations.md`` §1.
1184+ `SWEJSONCommandSchema` (``application/swe+json`` with
1185+ ``recordSchema`` plus ``encoding``) is also accepted for
1186+ spec-strict scenarios.
11741187
11751188 :param controlstream_resource: A fully-built
11761189 `ControlStreamResource` carrying ``name``, ``input_name``,
@@ -1201,18 +1214,24 @@ def add_insert_controlstream(self, controlstream_resource: ControlStreamResource
12011214
12021215 def add_and_insert_control_stream (self , control_stream_record_schema : DataRecordSchema , input_name : str = None ,
12031216 valid_time : TimePeriod = None ,
1204- command_format : str = "application/swe+ json" ) -> ControlStream :
1217+ command_format : str = "application/json" ) -> ControlStream :
12051218 """Accepts a DataRecordSchema and creates a ControlStreamResource
12061219 with the matching command-schema variant, then POSTs it to the
12071220 parent node.
12081221
12091222 Per CS API Part 2 §16.x, command schemas come in two wire forms:
12101223
1211- - ``application/swe+json`` → `SWEJSONCommandSchema` carrying
1212- `recordSchema` (the SWE Common component) and `encoding`
1213- (`JSONEncoding`). This is the spec-compliant default.
12141224 - ``application/json`` → `JSONCommandSchema` carrying
12151225 `parametersSchema` (the SWE Common component); no `encoding`.
1226+ **This is the default.** It matches what OSH returns from
1227+ ``GET /controlstreams/{id}/schema?f=json`` (the form
1228+ ``discover_controlstreams`` parses), keeps round-trip sync
1229+ symmetric, and avoids the SWE+JSON ``encoding``-omission
1230+ deviation documented in ``docs/osh_spec_deviations.md`` §1.
1231+ - ``application/swe+json`` → `SWEJSONCommandSchema` carrying
1232+ `recordSchema` (the SWE Common component) and `encoding`
1233+ (`JSONEncoding`). Spec-canonical; pass
1234+ ``command_format='application/swe+json'`` to opt in.
12161235
12171236 :param control_stream_record_schema: DataRecordSchema to wrap.
12181237 Must carry a ``name`` matching NameToken
@@ -1222,8 +1241,9 @@ def add_and_insert_control_stream(self, control_stream_record_schema: DataRecord
12221241 is lowercased and whitespace-stripped.
12231242 :param valid_time: Optional `TimePeriod`; defaults to
12241243 ``[now, now + 1 year]``.
1225- :param command_format: ``"application/swe+json"`` (default) or
1226- ``"application/json"``. Anything else raises ``ValueError``.
1244+ :param command_format: ``"application/json"`` (default) or
1245+ ``"application/swe+json"``. Anything else raises
1246+ ``ValueError``.
12271247 :return: ControlStream object added to the system.
12281248 """
12291249 input_name_checked = input_name if input_name is not None else control_stream_record_schema .label .lower ().replace (
0 commit comments