optolink-bridge 1.2.0 → 1.3.0

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 CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  This file documents all *major & minor* releases. For revisions, please consult the [commit history](https://github.com/kristian/optolink-bridge/commits/main).
4
4
 
5
+ ## [1.3] - 2026-04-12
6
+
7
+ Introduce a `max_buffer_chunks` option to prevent stalled streams from filling up the memory with too many buffered chunks, and expose the vito + opto serial ports and bridges on the event emitter for debugging purposes.
8
+
5
9
  ## [1.2] - 2026-01-08
6
10
 
7
11
  Introduce a `publish_bus_state` MQTT option
package/config.toml CHANGED
@@ -132,6 +132,9 @@ max_decimals = 4
132
132
  # (this is because if poll_items is empty, optolink-bridge will start in pass-through mode and is not able to intercept messages)
133
133
  auto_reload_addr_items = true
134
134
 
135
+ # the maximum number of chunks to buffer in one direction, before considering the stream to be stalled and dropping oldest chunks, defaults to 1000
136
+ max_buffered_chunks = 100
137
+
135
138
  [mqtt]
136
139
 
137
140
  # broker protocol / host / port
package/index.js CHANGED
@@ -229,7 +229,7 @@ const packetQueue = async.queue(async task => {
229
229
  const topic = mqttDpTopic(dp), value = dp ? dp.parse(packet.data) : packet.data;
230
230
  logger.debug(`Publishing ${dp ? dp.name : 'unknown'} data point (${formatAddr(packet.addr)}) to ${topic}:`, value);
231
231
  await mqttClient.publishAsync(topic, Buffer.isBuffer(value) ? bufferToFormat(value, config.buffer_format ?? 'hex') :
232
- `${ typeof value === 'number' ? parseFloat(value.toFixed(config.max_decimals ?? 4 )) : value }`);
232
+ `${ typeof value === 'number' ? parseFloat(value.toFixed(config.max_decimals ?? 4)) : value }`);
233
233
  }, 1 /* no concurrency, packets should to be processed in order */);
234
234
  packetQueue.error((err, task) => {
235
235
  logger.error(`Error while processing packet: ${task?.data?.toString('hex')}`, err);
@@ -260,6 +260,10 @@ const chunkQueue = async.queue(async task => {
260
260
  packetQueue.push({ data, packet, direction });
261
261
  } else {
262
262
  chunks.push(task.chunk);
263
+ if (chunks.length > (config.max_buffered_chunks ?? 100)) {
264
+ logger.warn(`The chunk queue has buffered more than ${config.max_buffered_chunks ?? 100} chunks in the same direction (${directionName(direction)}), which could indicate that the stream is stalled. Consider increasing the max_buffered_chunks setting in your config.toml file if you are polling huge packets or check if there are any issues with the serial connection.`);
265
+ chunks.shift(); // drop the oldest chunk
266
+ }
263
267
  }
264
268
  }, 1 /* no concurrency, chunks need to be processed in order */);
265
269
  chunkQueue.error((err, task) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "optolink-bridge",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Safely bridge the Optolink bus of your Viessmann heating system and publish attributes via MQTT + Home Assistant Device Discovery",
5
5
  "keywords": [
6
6
  "viessmann",
@@ -54,9 +54,9 @@
54
54
  "buffer-to-str": "^1.0.0",
55
55
  "change-case": "^5.4.4",
56
56
  "chokidar": "^5.0.0",
57
- "mqtt": "^5.14.1",
57
+ "mqtt": "^5.15.1",
58
58
  "serialport": "^13.0.0",
59
- "smol-toml": "^1.5.2",
59
+ "smol-toml": "^1.6.1",
60
60
  "yocto-queue": "^1.2.2"
61
61
  },
62
62
  "packageManager": "yarn@4.12.0"
package/serial.js CHANGED
@@ -56,11 +56,12 @@ export async function connect(vitoPath = '/dev/ttyS0', optoPath = '/dev/ttyUSB0'
56
56
  lock: true // exclusive
57
57
  };
58
58
 
59
- const vitoPort = new SerialPort({
59
+ // expose the vito + opto ports, mainly for debugging purpose
60
+ const vitoPort = eventEmitter.vitoPort = new SerialPort({
60
61
  path: vitoPath,
61
62
  ...portOptions
62
63
  });
63
- const optoPort = new SerialPort({
64
+ const optoPort = eventEmitter.optoPort = new SerialPort({
64
65
  path: optoPath,
65
66
  ...portOptions
66
67
  });
@@ -80,8 +81,8 @@ export async function connect(vitoPath = '/dev/ttyS0', optoPath = '/dev/ttyUSB0'
80
81
  };
81
82
 
82
83
  eventEmitter.pipeline = Promise.all([
83
- pipeline(vitoPort, createBridgeStream(fromVitoToOpto, vitoPort, optoPort), optoPort),
84
- pipeline(optoPort, createBridgeStream(fromOptoToVito, vitoPort, optoPort), vitoPort)
84
+ pipeline(vitoPort, eventEmitter.vitoToOptoBridge = createBridgeStream(fromVitoToOpto, vitoPort, optoPort), optoPort),
85
+ pipeline(optoPort, eventEmitter.optoToVitoBridge = createBridgeStream(fromOptoToVito, vitoPort, optoPort), vitoPort)
85
86
  ]);
86
87
 
87
88
  return eventEmitter;