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 🙏

© 2024 – Pkg Stats / Ryan Hefner

comfoair

v1.0.6

Published

Library to control a Zehnder Comfoair 350 and similar ventilation devices

Downloads

38

Readme

comfoair

Reads and controls ventilation devices like Zehnder ComfoAir 350 with Node.js

Comfoair is an Open Source Node.js implementaion of the communication protocoll done and used by Zehnder. You can use this libary to control your personal ventialtion system.

This work based on the protocol description at http://www.see-solutions.de/sonstiges/Protokollbeschreibung_ComfoAir.pdf, the very usefull openHAB binding 'ComfoAir', the fantastic FHEM module 'ComfoAir' and a little bit research of my own. Hope you will like it.

Supports the following systems (afaik)

  • Zehnder ComfoAir 350
  • StorkAir WHR930
  • Wernig/Santos G90-380
  • Paul 370 DC

Status

| Category | Status | | ---------------- | ------------------------------------------------------------------------------------------------------------------------- | | Version | npm version | | Dependencies | Dependency Status | | License | npm |

Install

Simply

npm install comfoair

How to use

Connect your unit via serial bus and configure the port in your code. The comfoair library using serialport, so you can use every port (and syntax), that serialport is supporting. The speed (baud) should always be 9600, this is also default. If your ventilation device - like mine - is too far away from your controling system and you have lan/wlan access, you can pipe your serial port through ethernet with ser2net and socat. Look at this nice description for example: https://www.acmesystems.it/socat. The comfoair lib can either be uses as stream or simply by calling the given functions.

Examples

Depends on how you like it

By function call

Create an object and call one of the functions, given from the API.

To get your firmware version and device name:

const Comfoair = require('comfoair');

const ventilation = new Comfoair({
    port: '/dev/ttyUSB0',
    baud: 9600
});

ventilation.on('error', (err) => {
    console.log('ERROR: ' + err.message);
});

ventilation.on('close', () => {
    console.log('Connection to Comfoair closed');
});

ventilation.on('open', () => {
    console.log('Connected to Comfoair :)');

    ventilation.getTemperatures((err, resp) => {
        if (err) console.log(err.message);
        else console.log(resp);
	
	    ventilation.close();
    });
});

To set the ventilation level:

const Comfoair = require('comfoair');

const ventilation = new Comfoair({
    port: '/dev/ttyUSB0',
    baud: 9600
});

ventilation.on('error', (err) => {
    console.log('ERROR: ' + err.message);
});

ventilation.on('close', () => {
    console.log('Connection to Comfoair closed');
});

ventilation.on('open', () => {
    console.log('Connected to Comfoair :)');

    ventilation.setLevel('middle', (err, resp) => {
        if (err) console.log(err.message);
        else console.log(resp);

        ventilation.close();
    });
});

As streaming object

Also you can use it as a duplex stream.

const ComfoairStream = require('comfoair').ComfoairStream;

const ventilationStream = new ComfoairStream({
    port: '/dev/ttyUSB0',
    baud: 9600
});

ventilationStream.on('error', (err) => {
    console.log('ERROR: ' + err.message);
});

ventilationStream.on('close', () => {
    console.log('Connection to Comfoair closed');
});

ventilationStream.on('data', chunk => {
    console.log(chunk);
    if (chunk.type === 'RES') {
        ventilationStream.close();
    }
});

const command = {
    name: 'getFanState',
    params: {}
};

ventilationStream.on('open', () => {
    console.log('Connected to Comfoair :)');

    ventilationStream.write(command, (err) => {
        if (err) return console.log('ERROR: ' + err.message);
    });
});

API

getBootloaderVersion

Request bootloader version and device type. No parameter required.

getBootloaderVersion(callback);

Response

{
    "type": "RES",
    "valid": true,
    "payload": {
        "description": "Bootloader version",
        "major": {
            "value": 3,
            "label": "Version Major"
        },
        "minor": {
            "value": 60,
            "label": "Version Minor"
        },
        "beta": {
            "value": 32,
            "label": "Beta"
        },
        "deviceName": {
            "value": "CA350 luxe",
            "label": "Device name"
        }
    }
}

getFirmwareVersion

Request firmware version and device type. No parameter required.

getFirmwareVersion(callback);

Response

{
    "type": "RES",
    "valid": true,
    "payload": {
        "description": "Firmware version",
        "major": {
            "value": 3,
            "label": "Version Major"
        },
        "minor": {
            "value": 60,
            "label": "Version Minor"
        },
        "beta": {
            "value": 32,
            "label": "Beta"
        },
        "deviceName": {
            "value": "CA350 luxe",
            "label": "Device name"
        }
    }
}

getFanState

Request state of the supply and the exhaust fan. No parameter required.

getFanState(callback);

Response

{
    "type": "RES",
    "valid": true,
    "payload": {
        "description": "Fan state",
        "supplyAir": {
            "value": 35,
            "label": "Supply air",
            "unit": "%"
        },
        "outgoingAir": {
            "value": 35,
            "label": "Outgoing air",
            "unit": "%"
        },
        "rotationsSupply": {
            "value": 1138,
            "label": "Rotations supply",
            "unit": "rpm"
        },
        "rotationsOutgoing": {
            "value": 1120,
            "label": "Rotations outgoing",
            "unit": "rpm"
        }
    }
}

