optolink-bridge 1.0.0 → 1.1.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/config.toml CHANGED
@@ -193,9 +193,9 @@ prefix = "homeassistant"
193
193
  # you may set its entry in the overrides section to false, e.g.: some_data_point = false
194
194
  [mqtt.device_discovery.overrides.sensor]
195
195
 
196
- outside_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1, icon = "mdi:thermometer" }
197
- averaged_outside_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1, icon = "mdi:thermometer" }
198
- averaging_time_outside_temperature = { device_class = "duration", unit_of_measurement = "min" }
196
+ outside_temperature = { device_class = "temperature", state_class = "measurement", unit_of_measurement = "°C", suggested_display_precision = 1, icon = "mdi:thermometer" }
197
+ averaged_outside_temperature = { device_class = "temperature", state_class = "measurement", unit_of_measurement = "°C", suggested_display_precision = 1, icon = "mdi:thermometer" }
198
+ averaging_time_outside_temperature = { device_class = "duration", state_class = "measurement", unit_of_measurement = "min" }
199
199
 
200
200
  operating_mode = { icon = "mdi:cog", value_template = """
201
201
  {% set mapping = ({
@@ -212,32 +212,32 @@ operating_mode = { icon = "mdi:cog", value_template = """
212
212
 
213
213
  hkl_level = { state_class = "measurement", icon = "mdi:chart-line" }
214
214
  hkl_slope = { state_class = "measurement", icon = "mdi:chart-line" }
215
- hysteresis = { device_class = "temperature", unit_of_measurement = "K", icon = "mdi:thermometer-lines" }
215
+ hysteresis = { device_class = "temperature", unit_of_measurement = "K", state_class = "measurement", icon = "mdi:thermometer-lines" }
216
216
 
217
- buffer_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
218
- primary_circuit_flow_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
219
- secondary_circuit_flow_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
220
- return_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
217
+ buffer_temperature = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
218
+ primary_circuit_flow_temperature = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
219
+ secondary_circuit_flow_temperature = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
220
+ return_temperature = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
221
221
 
222
- "3way_valve_position" = { unit_of_measurement = "%", suggested_display_precision = 0, icon = "mdi:valve", value_template = "{{ value | float * 100 }}" }
222
+ "3way_valve_position" = { unit_of_measurement = "%", state_class = "measurement", suggested_display_precision = 0, icon = "mdi:valve", value_template = "{{ value | float * 100 }}" }
223
223
 
224
- suction_gas_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
225
- suction_gas_pressure = { device_class = "pressure", unit_of_measurement = "bar", suggested_display_precision = 1 }
226
- hot_gas_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
227
- hot_gas_pressure = { device_class = "pressure", unit_of_measurement = "bar", suggested_display_precision = 1 }
228
- liquid_gas_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
229
- condensation_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
230
- evaporation_temperature = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
231
- compressor_power = { unit_of_measurement = "%", suggested_display_precision = 0, icon = "mdi:heat-pump", value_template = "{{ value | int }}" }
232
- ecv_position = { unit_of_measurement = "%", suggested_display_precision = 0, icon = "mdi:valve", value_template = "{{ value | int }}" }
224
+ suction_gas_temperature = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
225
+ suction_gas_pressure = { device_class = "pressure", unit_of_measurement = "bar", state_class = "measurement", suggested_display_precision = 1 }
226
+ hot_gas_temperature = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
227
+ hot_gas_pressure = { device_class = "pressure", unit_of_measurement = "bar", state_class = "measurement", suggested_display_precision = 1 }
228
+ liquid_gas_temperature = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
229
+ condensation_temperature = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
230
+ evaporation_temperature = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
231
+ compressor_power = { unit_of_measurement = "%", state_class = "measurement", suggested_display_precision = 0, icon = "mdi:heat-pump", value_template = "{{ value | int }}" }
232
+ ecv_position = { unit_of_measurement = "%", state_class = "measurement", suggested_display_precision = 0, icon = "mdi:valve", value_template = "{{ value | int }}" }
233
233
 
234
- dhw_temperature_storage = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
235
- dhw_temperature_setpoint = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
236
- dhw_temperature_setpoint2 = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
234
+ dhw_temperature_storage = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
235
+ dhw_temperature_setpoint = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
236
+ dhw_temperature_setpoint2 = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
237
237
 
238
- room_temperature_setpoint_normal = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
239
- room_temperature_setpoint_reduced = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
240
- room_temperature_setpoint_party = { device_class = "temperature", unit_of_measurement = "°C", suggested_display_precision = 1 }
238
+ room_temperature_setpoint_normal = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
239
+ room_temperature_setpoint_reduced = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
240
+ room_temperature_setpoint_party = { device_class = "temperature", unit_of_measurement = "°C", state_class = "measurement", suggested_display_precision = 1 }
241
241
 
242
242
  compressor_phase = { icon = "mdi:information", value_template = """
243
243
  {% set mapping = ({
@@ -264,10 +264,10 @@ compressor_phase = { icon = "mdi:information", value_template = """
264
264
  }) %}
