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.jsArrancamos 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: 0Crearemos 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


















