zigbee2mqtt-user-extensions icon indicating copy to clipboard operation
zigbee2mqtt-user-extensions copied to clipboard

calculation of dewpoint

Open dirstel opened this issue 1 year ago • 5 comments

The dewpoint is a value for deciding weather air should/could be exchangened in order to lower/raise relative humidity. Unfortunatly most thermometer/hygrometer-combinations do not report this value. As dewpoint my be calculated by knowing temperature and relative humidity an extension would be nice. Having basic coding skills this should be doable for me, but I do not have any clue where to start. Basicly the code should do the following:

  • listen for messages publishing tempreature and humidity
  • perform calculation
  • enrich message with calculated value

code for calculation in JavaScript:

        var tempC = msg.payload.temperature;
        var humidRel = msg.payload.humidity;
        
        // Konstanten
        var molekulargewicht = 18.016; // des Wasserdampfes in kg/kmol
        var gaskonstante = 8214.3; // in J/(kmol*K)
        var tempK = tempC + 273.15;
        
        var a, b;
        if (tempC >= 0) {
            a = 7.5;
            b = 237.3;
        } else {
            a = 7.6;
            b = 240.7;
        }
         
        // Sättigungsdampfdruck (hPa)
        var saettigungsDampfDruck = 6.1078 * Math.pow(10, (a * tempC) / (b + tempC));
         
        // Dampfdruck (hPa)
        var dampfDruck = saettigungsDampfDruck * (humidRel / 100);
         
        // Wasserdampfdichte bzw. absolute Feuchte (g/m3)
        var humidAbs = Math.pow(10,5) * molekulargewicht / gaskonstante * dampfDruck / tempK;
         
        // v-Parameter
        var v = Math.log10(dampfDruck / 6.1078);
         
        // Taupunkttemperatur (°C)
        var dewpointC = (b * v) / (a - v);

Any help would be apreciated!

dirstel avatar Nov 27 '24 07:11 dirstel

I manged to build a working extension. Only part missing is the enriching of the message published by Z2M via MQTT:

[ok] listen for messages publishing tempreature and humidity [ok] perform calculation [ ] enrich message with calculated value

Is this the correct binding to "intercept" and enrich a MQTT-Message? How is this done? PS: I did not find any class diagram or sth similar - getting through all this classes was quite hard ;)

class DewpointCalculator {
    constructor(zigbee, mqtt, state, publishEntityState, eventBus, settings, logger) {
        this.eventBus = eventBus;
        this.logger = logger;
        this.eventBus.on('stateChange', this.onStateChange.bind(this), this.constructor.name);
        logger.info('Loaded DewpointCalculator');
    }

    async onStateChange(data) {
        const { entity, update } = data;
        if ((data.to.hasOwnProperty('temperature')) & (data.to.hasOwnProperty('humidity'))) {
            if (!data.to.hasOwnProperty('dewpoint')) {

                // this should be added to the mqtt-message published 
                this.logger.info(this.calculateDewpoint(data.to['temperature'], data.to['humidity']));
                
            }
        }
    }

    calculateDewpoint(tempC, humidRel) {
        var molecularWeight = 18.016; // of water vapor in kg/kmol
        var gasConstant = 8214.3; // in J/(kmol*K)
        var tempK = tempC + 273.15;

        var a, b;
        if (tempC >= 0) {
            a = 7.5;
            b = 237.3;
        } else {
            a = 7.6;
            b = 240.7;
        }
         
        // saturation vapor pressure (hPa)
        var saturationVaporPressure=6.1078*Math.pow(10,(a*tempC)/(b+tempC));
        // vapor pressure (hPa)
        var vaporPressure = saturationVaporPressure*(humidRel/100);
        // Wasserdampfdichte bzw. absolute Feuchte (g/m3)
        var humidAbs = Math.pow(10,5)*molecularWeight/gasConstant*vaporPressure/tempK;
        // v-Parameter
        var v = Math.log10(vaporPressure/6.1078);
        // Taupunkttemperatur (°C)
        var dewpointC = (b*v)/(a-v);

        return(dewpointC.toFixed(2));
    }

    async onMQTTMessage(topic, message) {
        // console.log({topic, message});
    }

    async stop() {
        this.eventBus.removeListeners(this.constructor.name);
        this.logger.info('Unloaded DewpointCalculator');
    }
}

module.exports = DewpointCalculator;

dirstel avatar Dec 09 '24 17:12 dirstel

