npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bluedynamic/node-red-contrib-geyser-modular

v0.0.47

Published

Geyser Modular Palette - Device → Logic → Functions nodes for Node-RED

Readme

@bluedynamic/node-red-contrib-geyser-modular

Geyser Modular Palette – Device → Logic → Functions nodes for Node-RED.

Disclaimer: This is a personal / pet project. Not officially supported. Use at your own risk. No warranty; use of this software is entirely at the user’s responsibility.

This folder (and its contents) is the palette package. It is published to Git and, optionally, to npm.

Docs layout: Behaviour, MQTT layout, and message contracts are documented here (Node properties below) and in VARIABLE-MAP.md. In Node-RED, each node’s edit dialog only shows short tips so the UI stays readable; open this README on Git (palette repo) for the full reference.


Installation

From npm (if published): In Node-RED → Manage paletteInstall → search for @bluedynamic/node-red-contrib-geyser-modular.

From a .tgz file: In Node-RED → Manage paletteInstallInstall from file and select the .tgz. You can build the .tgz yourself with npm pack in this folder, or use a built file from the NodeRED-palettes share.


How "Update" works in Manage palette

Node-RED checks the npm registry. So:

  1. Publish this package to npm (see Releases below).
  2. Users install once via Manage palette → Install → search for @bluedynamic/node-red-contrib-geyser-modular.
  3. When a new version is published to npm, users open Manage palette, find the package, and click Update.

Releases (GitHub + npm)

  • GitHub: Create a tag (e.g. v0.0.2) and push. The repo’s workflow builds the palette and attaches the .tgz to a GitHub Release.
  • npm (for Update): In the repo Settings → Secrets, add NPM_TOKEN (from npm access tokens). Then each tag push can publish to npm so "Manage palette → Update" sees new versions.
  • Bump version in package.json before tagging (e.g. 0.0.2 then tag v0.0.2).

Nodes

Stable (intended long-term)

Rename: The hardware node was device-geyserwise; it is now device-geyserwiseESP (Geyserwise-on-ESP). Existing flows will show an unknown node until you delete the old node, add device-geyserwiseESP, and re-wire (same properties: Name, Device ID, MQTT, GW ID, load, priority).

  • device-geyserwiseESP – Normalizes Geyserwise-on-ESP MQTT data (3 outputs: device_id, full event, spare).
  • device-logic – Single-node port of G1-Logic110: reads/writes global.bluedyn[device_id] (min temp, boost, solar, timer, load request, heating method, boost reset, set_power from load_allow). Trigger from output 1 of device-geyserwiseESP (msg.payload = device_id).
  • coordinator – Multi-device load allocator: 1 input, 3 outputs. Routes by msg.topic (device_id, batt_soc, consumption); writes settings.load_allow per device.
  • device-home-assistant1 in / 3 out. Device ID in config (recommended), 12 entity helpers, optional MQTT broker. O2 = timer snapshot (debug). Writes logic.timer1_* … timer3_* and logic.timer_status.
  • bluedyn-mqtt-sync – MQTT prefix/#global.bluedyn (same broker config as core mqtt in/out / device-geyserwiseESP). QoS 1; leaf publishes retained (MQTT has no bulk read — retain is how late subscribers catch last values); inbound { v, t }, JSON, or plain strings; optional Clock check on prefix/_mqtt/time.

Temporary / scaffolding (remove or replace later)

  • bluedyn-get / bluedyn-set – Thin read/write helpers for global.bluedyn (GGV / SGV). Provisional: once the final logic/device node exists, this behaviour is expected to move inside that node (e.g. shared helpers / internal code paths), not stay as separate palette nodes.
  • test-gwise – Hard-coded MQTT subscription for bench/debug only; delete when no longer needed.

Palette look: All nodes use the same fill #3936e0 (default Node-RED label contrast, no custom editor CSS). device-geyserwiseESP / test-gwise use geyserwise.svg; bluedyn-mqtt-sync uses font-awesome/fa-exchange.


Node properties

Configurable fields in the editor, plus runtime behaviour (messages, MQTT). Internal constants (QoS, retain, TZ label, skew threshold) are described where they apply.

Temporary nodes (bluedyn-get, bluedyn-set, test-gwise) are documented below for current flows, but are not the long-term shape of the palette—see the Nodes list above.

