Koenkk / zigbee2mqtt

Zigbee 🐝 to MQTT bridge 🌉, get rid of your proprietary Zigbee bridges 🔨
https://www.zigbee2mqtt.io
GNU General Public License v3.0
11.77k stars 1.64k forks source link

New Tuya CO/CH4 sensor revision (_TZE204_iuk8kupi) #23560

Open csrider opened 1 month ago

csrider commented 1 month ago

Link

https://www.aliexpress.us/item/3256804323091378.html

Database entry

{"id":48,"type":"Router","ieeeAddr":"0xa4c138bc81f0df5f","nwkAddr":64828,"manufId":4417,"manufName":"_TZE204_iuk8kupi","powerSource":"Mains (single phase)","modelId":"TS0601","epList":[1,242],"endpoints":{"1":{"profId":260,"epId":1,"devId":81,"inClusterList":[4,5,61184,0],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"65503":"4�F.i","65506":56,"65508":0,"modelId":"TS0601","manufacturerName":"_TZE204_iuk8kupi","powerSource":1,"zclVersion":3,"appVersion":74,"stackVersion":0,"hwVersion":1,"dateCode":""}}},"binds":[],"configuredReportings":[],"meta":{}},"242":{"profId":41440,"epId":242,"devId":97,"inClusterList":[],"outClusterList":[33],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":74,"stackVersion":0,"hwVersion":1,"dateCode":"","zclVersion":3,"interviewCompleted":true,"meta":{},"lastSeen":1723120228781}

Comments

This (_TZE204_iuk8kupi) appears to be a new version of the "_TZE200_iuk8kupi" sensor (Z2M already supports the TZE200, but not the 204, yet).

I'm new to HA, so I do not yet feel confident enough to contribute the new code directly. I hope this helps you or someone more confident and capable to easily add support for this new device.

This was my first time diving into an external definition; but with a lot of hacking and testing (I actually let some gas out of my kitchen stovetop to get CH4 readings/triggers, so I know at least the gas values are correct), I was able to make this seemingly work in my environment. NOTE: These array elements are out of order, but that allowed me to present the values in Z2M in a way that matched the order of values for my existing (working) TZE200_iuk8kupi.

tuyaDatapoints: [
  [18, 'gas_detected', trueFalseConverter],
  [2, 'gas_level', ch4Converter],
  [1, 'co_detected', trueFalseConverter],
  [19, 'co_level', coConverter],
]

Key Notes:

I'm including my external converter file for the TZE204 that currently seems to be working in my environment. It's not production worthy in the slightest, but I hope it helps someone more skilled than I implement this new device!

External definition

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const ea = exposes.access;
const { Buffer } = require('buffer');

const trueFalseConverter = {
    to: (value) => value ? 1 : 0,
    from: (value) => {
        const rawValue = Buffer.isBuffer(value) ? value.readUInt8(0) : value;
        const convertedValue = rawValue !== 1;
        //console.log(`trueFalseConverter.from: raw value = ${rawValue}, converted value = ${convertedValue}`);
        return convertedValue;
    },
};

const ch4Converter = {
    to: (value) => value,
    from: (value) => {
        //console.log(`TEST!!! ch4Converter.from: value = ${value}`);
        return value;
    },
};

const coConverter = {
    to: (value) => value,
    from: (value) => {
        //console.log(`TEST!!! coConverter.from: value = ${value}`);
        return value;
    },
};

const parseDataValue = (dpValue) => {
    const data = dpValue.data;
    if (Buffer.isBuffer(data)) {
        //console.log(`Data type: ${dpValue.datatype}, Raw data: ${data.toString('hex')}`);
        switch (dpValue.datatype) {
            case 2: //numerical gas values
                return data.readUInt32BE(0);
            case 4: //binary gas detection
                return data.readUInt8(0);
            default:
                console.warn(`Unhandled datatype: ${dpValue.datatype}`);
                return data;
        }
    } else {
        console.warn("Data is not an instance of Buffer:", dpValue);
        return data;
    }
};

const scaleValue = (value, factor) => value / factor;

const definition = {
    fingerprint: [
        { modelID: 'TS0601', manufacturerName: '_TZE204_iuk8kupi' }
    ],
    model: 'TS0601_gas_sensor',
    vendor: 'TuYa',
    description: 'Gas sensor',
    fromZigbee: [
        {
            cluster: 'manuSpecificTuya',
            type: ['commandDataReport', 'commandDataResponse'],
            convert: (model, msg, publish, options, meta) => {
                const result = {};
                //console.log(`Received message: ${JSON.stringify(msg)}`);

                for (const dpValue of msg.data.dpValues) {
                    //console.log(`Processing DP: ${JSON.stringify(dpValue)}`);
                    const value = parseDataValue(dpValue);
                    //console.log(`Parsed DP value: ${value}`);

                    switch (dpValue.dp) {
                        case 1:
                            result.co_detected = trueFalseConverter.from(value);
                            break;
                        case 2:
                            result.gas_level = scaleValue(ch4Converter.from(value), 1000); // Assuming scaling factor
                            break;
                        case 18:
                            result.gas_detected = trueFalseConverter.from(value);
                            break;
                        case 19:
                            result.co_level = scaleValue(coConverter.from(value), 100); // best guess factor based on TZE200 sensor CO levels (0 - 0.03)
                            break;
                        default:
                            console.warn(`Unhandled DP: ${dpValue.dp}`);
                    }
                }

                //console.log(`Parsed result: ${JSON.stringify(result)}`);
                return result;
            }
        }
    ],
    toZigbee: [],
    exposes: [
        exposes.binary('gas_detected', ea.STATE, true, false).withDescription('Indicates if CO (carbon monoxide) is detected'),
        exposes.numeric('gas_level', ea.STATE).withUnit('LEL %').withDescription('Measured gas concentration (5 and above is the flashpoint for natural-gas!)'),
        exposes.binary('co_detected', ea.STATE, true, false).withDescription('Indicates whether the device detected gas'),
        exposes.numeric('co_level', ea.STATE).withUnit('ppm').withDescription('The measured CO (carbon monoxide) value in ppm')
    ],
    meta: {
        tuyaDatapoints: [
            [18, 'gas_detected', trueFalseConverter],
            [2, 'gas_level', ch4Converter],
            [1, 'co_detected', trueFalseConverter],
            [19, 'co_level', coConverter],
        ],
    },
};

module.exports = definition;
shirou93 commented 2 weeks ago

I bought same device. Please add support.

This converter have propably mistake with boolean sensor. Gas sensor <> CO sensor. When sensor detect gas will show CO sensor true.

shirou93 commented 2 weeks ago

It's looks same as _TZE200_iuk8kupi only different device code https://github.com/Koenkk/zigbee-herdsman-converters/pull/7906

shirou93 commented 2 weeks ago

hi, @csrider please check on dev zigbee2mqtt and test. My device work properly.