getFlapState

Request state of the bypass and preheater. No parameter required.

getFlapState(callback);

Response

{
    "type": "RES",
    "valid": true,
    "payload": {
        "description": "Flap state",
        "bypass": {
            "value": 0,
            "label": "Bypass",
            "unit": "%"
        },
        "preheat": {
            "value": "Unknown",
            "label": "Preheat"
        },
        "bypassMotorCurrent": {
            "value": 0,
            "label": "Bypass Motor Current",
            "unit": "A"
        },
        "preheatMotorCurrent": {
            "value": 0,
            "label": "Preheat Motor Current",
            "unit": "A"
        }
    }
}

getOperatingHours

Request operating hours of the different modes. No parameter required.

getOperatingHours(callback);

Response

{
    "type": "RES",
    "valid": true,
    "payload": {
        "description": "Operating hours",
        "away": {
            "value": 13492,
            "label": "away",
            "unit": "h"
        },
        "low": {
            "value": 12833,
            "label": "low",
            "unit": "h"
        },
        "middle": {
            "value": 7699,
            "label": "middle",
            "unit": "h"
        },
        "frostProtection": {
            "value": 662,
            "label": "frost protection",
            "unit": "h"
        },
        "preHeating": {
            "value": 0,
            "label": "preheating",
            "unit": "h"
        },
        "bypassOpen": {
            "value": 10008,
            "label": "bypass open",
            "unit": "h"
        },
        "filter": {
            "value": 1825,
            "label": "filter",
            "unit": "h"
        },
        "high": {
            "value": 1068,
            "label": "high",
            "unit": "h"
        }
    }
}

getVentilationLevel

Request ventilation levels. No parameter required.

getVentilationLevel(callback);

Response

{
    "type": "RES",
    "valid": true,
    "payload": {
        "description": "Get ventilation levels",
        "exhaustAway": {
            "value": 15,
            "label": "Exhaust fan level away",
            "unit": "%"
        },
        "exhaustLow": {
            "value": 35,
            "label": "Exhaust fan level low",
            "unit": "%"
        },
        "exhaustMiddle": {
            "value": 50,
            "label": "Exhaust fan level middle",
            "unit": "%"
        },
        "supplyAway": {
            "value": 15,
            "label": "Supply fan level away",
            "unit": "%"
        },
        "supplyLow": {
            "value": 35,
            "label": "Supply fan level low",
            "unit": "%"
        },
        "supplyMiddle": {
            "value": 50,
            "label": "Supply fan level middle",
            "unit": "%"
        },
        "exhaustCurrent": {
            "value": 15,
            "label": "Current exhaust fan level",
            "unit": "%"
        },
        "supplyCurrent": {
            "value": 15,
            "label": "Current supply fan level",
            "unit": "%"
        },
        "currentLevel": {
            "value": 1,
            "label": "Current ventilation level",
        },
        "supplyFanRunning": {
            "value": true,
            "label": "Supply fan is running"
        },
        "exhaustHigh": {
            "value": 70,
            "label": "Exhaust fan level high",
            "unit": "%"
        },
        "supplyHigh": {
            "value": 70,
            "label": "Exhaust fan level high",
            "unit": "%"
        }
    }
}

getTemperatures

Request current temperatures. No parameter required.

getTemperatures(callback);

Response

{
    "type": "RES",
    "valid": true,
    "payload": {
        "description": "Temperatures",
        "comfort": {
            "value": 21,
            "label": "comfort",
            "unit": "°C"
        },
        "outsideAir": {
            "value": 11,
            "label": "outside air",
            "unit": "°C"
        },
        "supplyAir": {
            "value": 20.5,
            "label": "supply air",
            "unit": "°C"
        },
        "outgoingAir": {
            "value": 19.5,
            "label": "outgoing air",
            "unit": "°C"
        },
        "exhaustAir": {
            "value": 11.5,
            "label": "exhaust air",
            "unit": "°C"
        },
        "sensorConnected": {
            "value": [],
            "label": "sensor connected"
        },
        "groundHeatExchanger": {
            "value": 0,
            "label": "ground heat exchanger",
            "unit": "°C"
        },
        "preheating": {
            "value": 0,
            "label": "preheating",
            "unit": "°C"
        },
        "cookerHood": {
            "value": 0,
            "label": "cooker hood",
            "unit": "°C"
        }
    }
}

getTemperatureStates

Request temperature states. No parameter required.

getTemperatureStates(callback);

Response

{
    "type": "RES",
    "valid": true,
    "payload": {
        "description": "Temperature states",
        "outsideAir": {
            "value": 11,
            "label": "outside air",
            "unit": "°C"
        },
        "supplyAir": {
            "value": 20.5,
            "label": "supply air",
            "unit": "°C"
        },
        "outgoingAir": {
            "value": 19,
            "label": "outgoing air",
            "unit": "°C"
        },
        "exhaustAir": {
            "value": 11.5,
            "label": "exhaust air",
            "unit": "°C"
        }
    }
}

