@ceralive/modem-control 0.2.0 → 1.1.0
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.
- package/README.md +317 -0
- package/dist/backend/at-lease.d.ts +57 -0
- package/dist/backend/at-lease.js +109 -0
- package/dist/backend/cell-info.d.ts +46 -0
- package/dist/backend/cell-info.js +124 -0
- package/dist/backend/constants.d.ts +24 -0
- package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
- package/dist/backend/device-classifier.d.ts +46 -0
- package/dist/backend/device-classifier.js +258 -0
- package/dist/backend/enrichment.d.ts +28 -0
- package/dist/backend/enrichment.js +59 -0
- package/dist/backend/features.d.ts +61 -0
- package/dist/backend/features.js +109 -0
- package/dist/backend/identity-ladder.d.ts +57 -0
- package/dist/backend/identity-ladder.js +158 -0
- package/dist/backend/identity-registry.d.ts +51 -0
- package/dist/backend/identity-registry.js +101 -0
- package/dist/backend/index.d.ts +30 -0
- package/dist/backend/index.js +35 -0
- package/dist/backend/lifecycle-interlock.d.ts +25 -0
- package/dist/backend/lifecycle-interlock.js +18 -0
- package/dist/backend/managed-objects.d.ts +39 -0
- package/dist/backend/managed-objects.js +82 -0
- package/dist/backend/mapping.d.ts +11 -0
- package/dist/backend/mapping.js +148 -0
- package/dist/backend/mm-backend.d.ts +44 -0
- package/dist/backend/mm-backend.js +154 -0
- package/dist/backend/mm-location.d.ts +21 -0
- package/dist/backend/mm-location.js +237 -0
- package/dist/backend/mm-mutations.d.ts +42 -0
- package/dist/backend/mm-mutations.js +259 -0
- package/dist/backend/modem-actor.d.ts +41 -0
- package/dist/backend/modem-actor.js +78 -0
- package/dist/backend/nm-auto-apn.d.ts +54 -0
- package/dist/backend/nm-auto-apn.js +124 -0
- package/dist/backend/nm-gsm-fields.d.ts +23 -0
- package/dist/backend/nm-gsm-fields.js +107 -0
- package/dist/backend/nmcli-nm-port.d.ts +28 -0
- package/dist/backend/nmcli-nm-port.js +173 -0
- package/dist/backend/nmcli-runner.d.ts +24 -0
- package/dist/backend/nmcli-runner.js +35 -0
- package/dist/backend/observer.d.ts +37 -0
- package/dist/backend/observer.js +219 -0
- package/dist/backend/power-contract.d.ts +49 -0
- package/dist/backend/power-contract.js +34 -0
- package/dist/backend/recovery-attribution.d.ts +32 -0
- package/dist/backend/recovery-attribution.js +57 -0
- package/dist/backend/recovery-budget.d.ts +45 -0
- package/dist/backend/recovery-budget.js +44 -0
- package/dist/backend/recovery-ladder.d.ts +94 -0
- package/dist/backend/recovery-ladder.js +116 -0
- package/dist/backend/router-ethernet.d.ts +19 -0
- package/dist/backend/router-ethernet.js +66 -0
- package/dist/backend/row-store.d.ts +13 -0
- package/dist/backend/row-store.js +82 -0
- package/dist/backend/signal-setup.d.ts +30 -0
- package/dist/backend/signal-setup.js +92 -0
- package/dist/backend/sim-unlock.d.ts +13 -0
- package/dist/backend/sim-unlock.js +153 -0
- package/dist/backend/transition-preconditions.d.ts +76 -0
- package/dist/backend/transition-preconditions.js +64 -0
- package/dist/backend/usage/accounting.d.ts +39 -0
- package/dist/backend/usage/accounting.js +73 -0
- package/dist/backend/usage/billing-cycle.d.ts +11 -0
- package/dist/backend/usage/billing-cycle.js +39 -0
- package/dist/backend/usage/boot-id.d.ts +6 -0
- package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
- package/dist/backend/usage/index.d.ts +8 -0
- package/dist/backend/usage/index.js +11 -0
- package/dist/backend/usage/policy-store.d.ts +50 -0
- package/dist/backend/usage/policy-store.js +161 -0
- package/dist/backend/usage/policy-write.d.ts +65 -0
- package/dist/backend/usage/policy-write.js +112 -0
- package/dist/backend/usage/proc-net-dev.d.ts +18 -0
- package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
- package/dist/backend/usage/sampler.d.ts +75 -0
- package/dist/backend/usage/sampler.js +211 -0
- package/dist/backend/usage/store.d.ts +38 -0
- package/dist/backend/usage/store.js +126 -0
- package/dist/backend/usb-device-snapshot.d.ts +31 -0
- package/dist/backend/usb-device-snapshot.js +1 -0
- package/dist/backend/usb-enumerator.d.ts +21 -0
- package/dist/backend/usb-enumerator.js +153 -0
- package/dist/backend/usb-mode-transition.d.ts +29 -0
- package/dist/backend/usb-mode-transition.js +196 -0
- package/dist/band/band-names.d.ts +42 -0
- package/dist/band/band-names.js +150 -0
- package/dist/band/certification.d.ts +84 -0
- package/dist/band/certification.js +127 -0
- package/dist/band/certified-bands.json +4 -0
- package/dist/band/index.d.ts +2 -0
- package/dist/band/index.js +8 -0
- package/dist/capability/detect.d.ts +52 -0
- package/dist/capability/detect.js +86 -0
- package/dist/capability/five-g-preference.d.ts +104 -0
- package/dist/capability/five-g-preference.js +171 -0
- package/dist/capability/index.d.ts +3 -0
- package/dist/capability/index.js +10 -0
- package/dist/capability/support-claim.d.ts +39 -0
- package/dist/capability/support-claim.js +81 -0
- package/dist/domain/brand.d.ts +10 -0
- package/dist/domain/brand.js +21 -0
- package/dist/domain/errors.d.ts +32 -0
- package/dist/domain/errors.js +48 -0
- package/dist/domain/generation.d.ts +8 -0
- package/dist/domain/generation.js +12 -0
- package/dist/domain/guards.d.ts +6 -0
- package/dist/domain/guards.js +127 -0
- package/dist/domain/identity.d.ts +109 -0
- package/dist/domain/identity.js +86 -0
- package/dist/domain/index.d.ts +14 -0
- package/dist/domain/index.js +18 -0
- package/dist/domain/mm-enums.d.ts +12 -0
- package/dist/domain/mm-enums.js +139 -0
- package/dist/domain/modem-presentation.d.ts +10 -0
- package/dist/domain/modem-presentation.js +39 -0
- package/dist/domain/observation.d.ts +39 -0
- package/dist/domain/observation.js +4 -0
- package/dist/domain/operation.d.ts +118 -0
- package/dist/domain/operation.js +85 -0
- package/dist/domain/physical-identity.d.ts +43 -0
- package/dist/domain/physical-identity.js +113 -0
- package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
- package/dist/domain/policy.js +38 -0
- package/dist/domain/shadow-divergence.d.ts +27 -0
- package/dist/domain/shadow-divergence.js +70 -0
- package/dist/domain/snapshot.d.ts +53 -0
- package/dist/domain/snapshot.js +75 -0
- package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
- package/dist/domain/state.js +38 -0
- package/dist/fcc/coverage.d.ts +63 -0
- package/dist/fcc/coverage.js +102 -0
- package/dist/fcc/index.d.ts +3 -0
- package/dist/fcc/index.js +12 -0
- package/dist/fcc/policy-store.d.ts +40 -0
- package/dist/fcc/policy-store.js +146 -0
- package/dist/fcc/policy-write.d.ts +32 -0
- package/dist/fcc/policy-write.js +37 -0
- package/dist/hardware/hilink-protocol.d.ts +39 -0
- package/dist/hardware/hilink-protocol.js +58 -0
- package/dist/hardware/index.d.ts +3 -0
- package/dist/hardware/index.js +15 -0
- package/dist/hardware/router-parsers.d.ts +89 -0
- package/dist/hardware/router-parsers.js +234 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +26 -0
- package/dist/journal/codec.d.ts +44 -0
- package/dist/journal/codec.js +198 -0
- package/dist/journal/engine.d.ts +28 -0
- package/dist/journal/engine.js +68 -0
- package/dist/journal/entry.d.ts +74 -0
- package/dist/journal/entry.js +56 -0
- package/dist/journal/index.d.ts +6 -0
- package/dist/journal/index.js +6 -0
- package/dist/journal/legacy-ceraui.d.ts +73 -0
- package/dist/journal/legacy-ceraui.js +227 -0
- package/dist/journal/recovery.d.ts +58 -0
- package/dist/journal/recovery.js +117 -0
- package/dist/journal/store.d.ts +55 -0
- package/dist/journal/store.js +150 -0
- package/dist/location/fix-state.d.ts +53 -0
- package/dist/location/fix-state.js +75 -0
- package/dist/location/index.d.ts +2 -0
- package/dist/location/index.js +8 -0
- package/dist/location/nmea.d.ts +10 -0
- package/dist/location/nmea.js +89 -0
- package/dist/observations/envelope.d.ts +63 -0
- package/dist/observations/envelope.js +77 -0
- package/dist/observations/freshness.d.ts +28 -0
- package/dist/observations/freshness.js +76 -0
- package/dist/observations/index.d.ts +13 -0
- package/dist/observations/index.js +24 -0
- package/dist/observations/metric.d.ts +47 -0
- package/dist/observations/metric.js +73 -0
- package/dist/observations/model.d.ts +80 -0
- package/dist/observations/model.js +11 -0
- package/dist/observations/provenance.d.ts +94 -0
- package/dist/observations/provenance.js +67 -0
- package/dist/observations/raw.d.ts +42 -0
- package/dist/observations/raw.js +146 -0
- package/dist/observations/reading.d.ts +51 -0
- package/dist/observations/reading.js +65 -0
- package/dist/observations/sources/hilink.d.ts +10 -0
- package/dist/observations/sources/hilink.js +84 -0
- package/dist/observations/sources/modemmanager.d.ts +18 -0
- package/dist/observations/sources/modemmanager.js +239 -0
- package/dist/observations/sources/router-shared.d.ts +29 -0
- package/dist/observations/sources/router-shared.js +68 -0
- package/dist/observations/sources/ufi.d.ts +10 -0
- package/dist/observations/sources/ufi.js +111 -0
- package/dist/observations/sources/zte.d.ts +7 -0
- package/dist/observations/sources/zte.js +71 -0
- package/dist/observations/state-separation.d.ts +64 -0
- package/dist/observations/state-separation.js +52 -0
- package/dist/operations/contracts.d.ts +72 -0
- package/dist/operations/contracts.js +1 -0
- package/dist/operations/index.d.ts +1 -0
- package/dist/operations/index.js +1 -0
- package/dist/operations/operation-engine.d.ts +16 -0
- package/dist/operations/operation-engine.js +194 -0
- package/dist/ports/index.d.ts +12 -0
- package/{src/ports/index.ts → dist/ports/index.js} +12 -8
- package/dist/ports/location.d.ts +87 -0
- package/dist/ports/location.js +36 -0
- package/dist/ports/modem-manager.d.ts +89 -0
- package/dist/ports/modem-manager.js +9 -0
- package/dist/ports/mutation-admission.d.ts +27 -0
- package/dist/ports/mutation-admission.js +9 -0
- package/dist/ports/network-manager.d.ts +68 -0
- package/dist/ports/network-manager.js +13 -0
- package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
- package/dist/ports/observation.js +7 -0
- package/dist/ports/ops.d.ts +44 -0
- package/dist/ports/ops.js +16 -0
- package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
- package/dist/ports/receipts.js +9 -0
- package/dist/ports/reconcile.d.ts +33 -0
- package/dist/ports/reconcile.js +200 -0
- package/dist/ports/resource-ownership.d.ts +29 -0
- package/dist/ports/resource-ownership.js +1 -0
- package/dist/ports/router.d.ts +19 -0
- package/dist/ports/router.js +7 -0
- package/dist/ports/sms.d.ts +64 -0
- package/dist/ports/sms.js +24 -0
- package/dist/ports/uhubctl.d.ts +6 -0
- package/dist/ports/uhubctl.js +1 -0
- package/dist/providers/contracts.d.ts +124 -0
- package/dist/providers/contracts.js +10 -0
- package/dist/providers/huawei-hilink/index.d.ts +2 -0
- package/dist/providers/huawei-hilink/index.js +2 -0
- package/dist/providers/huawei-hilink/operations.d.ts +20 -0
- package/dist/providers/huawei-hilink/operations.js +56 -0
- package/dist/providers/huawei-hilink/provider.d.ts +52 -0
- package/dist/providers/huawei-hilink/provider.js +76 -0
- package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
- package/dist/providers/huawei-hilink/runtime.js +171 -0
- package/dist/providers/huawei-hilink/session.d.ts +28 -0
- package/dist/providers/huawei-hilink/session.js +120 -0
- package/dist/providers/huawei-hilink/transport.d.ts +19 -0
- package/dist/providers/huawei-hilink/transport.js +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/matcher.d.ts +3 -0
- package/dist/providers/matcher.js +205 -0
- package/dist/providers/modem-manager/errors.d.ts +7 -0
- package/dist/providers/modem-manager/errors.js +37 -0
- package/dist/providers/modem-manager/generic-operations.d.ts +8 -0
- package/dist/providers/modem-manager/generic-operations.js +207 -0
- package/dist/providers/modem-manager/index.d.ts +3 -0
- package/dist/providers/modem-manager/index.js +3 -0
- package/dist/providers/modem-manager/module-operations.d.ts +21 -0
- package/dist/providers/modem-manager/module-operations.js +118 -0
- package/dist/providers/modem-manager/provider.d.ts +39 -0
- package/dist/providers/modem-manager/provider.js +152 -0
- package/dist/providers/modem-manager/snapshot.d.ts +5 -0
- package/dist/providers/modem-manager/snapshot.js +204 -0
- package/dist/providers/modem-manager/types.d.ts +135 -0
- package/dist/providers/modem-manager/types.js +1 -0
- package/dist/providers/network-manager/adapter.d.ts +71 -0
- package/dist/providers/network-manager/adapter.js +348 -0
- package/dist/providers/network-manager/index.d.ts +2 -0
- package/dist/providers/network-manager/index.js +2 -0
- package/dist/providers/network-manager/types.d.ts +163 -0
- package/dist/providers/network-manager/types.js +77 -0
- package/dist/providers/registry.d.ts +13 -0
- package/dist/providers/registry.js +33 -0
- package/dist/providers/ufi-himi/index.d.ts +6 -0
- package/dist/providers/ufi-himi/index.js +6 -0
- package/dist/providers/ufi-himi/operations.d.ts +41 -0
- package/dist/providers/ufi-himi/operations.js +66 -0
- package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
- package/dist/providers/ufi-himi/prohibitions.js +88 -0
- package/dist/providers/ufi-himi/provider.d.ts +41 -0
- package/dist/providers/ufi-himi/provider.js +204 -0
- package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
- package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
- package/dist/providers/ufi-himi/session.d.ts +46 -0
- package/dist/providers/ufi-himi/session.js +92 -0
- package/dist/providers/ufi-himi/transport.d.ts +29 -0
- package/dist/providers/ufi-himi/transport.js +25 -0
- package/dist/providers/zte-goform/index.d.ts +2 -0
- package/dist/providers/zte-goform/index.js +2 -0
- package/dist/providers/zte-goform/provider.d.ts +51 -0
- package/dist/providers/zte-goform/provider.js +101 -0
- package/dist/providers/zte-goform/session.d.ts +17 -0
- package/dist/providers/zte-goform/session.js +133 -0
- package/dist/providers/zte-goform/transport.d.ts +16 -0
- package/dist/providers/zte-goform/transport.js +1 -0
- package/dist/radio/band-truth.d.ts +50 -0
- package/dist/radio/band-truth.js +92 -0
- package/dist/radio/index.d.ts +3 -0
- package/dist/radio/index.js +10 -0
- package/dist/radio/mode-combinations.d.ts +78 -0
- package/dist/radio/mode-combinations.js +198 -0
- package/dist/radio/mode-truth.d.ts +67 -0
- package/dist/radio/mode-truth.js +112 -0
- package/dist/redact.d.ts +15 -0
- package/dist/redact.js +189 -0
- package/dist/safety/composition-root.d.ts +28 -0
- package/dist/safety/composition-root.js +65 -0
- package/dist/safety/flock-resource-ownership.d.ts +11 -0
- package/dist/safety/flock-resource-ownership.js +151 -0
- package/dist/safety/index.d.ts +2 -0
- package/dist/safety/index.js +2 -0
- package/dist/sms/dbus-messaging.d.ts +17 -0
- package/dist/sms/dbus-messaging.js +185 -0
- package/dist/sms/inbox-store.d.ts +10 -0
- package/dist/sms/inbox-store.js +82 -0
- package/dist/sms/index.d.ts +4 -0
- package/dist/sms/index.js +10 -0
- package/dist/sms/mmcli-parse.d.ts +54 -0
- package/dist/sms/mmcli-parse.js +224 -0
- package/dist/sms/normalize.d.ts +42 -0
- package/dist/sms/normalize.js +95 -0
- package/dist/testing/domain-fakes.d.ts +58 -0
- package/dist/testing/domain-fakes.js +98 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +17 -0
- package/dist/testing/provider-fakes.d.ts +45 -0
- package/dist/testing/provider-fakes.js +64 -0
- package/dist/transport/calls.d.ts +9 -0
- package/dist/transport/calls.js +88 -0
- package/dist/transport/codec.d.ts +3 -0
- package/dist/transport/codec.js +207 -0
- package/dist/transport/dbus-native.d.ts +57 -0
- package/dist/transport/dbus-native.js +17 -0
- package/dist/transport/errors.d.ts +21 -0
- package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
- package/dist/transport/index.d.ts +4 -0
- package/dist/transport/index.js +9 -0
- package/dist/transport/signals.d.ts +15 -0
- package/dist/transport/signals.js +123 -0
- package/dist/transport/signature.d.ts +7 -0
- package/dist/transport/signature.js +94 -0
- package/dist/transport/transport.d.ts +2 -0
- package/dist/transport/transport.js +202 -0
- package/dist/transport/types.d.ts +61 -0
- package/dist/transport/types.js +19 -0
- package/dist/usb-mode/catalog-schema.d.ts +139 -0
- package/dist/usb-mode/catalog-schema.js +97 -0
- package/dist/usb-mode/catalog.d.ts +21 -0
- package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
- package/dist/usb-mode/certified-catalog.json +67 -0
- package/dist/usb-mode/index.d.ts +5 -0
- package/dist/usb-mode/index.js +15 -0
- package/dist/usb-mode/ingestion.d.ts +111 -0
- package/dist/usb-mode/ingestion.js +187 -0
- package/dist/usb-mode/promotion-review.d.ts +21 -0
- package/dist/usb-mode/promotion-review.js +87 -0
- package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
- package/dist/usb-mode/usb-devices-parse.js +157 -0
- package/dist/ussd/calls.d.ts +32 -0
- package/dist/ussd/calls.js +96 -0
- package/dist/ussd/index.d.ts +5 -0
- package/dist/ussd/index.js +12 -0
- package/dist/ussd/mm-ussd.d.ts +37 -0
- package/dist/ussd/mm-ussd.js +205 -0
- package/dist/ussd/refusal.d.ts +53 -0
- package/dist/ussd/refusal.js +154 -0
- package/dist/ussd/registration.d.ts +20 -0
- package/dist/ussd/registration.js +101 -0
- package/dist/ussd/session.d.ts +84 -0
- package/dist/ussd/session.js +163 -0
- package/package.json +38 -4
- package/src/backend/at-lease.test.ts +0 -106
- package/src/backend/at-lease.ts +0 -158
- package/src/backend/cell-info.test.ts +0 -154
- package/src/backend/cell-info.ts +0 -160
- package/src/backend/device-classifier.test.ts +0 -168
- package/src/backend/device-classifier.ts +0 -248
- package/src/backend/enrichment.ts +0 -96
- package/src/backend/features.test.ts +0 -162
- package/src/backend/features.ts +0 -179
- package/src/backend/identity-ladder.test.ts +0 -117
- package/src/backend/identity-ladder.ts +0 -221
- package/src/backend/identity-registry.test.ts +0 -89
- package/src/backend/identity-registry.ts +0 -151
- package/src/backend/index.ts +0 -221
- package/src/backend/lifecycle-interlock.ts +0 -38
- package/src/backend/managed-objects.ts +0 -108
- package/src/backend/mapping.ts +0 -160
- package/src/backend/mm-backend.ts +0 -191
- package/src/backend/mm-mutations.ts +0 -228
- package/src/backend/modem-actor.test.ts +0 -95
- package/src/backend/modem-actor.ts +0 -112
- package/src/backend/nm-auto-apn.ts +0 -161
- package/src/backend/nm-gsm-fields.ts +0 -122
- package/src/backend/nmcli-nm-port.ts +0 -228
- package/src/backend/nmcli-runner.ts +0 -52
- package/src/backend/observer.ts +0 -297
- package/src/backend/power-contract.test.ts +0 -40
- package/src/backend/power-contract.ts +0 -83
- package/src/backend/recovery-attribution.test.ts +0 -102
- package/src/backend/recovery-attribution.ts +0 -86
- package/src/backend/recovery-budget.test.ts +0 -64
- package/src/backend/recovery-budget.ts +0 -84
- package/src/backend/recovery-ladder.test.ts +0 -257
- package/src/backend/recovery-ladder.ts +0 -249
- package/src/backend/router-ethernet.test.ts +0 -71
- package/src/backend/router-ethernet.ts +0 -90
- package/src/backend/row-store.ts +0 -105
- package/src/backend/signal-setup.ts +0 -112
- package/src/backend/sim-unlock.ts +0 -193
- package/src/backend/transition-preconditions.ts +0 -149
- package/src/backend/usage/accounting.test.ts +0 -147
- package/src/backend/usage/accounting.ts +0 -123
- package/src/backend/usage/billing-cycle.test.ts +0 -62
- package/src/backend/usage/billing-cycle.ts +0 -45
- package/src/backend/usage/index.ts +0 -37
- package/src/backend/usage/proc-net-dev.test.ts +0 -56
- package/src/backend/usage/sampler.test.ts +0 -219
- package/src/backend/usage/sampler.ts +0 -228
- package/src/backend/usage/store.test.ts +0 -148
- package/src/backend/usage/store.ts +0 -177
- package/src/backend/usb-enumerator.test.ts +0 -87
- package/src/backend/usb-enumerator.ts +0 -181
- package/src/backend/usb-mode-transition.test.ts +0 -323
- package/src/backend/usb-mode-transition.ts +0 -253
- package/src/domain/brand.ts +0 -29
- package/src/domain/errors.ts +0 -77
- package/src/domain/guards.test.ts +0 -218
- package/src/domain/guards.ts +0 -144
- package/src/domain/identity.test.ts +0 -83
- package/src/domain/identity.ts +0 -165
- package/src/domain/index.ts +0 -12
- package/src/domain/snapshot.test.ts +0 -266
- package/src/domain/snapshot.ts +0 -120
- package/src/index.test.ts +0 -6
- package/src/index.ts +0 -15
- package/src/ports/README.md +0 -61
- package/src/ports/forbidden-surface.test.ts +0 -241
- package/src/ports/modem-manager.ts +0 -72
- package/src/ports/network-manager.ts +0 -87
- package/src/ports/ops.ts +0 -60
- package/src/ports/ops.type-test.ts +0 -39
- package/src/ports/receipts.test.ts +0 -153
- package/src/ports/reconcile.test.ts +0 -152
- package/src/ports/reconcile.ts +0 -338
- package/src/ports/router.ts +0 -29
- package/src/redact.test.ts +0 -82
- package/src/redact.ts +0 -73
- package/src/transport/README.md +0 -65
- package/src/transport/calls.ts +0 -113
- package/src/transport/characterization.test.ts +0 -260
- package/src/transport/codec.test.ts +0 -118
- package/src/transport/codec.ts +0 -240
- package/src/transport/conformance-python.test.ts +0 -152
- package/src/transport/conformance-same-lib.test.ts +0 -115
- package/src/transport/dbus-native-lib.d.ts +0 -19
- package/src/transport/dbus-native.ts +0 -85
- package/src/transport/index.ts +0 -30
- package/src/transport/no-library-leak.test.ts +0 -61
- package/src/transport/reliability.test.ts +0 -173
- package/src/transport/signals.ts +0 -150
- package/src/transport/signature.ts +0 -110
- package/src/transport/test-support/fake-service.ts +0 -168
- package/src/transport/test-support/independent-producer.py +0 -110
- package/src/transport/test-support/private-bus.ts +0 -66
- package/src/transport/transport.ts +0 -250
- package/src/transport/types.ts +0 -118
- package/src/usb-mode/catalog-schema.test.ts +0 -181
- package/src/usb-mode/catalog-schema.ts +0 -113
- package/src/usb-mode/certified-catalog.json +0 -67
- package/src/usb-mode/index.ts +0 -27
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { epochMillis, runtimePath } from '../../domain/index.js';
|
|
2
|
+
import { resolveFccUnlockCoverage } from '../../fcc/index.js';
|
|
3
|
+
import { advanceGnssFixState, GNSS_OFF } from '../../location/index.js';
|
|
4
|
+
import { createDbusSmsPort } from '../../sms/index.js';
|
|
5
|
+
export class ModemManagerModuleOperations {
|
|
6
|
+
operations;
|
|
7
|
+
#options;
|
|
8
|
+
#smsPorts = new Map();
|
|
9
|
+
#locationStates = new Map();
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.#options = options;
|
|
12
|
+
this.operations = {
|
|
13
|
+
location: {
|
|
14
|
+
status: async (context) => {
|
|
15
|
+
const path = await this.#modemPath(context);
|
|
16
|
+
return path === undefined
|
|
17
|
+
? { ok: false, reason: 'modem-not-found' }
|
|
18
|
+
: this.#options.location.getLocationStatus(runtimePath(path));
|
|
19
|
+
},
|
|
20
|
+
enable: async (context, sources) => {
|
|
21
|
+
const path = await this.#modemPath(context);
|
|
22
|
+
if (path === undefined)
|
|
23
|
+
return this.#unsupportedToggle();
|
|
24
|
+
const result = await this.#options.location.enableGnss(runtimePath(path), sources);
|
|
25
|
+
if (result.outcome === 'applied') {
|
|
26
|
+
this.#setLocationState(context, {
|
|
27
|
+
kind: 'gnss-enabled',
|
|
28
|
+
at: epochMillis(this.#options.now()),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
},
|
|
33
|
+
disable: async (context) => {
|
|
34
|
+
const path = await this.#modemPath(context);
|
|
35
|
+
if (path === undefined)
|
|
36
|
+
return this.#unsupportedToggle();
|
|
37
|
+
const result = await this.#options.location.disableGnss(runtimePath(path));
|
|
38
|
+
if (result.outcome === 'applied')
|
|
39
|
+
this.#setLocationState(context, { kind: 'gnss-disabled' });
|
|
40
|
+
return result;
|
|
41
|
+
},
|
|
42
|
+
readFix: async (context) => {
|
|
43
|
+
const path = await this.#modemPath(context);
|
|
44
|
+
if (path === undefined)
|
|
45
|
+
return { outcome: 'unsupported', reason: 'modem-not-found' };
|
|
46
|
+
const read = await this.#options.location.readFix(runtimePath(path));
|
|
47
|
+
this.#setLocationState(context, {
|
|
48
|
+
kind: 'read',
|
|
49
|
+
at: epochMillis(this.#options.now()),
|
|
50
|
+
read,
|
|
51
|
+
});
|
|
52
|
+
return read;
|
|
53
|
+
},
|
|
54
|
+
state: (context) => this.#locationStates.get(String(context.physicalModemId)) ?? GNSS_OFF,
|
|
55
|
+
tick: (context) => this.#setLocationState(context, {
|
|
56
|
+
kind: 'tick',
|
|
57
|
+
at: epochMillis(this.#options.now()),
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
sms: (context) => this.#sms(context),
|
|
61
|
+
ussd: options.ussd,
|
|
62
|
+
initiateUssd: (context, ussdCommand) => this.#ussd(context, (path) => options.ussd.initiate(runtimePath(path), ussdCommand)),
|
|
63
|
+
respondUssd: (context, ussdResponse) => this.#ussd(context, (path) => options.ussd.respond(runtimePath(path), ussdResponse)),
|
|
64
|
+
cancelUssd: (context) => this.#ussd(context, (path) => options.ussd.cancel(runtimePath(path))),
|
|
65
|
+
fccCoverage: resolveFccUnlockCoverage,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async stop() {
|
|
69
|
+
for (const port of this.#smsPorts.values())
|
|
70
|
+
await port.stop();
|
|
71
|
+
this.#smsPorts.clear();
|
|
72
|
+
this.#options.ussd.stop();
|
|
73
|
+
}
|
|
74
|
+
async #sms(context) {
|
|
75
|
+
const snapshot = await this.#options.readSnapshot(context);
|
|
76
|
+
if (!snapshot.ok)
|
|
77
|
+
return { ok: false, reason: snapshot.reason };
|
|
78
|
+
if (!snapshot.capabilities.sms)
|
|
79
|
+
return { ok: false, reason: 'unsupported' };
|
|
80
|
+
let port = this.#smsPorts.get(snapshot.modemPath);
|
|
81
|
+
if (port === undefined) {
|
|
82
|
+
port = createDbusSmsPort({
|
|
83
|
+
transport: this.#options.transport,
|
|
84
|
+
modemPath: snapshot.modemPath,
|
|
85
|
+
destination: this.#options.destination,
|
|
86
|
+
});
|
|
87
|
+
this.#smsPorts.set(snapshot.modemPath, port);
|
|
88
|
+
}
|
|
89
|
+
return { ok: true, port };
|
|
90
|
+
}
|
|
91
|
+
async #ussd(context, run) {
|
|
92
|
+
const path = await this.#modemPath(context);
|
|
93
|
+
return path === undefined
|
|
94
|
+
? {
|
|
95
|
+
ok: false,
|
|
96
|
+
snapshot: this.#options.ussd.snapshot(runtimePath('/')),
|
|
97
|
+
refusal: 'unsupported',
|
|
98
|
+
}
|
|
99
|
+
: run(path);
|
|
100
|
+
}
|
|
101
|
+
async #modemPath(context) {
|
|
102
|
+
const snapshot = await this.#options.readSnapshot(context);
|
|
103
|
+
return snapshot.ok ? snapshot.modemPath : undefined;
|
|
104
|
+
}
|
|
105
|
+
#setLocationState(context, event) {
|
|
106
|
+
const key = String(context.physicalModemId);
|
|
107
|
+
const next = advanceGnssFixState(this.#locationStates.get(key) ?? GNSS_OFF, event);
|
|
108
|
+
this.#locationStates.set(key, next);
|
|
109
|
+
return next;
|
|
110
|
+
}
|
|
111
|
+
#unsupportedToggle() {
|
|
112
|
+
return {
|
|
113
|
+
outcome: 'unsupported',
|
|
114
|
+
reason: 'modem-not-found',
|
|
115
|
+
enabledSources: new Set(),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type MmDbusBackend, type QuiesceHook } from '../../backend/index.js';
|
|
2
|
+
import { type BandCertificationCatalog, type BandSku } from '../../band/index.js';
|
|
3
|
+
import { type NormalizedModemObservation } from '../../observations/index.js';
|
|
4
|
+
import type { ObservationListener } from '../../ports/index.js';
|
|
5
|
+
import type { DbusTransport } from '../../transport/index.js';
|
|
6
|
+
import type { ProviderDefinition, ProviderExecutionContext } from '../contracts.js';
|
|
7
|
+
import type { ModemManagerProviderLifecycle, ModemManagerProviderOperations, ModemManagerSnapshotResult } from './types.js';
|
|
8
|
+
export interface ModemManagerProviderOptions {
|
|
9
|
+
readonly transport: DbusTransport;
|
|
10
|
+
readonly destination?: string;
|
|
11
|
+
readonly quiesce?: QuiesceHook;
|
|
12
|
+
readonly now?: () => number;
|
|
13
|
+
/**
|
|
14
|
+
* The band-lock certification catalog. Defaults to the one shipped in this package,
|
|
15
|
+
* which is EMPTY — so a band write is refused on every device until a human-reviewed
|
|
16
|
+
* commit adds an entry carrying its bench transcript.
|
|
17
|
+
*/
|
|
18
|
+
readonly bandCertificationCatalog?: BandCertificationCatalog;
|
|
19
|
+
/**
|
|
20
|
+
* The device's SKU, if the embedding process can resolve one.
|
|
21
|
+
*
|
|
22
|
+
* ModemManager's `Modem` interface carries `Model` and `Revision` but NO USB
|
|
23
|
+
* `vid:pid`, and `BandSku` is keyed on all three — so this package structurally
|
|
24
|
+
* cannot build one from the bus alone, and a partial match would certify a family
|
|
25
|
+
* no reviewer looked at. With no resolver injected there is no SKU, hence no entry,
|
|
26
|
+
* hence no band write: fail-closed, which is this module's whole stance.
|
|
27
|
+
*/
|
|
28
|
+
readonly bandSku?: (context: ProviderExecutionContext) => BandSku | undefined;
|
|
29
|
+
}
|
|
30
|
+
export declare class ModemManagerProvider implements ModemManagerProviderLifecycle {
|
|
31
|
+
#private;
|
|
32
|
+
readonly definition: ProviderDefinition<NormalizedModemObservation, ModemManagerProviderOperations>;
|
|
33
|
+
constructor(options: ModemManagerProviderOptions);
|
|
34
|
+
start(): ReturnType<MmDbusBackend['start']>;
|
|
35
|
+
observe(listener: ObservationListener): () => void;
|
|
36
|
+
stop(): Promise<void>;
|
|
37
|
+
readSnapshot(context: ProviderExecutionContext): Promise<ModemManagerSnapshotResult>;
|
|
38
|
+
}
|
|
39
|
+
export declare function createModemManagerProvider(options: ModemManagerProviderOptions): ModemManagerProvider;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { createMmDbusBackend, fetchManagedObjects, MM_BUS_NAME, MmLocation, ModemActor, } from '../../backend/index.js';
|
|
2
|
+
import { BAND_CERTIFICATION_CATALOG, findBandCertification, } from '../../band/index.js';
|
|
3
|
+
import { epochMillis, sourceEpoch, stableKeyFromPhysicalModemId, } from '../../domain/index.js';
|
|
4
|
+
import { unavailableObservation } from '../../observations/index.js';
|
|
5
|
+
import { MmUssd } from '../../ussd/index.js';
|
|
6
|
+
import { mapModemManagerError } from './errors.js';
|
|
7
|
+
import { createGenericOperations } from './generic-operations.js';
|
|
8
|
+
import { ModemManagerModuleOperations } from './module-operations.js';
|
|
9
|
+
import { buildModemManagerSnapshot } from './snapshot.js';
|
|
10
|
+
const PROFILE = 'generic-mm';
|
|
11
|
+
export class ModemManagerProvider {
|
|
12
|
+
definition;
|
|
13
|
+
#transport;
|
|
14
|
+
#destination;
|
|
15
|
+
#now;
|
|
16
|
+
#backend;
|
|
17
|
+
#bandCatalog;
|
|
18
|
+
#bandSku;
|
|
19
|
+
#stableKeyByPath = new Map();
|
|
20
|
+
#moduleOperations;
|
|
21
|
+
#startPromise;
|
|
22
|
+
#epoch = 1;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.#transport = options.transport;
|
|
25
|
+
this.#destination = options.destination ?? MM_BUS_NAME;
|
|
26
|
+
this.#now = options.now ?? Date.now;
|
|
27
|
+
this.#bandCatalog = options.bandCertificationCatalog ?? BAND_CERTIFICATION_CATALOG;
|
|
28
|
+
this.#bandSku = options.bandSku ?? (() => undefined);
|
|
29
|
+
const actor = new ModemActor(options.quiesce);
|
|
30
|
+
const resolveStableKey = (modem) => this.#stableKeyByPath.get(modem) ?? modem;
|
|
31
|
+
this.#backend = createMmDbusBackend({
|
|
32
|
+
transport: this.#transport,
|
|
33
|
+
destination: this.#destination,
|
|
34
|
+
actor,
|
|
35
|
+
now: this.#now,
|
|
36
|
+
});
|
|
37
|
+
const location = new MmLocation({
|
|
38
|
+
transport: this.#transport,
|
|
39
|
+
actor,
|
|
40
|
+
destination: this.#destination,
|
|
41
|
+
resolveStableKey,
|
|
42
|
+
now: this.#now,
|
|
43
|
+
});
|
|
44
|
+
const ussd = new MmUssd({
|
|
45
|
+
transport: this.#transport,
|
|
46
|
+
actor,
|
|
47
|
+
destination: this.#destination,
|
|
48
|
+
resolveStableKey,
|
|
49
|
+
});
|
|
50
|
+
this.#moduleOperations = new ModemManagerModuleOperations({
|
|
51
|
+
transport: this.#transport,
|
|
52
|
+
destination: this.#destination,
|
|
53
|
+
location,
|
|
54
|
+
ussd,
|
|
55
|
+
now: this.#now,
|
|
56
|
+
readSnapshot: (context) => this.readSnapshot(context),
|
|
57
|
+
});
|
|
58
|
+
const operations = {
|
|
59
|
+
access: 'read-write',
|
|
60
|
+
...createGenericOperations({
|
|
61
|
+
backend: this.#backend,
|
|
62
|
+
readSnapshot: (context) => this.readSnapshot(context),
|
|
63
|
+
}),
|
|
64
|
+
...this.#moduleOperations.operations,
|
|
65
|
+
};
|
|
66
|
+
this.definition = {
|
|
67
|
+
id: 'modemmanager',
|
|
68
|
+
profileVersion: '1',
|
|
69
|
+
eligibleTransports: ['modemmanager'],
|
|
70
|
+
passiveMatchers: [],
|
|
71
|
+
unauthenticatedProbes: [
|
|
72
|
+
{ id: 'object-manager-modem', run: (request) => this.#probe(request) },
|
|
73
|
+
],
|
|
74
|
+
capabilityReaders: [
|
|
75
|
+
{ id: 'runtime-modem-interface', read: (context) => this.#capability(context) },
|
|
76
|
+
],
|
|
77
|
+
observe: (context) => this.#observeNormalized(context),
|
|
78
|
+
operations: () => operations,
|
|
79
|
+
contractFixtures: [],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
start() {
|
|
83
|
+
this.#startPromise ??= this.#backend.start();
|
|
84
|
+
return this.#startPromise;
|
|
85
|
+
}
|
|
86
|
+
observe(listener) {
|
|
87
|
+
return this.#backend.observe(listener);
|
|
88
|
+
}
|
|
89
|
+
async stop() {
|
|
90
|
+
await this.#moduleOperations.stop();
|
|
91
|
+
await this.#backend.stop();
|
|
92
|
+
}
|
|
93
|
+
async readSnapshot(context) {
|
|
94
|
+
try {
|
|
95
|
+
await this.start();
|
|
96
|
+
const tree = await fetchManagedObjects(this.#transport, this.#destination);
|
|
97
|
+
const snapshot = buildModemManagerSnapshot(tree, context, this.#now, this.#epoch, this.#bandCertification(context));
|
|
98
|
+
if (snapshot === undefined)
|
|
99
|
+
return { ok: false, reason: 'not-found' };
|
|
100
|
+
this.#stableKeyByPath.set(snapshot.modemPath, `modem:${context.physicalModemId}`);
|
|
101
|
+
return { ok: true, ...snapshot };
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
return { ok: false, reason: mapModemManagerError(error).reason };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
#bandCertification(context) {
|
|
108
|
+
const sku = this.#bandSku(context);
|
|
109
|
+
return sku === undefined ? undefined : findBandCertification(this.#bandCatalog, sku);
|
|
110
|
+
}
|
|
111
|
+
async #probe(request) {
|
|
112
|
+
const snapshot = await this.readSnapshot({ ...request, profile: PROFILE });
|
|
113
|
+
return {
|
|
114
|
+
signal: snapshot.ok
|
|
115
|
+
? 'match'
|
|
116
|
+
: snapshot.reason === 'not-found'
|
|
117
|
+
? 'mismatch'
|
|
118
|
+
: 'unknown',
|
|
119
|
+
strength: 'strong',
|
|
120
|
+
profiles: [PROFILE],
|
|
121
|
+
detail: snapshot.ok ? 'runtime-modem-interface-present' : snapshot.reason,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async #capability(context) {
|
|
125
|
+
const snapshot = await this.readSnapshot(context);
|
|
126
|
+
return {
|
|
127
|
+
signal: snapshot.ok
|
|
128
|
+
? 'match'
|
|
129
|
+
: snapshot.reason === 'not-found'
|
|
130
|
+
? 'mismatch'
|
|
131
|
+
: 'unknown',
|
|
132
|
+
strength: 'strong',
|
|
133
|
+
detail: snapshot.ok ? 'generic-controls-derived-from-runtime-properties' : snapshot.reason,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async #observeNormalized(context) {
|
|
137
|
+
const snapshot = await this.readSnapshot(context);
|
|
138
|
+
if (snapshot.ok)
|
|
139
|
+
return [snapshot.observation];
|
|
140
|
+
return [
|
|
141
|
+
unavailableObservation('modemmanager', {
|
|
142
|
+
stableKey: stableKeyFromPhysicalModemId(context.physicalModemId),
|
|
143
|
+
generation: context.generation,
|
|
144
|
+
sourceEpoch: sourceEpoch(this.#epoch),
|
|
145
|
+
observedAt: epochMillis(this.#now()),
|
|
146
|
+
}, snapshot.reason === 'not-found' ? 'device-absent' : 'provider-unavailable'),
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export function createModemManagerProvider(options) {
|
|
151
|
+
return new ModemManagerProvider(options);
|
|
152
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type DecodedManagedObjects } from '../../backend/managed-objects.js';
|
|
2
|
+
import { type BandCertificationEntry } from '../../band/index.js';
|
|
3
|
+
import type { ProviderExecutionContext } from '../contracts.js';
|
|
4
|
+
import type { ModemManagerProviderSnapshot } from './types.js';
|
|
5
|
+
export declare function buildModemManagerSnapshot(tree: DecodedManagedObjects, context: ProviderExecutionContext, now: () => number, epoch: number, bandCertificationEntry: BandCertificationEntry | undefined): ModemManagerProviderSnapshot | undefined;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { MODEM_IFACE, MODEM_LOCATION_IFACE, MODEM3GPP_IFACE, MODEM3GPP_USSD_IFACE, SIM_IFACE, } from '../../backend/constants.js';
|
|
2
|
+
import { findInterface, pathsWithInterface, propValue, } from '../../backend/managed-objects.js';
|
|
3
|
+
import { decodeBandList } from '../../band/index.js';
|
|
4
|
+
import { MESSAGING_IFACE } from '../../capability/index.js';
|
|
5
|
+
import { decodeStateFailedReason, epochMillis, sourceEpoch, stableKeyFromPhysicalModemId, } from '../../domain/index.js';
|
|
6
|
+
import { readSimPresence } from '../../hardware/router-parsers.js';
|
|
7
|
+
import { normalizeModemManagerObservation } from '../../observations/index.js';
|
|
8
|
+
import { describeBandWriteCertification, readRadioModeTruth } from '../../radio/index.js';
|
|
9
|
+
const SIGNAL_IFACE = `${MODEM_IFACE}.Signal`;
|
|
10
|
+
function rawValue(value) {
|
|
11
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')
|
|
12
|
+
return value;
|
|
13
|
+
if (Array.isArray(value)) {
|
|
14
|
+
const result = [];
|
|
15
|
+
for (const item of value) {
|
|
16
|
+
const decoded = rawValue(item);
|
|
17
|
+
if (decoded !== undefined)
|
|
18
|
+
result.push(decoded);
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
function propsRecord(props) {
|
|
25
|
+
if (props === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
const record = {};
|
|
28
|
+
for (const [name, wrapped] of props) {
|
|
29
|
+
const decoded = rawValue(wrapped.value);
|
|
30
|
+
if (decoded !== undefined)
|
|
31
|
+
record[name] = decoded;
|
|
32
|
+
}
|
|
33
|
+
return record;
|
|
34
|
+
}
|
|
35
|
+
function tuple(value) {
|
|
36
|
+
if (!Array.isArray(value))
|
|
37
|
+
return [0, 0];
|
|
38
|
+
const allowed = value[0];
|
|
39
|
+
const preferred = value[1];
|
|
40
|
+
return [typeof allowed === 'number' ? allowed : 0, typeof preferred === 'number' ? preferred : 0];
|
|
41
|
+
}
|
|
42
|
+
function modePairs(value) {
|
|
43
|
+
if (!Array.isArray(value))
|
|
44
|
+
return [];
|
|
45
|
+
const result = [];
|
|
46
|
+
for (const candidate of value) {
|
|
47
|
+
const [allowed, preferred] = tuple(candidate);
|
|
48
|
+
if (allowed > 0)
|
|
49
|
+
result.push({ allowed, preferred });
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A `DbusValue` as the retention layer keeps it.
|
|
55
|
+
*
|
|
56
|
+
* `rawValue` already produces exactly this for a struct (an array of its members), so
|
|
57
|
+
* `SupportedModes` and `CurrentModes` reach the verbatim decoder in the same shape the
|
|
58
|
+
* diagnostics block retains — one representation, so the two cannot disagree.
|
|
59
|
+
*/
|
|
60
|
+
function structValue(value) {
|
|
61
|
+
return value === undefined ? undefined : rawValue(value);
|
|
62
|
+
}
|
|
63
|
+
function signalState(modem, signal) {
|
|
64
|
+
const quality = tuple(propValue(modem, 'SignalQuality'));
|
|
65
|
+
return {
|
|
66
|
+
...(quality[0] >= 0 ? { quality: quality[0] } : {}),
|
|
67
|
+
...(Array.isArray(propValue(modem, 'SignalQuality')) ? { recent: quality[1] !== 0 } : {}),
|
|
68
|
+
extendedAvailable: signal !== undefined,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function simState(tree, modem) {
|
|
72
|
+
const activePath = propValue(modem, 'Sim');
|
|
73
|
+
const slots = propValue(modem, 'SimSlots');
|
|
74
|
+
const paths = Array.isArray(slots)
|
|
75
|
+
? slots.filter((value) => typeof value === 'string' && value !== '/')
|
|
76
|
+
: [];
|
|
77
|
+
const active = typeof activePath === 'string' ? findInterface(tree, activePath, SIM_IFACE) : undefined;
|
|
78
|
+
const lockRequired = propValue(modem, 'UnlockRequired');
|
|
79
|
+
const rawFailedReason = propValue(modem, 'StateFailedReason');
|
|
80
|
+
const failedReason = typeof rawFailedReason === 'number'
|
|
81
|
+
? decodeStateFailedReason(rawFailedReason)
|
|
82
|
+
: typeof rawFailedReason === 'string'
|
|
83
|
+
? rawFailedReason
|
|
84
|
+
: undefined;
|
|
85
|
+
const primarySlot = propValue(modem, 'PrimarySimSlot');
|
|
86
|
+
const simType = propValue(active, 'SimType');
|
|
87
|
+
const esimStatus = propValue(active, 'EsimStatus');
|
|
88
|
+
const reading = readSimPresence({
|
|
89
|
+
...(typeof activePath === 'string' ? { sim: activePath } : {}),
|
|
90
|
+
...(paths.length > 0 ? { simSlots: paths } : {}),
|
|
91
|
+
...(failedReason === undefined ? {} : { failedReason }),
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
// POSITIVE evidence only: an exported SIM object path proves a SIM. Its ABSENCE
|
|
95
|
+
// proves nothing — `presence` below is the answer to "is there a SIM", and it
|
|
96
|
+
// says `unknown` unless the modem stated `sim-missing` itself.
|
|
97
|
+
present: typeof activePath === 'string' && activePath !== '/',
|
|
98
|
+
presence: reading.presence,
|
|
99
|
+
presenceEvidence: reading.evidence,
|
|
100
|
+
slotCount: paths.length,
|
|
101
|
+
primarySlot: typeof primarySlot === 'number' ? primarySlot : 0,
|
|
102
|
+
...(typeof lockRequired === 'number' ? { lockRequired } : {}),
|
|
103
|
+
...(typeof simType === 'number' ? { simType } : {}),
|
|
104
|
+
...(typeof esimStatus === 'number' ? { esimStatus } : {}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function radioPower(value) {
|
|
108
|
+
switch (value) {
|
|
109
|
+
case 1:
|
|
110
|
+
return 'off';
|
|
111
|
+
case 2:
|
|
112
|
+
return 'low';
|
|
113
|
+
case 3:
|
|
114
|
+
return 'on';
|
|
115
|
+
default:
|
|
116
|
+
return 'unknown';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function hasProperty(props, name) {
|
|
120
|
+
return props.some(([property]) => property === name);
|
|
121
|
+
}
|
|
122
|
+
function findModemPath(tree, context) {
|
|
123
|
+
const [kind, ...parts] = String(context.physicalModemId).split(':');
|
|
124
|
+
const expected = parts.join(':');
|
|
125
|
+
for (const path of pathsWithInterface(tree, MODEM_IFACE)) {
|
|
126
|
+
const modem = findInterface(tree, path, MODEM_IFACE);
|
|
127
|
+
const candidates = kind === 'serial'
|
|
128
|
+
? [propValue(modem, 'DeviceIdentifier')]
|
|
129
|
+
: [propValue(modem, 'Device'), propValue(modem, 'Physdev')];
|
|
130
|
+
if (candidates.some((candidate) => candidate === expected))
|
|
131
|
+
return path;
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
export function buildModemManagerSnapshot(tree, context, now, epoch, bandCertificationEntry) {
|
|
136
|
+
const modemPath = findModemPath(tree, context);
|
|
137
|
+
if (modemPath === undefined)
|
|
138
|
+
return undefined;
|
|
139
|
+
const modem = findInterface(tree, modemPath, MODEM_IFACE);
|
|
140
|
+
if (modem === undefined)
|
|
141
|
+
return undefined;
|
|
142
|
+
const modem3gpp = findInterface(tree, modemPath, MODEM3GPP_IFACE);
|
|
143
|
+
const signal = findInterface(tree, modemPath, SIGNAL_IFACE);
|
|
144
|
+
const activePath = propValue(modem, 'Sim');
|
|
145
|
+
const sim = typeof activePath === 'string' ? findInterface(tree, activePath, SIM_IFACE) : undefined;
|
|
146
|
+
const interfaces = tree.find(([path]) => path === modemPath)?.[1] ?? [];
|
|
147
|
+
const interfaceNames = new Set(interfaces.map(([name]) => name));
|
|
148
|
+
const slots = propValue(modem, 'SimSlots');
|
|
149
|
+
const bandCertification = describeBandWriteCertification({
|
|
150
|
+
entry: bandCertificationEntry,
|
|
151
|
+
supported: decodeBandList(propValue(modem, 'SupportedBands')),
|
|
152
|
+
});
|
|
153
|
+
const capabilities = {
|
|
154
|
+
modeRead: hasProperty(modem, 'CurrentModes'),
|
|
155
|
+
modeWrite: hasProperty(modem, 'CurrentModes'),
|
|
156
|
+
bandRead: hasProperty(modem, 'SupportedBands'),
|
|
157
|
+
bandWrite: hasProperty(modem, 'SupportedBands') &&
|
|
158
|
+
bandCertification.satisfied &&
|
|
159
|
+
bandCertification.offerable.length > 0,
|
|
160
|
+
signalRead: hasProperty(modem, 'SignalQuality') || signal !== undefined,
|
|
161
|
+
simRead: hasProperty(modem, 'Sim') || hasProperty(modem, 'SimSlots'),
|
|
162
|
+
multiSim: Array.isArray(slots) && slots.filter((entry) => entry !== '/').length > 1,
|
|
163
|
+
location: interfaceNames.has(MODEM_LOCATION_IFACE),
|
|
164
|
+
sms: interfaceNames.has(MESSAGING_IFACE),
|
|
165
|
+
ussd: interfaceNames.has(MODEM3GPP_USSD_IFACE),
|
|
166
|
+
powerRead: hasProperty(modem, 'PowerState'),
|
|
167
|
+
};
|
|
168
|
+
const [allowed, preferred] = tuple(propValue(modem, 'CurrentModes'));
|
|
169
|
+
const modeTruth = readRadioModeTruth({
|
|
170
|
+
currentModes: structValue(propValue(modem, 'CurrentModes')),
|
|
171
|
+
supportedModes: structValue(propValue(modem, 'SupportedModes')),
|
|
172
|
+
});
|
|
173
|
+
const normalizationContext = {
|
|
174
|
+
stableKey: stableKeyFromPhysicalModemId(context.physicalModemId),
|
|
175
|
+
generation: context.generation,
|
|
176
|
+
sourceEpoch: sourceEpoch(epoch),
|
|
177
|
+
observedAt: epochMillis(now()),
|
|
178
|
+
};
|
|
179
|
+
// The struct properties are handed to normalization VERBATIM — `CurrentModes` as its
|
|
180
|
+
// `(uu)` pair, `SignalQuality` as its `(ub)` pair. Flattening them here (which this
|
|
181
|
+
// did) kept the first member and dropped the preferred mode and the recency flag
|
|
182
|
+
// before the diagnostics block ever saw them, which is a drop no downstream layer
|
|
183
|
+
// can undo. The normalizer reads either shape.
|
|
184
|
+
const modemRaw = propsRecord(modem) ?? {};
|
|
185
|
+
return {
|
|
186
|
+
modemPath,
|
|
187
|
+
capabilities,
|
|
188
|
+
bandCertification,
|
|
189
|
+
radio: {
|
|
190
|
+
current: { allowed, preferred },
|
|
191
|
+
supported: modePairs(propValue(modem, 'SupportedModes')),
|
|
192
|
+
truth: modeTruth,
|
|
193
|
+
},
|
|
194
|
+
signal: signalState(modem, signal),
|
|
195
|
+
sim: simState(tree, modem),
|
|
196
|
+
power: radioPower(propValue(modem, 'PowerState')),
|
|
197
|
+
observation: normalizeModemManagerObservation({
|
|
198
|
+
modem: modemRaw,
|
|
199
|
+
...(modem3gpp === undefined ? {} : { modem3gpp: propsRecord(modem3gpp) ?? {} }),
|
|
200
|
+
...(sim === undefined ? {} : { sim: propsRecord(sim) ?? {} }),
|
|
201
|
+
...(signal === undefined ? {} : { signal: propsRecord(signal) ?? {} }),
|
|
202
|
+
}, normalizationContext),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { DesiredRadio, ObservationEnvelope, OperationDescriptor, OperationResult, RadioPower } from '../../domain/index.js';
|
|
2
|
+
import type { SimPresence, SimPresenceEvidence } from '../../hardware/router-parsers.js';
|
|
3
|
+
import type { GnssFixState } from '../../location/index.js';
|
|
4
|
+
import type { NormalizedModemObservation } from '../../observations/index.js';
|
|
5
|
+
import type { FixRead, GnssSource, LocationStatusResult, LocationToggleResult, ModemBands, ObservationList, ObservationListener, SmsObservationPort } from '../../ports/index.js';
|
|
6
|
+
import type { BandWriteCertification, ModeSelection, RadioModeTruth } from '../../radio/index.js';
|
|
7
|
+
import type { MmUssd, UssdVerbResult } from '../../ussd/index.js';
|
|
8
|
+
import type { ProviderExecutionContext, ProviderOperationsSurface } from '../contracts.js';
|
|
9
|
+
import type { ModemManagerRefusalReason } from './errors.js';
|
|
10
|
+
export type ModemManagerCapabilities = {
|
|
11
|
+
readonly modeRead: boolean;
|
|
12
|
+
readonly modeWrite: boolean;
|
|
13
|
+
readonly bandRead: boolean;
|
|
14
|
+
readonly bandWrite: boolean;
|
|
15
|
+
readonly signalRead: boolean;
|
|
16
|
+
readonly simRead: boolean;
|
|
17
|
+
readonly multiSim: boolean;
|
|
18
|
+
readonly location: boolean;
|
|
19
|
+
readonly sms: boolean;
|
|
20
|
+
readonly ussd: boolean;
|
|
21
|
+
readonly powerRead: boolean;
|
|
22
|
+
};
|
|
23
|
+
export type ModemManagerRadioState = {
|
|
24
|
+
readonly current: {
|
|
25
|
+
readonly allowed: number;
|
|
26
|
+
readonly preferred: number;
|
|
27
|
+
};
|
|
28
|
+
readonly supported: readonly {
|
|
29
|
+
readonly allowed: number;
|
|
30
|
+
readonly preferred: number;
|
|
31
|
+
}[];
|
|
32
|
+
/**
|
|
33
|
+
* The SAME properties decoded without loss — `preferred: 'none'` stays `none`, an
|
|
34
|
+
* unfamiliar mode bit stays offerable as `unknown-combination`, and a member that
|
|
35
|
+
* is not a `(uu)` lands in `undecodable` rather than vanishing. `supported` above
|
|
36
|
+
* is the pre-existing raw-mask view and DROPS a zero-allowed member; this one does
|
|
37
|
+
* not, which is the whole point of carrying both.
|
|
38
|
+
*/
|
|
39
|
+
readonly truth: RadioModeTruth;
|
|
40
|
+
};
|
|
41
|
+
export type ModemManagerSignalState = {
|
|
42
|
+
readonly quality?: number;
|
|
43
|
+
readonly recent?: boolean;
|
|
44
|
+
readonly extendedAvailable: boolean;
|
|
45
|
+
};
|
|
46
|
+
export type ModemManagerSimState = {
|
|
47
|
+
/**
|
|
48
|
+
* The modem exports an ACTIVE SIM object path. `true` is positive evidence of a
|
|
49
|
+
* SIM; `false` is NOT evidence of absence — read `presence` for that.
|
|
50
|
+
*/
|
|
51
|
+
readonly present: boolean;
|
|
52
|
+
/** `absent` only ever comes from `StateFailedReason: sim-missing`. */
|
|
53
|
+
readonly presence: SimPresence;
|
|
54
|
+
readonly presenceEvidence: SimPresenceEvidence;
|
|
55
|
+
readonly slotCount: number;
|
|
56
|
+
readonly primarySlot: number;
|
|
57
|
+
readonly lockRequired?: number;
|
|
58
|
+
readonly simType?: number;
|
|
59
|
+
readonly esimStatus?: number;
|
|
60
|
+
};
|
|
61
|
+
export type ModemManagerProviderSnapshot = {
|
|
62
|
+
readonly modemPath: string;
|
|
63
|
+
readonly capabilities: ModemManagerCapabilities;
|
|
64
|
+
readonly radio: ModemManagerRadioState;
|
|
65
|
+
/** The band-lock gate for THIS device, resolved from the shipped certification catalog. */
|
|
66
|
+
readonly bandCertification: BandWriteCertification;
|
|
67
|
+
readonly signal: ModemManagerSignalState;
|
|
68
|
+
readonly sim: ModemManagerSimState;
|
|
69
|
+
readonly power: RadioPower;
|
|
70
|
+
readonly observation: ObservationEnvelope<NormalizedModemObservation>;
|
|
71
|
+
};
|
|
72
|
+
export type ModemManagerSnapshotResult = ({
|
|
73
|
+
readonly ok: true;
|
|
74
|
+
} & ModemManagerProviderSnapshot) | {
|
|
75
|
+
readonly ok: false;
|
|
76
|
+
readonly reason: ModemManagerRefusalReason;
|
|
77
|
+
};
|
|
78
|
+
export interface ContextReadOperation<O> {
|
|
79
|
+
readonly descriptor: OperationDescriptor<never, O>;
|
|
80
|
+
read(context: ProviderExecutionContext): Promise<OperationResult<O>>;
|
|
81
|
+
}
|
|
82
|
+
export interface ContextWriteOperation<I, O> {
|
|
83
|
+
/** The shape-only descriptor: what this operation is, before any device answered. */
|
|
84
|
+
readonly descriptor: OperationDescriptor<I, O>;
|
|
85
|
+
/**
|
|
86
|
+
* The descriptor as the LIVE device entitles it.
|
|
87
|
+
*
|
|
88
|
+
* A static descriptor cannot carry a device's own catalog, so it cannot say which
|
|
89
|
+
* mode combinations this modem advertises or whether this SKU's band lock is
|
|
90
|
+
* certified. Those are exactly the facts a consumer needs before it offers a
|
|
91
|
+
* control, so they are read here instead of being inferred from a capability flag.
|
|
92
|
+
*/
|
|
93
|
+
describe(context: ProviderExecutionContext): Promise<OperationDescriptor<I, O>>;
|
|
94
|
+
read(context: ProviderExecutionContext): Promise<OperationResult<O>>;
|
|
95
|
+
write(context: ProviderExecutionContext, input: I): Promise<OperationResult<O>>;
|
|
96
|
+
}
|
|
97
|
+
export type SmsPortResult = {
|
|
98
|
+
readonly ok: true;
|
|
99
|
+
readonly port: SmsObservationPort;
|
|
100
|
+
} | {
|
|
101
|
+
readonly ok: false;
|
|
102
|
+
readonly reason: ModemManagerRefusalReason;
|
|
103
|
+
};
|
|
104
|
+
export interface ModemManagerProviderOperations extends ProviderOperationsSurface {
|
|
105
|
+
readonly radio: ContextWriteOperation<DesiredRadio, ModemManagerRadioState>;
|
|
106
|
+
/**
|
|
107
|
+
* The modem's OWN mode vocabulary — an advertised `(allowed, preferred)` pair,
|
|
108
|
+
* selected verbatim. Separate from `radio` because `DesiredRadio` is an ordered RAT
|
|
109
|
+
* preference and cannot express `preferred: none`, which real hardware advertises.
|
|
110
|
+
*/
|
|
111
|
+
readonly modes: ContextWriteOperation<ModeSelection, RadioModeTruth>;
|
|
112
|
+
readonly bands: ContextWriteOperation<readonly string[], ModemBands>;
|
|
113
|
+
readonly signal: ContextReadOperation<ModemManagerSignalState>;
|
|
114
|
+
readonly sim: ContextReadOperation<ModemManagerSimState>;
|
|
115
|
+
readonly power: ContextReadOperation<RadioPower>;
|
|
116
|
+
readonly location: {
|
|
117
|
+
status(context: ProviderExecutionContext): Promise<LocationStatusResult>;
|
|
118
|
+
enable(context: ProviderExecutionContext, sources: readonly GnssSource[]): Promise<LocationToggleResult>;
|
|
119
|
+
disable(context: ProviderExecutionContext): Promise<LocationToggleResult>;
|
|
120
|
+
readFix(context: ProviderExecutionContext): Promise<FixRead>;
|
|
121
|
+
state(context: ProviderExecutionContext): GnssFixState;
|
|
122
|
+
tick(context: ProviderExecutionContext): GnssFixState;
|
|
123
|
+
};
|
|
124
|
+
sms(context: ProviderExecutionContext): Promise<SmsPortResult>;
|
|
125
|
+
readonly ussd: MmUssd;
|
|
126
|
+
initiateUssd(context: ProviderExecutionContext, ussdCommand: string): Promise<UssdVerbResult>;
|
|
127
|
+
respondUssd(context: ProviderExecutionContext, ussdResponse: string): Promise<UssdVerbResult>;
|
|
128
|
+
cancelUssd(context: ProviderExecutionContext): Promise<UssdVerbResult>;
|
|
129
|
+
readonly fccCoverage: (vid: string | undefined, pid: string | undefined) => 'present' | 'absent' | 'unknown';
|
|
130
|
+
}
|
|
131
|
+
export interface ModemManagerProviderLifecycle {
|
|
132
|
+
start(): Promise<ObservationList>;
|
|
133
|
+
observe(listener: ObservationListener): () => void;
|
|
134
|
+
stop(): Promise<void>;
|
|
135
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|