Node-Red module for streaming Easee charger data.
- SignalR streaming client
- Pre-defined list of REST API GET/POST commands
- Custom commands through REST API
npm i @runnane/node-red-contrib-easee
Add the easee Charger Streaming Client node
Configure the node with username/password and the Charger ID.
Configure the node with username/password and a Charger ID ("EH000000").
Streaming telemetry from the signalR enpoint will be available in the fourth output,
the ProductUpdate one.
If the Easee hub refuses the subscription for a charger — a charger ID the account
cannot access, for example — the reason is sent on the second (Errors) output and
shown in the node status. Earlier versions showed the node as connected and emitted
nothing.
Negotiation no longer depends on which tough-cookie package other installed nodes
brought along. An older one used to make negotiation fail with
Cannot read properties of undefined (reading 'secure').
Use the easee REST Client node
Configure the node with an account username/password.
The REST node will not authenticate on its own, so you will need to authenticate/renew tokens.
However, if you use the easee Charger Streaming Client node,
you do not need to authenticate additionally with the REST node, as the signalR socket
will authenticate and renew automatically.
There are two ways of sending commands:
Send the your selected command as the topic into the node.
You can set the charger, site and/or circuit variables directly in the node, or send them as
msg.charger, msg.site and msg.circuit to override.
Implemented commands that may be sent as topic, are:
loginrefresh_tokenchargercharger_detailscharger_state(see the note below — Easee changed the underlying endpoint)charger_sitecharger_configcharger_session_latestcharger_session_ongoingstop_chargingstart_chargingpause_chargingresume_chargingtoggle_chargingdynamic_current(Without msg.payload.body for reading (GET), and with msg.payload.body for setting (POST).)reboot
Example, get charger details:
node.send({
topic: "charger_details",
charger: "EH000000",
});Easee retired GET /api/chargers/{id}/state on 1 September 2026; it now returns
404. Since then charger_state reads the same values from the replacement
observations endpoint instead.
Your flows do not need to change. The payload is still an object keyed by the same
field names as before (msg.payload.totalPower.value, msg.payload.chargerOpMode.value
and so on), and each value is still a parsed observation. Each one now also carries a
timestamp.
Two differences worth knowing about:
- Six fields are gone.
connectedToCloud,fatalErrorCode,isOnline,voltage,latestPulseanderrorswere derived cloud-side by the old endpoint rather than being device observations, and the replacement cannot return them. A field the charger has not reported is omitted from the payload rather than sent asnull, so check with"fieldName" in msg.payloadif you need to distinguish the two. - The endpoint is rate limited to 100 requests per 5 minutes. If you drive
charger_statefrom an inject node, keep the interval at 3 seconds or slower. For continuous updates prefer the streaming client node, which pushes changes instead of polling.
Send the full path as msg.command, and optionally the POST body as msg.payload. See get_api-chargers for full list of commands. When adding a body, the request will be sent as a POST, else as a GET. If you wish to send a POST without body, add an empty object as POST argument.
Example to set dynamic current to 3x25A by doing a custom command with POST body:
Set dynamic current:
node.send({
payload: {
path: "/sites/1234/circuits/1345/dynamic_current",
body: { phase1: 25, phase2: 25, phase3: 25 },
}
});Pause charging:
node.send({
payload: {
path: "/chargers/EH000000/commands/pause_charging",
body: {},
}
});The nodes are written in TypeScript and compiled to dist/, which is what the npm
package ships. Development uses pnpm, Biome,
Vitest and TypeScript 7, and needs Node.js 22.12 or newer; the
published package itself still runs on Node.js 18 and later.
pnpm install
pnpm gates # lint + format, typecheck, build, tests with coverage, Node-RED load checkpnpm check # Biome lint + format check
pnpm check:fix # apply Biome's fixes
pnpm typecheck # TypeScriptpnpm build # the package tests inspect the built dist/
pnpm test # all tests
pnpm test:watch # watch mode
pnpm test:coverage # with the coverage floor enforced
pnpm test:unit
pnpm test:integration
pnpm test:compat # load every node from dist/ into a real Node-RED runtimeGitHub Actions runs on every pull request:
- Gates:
pnpm gateson Node.js 22, 24 and 26 - Compatibility: the packed package is installed with npm and every node is loaded into Node-RED on Node.js 18, 20, 22 and 24
- Security:
pnpm auditof the dependencies that ship, with known advisories accepted explicitly
Releases are cut from GitHub Actions: Actions → Release → Run workflow, choose
patch, minor or major, and untick Dry run. The workflow runs the checks, bumps
the version, pushes the vX.Y.Z tag, publishes to npm with
trusted publishing (so every version
carries provenance), and creates the GitHub release.
See example flows

This project is licensed under the MIT License - see the LICENSE file for details.
- Author: Jon Tungland (@runnane)
- Original Fork: Initially forked from node-red-contrib-signalrcore by Scott Page (Apache License 2.0), then extensively rewritten
- API Documentation: developer.easee.com
- Enumerations: developer.easee.com
This project was migrated from Apache License 2.0 to MIT License in 2025. The original Apache License 2.0 code from the forked project node-red-contrib-signalrcore has been preserved in the LICENSE file for attribution purposes. All subsequent modifications and additions by Jon Tungland are licensed under the MIT License.
All dependencies are compatible with the MIT License:
@microsoft/signalr: MIT License