{"id":371,"date":"2026-09-30T13:12:13","date_gmt":"2026-09-30T13:12:13","guid":{"rendered":"https:\/\/www.zebadger.com\/wp\/?p=371"},"modified":"2026-09-30T13:38:51","modified_gmt":"2026-09-30T13:38:51","slug":"getting-a-tuya-zy-n1-sound-detector-working-in-zigbee2mqtt-and-using-it-as-a-smoke-alarm-listener","status":"publish","type":"post","link":"https:\/\/www.zebadger.com\/wp\/?p=371","title":{"rendered":"Getting a Tuya ZY-N1 Sound Detector Working in Zigbee2MQTT (and Using It as a Smoke Alarm Listener)"},"content":{"rendered":"\r\n<p class=\"wp-block-paragraph\"><strong><span style=\"color: #ff0000;\">IMPORTANT NOTE:\u00a0 If you wait a few weeks this should be supported by Z2M by default, so you might want to just wait.<\/span><\/strong><br \/><br \/>The Tuya ZY-N1 sound detector works in Zigbee2MQTT today with a community external converter, and it makes a handy smoke alarm listener for Home Assistant. Getting there took one non-obvious setting change, so here is the whole route.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">The ZY-N1 is a small USB-powered Zigbee sound sensor that reports how loud a room is. It shows up as <code>TS0601<\/code> with manufacturer <code>_TZE204_r6kfl9ta<\/code>. At the time of writing there is no built-in support in Zigbee2MQTT: the device request is still an open issue, <a href=\"https:\/\/github.com\/Koenkk\/zigbee2mqtt\/issues\/33195\">Koenkk\/zigbee2mqtt #33195<\/a>, and that issue is where the working converter comes from.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">My setup: Home Assistant with the Zigbee2MQTT add-on (version 2.x). The converter was tested by its author on Zigbee2MQTT 2.14.1, and automatic loading of external converters needs 2.x.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Step 1: Pair it and check the fingerprint<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Pair the sensor first, before adding any converter. Turn on Permit Join in Zigbee2MQTT and plug the sensor in. It joins as a router, because it is mains powered, and appears as unsupported with an &#8220;Automatically generated definition&#8221;.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Open the device&#8217;s About tab and check two values. The Zigbee model must be <code>TS0601<\/code> and the manufacturer must be exactly <code>_TZE204_r6kfl9ta<\/code>. The converter only matches that exact pair, so a unit with a different manufacturer string needs a different converter.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Step 2: Add the external converter<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">The converter is a single JavaScript file that tells Zigbee2MQTT how to read the sensor. Its author posted it in the &#8220;External definition&#8221; section of <a href=\"https:\/\/github.com\/Koenkk\/zigbee2mqtt\/issues\/33195\">issue #33195<\/a>, along with detailed notes on what each datapoint does.<\/p>\r\n\r\n\r\n\r\n<ol class=\"wp-block-list\">\r\n<li>Using the File Editor or Studio Code Server add-on, find the folder that holds Zigbee2MQTT&#8217;s own <code>configuration.yaml<\/code> and <code>database.db<\/code>. On a standard add-on install that is <code>\/homeassistant\/zigbee2mqtt\/<\/code>.<\/li>\r\n\r\n\r\n\r\n<li>Inside it, create a folder called exactly <code>external_converters<\/code>.<\/li>\r\n\r\n\r\n\r\n<li>In that folder, create a file called <code>zy-n1.mjs<\/code>. The <code>.mjs<\/code> extension matters, because the file uses <code>import<\/code> statements.<\/li>\r\n\r\n\r\n\r\n<li>Paste in the whole code block from the issue, from the first <code>import<\/code> line to the final <code>];<\/code>. Leave out the triple backticks GitHub shows around it.<\/li>\r\n<\/ol>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">The two <code>import<\/code> lines at the top need no setup. They borrow code from <code>zigbee-herdsman-converters<\/code>, the device library that ships inside Zigbee2MQTT.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">What the converter gives you:<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-table\">\r\n<table>\r\n<thead>\r\n<tr>\r\n<th>Entity<\/th>\r\n<th>What it does<\/th>\r\n<th>Updates<\/th>\r\n<\/tr>\r\n<\/thead>\r\n<tbody>\r\n<tr>\r\n<td><code>sound_class<\/code><\/td>\r\n<td>Loudness class: 0 quiet, 1 medium, 2 loud<\/td>\r\n<td>Live, pushed by the device<\/td>\r\n<\/tr>\r\n<tr>\r\n<td><code>noise_detected<\/code><\/td>\r\n<td>Noise detected, yes or no<\/td>\r\n<td>Live, pushed by the device<\/td>\r\n<\/tr>\r\n<tr>\r\n<td><code>sound_level<\/code><\/td>\r\n<td>Sound level in dB<\/td>\r\n<td>Only when asked for (see Step 3)<\/td>\r\n<\/tr>\r\n<tr>\r\n<td><code>dpNN_raw<\/code><\/td>\r\n<td>Unmapped settings, read-only<\/td>\r\n<td>When asked for<\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n<\/figure>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">The sensitivity and timing settings from the printed manual are not mapped yet. That is why the converter exposes them only as raw, read-only values.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">The gotcha: external JavaScript was switched off<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">My converter was correct and in the right place, but Zigbee2MQTT ignored it because the <code>enable_external_js<\/code> setting was off. The symptoms were confusing:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>The device kept showing &#8220;Automatically generated definition&#8221;, even after deleting and re-pairing it.<\/li>\r\n\r\n\r\n\r\n<li>Clicking Reconfigure gave the error <code>Device \"...\" cannot be configured<\/code>. That message means the device&#8217;s definition has no configure step, which is true of the generic fallback but not of the converter.<\/li>\r\n\r\n\r\n\r\n<li>The startup log said nothing at all about external converters.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">The setting is documented in the <a href=\"https:\/\/www.zigbee2mqtt.io\/guide\/configuration\/all-settings.html#enable-external-js\">Zigbee2MQTT settings reference<\/a>. It allows external extensions and converters to run, which is why it can be switched off. You can turn it on in the Zigbee2MQTT web UI under Settings, Advanced, or add it to Zigbee2MQTT&#8217;s <code>configuration.yaml<\/code>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>advanced:\r\n  enable_external_js: true<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">If an <code>advanced:<\/code> section already exists, add the line under it rather than creating a second one. The change needs a restart of the Zigbee2MQTT add-on. After that, the device page showed Tuya ZY-N1 instead of the generated definition.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Step 3: Reconfigure and poll for the dB reading<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">With the converter loaded, click Reconfigure on the device page. This runs the converter&#8217;s setup, which also asks the sensor for all its values, including the dB level.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">The sensor never sends the dB level on its own; it only answers when asked. The converter&#8217;s author works around this by re-running the configure step once a minute from Home Assistant, and I did the same. Replace <code>SoundDetector<\/code> with your device&#8217;s friendly name:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>alias: Poll ZY-N1 sound level\r\ntriggers:\r\n  - trigger: time_pattern\r\n    minutes: \"\/1\"\r\nactions:\r\n  - action: mqtt.publish\r\n    data:\r\n      topic: zigbee2mqtt\/bridge\/request\/device\/configure\r\n      payload: '{\"id\": \"SoundDetector\"}'\r\nmode: single<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">This step is optional. The smoke alarm alert below uses <code>sound_class<\/code>, which updates live without any polling.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Step 4: Listen for the smoke alarm<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">The alert fires when the sensor hears loud noise for 10 seconds in a row, allowing for the gaps between beeps. A smoke alarm beeps in bursts, so reacting to a single loud reading would be triggered by a door slam, while requiring perfectly continuous noise would miss the alarm.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">First, a template binary sensor in Home Assistant&#8217;s main <code>configuration.yaml<\/code> (not the Zigbee2MQTT one). The <code>delay_off<\/code> keeps it on through short pauses:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>template:\r\n  - binary_sensor:\r\n      - name: \"Loud noise sustained\"\r\n        unique_id: loud_noise_sustained\r\n        state: \"{{ is_state('sensor.sounddetector_sound_class', '2') }}\"\r\n        delay_off: \"00:00:05\"<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Reload it from Developer Tools, YAML, Template entities. Then the automation. I send alerts through a WhatsApp gateway and ntfy; swap in your own notify actions:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>alias: Smoke alarm heard\r\ntriggers:\r\n  - trigger: state\r\n    entity_id: binary_sensor.loud_noise_sustained\r\n    to: \"on\"\r\n    for: \"00:00:10\"\r\nactions:\r\n  - action: rest_command.whatsapp_send\r\n    continue_on_error: true\r\n    data:\r\n      chatid: \"YOUR_CHAT_ID\"\r\n      message: \"Smoke alarm sounding at home ({{ now().strftime('%H:%M') }})\"\r\n  - action: shell_command.notify\r\n    continue_on_error: true\r\n    data:\r\n      device: \"Smoke alarm\"\r\n      state: \"sounding\"\r\n  - delay: \"00:05:00\"\r\nmode: single<\/code><\/pre>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Two details matter here. <code>continue_on_error: true<\/code> means a failed WhatsApp call does not stop the second alert. The 5-minute delay with <code>mode: single<\/code> stops a continuous alarm from sending a message every few seconds.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Testing, and the 5-minute trap<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">My first real test failed, and the cause was the automation&#8217;s own cooldown. I had used &#8220;Run actions&#8221; to check the notifications a few minutes earlier. That run was still sitting in its 5-minute delay, so <code>mode: single<\/code> quietly ignored the new trigger. The second test worked, and both the WhatsApp message and the ntfy alert arrived.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">For testing, I used a siren near the sensor. In quiet conditions <code>sound_class<\/code> read 0; with the siren going it jumped to 2. If a test does not trigger, check these in History:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li><code>sound_class<\/code> never reaches 2: the sound is not loud enough at the sensor. Move it closer.<\/li>\r\n\r\n\r\n\r\n<li><code>sound_class<\/code> reaches 2 but the helper flickers: raise <code>delay_off<\/code> to about 8 seconds.<\/li>\r\n\r\n\r\n\r\n<li>The helper stays on for over 10 seconds: open the automation&#8217;s Traces to see whether a previous run was still in its delay. Restart the automation from its menu to clear it.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Finally, test with the real smoke alarm&#8217;s test button, held for 15 to 20 seconds, from where the sensor actually sits.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Wrap-up<\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">The ZY-N1 now alerts me within about 10 seconds of a sustained alarm, for the price of one converter file and one setting. A few limitations are worth knowing:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Any loud, sustained sound can trigger it, including loud music, a TV or another siren. Mine also fires when my SOS siren sounds, which I am happy with.<\/li>\r\n\r\n\r\n\r\n<li>The sensitivity and timing settings cannot be changed from Home Assistant until someone maps them.<\/li>\r\n\r\n\r\n\r\n<li>It is an extra alert for when you are away, not a replacement for proper smoke alarms. Zigbee or interconnected smoke detectors are the more dependable route to fire alerts.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">When official support lands in a Zigbee2MQTT release, delete the external converter file so it does not override the built-in definition.<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">Credit to GitHub user HACucoo, who wrote the converter and documented the device&#8217;s datapoints in the issue.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Sources<\/h2>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li><a href=\"https:\/\/github.com\/Koenkk\/zigbee2mqtt\/issues\/33195\">Koenkk\/zigbee2mqtt issue #33195: Tuya Sound Detector ZY-N1<\/a><\/li>\r\n\r\n\r\n\r\n<li><a href=\"https:\/\/www.zigbee2mqtt.io\/guide\/configuration\/all-settings.html#enable-external-js\">Zigbee2MQTT: All settings, enable_external_js<\/a><\/li>\r\n<\/ul>\r\n","protected":false},"excerpt":{"rendered":"<p>IMPORTANT NOTE:\u00a0 If you wait a few weeks this should be supported by Z2M by default, so you might want to just wait. The Tuya ZY-N1 sound detector works in Zigbee2MQTT today with a community external converter, and it makes a handy smoke alarm listener for Home Assistant. Getting there took one non-obvious setting change, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[1],"tags":[],"class_list":["post-371","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.zebadger.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/371","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.zebadger.com\/wp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.zebadger.com\/wp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.zebadger.com\/wp\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.zebadger.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=371"}],"version-history":[{"count":3,"href":"https:\/\/www.zebadger.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/371\/revisions"}],"predecessor-version":[{"id":375,"href":"https:\/\/www.zebadger.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/371\/revisions\/375"}],"wp:attachment":[{"href":"https:\/\/www.zebadger.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=371"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.zebadger.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=371"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.zebadger.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=371"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}