-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Link
https://www.merrytek.com/products/msa201-z/
Database entry
{"id":90,"type":"Router","ieeeAddr":"0x44e2f8fffeb08a84","nwkAddr":13958,"manufId":4098,"manufName":"_TZE200_hyhl5y36","powerSource":"Mains (single phase)","modelId":"TS0601","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":81,"inClusterList":[0,4,5,61184],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"65503":"�e�0f�e�0\u0012�e�0\u0012","65506":31,"65508":0,"65534":0,"modelId":"TS0601","manufacturerName":"_TZE200_hyhl5y36","stackVersion":0,"dateCode":"","zclVersion":3,"appVersion":65,"powerSource":1}}},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":65,"stackVersion":0,"hwVersion":1,"dateCode":"","zclVersion":3,"interviewCompleted":true,"interviewState":"SUCCESSFUL","meta":{"configured":332242049},"lastSeen":1767320402463}
Zigbee2MQTT version
2.7.2
External definition
const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const modernExtend = require('zigbee-herdsman-converters/lib/modernExtend');
const e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const definition = {
fingerprint: [
{
modelID: 'TS0601',
manufacturerName: '_TZE200_hyhl5y36',
},
],
model: 'MSA201Z',
vendor: 'Merrytek',
description: '24 GHz human presence sensor (TS0601, _TZE200_hyhl5y36)',
extend: [
tuya.modernExtend.tuyaBase({
dp: true,
// timeStart: "1970",
}),
],
exposes: [
e.enum('state', ea.STATE, ['Absence', 'Presence', 'Disabled']).withLabel('Status'),
e.presence(),
e.enum('current_status', ea.STATE, ['Approaching', 'Departing', 'Clear']),
e.illuminance().withLabel('Luminance'),
e.numeric('hold_delay_time', ea.STATE_SET)
.withUnit('s')
.withValueMin(0)
.withValueMax(300)
.withValueStep(1)
.withCategory('config')
.withDescription('Delay (seconds) before switching from Presence to Absence after no motion is detected. Recommended ≥ 15s to avoid premature Absence switching.'),
e.enum('sensitivity', ea.STATE_SET, ['Low', 'Medium', 'High'])
.withCategory('config')
.withDescription('Sensitivity of human presence detection. High: minimal motion interference; Medium: most scenarios; Low: some motion interference.'),
e.numeric('trigger_distance', ea.STATE_SET)
.withUnit('m')
.withValueMin(0.5)
.withValueMax(4)
.withValueStep(0.5)
.withCategory('config')
.withDescription('Distance within which the sensor detects motion, adjustable 0.5–4m in 0.5m steps.'),
e.numeric('forbidden_area', ea.STATE_SET)
.withUnit('m')
.withValueMin(0)
.withValueMax(1.8)
.withValueStep(0.1)
.withCategory('config')
.withDescription('Distance from the sensor within which motion is ignored (0–1.8m).'),
e.enum('ai_self_learning', ea.SET, ['AI self-learning'])
.withLabel('AI environment self-learning')
.withCategory('config')
.withDescription('AI self-learning to ignore non-human motion; takes ~1 minute with the area empty.'),
e.enum('fast_setting', ea.STATE_SET, ['Small', 'Medium', 'Large'])
.withCategory('config')
.withDescription('Fast setting by space size: Small <16m²; Medium 16–25m²; Large >25m².'),
e.binary('indicator', ea.STATE_SET, 'ON', 'OFF')
.withLabel('LED indicator')
.withCategory('config')
.withDescription('LED indicator when motion is detected or state changes.'),
e.enum('sensor_mode', ea.STATE_SET, ['Presence', 'Motion'])
.withCategory('config')
.withDescription('Presence: micro-movements; Motion: larger movements, ignores small activity.'),
e.binary('single_mode', ea.STATE_SET, 'ON', 'OFF')
.withCategory('config')
.withDescription('Single-person mode; keeps Presence while a person is in range, then Absence after 15s.'),
e.binary('absence_circling_report', ea.STATE_SET, 'ON', 'OFF')
.withCategory('config')
.withDescription('Periodic reporting of Absence state after switching to Absence.'),
e.numeric('absence_circling_interval', ea.STATE_SET)
.withUnit('min')
.withValueMin(2)
.withValueMax(30)
.withValueStep(1)
.withCategory('config')
.withDescription('Interval (minutes) between periodic Absence reports.'),
e.binary('find_device', ea.STATE_SET, 'ON', 'OFF')
.withCategory('config')
.withDescription('Indicator LED flashes to help locate the sensor.'),
e.binary('enable_sensor', ea.STATE_SET, 'ON', 'OFF')
.withCategory('config')
.withDescription('Enable or disable the sensor.'),
e.enum('factory_reset', ea.SET, ['Factory reset'])
.withCategory('config')
.withDescription('Restores factory defaults and removes custom settings.'),
e.enum('lux_mode', ea.STATE_SET, ['Threshold', 'Report'])
.withCategory('config')
.withDescription('Lux mode: Threshold for fixed daylight level, Report for periodic reports.'),
e.numeric('daylight_threshold', ea.STATE_SET)
.withUnit('lux')
.withValueMin(1)
.withValueMax(3000)
.withValueStep(1)
.withCategory('config')
.withDescription('Lux level defining sufficient daylight when Lux mode = Threshold.'),
e.enum('lux_report_mode', ea.STATE_SET, ['Timed', 'Difference'])
.withCategory('config')
.withDescription('Lux report style: Timed for fixed intervals; Difference (not implemented here).'),
e.numeric('lux_timed_interval', ea.STATE_SET)
.withUnit('s')
.withValueMin(5)
.withValueMax(3600)
.withValueStep(5)
.withCategory('config')
.withDescription('Interval (seconds) for timed lux reports.'),
e.numeric('lux_difference_threshold', ea.STATE_SET)
.withUnit('lux')
.withValueMin(1)
.withValueMax(2000)
.withValueStep(1)
.withCategory('config')
.withDescription('Lux change needed to trigger a Difference-mode report (not actually implemented).'),
e.numeric('lux_difference_value', ea.STATE)
.withCategory('diagnostic')
.withDescription('Reported lux value for Difference mode (not actually implemented).'),
e.text('interference_positions', ea.STATE)
.withCategory('diagnostic')
.withDescription('Distances (m) where non-human interference was detected.'),
e.enum('home_environment', ea.STATE, ['Normal', 'Slight', 'Strong', 'Severe'])
.withCategory('diagnostic')
.withDescription('Environmental interference level detected by the sensor.'),
],
meta: {
tuyaDatapoints: [
[
1,
null,
{
from: (v) => {
switch (v) {
case 0:
return {state: 'Absence', presence: false};
case 1:
return {state: 'Presence', presence: true};
case 2:
return {state: 'Disabled', presence: false};
default:
console.warn('Unknown DP1 value:', v);
return {state: 'Absence', presence: false};
}
},
to: (value) => {
switch (value.state) {
case 'Presence':
return 1;
case 'Absence':
return 0;
case 'Disabled':
return 2;
default:
return 0;
}
},
},
],
[2, 'trigger_distance', tuya.valueConverter.divideBy10],
[101, 'illuminance', tuya.valueConverter.raw],
[102, 'lux_difference_value', tuya.valueConverter.raw],
[
103,
'ai_self_learning',
{
from: (v) => ({0: 'end', 4: 'learning'})[v],
to: () => 1,
},
],
[
104,
'factory_reset',
{
from: () => 'idle',
to: (v) => ({'Factory reset': 1})[v] || 0,
},
],
[
105,
'fast_setting',
{
from: (v) => ({1: 'Large', 2: 'Medium', 3: 'Small'})[v] ?? 'Medium',
to: (v) => ({Small: 3, Medium: 2, Large: 1})[v] ?? 2,
},
],
[107, 'indicator', tuya.valueConverter.onOff],
[106, 'hold_delay_time', tuya.valueConverter.raw],
[
108,
'current_status',
tuya.valueConverterBasic.lookup({
Approaching: tuya.enum(0),
Departing: tuya.enum(1),
Clear: tuya.enum(2),
}),
],
[109, 'enable_sensor', tuya.valueConverter.onOff],
[
110,
'sensitivity',
tuya.valueConverterBasic.lookup({
Low: tuya.enum(3),
Medium: tuya.enum(2),
High: tuya.enum(1),
}),
],
[112, 'status_flip', tuya.valueConverter.onOff],
[113, 'interference_positions', tuya.valueConverter.raw],
[114, 'forbidden_area', tuya.valueConverter.divideBy10],
[115, 'daylight_threshold', tuya.valueConverter.raw],
[
116,
'sensor_mode',
{
from: (v) => ({1: 'Presence', 2: 'Motion'})[v] ?? 'Unknown',
to: (v) => ({Presence: 1, Motion: 2})[v] ?? 1,
},
],
[117, 'single_mode', tuya.valueConverter.onOff],
[118, 'find_device', tuya.valueConverter.onOff],
[
119,
'lux_mode',
tuya.valueConverterBasic.lookup({
Threshold: tuya.enum(0),
Report: tuya.enum(1),
}),
],
[
120,
'lux_report_mode',
tuya.valueConverterBasic.lookup({
Timed: tuya.enum(0),
Difference: tuya.enum(1),
}),
],
[121, 'lux_difference_threshold', tuya.valueConverter.raw],
[122, 'lux_timed_interval', tuya.valueConverter.raw],
[123, 'absence_circling_report', tuya.valueConverter.onOff],
[124, 'absence_circling_interval', tuya.valueConverter.raw],
[
125,
'home_environment',
{
from: (v) => ({0: 'Normal', 1: 'Slight', 2: 'Strong', 3: 'Severe'})[v] ?? 'Unknown',
to: (v) => ({Normal: 0, Slight: 1, Strong: 2, Severe: 3})[v] ?? 0,
},
],
],
},
};
module.exports = definition;What does/doesn't work with the external definition?
Fully functional.
Notes
This is a duplicate fingerprint for existing supported device:
New fingerprint: TS0601, _TZE200_hyhl5y36
Existing fingerprint: TS0601, _TZE284_ajuasrmx
This device was purchased from here:
https://forgeelectrical.com.au/forge-lifebing-breath-detection-motion-sensor.html
It might be useful to note in the documentation alternative name/branding for this device:
FORGE Zigbee Presence & Occupancy Sensor with Milliwave Radar (name from retailer)
Millimeter Microwave Lifebeing Sensor, Model MSA201 Z (product name on device label - the device doesn't include any manufacturer name)