Ultima entrada

  • Sensor arcón congelador ZT08 e integración en Home Assistant

    Hace unas semanas vi un termómetro zigbee con una sonda externa que me podia ir bien para el arcón congelador que tengo , el enlace de Aliexpres es este

    Lo elegí porque el que tenia antes a partir de -20ºC tenia problemas y no daba lectura , el mínimo teórico de este dispositivo son -40ºC

    Se puede colocar de diferentes maneras , desde magnética , adhesiva y con ventosa , al final me decante por la adhesiva en la tapa de las baterías.

    Va alimentado por tres pilas AAA

    Al alimentarlo ya nos muestra la temperatura local , la de las sonda , la hora y el nivel de bateria , pero las temperaturas en grados Fahrenheit

    El siguiente paso es integrarlo en zigbee2mqtt , la información de este dispositivo será la siguiente.

    Nada mas emparejarlo nos dice que no esta soportado , y nos devuelve este identificador “_TZE284_hodyryli

    Para disponer de el tendremos que usar un converter , aquí encontre uno que funcionaba , el código seria el siguiente :

    const tuya = require('zigbee-herdsman-converters/lib/tuya');
    const exposes = require('zigbee-herdsman-converters/lib/exposes');
    const e = exposes.presets;
    const ea = exposes.access;
    
    const fzLocal = {
        tuya_weather_station: {
            cluster: 'manuSpecificTuya',
            type: ['commandDataReport', 'commandActiveStatusReport', 'commandMcuSyncTime'],
            convert: (model, msg, publish, options, meta) => {
                const result = {};
                
                // Time synchro
                if (msg.type === 'commandMcuSyncTime') {
                    (async () => {
                        try {
                            const endpoint = msg.endpoint;
                            const now = new Date();
                            
                            const utcTime = Math.round(now.getTime() / 1000);
                            const localTime = utcTime - (now.getTimezoneOffset() * 60);
                            
                            const utcArray = [
                                (utcTime >> 24) & 0xFF,
                                (utcTime >> 16) & 0xFF,
                                (utcTime >> 8) & 0xFF,
                                utcTime & 0xFF,
                            ];
                            
                            const localArray = [
                                (localTime >> 24) & 0xFF,
                                (localTime >> 16) & 0xFF,
                                (localTime >> 8) & 0xFF,
                                localTime & 0xFF,
                            ];
                            
                            const payload = {
                                payloadSize: 8,
                                payload: [...utcArray, ...localArray],
                            };
                            
                            await endpoint.command('manuSpecificTuya', 'mcuSyncTime', payload, {disableDefaultResponse: true});
                            
                            // set 24h time format
                            await new Promise(resolve => setTimeout(resolve, 500));
                            try {
                                await tuya.sendDataPointBool(endpoint, 17, false);
                            } catch (e) {
                                // Ignore if DP 17 don't exist
                            }
                            
                        } catch (error) {
                            // Ignore time synchro errors
                        }
                    })();
                    
                    return {};
                }
                
                if (!msg.data || !msg.data.dpValues) return {};
                
                for (const dpValue of msg.data.dpValues) {
                    const dp = dpValue.dp;
                    const data = dpValue.data;
                    
                    let value;
                    // temperature
                    if (dpValue.datatype === 2) {
                        // Reads the data as an unsigned big-endian 32-bit integer (Negative numbers will be wrong, eg. 429496724.6)
                        // value = data.readUInt32BE(0);
                        // Reads the data as a signed big-endian 32-bit integer (Negative numbers will be correct)
                        value = data.readInt32BE(0);
                    } else if (dpValue.datatype === 4) {
                        value = data[0];
                    } else {
                        value = data[0];
                    }
                    
                    switch (dp) {
                        // temperature
                        case 1:
                            result.temperature = value / 10;
                            break;
                        // humidity
                        case 2:
                            result.humidity = value;
                            break;
                        // battery_state
                        case 3:
                            result.battery_state = value;
                            result.battery = value === 0 ? 10 : (value === 1 ? 50 : 100);
                            result.battery_low = value === 0;
                            break;
                        // time_format
                        case 17:
                            result.time_format = value ? '12h' : '24h';
                            break;
                        // temperature_external
                        case 38:
                            result.temperature_external = value / 10;
                            break;
                    }
                }
                
                return result;
            },
        },
    };
    
    const definition = {
        fingerprint: tuya.fingerprint('TS0601', ['_TZE284_hodyryli']),
        model: 'TS0601_weather_station',
        vendor: 'TuYa',
        description: 'Weather station with clock, internal/external temperature and humidity',
        fromZigbee: [fzLocal.tuya_weather_station],
        toZigbee: [tuya.tz.datapoints],
        configure: tuya.configureMagicPacket,
        exposes: [
            e.temperature().withDescription('Internal temperature'),
            e.humidity().withDescription('Internal humidity'),
            e.numeric('temperature_external', ea.STATE)
                .withUnit('°C')
                .withDescription('External temperature sensor'),
            e.battery()
                .withDescription('Battery level (10%=low, 50%=medium, 100%=full)'),
            e.battery_low()
                .withDescription('Battery low warning'),
            e.enum('battery_state', ea.STATE, [0, 1, 2])
                .withDescription('Raw battery state (0=low, 1=medium, 2=full)'),
            e.enum('time_format', ea.STATE, ['12h', '24h'])
                .withDescription('Clock time format'),
        ],
        meta: {
            tuyaDatapoints: [
                [1, 'temperature', tuya.valueConverter.divideBy10],
                [2, 'humidity', tuya.valueConverter.raw],
                [3, 'battery_state', tuya.valueConverter.raw],
                [17, 'time_format', tuya.valueConverter.onOff],
                [38, 'temperature_external', tuya.valueConverter.divideBy10],
            ],
        },
    };
    
    module.exports = definition;

    Crearemos el fichero TS0601.js y copiamos el código en el

    paramos el docker

    en configuration.yaml añadimos el fichero del converter

    groups: {}
    external_converters:
      - TS0601.js
      - TS0202.js

    Arrancamos el docker y ya nos aparece el nuevo dispositivo

    Ya podemos ver como empieza a exponer los datos

    Físicamente una vez colocado quedaria así , con los grados en Fahrenheit 🙁

      '0xa4c138f9601e97ca':
        friendly_name: '0xa4c138f9601e97ca'
        temperature_calibration: 0
        temperature_precision: 1
        humidity_calibration: 0
        humidity_precision: 0

    Crearemos nuestros sensores

      ### TERMOMETRO ARCON CONGELADOR
      
        - state_topic: "zigbee2mqtt/temperatura_congelador"
          availability_topic: "zigbee2mqtt/bridge/state"
          unit_of_measurement: "°C"
          device_class: "temperature"
          value_template: "{{ value_json.temperature }}"
          name: "temperatura_lavadero"  
          
        - state_topic: "zigbee2mqtt/temperatura_congelador"
          availability_topic: "zigbee2mqtt/bridge/state"
          unit_of_measurement: "°C"
          device_class: "temperature"
          value_template: "{{ value_json.temperature_external }}"
          name: "temperatura_congelador_temperatura"  
      
        - state_topic: "zigbee2mqtt/temperatura_congelador"
          availability_topic: "zigbee2mqtt/bridge/state"
          unit_of_measurement: "%"
          device_class: "humidity"
          value_template: "{{ value_json.humidity }}"
          name: "temperatura_congelador_humedad"  
      
        - state_topic: "zigbee2mqtt/temperatura_congelador"
          availability_topic: "zigbee2mqtt/bridge/state"
          unit_of_measurement: "%"
          icon: "mdi:battery"
          device_class: "battery"
          value_template: "{{ value_json.battery }}"
        
          expire_after: 86400
          force_update: true
          name: "temperatura_congelador_bateria"  
      
        - state_topic: "zigbee2mqtt/temperatura_congelador"
          availability_topic: "zigbee2mqtt/bridge/state"
          icon: "mdi:signal"
          unit_of_measurement: "lqi"
          value_template: "{{ value_json.linkquality }}"    
          name: "temperatura_congelador_estado"  
          
        - state_topic: "zigbee2mqtt/temperatura_congelador"
          availability_topic: "zigbee2mqtt/bridge/state"
          icon: "mdi:calendar-clock"
          value_template: "{{ value_json.last_seen }}"
          name: "temperatura_congelador_ultima_conexion" 

    Y después de reiniciar veremos como va guardando los diferentes valores

    Y con esto y un bizcocho …………

    BONUS : Me tocaba bastante los webs ver los grados en Fahrenheit por lo que mire a ver como ponerlos en Celsius

    Para ello cambiaremos el código del converter por este

    const tuya = require('zigbee-herdsman-converters/lib/tuya');
    const exposes = require('zigbee-herdsman-converters/lib/exposes');
    const e = exposes.presets;
    const ea = exposes.access;
    
    const fzLocal = {
        tuya_weather_station: {
            cluster: 'manuSpecificTuya',
            type: ['commandDataReport', 'commandActiveStatusReport', 'commandMcuSyncTime'],
            convert: (model, msg, publish, options, meta) => {
                const result = {};
                
                // Time synchro
                if (msg.type === 'commandMcuSyncTime') {
                    (async () => {
                        try {
                            const endpoint = msg.endpoint;
                            const now = new Date();
                            
                            const utcTime = Math.round(now.getTime() / 1000);
                            const localTime = utcTime - (now.getTimezoneOffset() * 60);
                            
                            const utcArray = [
                                (utcTime >> 24) & 0xFF,
                                (utcTime >> 16) & 0xFF,
                                (utcTime >> 8) & 0xFF,
                                utcTime & 0xFF,
                            ];
                            
                            const localArray = [
                                (localTime >> 24) & 0xFF,
                                (localTime >> 16) & 0xFF,
                                (localTime >> 8) & 0xFF,
                                localTime & 0xFF,
                            ];
                            
                            const payload = {
                                payloadSize: 8,
                                payload: [...utcArray, ...localArray],
                            };
                            
                            await endpoint.command('manuSpecificTuya', 'mcuSyncTime', payload, {disableDefaultResponse: true});
                            
                            // set 24h time format
                            await new Promise(resolve => setTimeout(resolve, 500));
                            try {
                                await tuya.sendDataPointBool(endpoint, 17, false);
                            } catch (e) {
                                // Ignore if DP 17 don't exist
                            }
                            
                        } catch (error) {
                            // Ignore time synchro errors
                        }
                    })();
                    
                    return {};
                }
                
                if (!msg.data || !msg.data.dpValues) return {};
                
                for (const dpValue of msg.data.dpValues) {
                    const dp = dpValue.dp;
                    const data = dpValue.data;
                    
                    let value;
                    // temperature
                    if (dpValue.datatype === 2) {
                        // Reads the data as an unsigned big-endian 32-bit integer (Negative numbers will be wrong, eg. 429496724.6)
                        // value = data.readUInt32BE(0);
                        // Reads the data as a signed big-endian 32-bit integer (Negative numbers will be correct)
                        value = data.readInt32BE(0);
                    } else if (dpValue.datatype === 4) {
                        value = data[0];
                    } else {
                        value = data[0];
                    }
                    
                    switch (dp) {
                        // temperature
                        case 1:
                            result.temperature = value / 10;
                            break;
                        // humidity
                        case 2:
                            result.humidity = value;
                            break;
                        // battery_state
                        case 3:
                            result.battery_state = value;
                            result.battery = value === 0 ? 10 : (value === 1 ? 50 : 100);
                            result.battery_low = value === 0;
                            break;
                        // temperature_unit (LCD display)
                        case 9:
                            result.temperature_unit = value === 0 ? 'celsius' : 'fahrenheit';
                            break;
                        // time_format
                        case 17:
                            result.time_format = value ? '12h' : '24h';
                            break;
                        // temperature_external
                        case 38:
                            result.temperature_external = value / 10;
                            break;
                    }
                }
                
                return result;
            },
        },
    };
    
    const definition = {
        fingerprint: tuya.fingerprint('TS0601', ['_TZE284_hodyryli']),
        model: 'TS0601_weather_station',
        vendor: 'TuYa',
        description: 'Weather station with clock, internal/external temperature and humidity',
        fromZigbee: [fzLocal.tuya_weather_station],
        toZigbee: [tuya.tz.datapoints],
        configure: tuya.configureMagicPacket,
        exposes: [
            e.temperature().withDescription('Internal temperature'),
            e.humidity().withDescription('Internal humidity'),
            e.numeric('temperature_external', ea.STATE)
                .withUnit('°C')
                .withDescription('External temperature sensor'),
            e.battery()
                .withDescription('Battery level (10%=low, 50%=medium, 100%=full)'),
            e.battery_low()
                .withDescription('Battery low warning'),
            e.enum('battery_state', ea.STATE, [0, 1, 2])
                .withDescription('Raw battery state (0=low, 1=medium, 2=full)'),
            e.enum('temperature_unit', ea.STATE_SET, ['celsius', 'fahrenheit'])
                .withDescription('Temperature unit displayed on LCD screen'),
            e.enum('time_format', ea.STATE, ['12h', '24h'])
                .withDescription('Clock time format'),
        ],
        meta: {
            tuyaDatapoints: [
                [1, 'temperature', tuya.valueConverter.divideBy10],
                [2, 'humidity', tuya.valueConverter.raw],
                [3, 'battery_state', tuya.valueConverter.raw],
                [9, 'temperature_unit', tuya.valueConverter.temperatureUnitEnum],
                [17, 'time_format', tuya.valueConverter.onOff],
                [38, 'temperature_external', tuya.valueConverter.divideBy10],
            ],
        },
    };
    
    module.exports = definition;

    Por defecto lo tendremos asi

    Al consultar los ajustes nos aparece un nuevo selector Celsius / Fahrenheit

    Cambiamos a Celsius y pulsamos varias veces para que se actualice

    Y tachannnnnnnnnnnnnn ya aparece en Celsius

2 comentarios en «Ultima entrada»

  1. Hola Antonio!! LLegue a tu web buscando un tutorial de como instalar un ZBDongle-E en un QNAP TS 251D pero aun ando bastante liado jaja ojala me puedas dar una mano si tienes un tiempo libre!! Abrazo desde Argentina!!

    Responder

Responder a antonio Cancelar la respuesta