265
265
  {{ mapping[value[:2]] | default('Unknown') }}""" }
266
266
 
267
- compressor_starts = { unit_of_measurement = "cycles", state_class = "total_increasing", icon = "mdi:restart" }
267
+ compressor_starts = { unit_of_measurement = "cycle(s)", state_class = "total_increasing", icon = "mdi:restart" }
268
268
  compressor_operating_hours = { device_class = "duration", unit_of_measurement = "h", state_class = "total_increasing", icon = "mdi:fan-clock" }
269
- compressor_heating_power = { device_class = "power", unit_of_measurement = "W" }
270
- compressor_power_consumption = { device_class = "power", unit_of_measurement = "W" }
269
+ compressor_heating_power = { device_class = "power", unit_of_measurement = "W", state_class = "measurement" }
270
+ compressor_power_consumption = { device_class = "power", unit_of_measurement = "W", state_class = "measurement" }
271
271
 
272
272
  annual_performance_factor = { state_class = "measurement", icon = "mdi:chart-line", value_template = "{{ value | float | round(2) }}" }
273
273
  annual_performance_factor_heating = { state_class = "measurement", icon = "mdi:chart-line", value_template = "{{ value | float | round(2) }}" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "optolink-bridge",
3
- "version": "1.0.0",
3
+ "version": "1.1.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",
@@ -53,11 +53,11 @@
53
53
  "binary-parser-encoder-bump": "^2.2.1",
54
54
  "buffer-to-str": "^1.0.0",
55
55
  "change-case": "^5.4.4",
56
- "chokidar": "^4.0.3",
56
+ "chokidar": "^5.0.0",
57
57
  "mqtt": "^5.14.1",
58
58
  "serialport": "^13.0.0",
59
- "smol-toml": "^1.4.2",
60
- "yocto-queue": "^1.2.1"
59
+ "smol-toml": "^1.5.2",
60
+ "yocto-queue": "^1.2.2"
61
61
  },
62
- "packageManager": "yarn@4.10.3"
62
+ "packageManager": "yarn@4.12.0"
63
63
  }
package/parse_vs2.js CHANGED
@@ -195,7 +195,7 @@ const valueParser = new Parser()
195
195
 
196
196
  let options;
197
197
  if (type === 'string') {
198
- options = { greedy: true };
198
+ options = { greedy: true, formatter: str => str.replace(/\x00/g, '') };
199
199
  } else if (type === 'buffer') {
200
200
  options = { readUntil: 'eof' };
201
201
  }
@@ -107,19 +107,13 @@ function getRawValue(value, index) {
107
107
  * - Everything else to a string
108
108
  * @param {Buffer|any} value the (raw) value to output
109
109
  * @param {object} [dp] the datapoint to parse the value
110
- * @param {boolean} [scales] scale numbers to common factors (0.1, 0.01, 1/3600)
111
110
  * @returns {string} to value formatted as string
112
111
  */
113
- function getValue(value, dp, scales) {
114
- if (typeof dp === 'boolean') {
115
- scales = dp;
116
- dp = undefined
117
- }
118
-
112
+ function getValue(value, dp) {
119
113
  if (Array.isArray(value)) {
120
- return `${getValue(value[0], dp)} → ${getValue(value[1], dp, scales)}`;
114
+ return `${getValue(value[0], dp)} → ${getValue(value[1], dp)}`;
121
115
  } else if (typeof value === 'object' && value.data && value.write === true) {
122
- return `${getValue(value.data, dp, scales)} (!)`;
116
+ return `${getValue(value.data, dp)} (!)`;
123
117
  } else if (Buffer.isBuffer(value) && dp) {
124
118
  try {
125
119
  value = dp.parse(value);
@@ -133,9 +127,10 @@ function getValue(value, dp, scales) {
133
127
  return `0x${value.toString('hex')}`;
134
128
  }
135
129
 
130
+ // in case the data point is unknown, output some scales for numbers
136
131
  if (typeof value === 'number') {
137
- return scales ? [value, value / 10, value / 100, value / 3600].map(getNumber) : getNumber(value);
138
- } else if (typeof value === 'bigint' && scales) {
132
+ return !dp ? [value, value / 10, value / 100, value / 3600].map(getNumber) : getNumber(value);
133
+ } else if (typeof value === 'bigint' && !dp) {
139
134
  return [value, value / BigInt(10), value / BigInt(100), value / BigInt(3600)].map(value => value.toString());
140
135
  }
141
136
 
@@ -285,7 +280,7 @@ for await (let line of lines) {
285
280
  let value;
286
281
  const dp = dps.get(packet.addr);
287
282
  if (dp) {
288
- value = `data point: ${getValue(packet.data, dp)}`;
283
+ value = `data point "${dp.name}": ${getValue(packet.data, dp)}`;
289
284
  } else {
290
285
  value = `debug: ${JSON.stringify(Object.fromEntries(Object.entries(parseValue('debug', packet.data)).map(([key, value]) => [key, getValue(value)])))}`;
291
286
  }
@@ -365,7 +360,7 @@ if (malformed.length) {
365
360
 
366
361
  console.log();
367
362
  function output(stats, valueFn) {
368
- const withDps = stats.filter(({ dp }) => dp);
363
+ const withDps = stats.filter(({ dp }) => dp).sort(({ addr: addrA }, { addr: addrB }) => addrA - addrB);
369
364
  if (withDps.length) {
370
365
  console.log();
371
366
  console.log('With configured data points:');
@@ -374,7 +369,7 @@ function output(stats, valueFn) {
374
369
  }
375
370
  }
376
371
 
377
- const woDps = stats.filter(({ dp }) => !dp);
372
+ const woDps = stats.filter(({ dp }) => !dp).sort(({ addr: addrA }, { addr: addrB }) => addrA - addrB);;
378
373
  if (woDps.length) {
379
374
  console.log();
380
375
  console.log('Without configured data points (output as raw / hex):');
@@ -441,13 +436,13 @@ if (types.mostly_variable_values) {
441
436
 
442
437
  if (types.identical_values) {
443
438
  console.log('Addresses that never changed / always contained identical values (e.g. configurations):');
444
- output(types.identical_values, (values, dp) => `${values.length}× ${getValue(values[0], dp, true)}`);
439
+ output(types.identical_values, (values, dp) => `${values.length}× ${getValue(values[0], dp)}`);
445
440
  console.log();
446
441
  }
447
442
 
448
443
  if (types.strings_or_arrays) {
449
444
  console.log('Addresses which contained mostly strings or arrays (e.g. labels & complex data types):');
450
- output(types.strings_or_arrays, (values, dp) => `e.g. ${getValue(values[0], dp, true)}`);
445
+ output(types.strings_or_arrays, (values, dp) => `e.g. ${getValue(values[0], dp)}`);
451
446
  console.log();
452
447
  }
453
448
 
package/.editorconfig DELETED
@@ -1,10 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- end_of_line = lf
5
- insert_final_newline = true
6
-
7
- [*.{js,json,yml}]
8
- charset = utf-8
9
- indent_style = space
10
- indent_size = 2
package/.gitattributes DELETED
@@ -1,4 +0,0 @@
1
- /.yarn/** linguist-vendored
2
- /.yarn/releases/* binary
3
- /.yarn/plugins/**/* binary
4
- /.pnp.* binary linguist-generated
@@ -1,5 +0,0 @@
1
- {
2
- "recommendations": [
3
- "arcanis.vscode-zipfs"
4
- ]
5
- }
@@ -1,11 +0,0 @@
1
- {
2
- "search.exclude": {
3
- "**/.yarn": true,
4
- "**/.pnp.*": true
5
- },
6
- "cSpell.words": [
7
- "Optolink",
8
- "Viessmann",
9
- "Vitoconnect"
10
- ]
11
- }
@@ -1,5 +0,0 @@
1
- # This file is automatically generated by @yarnpkg/sdks.
2
- # Manual changes might be lost!
3
-
4
- integrations:
5
- - vscode
Binary file