getFaults

Request operation faults. No parameter required.

getFaults(callback);

Response

{  
   "description":"Operating faults",
   "currentErrorA":{  
      "value":"A0",
      "label":"current error A"
   },
   "currentErrorE":{  
      "value":0,
      "label":"current error E"
   },
   "lastErrorA":{  
      "value":"A0",
      "label":"last error A"
   },
   "lastErrorE":{  
      "value":0,
      "label":"last error E"
   },
   "penultimateErrorA":{  
      "value":"A0",
      "label":"penultimate error A"
   },
   "penultimateErrorE":{  
      "value":0,
      "label":"penultimate error E"
   },
   "antepenultimateErrorA":{  
      "value":"A0",
      "label":"antepenultimate error A"
   },
   "antepenultimateErrorE":{  
      "value":0,
      "label":"antepenultimate error E"
   },
   "replaceFilter":{  
      "value":true,
      "label":"replace filter"
   },
   "currentErrorEA":{  
      "value":0,
      "label":"current error EA"
   },
   "lastErrorEA":{  
      "value":0,
      "label":"last error EA"
   },
   "penultimateErrorEA":{  
      "value":0,
      "label":"penultimate error EA"
   },
   "antepenultimateErrorEA":{  
      "value":0,
      "label":"antepenultimate error EA"
   },
   "currentErrorAHigh":{  
      "value":0,
      "label":"current error A high"
   },
   "lastErrorAHigh":{  
      "value":0,
      "label":"last error A high"
   },
   "penultimateErrorAHigh":{  
      "value":0,
      "label":"penultimate error A high"
   },
   "antepenultimateErrorAHigh":{  
      "value":0,
      "label":"antepenultimate error A high"
   },
   "type":"RES"
}

setLevel

Set the ventilation level.

| Parameter | Type | Description | |:----------|:--------------|:----------------------------------------------------| | level | string/number | 'away', 'low', 'middle', 'high', 0, 1, 2, 3, 'auto' |

Where 'away' is the same as 0, 'low' is the same as 1 and so on. The numbers 0 to 3 can also be passed als string. The setting 'auto' seems to be for the external Panel for the ventilation system and isn't testet.

setLevel('high', callback);

Response

{
    "type": "ACK"
}

setComfortTemperature

Set the comfort Temperature.

| Parameter | Type | Description | |:------------|:-------|:--------------------------------------| | temperature | number | Temperature to be held by the device |

setComfortTemperature(20, callback);

Response

{
    "type": "ACK"
}

setVentilationLevel

Set the rotaition speed of the different ventilation levels for supply and exhaust fan.

| Parameter | Type | Description | |:--------------|:-------|:------------------------------------| | exhaustAway | number | Speed for away level, exhaust fan | | exhaustLow | number | Speed for low level, exhaust fan | | exhaustMiddle | number | Speed for middle level, exhaust fan | | exhaustHigh | number | Speed for high level, exhaust fan | | supplyAway | number | Speed for away level, supply fan | | supplyLow | number | Speed for low level, supply fan | | supplyMiddle | number | Speed for middle level, supply fan | | supplyHigh | number | Speed for high level, supply fan |

setVentilationLevel(15, 35, 50, 70, 15, 35, 50, 70, callback);

Response

{
    "type": "ACK"
}

reset

Reset faults/settings/filter timer or run self test.

| Parameter | Type | Description | |:----------------|:--------|:------------------| | resetFaults | boolean | Clear all faults | | resetSettings | boolean | Reset settings | | runSelfTest | boolean | Run self test | | resetFilterTime | boolean | Rest filter timer |

reset(false, false, false, true, callback);

Response

{
    "type": "ACK"
}

runCommand

It is also possible to pass the commands as strings. For this you can use this function.


const commandName = 'setLevel';
const params = { level: 'away' };

runCommand(commandName, params, (err, response) => {
    if (err) return console.log(err.message);
    console.log(response);
});

Additional info from VincentSC

Zehnder talks about intake, exhaust, supply and extract. Or extract, supply, incoming and outgoing.

This library uses supply, exhaust, outgoing and outside.

intake-exhaust-supply-extract

image

This is my guess how it translates:

  • outside = intake / incoming
  • supply = supply
  • exhaust = extract
  • outgoing = exhaust / outgoing

Migration to 1.x.x

There are only tiny API changes. The main difference is in using this module in streaming Mode. In Version 0.x.x you can simply use the same Module for function calls and streaming. Starting with 1.0.0 you have to choose the streaming module explicit.

0.x.x

const Comfoair = require('comfoair');

const ventilationStream = new Comfoair({
    port: '/dev/ttyUSB0',
    baud: 9600
});

1.x.x

const ComfoairStream = require('comfoair').ComfoairStream;

const ventilationStream = new ComfoairStream({
    port: '/dev/ttyUSB0',
    baud: 9600
});