@rhizomatics/signalk-bluetti-plugin 1.4.1 → 1.5.1
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/CHANGELOG.md +13 -0
- package/README.md +40 -4
- package/cli.js +71 -11
- package/devices/ac180.yaml +58 -0
- package/devices/ac180p.yaml +34 -0
- package/devices/ac180t.yaml +58 -0
- package/devices/ac200l.yaml +48 -0
- package/devices/ac200m.yaml +48 -0
- package/devices/ac200p.yaml +89 -0
- package/devices/ac200pl.yaml +48 -0
- package/devices/ac2a.yaml +34 -0
- package/devices/ac2p.yaml +34 -0
- package/devices/ac300.yaml +57 -0
- package/devices/ac500.yaml +56 -0
- package/devices/ac50b.yaml +31 -0
- package/devices/ac60.yaml +33 -0
- package/devices/ac60p.yaml +33 -0
- package/devices/ac70.yaml +62 -0
- package/devices/ac70p.yaml +58 -0
- package/devices/ap300.yaml +34 -0
- package/devices/eb3a.yaml +41 -0
- package/devices/el100v2.yaml +43 -0
- package/devices/el30v2.yaml +38 -0
- package/devices/ep2000.yaml +75 -0
- package/devices/ep500.yaml +57 -0
- package/devices/ep500p.yaml +57 -0
- package/devices/ep600.yaml +151 -0
- package/devices/ep760.yaml +72 -0
- package/devices/ep800.yaml +21 -0
- package/devices/handsfree1.yaml +62 -0
- package/devices/pr100v2.yaml +34 -0
- package/devices/pr30v2.yaml +34 -0
- package/docs/assets/icon.svg +36 -0
- package/docs/assets/screenshots/bluetti_data.png +0 -0
- package/docs/examples/model_definition.yaml +112 -0
- package/index.js +75 -32
- package/lib/path-mapper.js +51 -9
- package/lib/register-loader.js +120 -0
- package/lib/scanner.js +1 -1
- package/package.json +13 -6
- package/lib/csv-loader.js +0 -160
- package/registers/ac200p.csv +0 -47
- package/registers/el100v2.csv +0 -27
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this plugin are documented here. Format loosely follows [Keep a Changelog](https://keepachangelog.com/).
|
|
4
4
|
|
|
5
|
+
## [1.5.1]
|
|
6
|
+
|
|
7
|
+
- Simplify the custom device configuration override
|
|
8
|
+
- 'Register Map' is now 'Device Configuration'
|
|
9
|
+
- Bundled register maps moved from `registers/` to `devices/`
|
|
10
|
+
|
|
11
|
+
## [1.5.0]
|
|
12
|
+
|
|
13
|
+
- Expanded Bluetti model support, over 20 now with register mappings
|
|
14
|
+
- Configuration moved from CSV to YAML
|
|
15
|
+
- Configurable directory to add YAML files for unsupported Bluetti models
|
|
16
|
+
- CLI `models` command lists supported register maps with field/constant counts
|
|
17
|
+
|
|
5
18
|
## [1.4.1]
|
|
6
19
|
|
|
7
20
|
- Basic test suite
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|

|
|
7
7
|
[](https://github.com/rhizomatics/signalk-bluetti-plugin/blob/main/LICENSE)
|
|
8
8
|
|
|
9
|
-
A SignalK plugin to display data from Bluetti power stations over a Bluetooth Low Energy (BLE) connection.
|
|
9
|
+
A SignalK plugin to display data from Bluetti power stations over a Bluetooth Low Energy (BLE) connection. Supports 20+ stations, extensible by configuration. Also offers a CLI for easier exploring and debugging.
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
@@ -73,19 +73,44 @@ node cli.js dump aa:bb:cc:dd:ee:ff --timeout 30 # discovery timeout if device
|
|
|
73
73
|
Connect to a device and decode its live register values the same way the plugin itself does — same GATT UUIDs, same encryption handshake (including the AES/ECDH handshake used by "V2"-protocol models like the EL100V2), same register parsing. Requires `--registers`. If `mac` is omitted, scans and connects to the first Bluetti-matching device found.
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
# Decode live registers using a bundled
|
|
76
|
+
# Decode live registers using a bundled device configuration (see devices/*.yaml for available models)
|
|
77
77
|
node cli.js info aa:bb:cc:dd:ee:ff --registers ac200p
|
|
78
78
|
node cli.js info --registers ac200p # scan and use the first Bluetti device found
|
|
79
79
|
|
|
80
|
-
# Or a custom
|
|
80
|
+
# Or a custom YAML, plus an encryption key file for legacy models that XOR-scramble frames
|
|
81
81
|
# (not needed for V2-protocol models — those are auto-detected and handshake automatically)
|
|
82
|
-
node cli.js info aa:bb:cc:dd:ee:ff --registers ./my-device-registers.
|
|
82
|
+
node cli.js info aa:bb:cc:dd:ee:ff --registers ./my-device-registers.yaml --encryption-key ~/19e1646709e0421b755fa9dda74.csv
|
|
83
83
|
|
|
84
84
|
node cli.js info aa:bb:cc:dd:ee:ff --registers ac200p --timeout 30 # discovery timeout if device isn't already known to BlueZ (default: 20)
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
### `models`
|
|
88
|
+
|
|
89
|
+
List every device configuration the CLI (and the plugin) can find — bundled ones plus anything dropped into the custom directory (`~/.signalk/bluetti` by default) — with a field and constant count per model. No device connection needed.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
node cli.js models
|
|
93
|
+
```
|
|
94
|
+
|
|
87
95
|
## FAQ
|
|
88
96
|
|
|
97
|
+
### Capacity figures show in Joules, how do I make it useful?
|
|
98
|
+
|
|
99
|
+
SignalK stores all data in SI units rather than any customary units used anywhere, for example temperatures are all held in Kelvin and unit preferences allow them to be converted in the display to C or F.
|
|
100
|
+
|
|
101
|
+
Unfortunately the default sets of preset unit preferences don't include the `energy` category used for capacity and more unfortunately as of SignalK v2.30.0 there's no easy way to override individual paths.
|
|
102
|
+
|
|
103
|
+
The fiddly way to do it should be as below (however there's a current [misconfiguration](https://github.com/SignalK/signalk-server/issues/2878) in the SignalK code which gets in the way):
|
|
104
|
+
|
|
105
|
+
1. Download your preferred set of unit preferences from the [SignalK repo](https://github.com/SignalK/signalk-server/tree/ac368c548fba923db0acf8a314cbdc6552b2b8cf/unitpreferences/presets)
|
|
106
|
+
2. Edit the downloaded file
|
|
107
|
+
|
|
108
|
+
- Change the `targetUnit` of `energy` category to `Wh`
|
|
109
|
+
- Change the `name` at the top of the file to reflect the change, for example `"Nautical (Metric, Wh capacity)"`
|
|
110
|
+
|
|
111
|
+
3. Go to the _Unit Preferences_ section of the _Data_ menu in SignalK
|
|
112
|
+
4. Use the _Upload_ button to upload your amended preferences preset and select it in the preset dropdown
|
|
113
|
+
|
|
89
114
|
### SignalK starts before the Bluetooth daemon — does the plugin need `bluetoothd` running at boot?
|
|
90
115
|
|
|
91
116
|
The plugin retries BLE adapter initialisation with backoff (starting at 2s, capping at 30s) if `bluetoothd`/D-Bus isn't up yet when the plugin starts, so a slow-starting Bluetooth stack on boot will no longer strand it — it keeps retrying until the adapter appears rather than failing once and giving up. You'll see `BLE adapter not ready … — retrying in Ns …` in the SignalK logs in the meantime.
|
|
@@ -112,3 +137,14 @@ sudo systemctl restart signalk
|
|
|
112
137
|
```
|
|
113
138
|
|
|
114
139
|
This tells systemd to start `bluetoothd` first and wait for it before starting SignalK, rather than relying on both racing to start in parallel at boot.
|
|
140
|
+
|
|
141
|
+
### My station isn't supported
|
|
142
|
+
|
|
143
|
+
Additional products can be added using a YAML file mapping modbus registers and providing static values — see [`docs/examples/model_definition.yaml`](docs/examples/model_definition.yaml) in the repo for an annotated template, and `devices/*.yaml` for real examples.
|
|
144
|
+
|
|
145
|
+
Drop your file into the plugin's **Custom Device Configuration Directory** (a plugin setting, defaulting to `~/.signalk/bluetti`) and it'll appear in the **Device Configuration** dropdown for a device, named after the file (without `.yaml`). A file there takes priority over a bundled one of the same name, so it also doubles as a way to override a built-in map.
|
|
146
|
+
|
|
147
|
+
Check one of these projects to see if some of the mapping work has been done:
|
|
148
|
+
|
|
149
|
+
- https://github.com/Patrick762/bluetti-bt-lib
|
|
150
|
+
- https://github.com/warhammerkid/bluetti_mqtt
|
package/cli.js
CHANGED
|
@@ -2,22 +2,58 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
const fs = require("fs");
|
|
5
|
+
const os = require("os");
|
|
5
6
|
const path = require("path");
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const DEVICES_DIR = path.join(__dirname, "devices");
|
|
9
|
+
// Same default the plugin itself uses when running under a real SignalK
|
|
10
|
+
// server (see index.js) — lets a user's custom register maps be found by the
|
|
11
|
+
// CLI too, without needing to pass --registers-dir.
|
|
12
|
+
const DEFAULT_USER_DEVICES_DIR = path.join(os.homedir(), ".signalk", "bluetti");
|
|
8
13
|
|
|
9
|
-
function
|
|
14
|
+
function listModels(dir) {
|
|
10
15
|
try {
|
|
11
16
|
return fs
|
|
12
|
-
.readdirSync(
|
|
13
|
-
.filter((f) => f
|
|
14
|
-
.map((f) => f.replace(/\.
|
|
17
|
+
.readdirSync(dir)
|
|
18
|
+
.filter((f) => /\.ya?ml$/.test(f))
|
|
19
|
+
.map((f) => f.replace(/\.ya?ml$/, ""))
|
|
15
20
|
.sort();
|
|
16
21
|
} catch {
|
|
17
22
|
return [];
|
|
18
23
|
}
|
|
19
24
|
}
|
|
20
25
|
|
|
26
|
+
function listBuiltinModels() {
|
|
27
|
+
return [...new Set([...listModels(DEVICES_DIR), ...listModels(DEFAULT_USER_DEVICES_DIR)])].sort((a, b) => a.localeCompare(b));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// One row per model, checking the custom directory before the bundled one so
|
|
31
|
+
// a user's own file overriding a built-in shows up as "custom" — same
|
|
32
|
+
// resolution order as resolveRegistersArg()/the plugin itself.
|
|
33
|
+
function listModelDetails() {
|
|
34
|
+
const { loadRegisters } = require("./lib/register-loader");
|
|
35
|
+
|
|
36
|
+
return listBuiltinModels().map((model) => {
|
|
37
|
+
for (const [dir, source] of [
|
|
38
|
+
[DEFAULT_USER_DEVICES_DIR, "custom"],
|
|
39
|
+
[DEVICES_DIR, "built-in"],
|
|
40
|
+
]) {
|
|
41
|
+
for (const ext of [".yaml", ".yml"]) {
|
|
42
|
+
const p = path.join(dir, `${model}${ext}`);
|
|
43
|
+
if (!fs.existsSync(p)) continue;
|
|
44
|
+
try {
|
|
45
|
+
const fields = loadRegisters(p);
|
|
46
|
+
const constants = fields.filter((f) => f.dataType === "const").length;
|
|
47
|
+
return { model, fields: fields.length - constants, constants, source, path: p };
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return { model, fields: "?", constants: "?", source, path: `${p} (failed to load: ${err.message})` };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { model, fields: "?", constants: "?", source: "?", path: "" };
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
21
57
|
function usage() {
|
|
22
58
|
const builtins = listBuiltinModels();
|
|
23
59
|
console.log(`
|
|
@@ -41,11 +77,16 @@ Commands:
|
|
|
41
77
|
the plugin does
|
|
42
78
|
If mac is omitted, scans and uses the first
|
|
43
79
|
Bluetti device found.
|
|
44
|
-
--registers <model|path> Built-in model (${builtins.join(", ") || "none bundled"}) or a
|
|
80
|
+
--registers <model|path> Built-in model (${builtins.join(", ") || "none bundled"}) or a register map
|
|
81
|
+
YAML path (required). Also checks
|
|
82
|
+
${DEFAULT_USER_DEVICES_DIR} for custom models.
|
|
45
83
|
--encryption-key <path> Path to the Bluetti-provided encryption CSV
|
|
46
84
|
(only needed for legacy XOR-scrambled models)
|
|
47
85
|
--timeout <seconds> Discovery/scan timeout if not already known (default: 20)
|
|
48
86
|
|
|
87
|
+
models List supported device configurations, with a field and
|
|
88
|
+
constant count per model
|
|
89
|
+
|
|
49
90
|
help Show this help
|
|
50
91
|
|
|
51
92
|
Examples:
|
|
@@ -55,6 +96,7 @@ Examples:
|
|
|
55
96
|
bluetti-cli dump
|
|
56
97
|
bluetti-cli info aa:bb:cc:dd:ee:ff --registers ac200p
|
|
57
98
|
bluetti-cli info --registers ac200p
|
|
99
|
+
bluetti-cli models
|
|
58
100
|
`);
|
|
59
101
|
}
|
|
60
102
|
|
|
@@ -138,11 +180,15 @@ async function findDevice(adapter, mac, timeoutMs) {
|
|
|
138
180
|
}
|
|
139
181
|
|
|
140
182
|
function resolveRegistersArg(value) {
|
|
141
|
-
const
|
|
142
|
-
|
|
183
|
+
for (const dir of [DEFAULT_USER_DEVICES_DIR, DEVICES_DIR]) {
|
|
184
|
+
for (const ext of [".yaml", ".yml"]) {
|
|
185
|
+
const p = path.join(dir, `${value}${ext}`);
|
|
186
|
+
if (fs.existsSync(p)) return p;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
143
189
|
if (fs.existsSync(value)) return value;
|
|
144
190
|
const builtins = listBuiltinModels();
|
|
145
|
-
throw new Error(`
|
|
191
|
+
throw new Error(`Device configuration not found: "${value}" (not a built-in model [${builtins.join(", ")}] or an existing file path)`);
|
|
146
192
|
}
|
|
147
193
|
|
|
148
194
|
async function printDeviceSummary(bleDevice, mac) {
|
|
@@ -212,14 +258,14 @@ async function printGattTree(bleDevice) {
|
|
|
212
258
|
}
|
|
213
259
|
|
|
214
260
|
async function printRegisterValues(bleDevice, mac, args) {
|
|
215
|
-
const {
|
|
261
|
+
const { loadRegisters, decodeValue } = require("./lib/register-loader");
|
|
216
262
|
const { resolvePath, convertUnits } = require("./lib/path-mapper");
|
|
217
263
|
const { readEncryptionKey } = require("./lib/encryption");
|
|
218
264
|
const { groupRegisters } = require("./lib/protocol");
|
|
219
265
|
const BluettiDevice = require("./lib/device");
|
|
220
266
|
|
|
221
267
|
const registerPath = resolveRegistersArg(args.registers);
|
|
222
|
-
const fields =
|
|
268
|
+
const fields = loadRegisters(registerPath);
|
|
223
269
|
console.log(`\nLoaded ${fields.length} register field(s) from ${registerPath}`);
|
|
224
270
|
|
|
225
271
|
const addrs = [...new Set(fields.flatMap((f) => Array.from({ length: f.count }, (_, i) => f.register + i)))];
|
|
@@ -355,6 +401,17 @@ async function cmdInfo(args) {
|
|
|
355
401
|
);
|
|
356
402
|
}
|
|
357
403
|
|
|
404
|
+
// ── models ───────────────────────────────────────────────────────────────
|
|
405
|
+
|
|
406
|
+
function cmdModels() {
|
|
407
|
+
const rows = listModelDetails();
|
|
408
|
+
if (rows.length === 0) {
|
|
409
|
+
console.log(`No device configurations found (checked ${DEVICES_DIR} and ${DEFAULT_USER_DEVICES_DIR}).`);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
printTable(rows, ["model", "fields", "constants", "source"]);
|
|
413
|
+
}
|
|
414
|
+
|
|
358
415
|
// ── main ─────────────────────────────────────────────────────────────────
|
|
359
416
|
|
|
360
417
|
async function main() {
|
|
@@ -371,6 +428,9 @@ async function main() {
|
|
|
371
428
|
case "info":
|
|
372
429
|
await cmdInfo(args);
|
|
373
430
|
break;
|
|
431
|
+
case "models":
|
|
432
|
+
cmdModels();
|
|
433
|
+
break;
|
|
374
434
|
case "help":
|
|
375
435
|
case "--help":
|
|
376
436
|
case "-h":
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Bluetti AC180 register map
|
|
2
|
+
# Source: https://github.com/Patrick762/bluetti-bt-lib (bluetti_bt_lib/devices/ac180.py)
|
|
3
|
+
#
|
|
4
|
+
# field_name is a standard Bluetti register name — the code maps these to
|
|
5
|
+
# SignalK paths on its own (see lib/path-mapper.js). `path` on a field is an
|
|
6
|
+
# override, only needed if you're exposing a register the code doesn't
|
|
7
|
+
# recognise. `constants` are fixed, non-register facts about this model —
|
|
8
|
+
# a bare scalar (string/number) or { value, unit } for one that needs unit
|
|
9
|
+
# conversion (e.g. total_capacity in Wh -> J). No verified nominal capacity
|
|
10
|
+
# is bundled for most models — add a total_capacity constant yourself if you
|
|
11
|
+
# know it, to enable the derived remaining-capacity calculation.
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
fields:
|
|
15
|
+
dc_output_power:
|
|
16
|
+
register: 140
|
|
17
|
+
unit: W
|
|
18
|
+
ac_output_power:
|
|
19
|
+
register: 142
|
|
20
|
+
unit: W
|
|
21
|
+
dc_input_power:
|
|
22
|
+
register: 144
|
|
23
|
+
unit: W
|
|
24
|
+
ac_input_power:
|
|
25
|
+
register: 146
|
|
26
|
+
unit: W
|
|
27
|
+
dc_input_voltage:
|
|
28
|
+
register: 1213
|
|
29
|
+
scale: 0.1
|
|
30
|
+
unit: V
|
|
31
|
+
dc_input_current:
|
|
32
|
+
register: 1214
|
|
33
|
+
scale: 0.1
|
|
34
|
+
unit: A
|
|
35
|
+
ac_input_frequency:
|
|
36
|
+
register: 1300
|
|
37
|
+
scale: 0.1
|
|
38
|
+
unit: Hz
|
|
39
|
+
ac_input_voltage:
|
|
40
|
+
register: 1314
|
|
41
|
+
scale: 0.1
|
|
42
|
+
unit: V
|
|
43
|
+
ac_input_current:
|
|
44
|
+
register: 1315
|
|
45
|
+
scale: 0.1
|
|
46
|
+
unit: A
|
|
47
|
+
ac_output_frequency:
|
|
48
|
+
register: 1500
|
|
49
|
+
scale: 0.1
|
|
50
|
+
unit: Hz
|
|
51
|
+
ac_output_voltage:
|
|
52
|
+
register: 1511
|
|
53
|
+
scale: 0.1
|
|
54
|
+
unit: V
|
|
55
|
+
constants:
|
|
56
|
+
battery_chemistry: LiFePO4
|
|
57
|
+
battery_manufacturer_name: Bluetti
|
|
58
|
+
battery_manufacturer_model: AC180
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Bluetti AC180P register map
|
|
2
|
+
# Source: https://github.com/Patrick762/bluetti-bt-lib (bluetti_bt_lib/devices/ac180p.py)
|
|
3
|
+
#
|
|
4
|
+
# field_name is a standard Bluetti register name — the code maps these to
|
|
5
|
+
# SignalK paths on its own (see lib/path-mapper.js). `path` on a field is an
|
|
6
|
+
# override, only needed if you're exposing a register the code doesn't
|
|
7
|
+
# recognise. `constants` are fixed, non-register facts about this model —
|
|
8
|
+
# a bare scalar (string/number) or { value, unit } for one that needs unit
|
|
9
|
+
# conversion (e.g. total_capacity in Wh -> J). No verified nominal capacity
|
|
10
|
+
# is bundled for most models — add a total_capacity constant yourself if you
|
|
11
|
+
# know it, to enable the derived remaining-capacity calculation.
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
fields:
|
|
15
|
+
dc_output_power:
|
|
16
|
+
register: 140
|
|
17
|
+
unit: W
|
|
18
|
+
ac_output_power:
|
|
19
|
+
register: 142
|
|
20
|
+
unit: W
|
|
21
|
+
dc_input_power:
|
|
22
|
+
register: 144
|
|
23
|
+
unit: W
|
|
24
|
+
ac_input_power:
|
|
25
|
+
register: 146
|
|
26
|
+
unit: W
|
|
27
|
+
ac_input_voltage:
|
|
28
|
+
register: 1314
|
|
29
|
+
scale: 0.1
|
|
30
|
+
unit: V
|
|
31
|
+
constants:
|
|
32
|
+
battery_chemistry: LiFePO4
|
|
33
|
+
battery_manufacturer_name: Bluetti
|
|
34
|
+
battery_manufacturer_model: AC180P
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Bluetti AC180T register map
|
|
2
|
+
# Source: https://github.com/Patrick762/bluetti-bt-lib (bluetti_bt_lib/devices/ac180t.py)
|
|
3
|
+
#
|
|
4
|
+
# field_name is a standard Bluetti register name — the code maps these to
|
|
5
|
+
# SignalK paths on its own (see lib/path-mapper.js). `path` on a field is an
|
|
6
|
+
# override, only needed if you're exposing a register the code doesn't
|
|
7
|
+
# recognise. `constants` are fixed, non-register facts about this model —
|
|
8
|
+
# a bare scalar (string/number) or { value, unit } for one that needs unit
|
|
9
|
+
# conversion (e.g. total_capacity in Wh -> J). No verified nominal capacity
|
|
10
|
+
# is bundled for most models — add a total_capacity constant yourself if you
|
|
11
|
+
# know it, to enable the derived remaining-capacity calculation.
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
fields:
|
|
15
|
+
dc_output_power:
|
|
16
|
+
register: 140
|
|
17
|
+
unit: W
|
|
18
|
+
ac_output_power:
|
|
19
|
+
register: 142
|
|
20
|
+
unit: W
|
|
21
|
+
dc_input_power:
|
|
22
|
+
register: 144
|
|
23
|
+
unit: W
|
|
24
|
+
ac_input_power:
|
|
25
|
+
register: 146
|
|
26
|
+
unit: W
|
|
27
|
+
dc_input_voltage:
|
|
28
|
+
register: 1213
|
|
29
|
+
scale: 0.1
|
|
30
|
+
unit: V
|
|
31
|
+
dc_input_current:
|
|
32
|
+
register: 1214
|
|
33
|
+
scale: 0.1
|
|
34
|
+
unit: A
|
|
35
|
+
ac_input_frequency:
|
|
36
|
+
register: 1300
|
|
37
|
+
scale: 0.1
|
|
38
|
+
unit: Hz
|
|
39
|
+
ac_input_voltage:
|
|
40
|
+
register: 1314
|
|
41
|
+
scale: 0.1
|
|
42
|
+
unit: V
|
|
43
|
+
ac_input_current:
|
|
44
|
+
register: 1315
|
|
45
|
+
scale: 0.1
|
|
46
|
+
unit: A
|
|
47
|
+
ac_output_frequency:
|
|
48
|
+
register: 1500
|
|
49
|
+
scale: 0.1
|
|
50
|
+
unit: Hz
|
|
51
|
+
ac_output_voltage:
|
|
52
|
+
register: 1511
|
|
53
|
+
scale: 0.1
|
|
54
|
+
unit: V
|
|
55
|
+
constants:
|
|
56
|
+
battery_chemistry: LiFePO4
|
|
57
|
+
battery_manufacturer_name: Bluetti
|
|
58
|
+
battery_manufacturer_model: AC180T
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Bluetti AC200L register map
|
|
2
|
+
# Source: https://github.com/Patrick762/bluetti-bt-lib (bluetti_bt_lib/devices/ac200l.py)
|
|
3
|
+
#
|
|
4
|
+
# field_name is a standard Bluetti register name — the code maps these to
|
|
5
|
+
# SignalK paths on its own (see lib/path-mapper.js). `path` on a field is an
|
|
6
|
+
# override, only needed if you're exposing a register the code doesn't
|
|
7
|
+
# recognise. `constants` are fixed, non-register facts about this model —
|
|
8
|
+
# a bare scalar (string/number) or { value, unit } for one that needs unit
|
|
9
|
+
# conversion (e.g. total_capacity in Wh -> J). No verified nominal capacity
|
|
10
|
+
# is bundled for most models — add a total_capacity constant yourself if you
|
|
11
|
+
# know it, to enable the derived remaining-capacity calculation.
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
fields:
|
|
15
|
+
battery_soc:
|
|
16
|
+
register: 43
|
|
17
|
+
unit: "%"
|
|
18
|
+
dc_input_power:
|
|
19
|
+
register: 36
|
|
20
|
+
unit: W
|
|
21
|
+
ac_input_power:
|
|
22
|
+
register: 37
|
|
23
|
+
unit: W
|
|
24
|
+
ac_output_power:
|
|
25
|
+
register: 38
|
|
26
|
+
unit: W
|
|
27
|
+
dc_output_power:
|
|
28
|
+
register: 39
|
|
29
|
+
unit: W
|
|
30
|
+
ac_output_voltage:
|
|
31
|
+
register: 71
|
|
32
|
+
unit: V
|
|
33
|
+
ac_output_frequency:
|
|
34
|
+
register: 74
|
|
35
|
+
scale: 0.1
|
|
36
|
+
unit: Hz
|
|
37
|
+
dc_input_voltage:
|
|
38
|
+
register: 86
|
|
39
|
+
scale: 0.1
|
|
40
|
+
unit: V
|
|
41
|
+
dc_input_current:
|
|
42
|
+
register: 88
|
|
43
|
+
scale: 0.01
|
|
44
|
+
unit: A
|
|
45
|
+
constants:
|
|
46
|
+
battery_chemistry: LiFePO4
|
|
47
|
+
battery_manufacturer_name: Bluetti
|
|
48
|
+
battery_manufacturer_model: AC200L
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Bluetti AC200M register map
|
|
2
|
+
# Source: https://github.com/Patrick762/bluetti-bt-lib (bluetti_bt_lib/devices/ac200m.py)
|
|
3
|
+
#
|
|
4
|
+
# field_name is a standard Bluetti register name — the code maps these to
|
|
5
|
+
# SignalK paths on its own (see lib/path-mapper.js). `path` on a field is an
|
|
6
|
+
# override, only needed if you're exposing a register the code doesn't
|
|
7
|
+
# recognise. `constants` are fixed, non-register facts about this model —
|
|
8
|
+
# a bare scalar (string/number) or { value, unit } for one that needs unit
|
|
9
|
+
# conversion (e.g. total_capacity in Wh -> J). No verified nominal capacity
|
|
10
|
+
# is bundled for most models — add a total_capacity constant yourself if you
|
|
11
|
+
# know it, to enable the derived remaining-capacity calculation.
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
fields:
|
|
15
|
+
battery_soc:
|
|
16
|
+
register: 43
|
|
17
|
+
unit: "%"
|
|
18
|
+
dc_input_power:
|
|
19
|
+
register: 36
|
|
20
|
+
unit: W
|
|
21
|
+
ac_input_power:
|
|
22
|
+
register: 37
|
|
23
|
+
unit: W
|
|
24
|
+
ac_output_power:
|
|
25
|
+
register: 38
|
|
26
|
+
unit: W
|
|
27
|
+
dc_output_power:
|
|
28
|
+
register: 39
|
|
29
|
+
unit: W
|
|
30
|
+
ac_output_voltage:
|
|
31
|
+
register: 71
|
|
32
|
+
unit: V
|
|
33
|
+
ac_output_frequency:
|
|
34
|
+
register: 74
|
|
35
|
+
scale: 0.1
|
|
36
|
+
unit: Hz
|
|
37
|
+
dc_input_voltage:
|
|
38
|
+
register: 86
|
|
39
|
+
scale: 0.1
|
|
40
|
+
unit: V
|
|
41
|
+
dc_input_current:
|
|
42
|
+
register: 88
|
|
43
|
+
scale: 0.01
|
|
44
|
+
unit: A
|
|
45
|
+
constants:
|
|
46
|
+
battery_chemistry: LiFePO4
|
|
47
|
+
battery_manufacturer_name: Bluetti
|
|
48
|
+
battery_manufacturer_model: AC200M
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Bluetti register map example — based on AC200P / AC200MAX community reverse-engineering.
|
|
2
|
+
# Replace with your device's actual map if you have better data.
|
|
3
|
+
#
|
|
4
|
+
# field_name is a standard Bluetti register name — the code maps these to
|
|
5
|
+
# SignalK paths on its own (see lib/path-mapper.js). `path` on a field is an
|
|
6
|
+
# override, only needed if you're exposing a register the code doesn't
|
|
7
|
+
# recognise. `constants` are fixed, non-register facts about this model —
|
|
8
|
+
# a bare scalar (string/number) or { value, unit } for one that needs unit
|
|
9
|
+
# conversion (e.g. total_capacity in Wh -> J). No verified nominal capacity
|
|
10
|
+
# is bundled for most models — add a total_capacity constant yourself if you
|
|
11
|
+
# know it, to enable the derived remaining-capacity calculation.
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
fields:
|
|
15
|
+
battery_percent:
|
|
16
|
+
register: 100
|
|
17
|
+
unit: "%"
|
|
18
|
+
battery_voltage:
|
|
19
|
+
register: 101
|
|
20
|
+
scale: 0.1
|
|
21
|
+
unit: V
|
|
22
|
+
battery_current:
|
|
23
|
+
register: 102
|
|
24
|
+
type: int16
|
|
25
|
+
scale: 0.1
|
|
26
|
+
unit: A
|
|
27
|
+
battery_power:
|
|
28
|
+
register: 103
|
|
29
|
+
type: int16
|
|
30
|
+
unit: W
|
|
31
|
+
battery_temperature:
|
|
32
|
+
register: 104
|
|
33
|
+
scale: 0.1
|
|
34
|
+
unit: °C
|
|
35
|
+
dc_input_power:
|
|
36
|
+
register: 107
|
|
37
|
+
unit: W
|
|
38
|
+
dc_input_voltage:
|
|
39
|
+
register: 108
|
|
40
|
+
scale: 0.1
|
|
41
|
+
unit: V
|
|
42
|
+
dc_input_current:
|
|
43
|
+
register: 109
|
|
44
|
+
scale: 0.1
|
|
45
|
+
unit: A
|
|
46
|
+
ac_output_power:
|
|
47
|
+
register: 110
|
|
48
|
+
unit: W
|
|
49
|
+
ac_output_voltage:
|
|
50
|
+
register: 111
|
|
51
|
+
scale: 0.1
|
|
52
|
+
unit: V
|
|
53
|
+
ac_output_current:
|
|
54
|
+
register: 112
|
|
55
|
+
scale: 0.1
|
|
56
|
+
unit: A
|
|
57
|
+
ac_output_frequency:
|
|
58
|
+
register: 113
|
|
59
|
+
scale: 0.1
|
|
60
|
+
unit: Hz
|
|
61
|
+
ac_input_power:
|
|
62
|
+
register: 114
|
|
63
|
+
unit: W
|
|
64
|
+
ac_input_voltage:
|
|
65
|
+
register: 115
|
|
66
|
+
scale: 0.1
|
|
67
|
+
unit: V
|
|
68
|
+
ac_input_current:
|
|
69
|
+
register: 116
|
|
70
|
+
scale: 0.1
|
|
71
|
+
unit: A
|
|
72
|
+
ac_input_frequency:
|
|
73
|
+
register: 117
|
|
74
|
+
scale: 0.1
|
|
75
|
+
unit: Hz
|
|
76
|
+
internal_temperature:
|
|
77
|
+
register: 118
|
|
78
|
+
scale: 0.1
|
|
79
|
+
unit: °C
|
|
80
|
+
time_remaining_minutes:
|
|
81
|
+
register: 119
|
|
82
|
+
unit: min
|
|
83
|
+
constants:
|
|
84
|
+
battery_chemistry: LiFePO4
|
|
85
|
+
total_capacity:
|
|
86
|
+
value: 2000
|
|
87
|
+
unit: Wh
|
|
88
|
+
battery_manufacturer_name: Bluetti
|
|
89
|
+
battery_manufacturer_model: AC200P
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Bluetti AC200PL register map
|
|
2
|
+
# Source: https://github.com/Patrick762/bluetti-bt-lib (bluetti_bt_lib/devices/ac200pl.py)
|
|
3
|
+
#
|
|
4
|
+
# field_name is a standard Bluetti register name — the code maps these to
|
|
5
|
+
# SignalK paths on its own (see lib/path-mapper.js). `path` on a field is an
|
|
6
|
+
# override, only needed if you're exposing a register the code doesn't
|
|
7
|
+
# recognise. `constants` are fixed, non-register facts about this model —
|
|
8
|
+
# a bare scalar (string/number) or { value, unit } for one that needs unit
|
|
9
|
+
# conversion (e.g. total_capacity in Wh -> J). No verified nominal capacity
|
|
10
|
+
# is bundled for most models — add a total_capacity constant yourself if you
|
|
11
|
+
# know it, to enable the derived remaining-capacity calculation.
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
fields:
|
|
15
|
+
battery_soc:
|
|
16
|
+
register: 43
|
|
17
|
+
unit: "%"
|
|
18
|
+
dc_input_power:
|
|
19
|
+
register: 36
|
|
20
|
+
unit: W
|
|
21
|
+
ac_input_power:
|
|
22
|
+
register: 37
|
|
23
|
+
unit: W
|
|
24
|
+
ac_output_power:
|
|
25
|
+
register: 38
|
|
26
|
+
unit: W
|
|
27
|
+
dc_output_power:
|
|
28
|
+
register: 39
|
|
29
|
+
unit: W
|
|
30
|
+
ac_output_voltage:
|
|
31
|
+
register: 71
|
|
32
|
+
unit: V
|
|
33
|
+
ac_output_frequency:
|
|
34
|
+
register: 74
|
|
35
|
+
scale: 0.1
|
|
36
|
+
unit: Hz
|
|
37
|
+
dc_input_voltage:
|
|
38
|
+
register: 86
|
|
39
|
+
scale: 0.1
|
|
40
|
+
unit: V
|
|
41
|
+
dc_input_current:
|
|
42
|
+
register: 88
|
|
43
|
+
scale: 0.01
|
|
44
|
+
unit: A
|
|
45
|
+
constants:
|
|
46
|
+
battery_chemistry: LiFePO4
|
|
47
|
+
battery_manufacturer_name: Bluetti
|
|
48
|
+
battery_manufacturer_model: AC200PL
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Bluetti AC2A register map
|
|
2
|
+
# Source: https://github.com/Patrick762/bluetti-bt-lib (bluetti_bt_lib/devices/ac2a.py)
|
|
3
|
+
#
|
|
4
|
+
# field_name is a standard Bluetti register name — the code maps these to
|
|
5
|
+
# SignalK paths on its own (see lib/path-mapper.js). `path` on a field is an
|
|
6
|
+
# override, only needed if you're exposing a register the code doesn't
|
|
7
|
+
# recognise. `constants` are fixed, non-register facts about this model —
|
|
8
|
+
# a bare scalar (string/number) or { value, unit } for one that needs unit
|
|
9
|
+
# conversion (e.g. total_capacity in Wh -> J). No verified nominal capacity
|
|
10
|
+
# is bundled for most models — add a total_capacity constant yourself if you
|
|
11
|
+
# know it, to enable the derived remaining-capacity calculation.
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
fields:
|
|
15
|
+
dc_output_power:
|
|
16
|
+
register: 140
|
|
17
|
+
unit: W
|
|
18
|
+
ac_output_power:
|
|
19
|
+
register: 142
|
|
20
|
+
unit: W
|
|
21
|
+
dc_input_power:
|
|
22
|
+
register: 144
|
|
23
|
+
unit: W
|
|
24
|
+
ac_input_power:
|
|
25
|
+
register: 146
|
|
26
|
+
unit: W
|
|
27
|
+
power_generation:
|
|
28
|
+
register: 154
|
|
29
|
+
scale: 0.1
|
|
30
|
+
unit: kWh
|
|
31
|
+
constants:
|
|
32
|
+
battery_chemistry: LiFePO4
|
|
33
|
+
battery_manufacturer_name: Bluetti
|
|
34
|
+
battery_manufacturer_model: AC2A
|