diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e3e9bf..adf38a0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,12 +12,15 @@ jobs: test: name: Run tests runs-on: ubuntu-latest + strategy: + matrix: + node-version: [ 12.x, 14.x ] steps: - uses: actions/checkout@v2 - name: Use Node.js uses: actions/setup-node@v2 with: - node-version: '14.x' + node-version: ${{ matrix.node-version }} - name: Install dependencies run: npm ci - name: Run tests diff --git a/CHANGELOG.md b/CHANGELOG.md index 818384d..f725421 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Publish only changed settings or modes in MQTT callback (fixes https://github.com/Jalle19/eda-modbus-bridge/issues/33) * Add alarms support (fixes https://github.com/Jalle19/eda-modbus-bridge/issues/31) * Format code using `prettier` +* Support running on Node.js 12.x (fixes https://github.com/Jalle19/eda-modbus-bridge/issues/38) ## 2.0.0 diff --git a/README.md b/README.md index 8c68902..b318006 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![Run tests](https://github.com/Jalle19/eda-modbus-bridge/actions/workflows/test.yml/badge.svg)](https://github.com/Jalle19/eda-modbus-bridge/actions/workflows/test.yml) -An HTTP/MQTT bridge for Enervent ventilation units with EDA automation (e.g. Pingvin). It provides a REST-ful HTTP interface -for interacting with the ventilation unit (reading temperatures and changing certain settings), as well as an MQTT -client which can publish readings/settings regularly and be used to control the ventilation unit. +An HTTP/MQTT bridge for Enervent ventilation units with EDA automation (e.g. Pingvin and Pandion). It provides a REST-ful +HTTP interface for interacting with the ventilation unit (reading temperatures and changing certain settings), as well +as an MQTT client which can publish readings/settings regularly and be used to control the ventilation unit. Communication happens over RS-485 (Modbus RTU) by connecting a serial device to the "Freeway" port on the ventilation unit's computer board. @@ -19,8 +19,8 @@ https://www.home-assistant.io/integrations/switch.rest/ with minimal effort. See ## Requirements -* Node.js (only tested with 14.x) -* An Enervent Pingvin ventilation unit (other EDA-based units may work, but the Modbus register numbers probably differ) +* Node.js 12.x or newer +* An Enervent ventilation unit with EDA automation (Pingvin and Pandion confirmed working) * An RS-485 device (e.g. `/dev/ttyUSB0`) connected to the Enervent unit's Freeway port ## Usage diff --git a/app/modbus.mjs b/app/modbus.mjs index b300dee..9eab92c 100644 --- a/app/modbus.mjs +++ b/app/modbus.mjs @@ -299,7 +299,7 @@ export const getAlarmStatuses = async (modbusClient) => { return alarms } -const getDeviceFamilyName = (familyTypeInt) => { +export const getDeviceFamilyName = (familyTypeInt) => { return ( [ 'Pingvin', // prettier-hack @@ -311,7 +311,7 @@ const getDeviceFamilyName = (familyTypeInt) => { 'LTR-6̈́', 'LTR-7', 'LTR-7 XL', - ][familyTypeInt] ?? 'unknown' + ][familyTypeInt] || 'unknown' ) } @@ -330,7 +330,7 @@ const getCoolingTypeName = (coolingTypeInt) => { ][coolingTypeInt] } -const getHeatingTypeName = (heatingTypeInt) => { +export const getHeatingTypeName = (heatingTypeInt) => { // 0=Ei lämmitintä, 1=VPK, 2=HP, 3=SLP, 4=SLP PWM. Mapping known values to the actual names used on the product, // these seem to be internal return ( @@ -340,7 +340,7 @@ const getHeatingTypeName = (heatingTypeInt) => { 'HP', 'EDE', 'SLP PWM', - ][heatingTypeInt] ?? 'unknown' + ][heatingTypeInt] || 'unknown' ) } diff --git a/package-lock.json b/package-lock.json index 64b293e..c239827 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "eda-modbus-bridge", "version": "2.0.0", "license": "GPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index 4bdf8c3..6e40862 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ }, "scripts": { "test": "NODE_OPTIONS=--experimental-vm-modules jest", - "check-prettier": "prettier --check eda-modbus-bridge.mjs app/", - "prettier": "prettier --write eda-modbus-bridge.mjs app/" + "check-prettier": "prettier --check eda-modbus-bridge.mjs app/ tests/", + "prettier": "prettier --write eda-modbus-bridge.mjs app/ tests/" }, "repository": { "type": "git", diff --git a/tests/modbus.test.mjs b/tests/modbus.test.mjs index 7c44b00..5b7b303 100644 --- a/tests/modbus.test.mjs +++ b/tests/modbus.test.mjs @@ -1,6 +1,9 @@ import { parseTemperature, - createModelNameString, parseAlarmTimestamp + createModelNameString, + parseAlarmTimestamp, + getDeviceFamilyName, + getHeatingTypeName, } from '../app/modbus.mjs' test('parse temperature', () => { @@ -12,28 +15,34 @@ test('parse temperature', () => { test('create model name from device information', () => { // Heating, no cooling, DC fan - expect(createModelNameString({ - familyType: 'Pingvin', - fanType: 'EC', - heatingTypeInstalled: 'EDE', - coolingTypeInstalled: null, - })).toEqual('Pingvin eco EDE') + expect( + createModelNameString({ + familyType: 'Pingvin', + fanType: 'EC', + heatingTypeInstalled: 'EDE', + coolingTypeInstalled: null, + }) + ).toEqual('Pingvin eco EDE') // Heating, cooling, DC fan - expect(createModelNameString({ - familyType: 'Pegasus', - fanType: 'EC', - heatingTypeInstalled: 'EDE', - coolingTypeInstalled: 'CG', - })).toEqual('Pegasus eco EDE - CG') + expect( + createModelNameString({ + familyType: 'Pegasus', + fanType: 'EC', + heatingTypeInstalled: 'EDE', + coolingTypeInstalled: 'CG', + }) + ).toEqual('Pegasus eco EDE - CG') // No heating, no cooling, AC fan - expect(createModelNameString({ - familyType: 'Pandion', - fanType: 'AC', - heatingTypeInstalled: null, - coolingTypeInstalled: null, - })).toEqual('Pandion') + expect( + createModelNameString({ + familyType: 'Pandion', + fanType: 'AC', + heatingTypeInstalled: null, + coolingTypeInstalled: null, + }) + ).toEqual('Pandion') }) test('parse alarm timestamp', () => { @@ -46,13 +55,22 @@ test('parse alarm timestamp', () => { 21, // day 13, // hour 45, // minute - ] + ], } const timestamp = parseAlarmTimestamp(alarmResult) // The ventilation unit is assumed to be using the same timezone as the computer running this software, // i.e. the result from Modbus is in local time. - expect(timestamp.toLocaleString('fi-FI')).toEqual('21.1.2022 klo 13.45.00') expect(timestamp.toLocaleString('en-US')).toEqual('1/21/2022, 1:45:00 PM') }) + +test('device family name', () => { + expect(getDeviceFamilyName(0)).toEqual('Pingvin') + expect(getDeviceFamilyName(999)).toEqual('unknown') +}) + +test('heating type name', () => { + expect(getHeatingTypeName(0)).toEqual('ED') + expect(getHeatingTypeName(999)).toEqual('unknown') +})