device-geyserwiseESP

Editor order: NameDevice IDMQTT brokerGW IDLoad sizeLoad priority → output tips (each output on its own line in the dialog).

| Property | Description | |----------|-------------| | Name | Display name; also written to bluedyn[<device_id>].settings.device_name when set. | | Device ID | Logical id (e.g. geyser1). Key under global.bluedyn[<device_id>]. Required. | | MQTT broker | Same shared MQTT config type as core mqtt in / mqtt out (and bluedyn-mqtt-sync). | | GW ID | Geyserwise-on-ESP MQTT topic prefix (e.g. geyserwise-tse-f6ee1a). Subscribes to GW_ID/#. Required. | | Load size (W) | Optional; pushed into settings.load_size. | | Load priority | Optional; pushed into settings.load_priority. |

Flows: MQTT subscription only (no node input). On each inbound hardware event it updates global.bluedyn (hardware bucket GeyserwiseESP + logic/system) and sends 3 outputs: O1 = msg.payload + msg.device_id = device id; O2 = full normalized object (device_id, device_type = geyserwiseESP, type, payload, mqtt_topic, …); O3 = unused.


device-logic

| Property | Description | |----------|-------------| | Name | Display label. | | solar_soc_start | SoC (%) to assert solar SoC gate (default 98). Clamped 0–100. | | solar_soc_hysteresis | SoC hysteresis for solar gate; clear threshold is solar_soc_start - solar_soc_hysteresis (default 5). Clamped 0–100 and capped at solar_soc_start so the stop threshold never goes below 0%. | | water_temp_variance | Shared water-temp hysteresis delta used in min/boost/solar switch logic (default 5). Clamped ≥ 0. |

Behaviour (G1-Logic110): Reads logic.* / settings.load_allow / power.batt_soc under global.bluedyn[device_id]. Applies hysteresis: min temp (min_temp / min_temp+water_temp_variance), boost tank band (boost_temp-water_temp_variance / boost_temp), solar SoC (solar_soc_start / solar_soc_start-solar_soc_hysteresis) and tank vs max_temp (max_temp-water_temp_variance / max_temp). Computes force_heat_status (boost temp OK ∧ boost_state === heat), solar_heat_status, and 4-way ANDsettings.load_request. Writes logic.heating_*, logic.heating_method (priority: Solar Dump → Boost Heat → Timer → Min Temp → Idle), optional boost reset (boost_state / climate_modeauto), and logic.set_power from settings.load_allow.

Outputs: O1 = msg.payload = device_id on every evaluation (route/trigger pulse). O2 / O3 only when load_request or heating_method changes (RBE); global.bluedyn is updated on every evaluation regardless.


coordinator

| Property | Description | |----------|-------------| | Name | Display label (default coordinator). | | max_load | Max allocatable load budget (W). | | reserve | Reserved headroom subtracted from max load (W). Default 500. | | enabled | Master enable/disable for allocations. | | delay | Debounce delay before applying logic (seconds). Default 30 in the editor. |

Input (single): Route with msg.topicdevice_id + string payload registers a device (or empty topic + string payload, or msg.device_id, for direct wire from hardware O1). batt_soc + numeric payload = SoC %. consumption + numeric payload = consumption (W). Use a core Change node (or Function) upstream to set msg.topic for Victron/HA feeds. Any other message is ignored (no outputs fired for that message).

Defaults: Until first valid telemetry is received, coordinator uses batt_soc = 50 and consumption = 0. Status shows a default-warning suffix (default soc, default cons, or default soc+cons).

Outputs: O1 devices/check summary, O2 logic summary, O3 per-device output list (load_request, load_size, load_allow). Node status shows CHECK ... | LOGIC ....


device-home-assistant

