signalk-server
Version:
An implementation of a [Signal K](http://signalk.org) server for boats.
195 lines (158 loc) • 6.51 kB
Markdown
# Unit Preferences System
This directory contains all configuration files for the SignalK unit preferences system.
## Directory Structure
```
unitpreferences/
├── config.json # Active preset configuration
├── categories.json # Category → SI unit mapping
├── default-categories.json # Default category assignments for paths
├── standard-units-definitions.json # Unit conversion formulas
├── custom-units-definitions.json # Custom unit definitions (created on demand)
└── presets/
├── metric.json # Built-in: Metric (SI)
├── imperial-us.json # Built-in: Imperial (US)
├── imperial-uk.json # Built-in: Imperial (UK)
└── custom/ # Custom presets directory
└── *.json # User-created presets
```
## File Descriptions
### config.json
Stores the currently active preset name.
```json
{
"activePreset": "metric"
}
```
### categories.json
Maps category names to their base SI units. Used to validate that a path's units match the category.
```json
{
"temperature": "K",
"speed": "m/s",
"distance": "m",
...
}
```
### default-categories.json
Maps SignalK path patterns to default categories. Used for auto-suggesting categories for paths.
```json
{
"environment.*.temperature": "temperature",
"navigation.speedOverGround": "speed",
...
}
```
### standard-units-definitions.json
Defines all unit conversions. Each SI unit has conversion formulas to display units.
```json
{
"K": {
"conversions": {
"C": {
"formula": "x - 273.15",
"inverseFormula": "x + 273.15",
"symbol": "°C"
},
"F": {
"formula": "(x - 273.15) * 9/5 + 32",
"inverseFormula": "(x - 32) * 5/9 + 273.15",
"symbol": "°F"
}
}
},
...
}
```
### custom-units-definitions.json
User-defined unit conversions that extend or override standard definitions. Created when users add custom conversions via the API.
### Preset Files (presets/\*.json)
Each preset defines target units for all categories:
```json
{
"name": "metric",
"displayName": "Metric System",
"description": "SI units",
"categories": {
"temperature": { "targetUnit": "C" },
"speed": { "targetUnit": "km/h" },
"distance": { "targetUnit": "kilometer" },
...
}
}
```
## REST API Endpoints
| Endpoint | Method | Description |
| ---------------------------------------------------- | ------ | ------------------------------------ |
| `/signalk/v1/unitpreferences/config` | GET | Get active preset config |
| `/signalk/v1/unitpreferences/config` | PUT | Set active preset |
| `/signalk/v1/unitpreferences/categories` | GET | Get category → SI unit mapping |
| `/signalk/v1/unitpreferences/definitions` | GET | Get merged unit definitions |
| `/signalk/v1/unitpreferences/custom-definitions` | GET | Get custom unit definitions |
| `/signalk/v1/unitpreferences/custom-definitions` | PUT | Save custom unit definitions |
| `/signalk/v1/unitpreferences/custom-categories` | GET | Get custom categories |
| `/signalk/v1/unitpreferences/custom-categories` | PUT | Save custom categories |
| `/signalk/v1/unitpreferences/presets` | GET | List all presets (built-in + custom) |
| `/signalk/v1/unitpreferences/presets/:name` | GET | Get preset details |
| `/signalk/v1/unitpreferences/presets/custom/:name` | PUT | Create/update custom preset |
| `/signalk/v1/unitpreferences/presets/custom/:name` | DELETE | Delete custom preset |
| `/signalk/v1/unitpreferences/presets/custom/upload` | POST | Upload custom preset file (admin) |
| `/signalk/v1/unitpreferences/active` | GET | Get fully resolved active preset |
| `/signalk/v1/unitpreferences/default-categories` | GET | Get default category mappings |
| `/signalk/v1/unitpreferences/default-category/:path` | GET | Get default category for a path |
## WebSocket Stream
When connecting to the WebSocket stream with `sendMeta=all`, metadata includes full `displayUnits` with conversion formulas:
```
ws://localhost:3000/signalk/v1/stream?subscribe=none&sendMeta=all
```
Subscribe to a path:
```json
{
"context": "vessels.self",
"subscribe": [{ "path": "environment.wind.speedApparent" }]
}
```
Metadata response includes enhanced `displayUnits`:
```json
{
"context": "vessels.urn:mrn:imo:mmsi:123456789",
"updates": [
{
"timestamp": "2024-01-15T12:00:00.000Z",
"meta": [
{
"path": "environment.wind.speedApparent",
"value": {
"units": "m/s",
"description": "Apparent wind speed",
"displayUnits": {
"category": "speed",
"targetUnit": "kn",
"formula": "value * 1.94384",
"inverseFormula": "value * 0.514444",
"symbol": "kn",
"displayFormat": "0.0"
}
}
}
]
}
]
}
```
The `displayUnits` object provides everything needed to convert and display values:
- **category** - The unit category (e.g., "speed", "temperature")
- **targetUnit** - The display unit from the active preset
- **formula** - Math.js expression to convert from SI to display unit (use `value` as variable)
- **inverseFormula** - Convert from display unit back to SI
- **symbol** - Display symbol (e.g., "kn", "°C")
- **displayFormat** - Optional format string
## How It Works
1. **Path metadata** stores `displayUnits.category` (e.g., "temperature")
2. **Active preset** determines target unit for each category (e.g., temperature → "C")
3. **Unit definitions** provide conversion formula from SI unit to target unit
4. **REST API / WebSocket** enhance metadata with full conversion info
5. **Data Browser / Apps** use the formula to convert and display values
## Path Override Priority
1. **Path-specific override** - `displayUnits.targetUnit` on path metadata
2. **Preset default** - Target unit from active preset for the category
3. **SI unit** - Raw value in base SI unit if no conversion defined