# Extending

## Architecture

The primary things managed and provided by the plugin are:

- ESL Vendor
  - Sub-package per vendor
- ESL Device
  - Metadata in the vendor package, using a `pid` or sometimes `pid` combined with `hwid` in the BLE results to pinpoint a model
- SVG Template
- SignalK API base URL
  - Used for automatic unit conversion on `signalk`-sourced numeric values and for resolving an explicit `category=` binding - neither has an in-process equivalent, both go via this server's own REST API
  - Optional: left blank, the plugin probes the probable values in likelihood order at startup - `http://localhost:3000`, `http://localhost`, `https://localhost`. Set it explicitly to skip probing
  - Either way, errors clearly if nothing responds (wrong port) or the probe is rejected (anonymous read access not enabled) - these endpoints must allow anonymous read access, since the plugin has no login flow

## Hardware

Additional vendors and devices can be added by a separate npm package that implements the `VendorDriver` interface and registers itself - there's no scanning of installed packages, registration is always an explicit call by the extension's own code.

- `import esl from '@rhizomatics/signalk-einklabel-plugin'; esl.registerVendorDriver(myDriver)`
- In the SignalK runtime, call this from the extension's own plugin `start()`. In the CLI, load the extension with `esl-cli --require <module> <command>`.
- Declare this package as a regular npm `dependency` in the extension package - **not** a `peerDependency`, per SignalK's own guidance that npm's peer-dependency resolution interacts poorly with the server's plugin install layout - and declare the SignalK-level relationship via `"signalk": { "requires": ["@rhizomatics/signalk-einklabel-plugin"] }` in the extension's own `package.json` instead, so the App Store can install/report it.

## Template Providers

An alternative way to produce a device's content, alongside hand-authored SVG templates, can be added the same way - `esl.registerTemplateProvider({ suffix, listTemplates, render })`, from a separate package's own plugin `start()` (or `esl-cli --require <module>`). This is what [`@rhizomatics/signalk-einklabel-genai-plugin`](https://signalk-einklabel.rhizomatics.org.uk/templates.md#genai-rendering) is: `listTemplates()` returns the entries it currently offers (already including its own `suffix`, e.g. `"forecast (GenAI)"`) for the "Template" dropdown, and `render(request)` - given the same signalk/resources/label context any SVG template's bindings get - returns a `Bitmap`, exactly as `new esl.Renderer().render()` would for an SVG template. Rejecting from `render()` routes the repaint through this plugin's own generic fallback-warning template, the same as a broken hand-authored template failing to render. An optional `describeContent(request)` returns a human-readable description of what `render()` will be given (the GenAI plugin returns its fully-substituted LLM prompt); when present, it's logged to the plugin's debug output just before each `render()`, standing in for the `<desc>` bindings an SVG template's debug output would show.

`esl.Renderer` (the SVG renderer), `esl.bitmapToPng`, `esl.buildLabel`, `esl.findBindingsInText`, and `esl.substituteBindingsInText` are also exported for a `TemplateProvider` extension to reuse - e.g. to resolve its own `{...}`-style placeholders against the request's context, or to rasterize whatever SVG it produces into the `Bitmap` its `render()` must return.

## CLI Commands

`esl-cli` can also be extended with new subcommands by a `-r/--require`'d package, which is how [`@rhizomatics/signalk-einklabel-genai-plugin`](https://signalk-einklabel.rhizomatics.org.uk/templates.md#genai-rendering) adds its own `prompt`/`generate` commands for testing prompts without a device. See [Command Line Interface](https://signalk-einklabel.rhizomatics.org.uk/cli.md) for the built-in commands.