| Property | Description | |----------|-------------| | Name | Display label. | | Device ID | e.g. geyser1 — target global.bluedyn[<device_id>].logic. Recommended so HA works before device-logic runs. | | MQTT broker (Home Assistant) | Optional. Same broker config type as device-geyserwiseESP / core mqtt. If set, subscribes to Subscribe topic (default homeassistant/#, QoS 1). If empty, use the input only (e.g. server-state-changed). | | Subscribe topic | MQTT wildcard for HA state topics. Payload + topic are converted to entity_id (e.g. homeassistant/input_datetime/foo/stateinput_datetime.foo). | | Timer 1–3: start, end, temp, status | Optional full HA entity_id each (exact string). Empty = that slot ignored. |

Resolve order for device id: config Device IDmsg.device_idstashed id from device-logic / device-geyserwiseESP O1 (msg.payload string). If still empty, entity updates warn and skip.

Status: HA MQTT line (subscribed / connected when exposed by broker) + count of matched timer-helper messages.

Slot → logic mapping (fixed):

| Config field | Writes to | Coercion | |--------------|-----------|----------| | entity_timer1_startentity_timer3_start | logic.timer1_startlogic.timer3_start | string (e.g. 05:00:00) | | entity_timer1_endentity_timer3_end | logic.timer1_endlogic.timer3_end | string | | entity_timer1_tempentity_timer3_temp | logic.timer1_templogic.timer3_temp | number | | entity_timer1_statusentity_timer3_status | logic.timer1_statuslogic.timer3_status | bool (on/off, etc.) |

After every successful write, logic.timer_status is set to true if any of timer1_status, timer2_status, or timer3_status is on, else false — so device-logic can keep using a single aggregate timer gate.

Input: events: state / server-state-changed / Link-in, or MQTT subscription. msg.entity_id must exactly equal the configured string.

Value: Uses msg.payload, else msg.data.new_state.state, else msg.new_state.state.

Outputs: O1msg.payload + msg.device_id = device id (after each successful helper write). O2 — timer snapshot (entity_id, field, value, nested payload with all timers + timer_status, mqtt_topic if from MQTT). O3 reserved. Debug must use the middle output. No O2 until a configured entity matches and device id is resolved — reinstall palette + deploy if you added O2 recently.


bluedyn-mqtt-sync

| Property | Description | |----------|-------------| | Name | Optional label. | | MQTT broker | Shared broker config; must support subscribe + publish like the standard MQTT broker node. | | Topic prefix | Base MQTT topic (default bluedyn). Subscribe: prefix/#. Publishes under prefix/.... | | Sync interval (s) | Periodic outbound scan. 0 = timer off; use the node input (e.g. inject) to trigger a push. | | Suppress TX after RX (ms) | After an inbound message on a path, skip outbound publish on that path for this many ms (reduces echo loops). | | Clock check | Off — does not use prefix/_mqtt/time at all; MQTT ↔ bluedyn sync works as usual (typical for a single machine or when you don’t care about clock sanity). Host — periodically publishes { epochMs, tz } to prefix/_mqtt/time (TZ is fixed in code). Clientsubscribes to that topic and compares host epochMs to Date.now(); if skew > 10 s, node.error + red status (NTP issue). | | Time publish interval (s) | Host only: how often to publish prefix/_mqtt/time (minimum 10 s in the UI). | | Retain sync publishes | If enabled, each leaf publish uses MQTT retain. New subscribers (e.g. GX client after deploy) then receive the last value per topic as soon as they subscribe — no need to wait for the next live change. Trade-off: broker holds last message per topic (usually what you want for state mirrors). | | Full push after deploy | Use on the machine that is source-of-truth for global.bluedyn (host). Once ~2 s after deploy, republishes every leaf, ignoring the change-only cache, so all current values hit the broker in one burst. | | Full push every (s) | Host only. If > 0, repeats a full republish on that interval so clients that connected earlier can still receive a refresh without waiting for each key to change. 0 = off. | | Input msg.republishFull | Send msg.republishFull = true into the node (e.g. inject) to run one full push on demand (same as deploy option, without waiting). |

Fixed in code (not in editor): sync uses QoS 1. Clock-check time topic uses retain off. Optional retain applies only to normal sync leaf payloads when the checkbox is on. Time JSON tz is Africa/Johannesburg. Client clock skew > 10 snode.error + red status.

MQTT reality (no “download all topics”): Standard MQTT has no request for “everything on prefix/#”. A subscriber only gets (a) live publishes after it subscribes, and (b) retained messages the broker stored per topic. So cold start on a client needs either retain on those publishes, or the host to republish the full state (deploy push, interval, or inject). After that, change-only sync is fine.

MQTT: Inbound: { v, t }, JSON, or plain string/number; merged into global.bluedyn and bluedyn._updated[path]. Topics under _mqtt/ (except _mqtt/time for clock check), snapshot, and leading _ are ignored for normal sync. Outbound: one topic per leaf; by default change-only vs last sent value — tx 0 is normal on a client that only receives and doesn’t alter bluedyn. Time topic: { epochMs, tz }, not stored in bluedyn.

Status line: MQTT disconnected (red) if the broker config is not connected; otherwise MQTT OK · … with subscribe / rx … / tx N / clock skew text. When there is nothing to publish, the node does not keep resetting the line to grey tx 0 (that hid the last rx … and looked like a drop-out). One message at a time on the status is normal: MQTT delivers topics individually, and each inbound update applies to global.bluedyn as it arrives — not a batch snapshot.

Troubleshooting

  • Old editor fields (QoS, Retain, TZ, skew ms): Not in the current node template. If they still appear, Node-RED is loading an older copy of the package — reinstall from a fresh build of this repo, restart Node-RED, redeploy.
  • Client global.bluedyn almost empty but MQTT Explorer shows data: Use a build with plain-string inbound support; verify topic prefix and broker. Explorer can show traffic that was never retained and happened before the client subscribed — MQTT won’t replay that. This node retains what it publishes; if HA (or another writer) publishes the same tree without retain, fix retain on that side or ensure a live republish after the client connects.
  • Client on LAN / clock check: On the host set Clock check → Host; on the clientClient; same broker and prefix. Not required for sync itself — only for detecting clock skew. Red skew status is not overwritten by routine tx/rx lines.
  • MQTT disconnected unless an mqtt in is also on the flow: Node-RED’s mqtt-broker only opens the TCP connection when at least one node calls register() (as mqtt in / mqtt out do). Palette nodes now register the same way, so bluedyn-mqtt-sync / device-geyserwiseESP can connect without a dummy mqtt in.

bluedyn-set (Set Global Var) — TEMPORARY

Provisional node. Exposes SGV-style writes for early flows. When the final node is implemented, this logic should live inside that node (or shared module), and this palette node may be removed.

| Property | Description | |----------|-------------| | Name | Optional label. |

Input: msg.type (string key) and msg.payload (required; must not be undefined). Optional msg.device_id overrides flow context.

Outputs: 1 = success (original msg forwarded), 2 = error (msg.error set).

Writes: If msg.type === flow.hardware (flow context hardware), writes bluedyn[device_id][hardwareKey] = payload (hardware blob). Otherwise msg.type must be a known key: writes to bluedyn.power[key] or bluedyn[device_id][folder][key] according to the internal key→folder map (see bluedyn-var.js / VARIABLE-MAP.md). Requires flow.device_id for non-power keys.


bluedyn-get (Get Global Var) — TEMPORARY

Provisional node. Exposes GGV-style reads for early flows. When the final node is implemented, this logic should live inside that node (or shared module), and this palette node may be removed.

| Property | Description | |----------|-------------| | Name | Optional label. |

Input: msg.type = key to read.

Read order: bluedyn.power[key] first (shared). Else, with flow.device_id, search settingssystemlogic under that device. If the key contains /, only bluedyn[device_id][flow.hardware][key] is used (no folder fallback). Uses flow.device_id and flow.hardware from flow context.

Not found: Red status; no output message (same idea as return null in a function node).


test-gwiseTEMPORARY

Bench / debug only. Not part of the final product; safe to drop from the palette once you no longer need a fixed-topic MQTT sniffer.

| Property | Description | |----------|-------------| | Name | Label. | | MQTT broker | Broker for a single hard-coded test subscription. |

Subscribes to geyserwise-tse-f6ee1a/sensor/water_temperature/state and shows payload in status.


Choosing icons (official references)

  1. Font Awesome 4.7 (names like fa-exchange, fa-refresh): fontawesome.com/v4.7.0/icons — in code: icon: "font-awesome/fa-exchange".
  2. Stock Node-RED SVGs (e.g. bridge.svg, arrow-in.svg): Creating nodes → Appearance — use icon: "arrow-in.svg" etc.
  3. Custom SVG: white on transparent, ~2:3, min ~40×60 — place under src/icons/ or next to the node; icon: "yourfile.svg".

Architecture

Device Nodes → Logic Nodes → Function Nodes.

See docs/ARCHITECTURE.md in the parent folder for the full design.