This seems interesting, but I can't come up with a way to inject this either.

I don't think there is a way to modify the message before z2m publishes it to include the info. You could publish it on a separate topic perhaps.

sjorge avatar Jun 13 '25 12:06 sjorge

facepalm it is THAT easy...

just add a new property to the data object...

data.to['dewpoint'] = +this.calculateDewpoint(data.to['temperature'], data.to['humidity']);

class DewpointCalculator {
    constructor(
        zigbee,
        mqtt,
        state,
        publishEntityState,
        eventBus,
        enableDisableExtension,
        restartCallback,
        addExtension,
        settings,
        logger,
    ) {
        this.zigbee = zigbee;
        this.mqtt = mqtt;
        this.state = state;
        this.publishEntityState = publishEntityState;
        this.eventBus = eventBus;
        this.enableDisableExtension = enableDisableExtension;
        this.restartCallback = restartCallback;
        this.addExtension = addExtension;
        this.settings = settings;
        this.logger = logger;

        this.logger.info('Loaded DewpointCalculator');
//        this.mqttBaseTopic = this.settings.get().mqtt.base_topic;
    }

    /**
     * Called when the extension starts (on Zigbee2MQTT startup, or when the extension is saved at runtime)
     */
    start() {
        this.logger.info('DewpointCalculator - start');

        // all possible events can be seen here: https://github.com/Koenkk/zigbee2mqtt/blob/master/lib/eventBus.ts
        this.eventBus.onStateChange(this, this.onStateChange.bind(this));

        // this.mqtt.publish('DewpointCalculator/state', 'running');
        // this.logger.info('DewpointCalculator - started');
    }

    /**
     * Called when the extension stops (on Zigbee2MQTT shutdown, or when the extension is saved/removed at runtime)
     */
    stop() {
        this.logger.info('DewpointCalculator - stop');

        // unload listener
        this.eventBus.removeListeners(this);

        // this.mqtt.publish('DewpointCalculator/state', 'stop');
        // this.logger.info('DewpointCalculator - stopped');
    }
    
    async onStateChange(data) {
        // see typing (properties) here: https://github.com/Koenkk/zigbee2mqtt/blob/master/lib/types/types.d.ts => namespace eventdata
        const { entity, update } = data;

        if ((data.to.hasOwnProperty('temperature')) & (data.to.hasOwnProperty('humidity'))) {
            if (!data.to.hasOwnProperty('dewpoint')) {
                // add custom property called "dewpoint" beeing calculated by method calculateDewpoint()
                data.to['dewpoint'] = +this.calculateDewpoint(data.to['temperature'], data.to['humidity']);
            }
        }
    }

    calculateDewpoint(tempC, humidRel) {
        var molecularWeight = 18.016; // of water vapor in kg/kmol
        var gasConstant = 8214.3; // in J/(kmol*K)
        var tempK = tempC + 273.15;

        var a, b;
        if (tempC >= 0) {
            a = 7.5;
            b = 237.3;
        } else {
            a = 7.6;
            b = 240.7;
        }
         
        // saturation vapor pressure (hPa)
        var saturationVaporPressure=6.1078*Math.pow(10,(a*tempC)/(b+tempC));
        // vapor pressure (hPa)
        var vaporPressure = saturationVaporPressure*(humidRel/100);
        // Wasserdampfdichte bzw. absolute Feuchte (g/m3)
        var humidAbs = Math.pow(10,5)*molecularWeight/gasConstant*vaporPressure/tempK;
        // v-Parameter
        var v = Math.log10(vaporPressure/6.1078);
        // Taupunkttemperatur (°C)
        var dewpointC = (b*v)/(a-v);

        return(+dewpointC.toFixed(2));
    }

    async onMQTTMessage(topic, message) {
        // console.log({topic, message});
    }

}

// eslint-disable-next-line no-undef
module.exports = DewpointCalculator;

dirstel avatar Jun 13 '25 13:06 dirstel

quickly added a https://github.com/Koenkk/zigbee2mqtt-user-extensions/pull/17 so will close this here.

dirstel avatar Jun 13 '25 13:06 dirstel

@sjorge: do you know a way to make HomeAssistant aware of the newly introduced value as a sensor? The sensor configuration is published in separte topics: Image

One way may be manipulating these topics, but that wouldn't be a "correct" way. So are there any calls on a device (which is part of the used hooks parameters) to add this value "official" to the device?

dirstel avatar Jun 14 '25 07:06 dirstel