{"_id":"@coremarine/sbg-ecom-nodered","_rev":"5-f609c3b6554d92461c3f3bf3ddb59c0c","name":"@coremarine/sbg-ecom-nodered","dist-tags":{"latest":"1.0.0"},"versions":{"0.0.1":{"name":"@coremarine/sbg-ecom-nodered","version":"0.0.1","keywords":["sbg-ecom","sbg","gnss","gps","mru","imu","binary","protocol","parser"],"author":{"name":"CoreMarine"},"license":"MIT","_id":"@coremarine/sbg-ecom-nodered@0.0.1","maintainers":[{"name":"valentina-rubane","email":"vru@core-marine.com"},{"name":"crisconru","email":"crisconru@gmail.com"},{"name":"coremarinedev","email":"dev@core-marine.com"}],"homepage":"https://github.com/core-marine-dev/devices/tree/main/packages/sbg-ecom-nodered","bugs":{"url":"https://github.com/core-marine-dev/devices/issues"},"dist":{"shasum":"b298606a1106094da94fff885f68da4b6715df18","tarball":"https://registry.npmjs.org/@coremarine/sbg-ecom-nodered/-/sbg-ecom-nodered-0.0.1.tgz","fileCount":9,"integrity":"sha512-JiTKVHomG/W5oq3FQeC5VrDBdHfz9UlsBNSX2yvQiN62iOpU8WRx5TYr6plt6qOmVPXG3fjZ6d3is2WTGX3y0A==","signatures":[{"sig":"MEUCIQD1metzfs8n8wxST37je8xDz0rMei3p1b6huAI9BPHF9gIgESqYU3Tr6M8kq0jF9sAQESgRk3db2owYEdVDI4Os8tw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":69822},"main":"index.js","engines":{"node":">=18.0.0"},"gitHead":"ab4f888812eac7985b5bd0b85a42bacb26986c8c","scripts":{"test":"mocha \"tests/**/*.test.js\"","docker":"sh manual_tests.sh","test:vitest":"vitest"},"_npmUser":{"name":"coremarinedev","email":"dev@core-marine.com"},"node-red":{"nodes":{"cma-sbg-ecom":"src/parser.js"},"version":">=3.0.0"},"repository":{"url":"git+https://github.com/core-marine-dev/devices.git","type":"git"},"_npmVersion":"10.5.2","description":"sbg-ecom","directories":{},"_nodeVersion":"20.13.1","dependencies":{"@coremarine/sbg-ecom":">=0.0.1"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sbg-ecom-nodered_0.0.1_1716483080385_0.043279237208693644","host":"s3://npm-registry-packages"}},"0.0.2":{"name":"@coremarine/sbg-ecom-nodered","version":"0.0.2","keywords":["node-red","sbg-ecom","sbg","gnss","gps","mru","imu","binary","protocol","parser"],"author":{"name":"CoreMarine"},"license":"MIT","_id":"@coremarine/sbg-ecom-nodered@0.0.2","maintainers":[{"name":"valentina-rubane","email":"vru@core-marine.com"},{"name":"crisconru","email":"crisconru@gmail.com"},{"name":"coremarinedev","email":"dev@core-marine.com"}],"homepage":"https://github.com/core-marine-dev/devices/tree/main/packages/sbg-ecom-nodered","bugs":{"url":"https://github.com/core-marine-dev/devices/issues"},"dist":{"shasum":"49f05aab5772b63b3a2ecbe9b5f59a21ea5b4dc8","tarball":"https://registry.npmjs.org/@coremarine/sbg-ecom-nodered/-/sbg-ecom-nodered-0.0.2.tgz","fileCount":9,"integrity":"sha512-t5ZwwChDS3BoHfyew+iNs4if704xnPLHWbh3D66sSWhqlHMw+zW0fkz+DpZ8+YcZg0Yh08IO4AeWeUgWFUpGIA==","signatures":[{"sig":"MEQCIBZ3nFSG2BbfE0IHIMknFoY7dOrBW6wmrhiSGLG+I6PIAiA8ohCqiKHvGKr+VIIy+1f+4+ft0ZGUK+1y+bo1A5Ch5A==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":69838},"main":"index.js","engines":{"node":">=18.0.0"},"gitHead":"73159d40f56caa70a0ce8cb08d0312f12b5d512e","scripts":{"test":"mocha \"tests/**/*.test.js\"","docker":"sh manual_tests.sh","test:vitest":"vitest"},"_npmUser":{"name":"coremarinedev","email":"dev@core-marine.com"},"node-red":{"nodes":{"cma-sbg-ecom":"src/parser.js"},"version":">=3.0.0"},"repository":{"url":"git+https://github.com/core-marine-dev/devices.git","type":"git"},"_npmVersion":"10.5.2","description":"sbg-ecom","directories":{},"_nodeVersion":"20.13.1","dependencies":{"@coremarine/sbg-ecom":">=0.0.1"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sbg-ecom-nodered_0.0.2_1716483338609_0.379480828512764","host":"s3://npm-registry-packages"}},"1.0.0":{"name":"@coremarine/sbg-ecom-nodered","version":"1.0.0","author":"CoreMarine","license":"MIT","description":"Node-RED node for the SBG Systems sbgECom binary protocol (ELLIPSE, EKINOX, APOGEE), emitting the unified CoreMarine CMA format","homepage":"https://github.com/core-marine-dev/devices/tree/main/packages/sbg-ecom-nodered","repository":{"type":"git","url":"git+https://github.com/core-marine-dev/devices.git","directory":"packages/sbg-ecom-nodered"},"bugs":{"url":"https://github.com/core-marine-dev/devices/issues"},"keywords":["node-red","sbg","sbgecom","ecom","ellipse","ekinox","apogee","ins","imu","ahrs","gnss","marine","cma","parser"],"engines":{"node":">=22"},"node-red":{"version":">=4.0.0","nodes":{"cma-sbg-ecom":"dist/parser.js"}},"main":"dist/parser.js","dependencies":{"@coremarine/sbg-ecom":"^1.0.0"},"devDependencies":{"@types/node-red":"1.3.5","tsx":"4.23.1"},"scripts":{"build":"pnpm run format && tsup && node copy-assets.mjs","lint":"eslint","format":"eslint --fix","test":"node --import tsx --test \"tests/**/*.test.ts\"","test:coverage":"node --import tsx --test --experimental-test-coverage --test-coverage-include=\"src/**\" \"tests/**/*.test.ts\"","test:unit":"node --import tsx --test \"tests/*.unit.test.ts\"","test:integration":"node --import tsx --test \"tests/*.integration.test.ts\"","dev":"pnpm run build && node dev-server.mjs dev","examples":"pnpm run build && node dev-server.mjs examples"},"_nodeVersion":"24.18.0","_id":"@coremarine/sbg-ecom-nodered@1.0.0","dist":{"integrity":"sha512-V7bxY+w4BqFOg180ZD71ib+wbBNTDxYIO1ZrdkT+yWmUBcvBx3Ppuob87LhtXLCauvAXwCWCv+tncPcoryxr0g==","shasum":"64091b0e9c49aac9de363e1e2face9bc84138b7d","tarball":"https://registry.npmjs.org/@coremarine/sbg-ecom-nodered/-/sbg-ecom-nodered-1.0.0.tgz","fileCount":8,"unpackedSize":88840,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@coremarine%2fsbg-ecom-nodered@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC4LTeWARJRuUdxRaxbdDO9WzLLvoc9cLJEGFKZuNmlNAIgbh0VmFhhIO9H41mqWct3/bq0+I0GvcysdHz3z+hPn8U="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:be604d0e-2837-468c-874c-305e1c2231ed"}},"directories":{},"maintainers":[{"name":"coremarinedev","email":"dev@core-marine.com"},{"name":"crisconru","email":"crisconru@gmail.com"},{"name":"egr666","email":"egr@core-marine.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sbg-ecom-nodered_1.0.0_1785601993486_0.5626014056870674"},"_hasShrinkwrap":false}},"time":{"created":"2024-05-23T16:51:20.270Z","modified":"2026-08-01T16:33:14.034Z","0.0.1":"2024-05-23T16:51:20.620Z","0.0.2":"2024-05-23T16:55:38.767Z","1.0.0":"2026-08-01T16:33:13.643Z"},"bugs":{"url":"https://github.com/core-marine-dev/devices/issues"},"author":"CoreMarine","license":"MIT","homepage":"https://github.com/core-marine-dev/devices/tree/main/packages/sbg-ecom-nodered","keywords":["node-red","sbg","sbgecom","ecom","ellipse","ekinox","apogee","ins","imu","ahrs","gnss","marine","cma","parser"],"repository":{"type":"git","url":"git+https://github.com/core-marine-dev/devices.git","directory":"packages/sbg-ecom-nodered"},"description":"Node-RED node for the SBG Systems sbgECom binary protocol (ELLIPSE, EKINOX, APOGEE), emitting the unified CoreMarine CMA format","maintainers":[{"name":"coremarinedev","email":"dev@core-marine.com"},{"name":"crisconru","email":"crisconru@gmail.com"},{"name":"egr666","email":"egr@core-marine.com"}],"readme":"# @coremarine/sbg-ecom-nodered\n\n![npm (scoped)](https://img.shields.io/npm/v/%40coremarine/sbg-ecom-nodered)\n[![publish](https://github.com/core-marine-dev/devices/actions/workflows/sbg-ecom-nodered.yml/badge.svg)](https://github.com/core-marine-dev/devices/actions/workflows/sbg-ecom-nodered.yml)\n![npm](https://img.shields.io/npm/dy/%40coremarine/sbg-ecom-nodered)\n\nNode-RED node for **SBG Systems** inertial systems — ELLIPSE, EKINOX and APOGEE — speaking\n**sbgECom**. It turns the device's output into **CMA**, the same JSON shape every CoreMarine device\nparser emits.\n\nWrapper of [@coremarine/sbg-ecom](https://www.npmjs.com/package/@coremarine/sbg-ecom).\n**All 34 output logs** of `SBG_ECOM_CLASS_LOG_ECOM_0` are decoded. Node >= 22, Node-RED >= 4.\n\n## Install\n\nFrom the Node-RED palette manager, or:\n\n```bash\nnpm install @coremarine/sbg-ecom-nodered\n```\n\nThe node appears in the **CoreMarine** palette category as **SBG sbgECom parser**.\n\n## Configuration\n\n| Field | Meaning |\n| --- | --- |\n| **Name** | Optional node label |\n| **Firmware** | Empty for the library default (`2.3`), or a version this build models |\n| **Memory** | On by default — append each input to the buffer instead of replacing it |\n\nYou rarely need to pin the firmware. §2.4 of the manual states that SBG only ever **adds** fields at\nthe end of a log, so `2.3` decodes a newer device correctly and publishes the extra bytes at\n`metadata.trailing` rather than failing. A version this build does not model is **refused, never\nsubstituted**.\n\n**Leave memory on for a live stream.** eCom framing is length-prefixed rather than terminated, so a\nframe only decodes once its *last* byte has arrived — with memory off, any frame split across two\nmessages is lost.\n\n## ⭐ One node, two protocols\n\nAn SBG device does **not** wrap its NMEA output in an eCom frame. The manual is explicit (§2.1.4):\nthe NMEA classes are *\"only used for identification purpose and does not contain any sbgECom\nmessage\"* — the class ids exist so you can **configure** which sentence goes out, and the sentence\nitself is emitted as raw ASCII **alongside** the binary frames, on the same wire. §2.1.1 Note 4 warns\nthe integrator directly.\n\nSo this node has **no protocol setting**. Wire your serial node straight in and both come out as CMA:\n\n```\nsentences[0].id  // '0:9'   protocol.name 'SBG ECOM'\nsentences[1].id  // 'GGA'   protocol.name 'NMEA'\nsentences[2].id  // '0:13'  protocol.name 'SBG ECOM'\n```\n\nThe example flow has a group demonstrating exactly this — it is the feature most likely to surprise\nyou, so it is the one to open first.\n\n## msg API\n\nSend `payload` to parse. Every other key is optional; **a key you do not send is absent from the\noutput too**, and on a bad request the corresponding output key holds an error **string**.\n\n| Key | In | Out |\n| --- | --- | --- |\n| `payload` | a **Buffer**, a base64 string, an **NMEA sentence** (any string starting with `$`), or a byte array | `CMA[]` |\n| `memory` | `{ command: 'get' }` / `{ command: 'set', payload: boolean }` | `{ memory, bytes }` |\n| `firmware` | `{ command: 'get' }` / `{ command: 'set', payload: '2.3' }` | `{ firmware, firmwares, clock? }` |\n| `ids` | any truthy value | `string[]` — the 34 eCom log ids **plus** every NMEA sentence id |\n| `definition` | `'0:6'`, `'GGA'`, or `{ id, protocol? }` | the field table |\n| `fake` | `'0:6'` or `{ id, protocol?, options? }` | a real wire frame, as a **Buffer** |\n\nFull details, tables and warnings are in the node's **help panel** inside Node-RED.\n\n### Input — what a string means\n\n| string | read as |\n| --- | --- |\n| starts with `$` | **the NMEA sentence**, passed through as text |\n| anything else | **base64** eCom bytes, validated strictly |\n\nThere is no ambiguity to resolve: base64's alphabet contains no `$`, and every NMEA sentence starts\nwith one. A **Buffer** works for both and is the normal case — a serial, TCP or file node hands you\none and it goes straight through.\n\nBase64 is accepted because **every `raw` in the output is base64**, so it is this package's own\nvocabulary for bytes: copy a `raw` out of a debug node, inject it back, and you are re-parsing the\nexact frame that misbehaved. A byte array works too, for JSON-only paths such as an inject node.\n\n### `id` is `'<class>:<message>'`\n\nThe convention most likely to catch a flow author out. Identity in sbgECom is a **pair** — §2.1.1\ngives `MSG` and `CLASS` as two independent header bytes, with no revision concept — so `MSG 6` means\n`EKF_EULER` in class `0x00` and something else entirely in class `0x02`. The id is therefore `'0:6'`,\nand the log's name is at `metadata.name`.\n\n### Timestamps — the device tells us the time, or nothing does\n\nThe first field of almost every log is `TIME_STAMP`, and **it is not a clock**: the manual calls it\ntime since power-up. You cannot say when a sample was taken from an uptime counter, so this node\nnever presents one as a time.\n\nIt becomes a time through `SBG_ECOM_LOG_UTC_TIME`, which publishes that counter *and* the matching\nUTC. Once such a log arrives with a valid clock status, every later sentence carries an absolute\n`metadata.timestamp.sentence`, promoted over `timestamp` — the device's clock beats the host's.\n\nUntil then there is no absolute time, deliberately: a guessed clock is worse than an honest absence.\n**Send `firmware: { command: 'get' }` to see whether the device has published one** — the reply\ncarries `clock` once it has. That is the answer to \"why has my data no timestamp?\", and it is why the\nchannel reports it at all.\n\n## Nothing is dropped silently\n\nA bad CRC, a wrong end flag, a log this build does not know, or bytes that are not sbgECom at all:\neach comes back as a sentence saying so, never as an empty array. **The signal is the presence of\n`errors`** on a sentence.\n\nA log the knowledge base does not model is **not** an error — it arrives with its real `id`, its\nbytes at `metadata.body` and `metadata.name: 'unknown'`. A log that has *grown* decodes every field\nthis build knows and publishes the extra bytes at `metadata.trailing`.\n\n## Examples\n\nImport from the Node-RED menu → **Import** → **Examples** → `@coremarine/sbg-ecom-nodered`, or open\n[`examples/sbg-ecom-examples.json`](examples/sbg-ecom-examples.json).\n\nEight groups, each with a comment node explaining what it shows:\n\n| group | what it demonstrates |\n| --- | --- |\n| Decoded logs | real frames from a CoreMarine ELLIPSE; the `'0:6'` id convention |\n| **The mixed stream** | binary frames and a `$GPGGA` sentence through one node, nothing configured |\n| Timestamps | fire 1/2, then 2/2 — a `UTC_TIME` log turning uptime into a real clock |\n| Failed and garbage | a corrupted CRC still decoding, and junk reported rather than dropped |\n| Memory | half a frame, then the rest |\n| Diagnostics | `ids`, `definition`, `firmware` — asking the running node what it expects |\n| Fake frames | a fabricated frame fed straight back in, round-tripping |\n| Flow errors | a `catch` node, so a mistake is visible rather than silent |\n\n**Every payload in that flow is a real frame from the committed test corpus**, and the flow is\ncovered by `tests/examples.unit.test.ts` — it is structurally validated and every inject is run\nthrough this wrapper's own handlers, so a shipped example cannot rot unnoticed.\n\n## Development\n\n```bash\npnpm run sbg-ecom:nodered:build       # tsup -> dist/ + copy parser.html and icons\npnpm run sbg-ecom:nodered:test        # node --test (unit + a real node-red integration run)\npnpm run sbg-ecom:nodered:lint        # eslint\npnpm run sbg-ecom:nodered:dev         # a local Node-RED on :1880 with a scratch flow\npnpm run sbg-ecom:nodered:examples    # the same, editing the SHIPPED example flow\n```\n\nTwo traps worth knowing before you change anything:\n\n- **The tests run against `dist/`, not `src/`.** Build the wrapper *and* the library first, or you\n  are testing a stale copy.\n- **`tsx` strips types without checking them**, and `:nodered:lint` does not typecheck either. Run\n  `npx tsc --noEmit -p tsconfig.json` inside this package whenever the library changes shape — three\n  real breaks hid behind a green suite that way during the septentrio refactor, and one hid here.\n","readmeFilename":""}