{"id":10259,"date":"2026-08-15T00:49:50","date_gmt":"2026-08-14T22:49:50","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/toolbox\/mqtt-home-assistant-discovery-builder\/"},"modified":"2026-08-15T00:49:50","modified_gmt":"2026-08-14T22:49:50","slug":"mqtt-home-assistant-discovery-builder","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/mqtt-home-assistant-discovery-builder\/","title":{"rendered":"MQTT Discovery Payload Builder for Home Assistant"},"content":{"rendered":"<div class=\"gtm-analyser-container\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<p style=\"color: var(--text-secondary, #a0a0b0); margin-bottom: 20px;\">Home Assistant picks up an MQTT entity on its own as soon as a retained config message sits on the right topic. This builder writes that message and checks the combinations that produce an entity which exists but never works.<\/p>\n<div style=\"display: flex; gap: 12px; flex-wrap: wrap;\">\n<div style=\"flex: 1; min-width: 170px;\">\n            <label for=\"mq-komponente\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Component<\/label><br \/>\n            <select id=\"mq-komponente\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\"><option value=\"sensor\">sensor<\/option><option value=\"binary_sensor\">binary_sensor<\/option><option value=\"switch\">switch<\/option><option value=\"number\">number<\/option><option value=\"light\">light<\/option><\/select>\n        <\/div>\n<div style=\"flex: 1; min-width: 170px;\">\n            <label for=\"mq-klasse\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Device class<\/label><br \/>\n            <select id=\"mq-klasse\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\"><\/select>\n        <\/div>\n<div style=\"flex: 1; min-width: 150px;\">\n            <label for=\"mq-einheit\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Unit<\/label><br \/>\n            <input type=\"text\" id=\"mq-einheit\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"W\">\n        <\/div>\n<div style=\"flex: 1; min-width: 170px;\">\n            <label for=\"mq-praefix\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Discovery prefix<\/label><br \/>\n            <input type=\"text\" id=\"mq-praefix\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"homeassistant\">\n        <\/div>\n<\/p><\/div>\n<div style=\"display: flex; gap: 12px; flex-wrap: wrap; margin-top: 12px;\">\n<div style=\"flex: 1; min-width: 190px;\">\n            <label for=\"mq-geraet\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Device name<\/label><br \/>\n            <input type=\"text\" id=\"mq-geraet\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"Zaehlerschrank\">\n        <\/div>\n<div style=\"flex: 1; min-width: 190px;\">\n            <label for=\"mq-kennung\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Device identifier<\/label><br \/>\n            <input type=\"text\" id=\"mq-kennung\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"sdm630_01\">\n        <\/div>\n<div style=\"flex: 1; min-width: 190px;\">\n            <label for=\"mq-name\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Entity name<\/label><br \/>\n            <input type=\"text\" id=\"mq-name\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"Bezug L1\">\n        <\/div>\n<div style=\"flex: 2; min-width: 240px;\">\n            <label for=\"mq-topic\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">State topic<\/label><br \/>\n            <input type=\"text\" id=\"mq-topic\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"zaehler\/sdm630\/status\">\n        <\/div>\n<\/p><\/div>\n<div style=\"display: flex; gap: 12px; flex-wrap: wrap; margin-top: 12px;\">\n<div style=\"flex: 1; min-width: 220px;\">\n            <label for=\"mq-vorlage\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Value template<\/label><br \/>\n            <input type=\"text\" id=\"mq-vorlage\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"{{ value_json.power_l1 }}\">\n        <\/div>\n<div style=\"flex: 1; min-width: 220px;\">\n            <label for=\"mq-verfuegbar\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Availability topic (optional)<\/label><br \/>\n            <input type=\"text\" id=\"mq-verfuegbar\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"zaehler\/sdm630\/verfuegbar\">\n        <\/div>\n<div style=\"flex: 1; min-width: 220px;\">\n            <label for=\"mq-hersteller\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Manufacturer<\/label><br \/>\n            <input type=\"text\" id=\"mq-hersteller\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"Eastron\">\n        <\/div>\n<\/p><\/div>\n<div style=\"display: flex; gap: 10px; flex-wrap: wrap; margin: 18px 0 0;\">\n        <button id=\"mq-btn\" class=\"button\" style=\"background: var(--accent, #7ee787); color: #0b1114; border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Build payload<\/button>\n    <\/div>\n<div id=\"mq-ergebnis\" style=\"margin-top: 22px;\"><\/div>\n<\/div>\n<p><script>\n(function () {\n    'use strict';<\/p>\n<p>    const T = {\"einleitung\":\"Home Assistant picks up an MQTT entity on its own as soon as a retained config message sits on the right topic. This builder writes that message and checks the combinations that produce an entity which exists but never works.\",\"label_komponente\":\"Component\",\"label_klasse\":\"Device class\",\"label_einheit\":\"Unit\",\"label_praefix\":\"Discovery prefix\",\"label_geraet\":\"Device name\",\"label_kennung\":\"Device identifier\",\"label_entitaet\":\"Entity name\",\"label_topic\":\"State topic\",\"label_vorlage\":\"Value template\",\"label_verfuegbar\":\"Availability topic (optional)\",\"label_hersteller\":\"Manufacturer\",\"klasse_keine\":\"none\",\"knopf\":\"Build payload\",\"fehler_name\":\"The entity name is missing.\",\"fehler_kennung\":\"The device identifier is missing.\",\"fehler_kennung_zeichen\":\"The device identifier may only hold letters, digits, underscore and dash.\",\"fehler_name_zeichen\":\"No usable object id can be derived from the entity name.\",\"fehler_topic\":\"The state topic is missing or holds a space, a plus or a hash. Wildcards belong in a subscription, not in a topic that is published to.\",\"fehler_praefix\":\"The discovery prefix is not a valid topic.\",\"fehler_verfuegbar\":\"The availability topic is not a valid topic.\",\"h_topic\":\"Discovery topic\",\"h_nutzlast\":\"Config payload\",\"h_befehl\":\"Publish it once\",\"h_befunde\":\"Findings\",\"b_einheit\":\"Device class %k expects one of these units: %l. Right now it is %e, and Home Assistant will reject the entity.\",\"b_energie\":\"An energy sensor needs a counter that only ever rises. If the value resets to zero on restart, the energy dashboard reads that as consumption.\",\"b_klasse_binaer\":\"%s is not a device class for a binary sensor.\",\"b_ohne_klasse\":\"A unit without a device class produces an entity that shows up nowhere but in the list.\",\"b_vorlage\":\"The value template mentions neither value_json nor value, so it will not read anything out of the message.\",\"b_vorlage_klammern\":\"The value template is missing its double braces.\",\"b_ohne_verfuegbar\":\"Without an availability topic the entity keeps showing its last value even when the device has been offline for days.\",\"b_retain\":\"The config message has to be published with the retain flag, otherwise the entity disappears on the next Home Assistant restart.\",\"b_topic_praefix\":\"The state topic sits below the discovery prefix. Keep them apart, or Home Assistant will try to read the measurements as configuration.\"};<\/p>\n<p>    \/\/ --- reine Rechenlogik ---<\/p>\n<p>    \/\/ Geraeteklassen mit den Einheiten, die Home Assistant dazu erwartet, und\n    \/\/ der passenden Zustandsklasse. Eine falsche Kombination erzeugt eine\n    \/\/ Entitaet, die in der Energieuebersicht nicht auftaucht.\n    const KLASSEN = {\n        '': { einheiten: [], zustand: '' },\n        'temperature': { einheiten: ['\u00b0C', '\u00b0F', 'K'], zustand: 'measurement' },\n        'humidity': { einheiten: ['%'], zustand: 'measurement' },\n        'pressure': { einheiten: ['hPa', 'mbar', 'Pa', 'bar', 'psi'], zustand: 'measurement' },\n        'power': { einheiten: ['W', 'kW'], zustand: 'measurement' },\n        'energy': { einheiten: ['Wh', 'kWh', 'MWh'], zustand: 'total_increasing' },\n        'voltage': { einheiten: ['V', 'mV'], zustand: 'measurement' },\n        'current': { einheiten: ['A', 'mA'], zustand: 'measurement' },\n        'battery': { einheiten: ['%'], zustand: 'measurement' },\n        'illuminance': { einheiten: ['lx'], zustand: 'measurement' },\n        'signal_strength': { einheiten: ['dBm', 'dB'], zustand: 'measurement' },\n        'carbon_dioxide': { einheiten: ['ppm'], zustand: 'measurement' },\n        'pm25': { einheiten: ['\u00b5g\/m\u00b3'], zustand: 'measurement' }\n    };<\/p>\n<p>    const BINAER_KLASSEN = ['', 'motion', 'door', 'window', 'moisture', 'smoke', 'occupancy', 'problem', 'connectivity'];<\/p>\n<p>    function text(wert) { return String(wert === undefined || wert === null ? '' : wert).trim(); }<\/p>\n<p>    function topicGueltig(topic) {\n        const t = text(topic);\n        if (t === '') { return false; }\n        if (\/[+#\\s]\/.test(t)) { return false; }\n        if (t.charAt(0) === '\/' || t.charAt(t.length - 1) === '\/') { return false; }\n        return true;\n    }<\/p>\n<p>    function kennungGueltig(wert) {\n        return \/^[A-Za-z0-9_-]+$\/.test(text(wert));\n    }<\/p>\n<p>    function bauen(e) {\n        const ergebnis = { ok: false, fehler: '', topic: '', nutzlast: null, json: '', befehl: '', befunde: [] };\n        const eingabe = e || {};<\/p>\n<p>        const komponente = text(eingabe.komponente) || 'sensor';\n        const praefix = text(eingabe.praefix) || 'homeassistant';\n        const geraet = text(eingabe.geraet);\n        const kennung = text(eingabe.kennung);\n        const name = text(eingabe.name);\n        const topic = text(eingabe.topic);\n        const klasse = text(eingabe.klasse);\n        const einheit = text(eingabe.einheit);\n        const vorlage = text(eingabe.vorlage);\n        const verfuegbar = text(eingabe.verfuegbar);\n        const hersteller = text(eingabe.hersteller);<\/p>\n<p>        if (name === '') { ergebnis.fehler = T.fehler_name; return ergebnis; }\n        if (kennung === '') { ergebnis.fehler = T.fehler_kennung; return ergebnis; }\n        if (!kennungGueltig(kennung)) { ergebnis.fehler = T.fehler_kennung_zeichen; return ergebnis; }\n        if (!topicGueltig(topic)) { ergebnis.fehler = T.fehler_topic; return ergebnis; }\n        if (!topicGueltig(praefix)) { ergebnis.fehler = T.fehler_praefix; return ergebnis; }\n        if (verfuegbar !== '' && !topicGueltig(verfuegbar)) { ergebnis.fehler = T.fehler_verfuegbar; return ergebnis; }<\/p>\n<p>        \/\/ Aus dem Namen wird die Objektkennung, damit sie zum Topic passt.\n        const objekt = name.toLowerCase()\n            .replace(\/[\u00e4\u00e0\u00e1\u00e2]\/g, 'a').replace(\/[\u00f6\u00f2\u00f3\u00f4]\/g, 'o').replace(\/[\u00fc\u00f9\u00fa\u00fb]\/g, 'u')\n            .replace(\/\u00df\/g, 'ss').replace(\/[^a-z0-9]+\/g, '_').replace(\/^_+|_+$\/g, '');\n        if (objekt === '') { ergebnis.fehler = T.fehler_name_zeichen; return ergebnis; }<\/p>\n<p>        const nutzlast = {\n            name: name,\n            unique_id: kennung + '_' + objekt,\n            object_id: kennung + '_' + objekt,\n            state_topic: topic\n        };\n        if (vorlage !== '') { nutzlast.value_template = vorlage; }\n        if (klasse !== '') { nutzlast.device_class = klasse; }\n        if (komponente === 'sensor') {\n            if (einheit !== '') { nutzlast.unit_of_measurement = einheit; }\n            const eintrag = KLASSEN[klasse];\n            if (eintrag && eintrag.zustand) { nutzlast.state_class = eintrag.zustand; }\n        }\n        if (komponente === 'switch' || komponente === 'light' || komponente === 'number') {\n            nutzlast.command_topic = topic.replace(\/\\\/status$\/, '') + '\/set';\n        }\n        if (verfuegbar !== '') {\n            nutzlast.availability_topic = verfuegbar;\n            nutzlast.payload_available = 'online';\n            nutzlast.payload_not_available = 'offline';\n        }\n        nutzlast.device = {\n            identifiers: [kennung],\n            name: geraet || name,\n            manufacturer: hersteller || undefined,\n            model: text(eingabe.modell) || undefined\n        };\n        if (!nutzlast.device.manufacturer) { delete nutzlast.device.manufacturer; }\n        if (!nutzlast.device.model) { delete nutzlast.device.model; }<\/p>\n<p>        ergebnis.topic = praefix + '\/' + komponente + '\/' + kennung + '\/' + objekt + '\/config';\n        ergebnis.nutzlast = nutzlast;\n        ergebnis.json = JSON.stringify(nutzlast, null, 2);\n        ergebnis.befehl = 'mosquitto_pub -h 192.168.1.10 -u benutzer -P geheim -r \\\\\\n'\n            + '  -t ' + hochkomma(ergebnis.topic) + ' \\\\\\n'\n            + '  -m ' + hochkomma(JSON.stringify(nutzlast));<\/p>\n<p>        \/\/ --- Befunde ---\n        if (komponente === 'sensor' && klasse !== '') {\n            const eintrag = KLASSEN[klasse];\n            if (eintrag && eintrag.einheiten.length && eintrag.einheiten.indexOf(einheit) === -1) {\n                ergebnis.befunde.push({ schwer: true, text: T.b_einheit.replace('%k', klasse).replace('%e', einheit || '-').replace('%l', eintrag.einheiten.join(', ')) });\n            }\n            if (klasse === 'energy') {\n                ergebnis.befunde.push({ schwer: false, text: T.b_energie });\n            }\n        }\n        if (komponente === 'binary_sensor' && klasse !== '' && BINAER_KLASSEN.indexOf(klasse) === -1) {\n            ergebnis.befunde.push({ schwer: true, text: T.b_klasse_binaer.replace('%s', klasse) });\n        }\n        if (komponente === 'sensor' && einheit !== '' && klasse === '') {\n            ergebnis.befunde.push({ schwer: false, text: T.b_ohne_klasse });\n        }\n        if (vorlage !== '' && vorlage.indexOf('value_json') === -1 && vorlage.indexOf('value') === -1) {\n            ergebnis.befunde.push({ schwer: true, text: T.b_vorlage });\n        }\n        if (vorlage !== '' && (vorlage.indexOf('{{') === -1 || vorlage.indexOf('}}') === -1)) {\n            ergebnis.befunde.push({ schwer: true, text: T.b_vorlage_klammern });\n        }\n        if (verfuegbar === '') {\n            ergebnis.befunde.push({ schwer: false, text: T.b_ohne_verfuegbar });\n        }\n        ergebnis.befunde.push({ schwer: false, text: T.b_retain });\n        if (topic.indexOf(praefix + '\/') === 0) {\n            ergebnis.befunde.push({ schwer: true, text: T.b_topic_praefix });\n        }<\/p>\n<p>        ergebnis.ok = true;\n        return ergebnis;\n    }<\/p>\n<p>    function hochkomma(wert) {\n        return \"'\" + String(wert).replace(\/'\/g, \"'\\\\''\") + \"'\";\n    }<\/p>\n<p>    if (typeof window !== 'undefined') {\n        window.LW_TEST = window.LW_TEST || {};\n        window.LW_TEST.MQ = { bauen: bauen, topicGueltig: topicGueltig, kennungGueltig: kennungGueltig, KLASSEN: KLASSEN };\n    }<\/p>\n<p>    \/\/ --- Anbindung an das Dokument ---<\/p>\n<p>    const btn = document.getElementById('mq-btn');\n    const out = document.getElementById('mq-ergebnis');\n    const komponenteFeld = document.getElementById('mq-komponente');\n    const klasseFeld = document.getElementById('mq-klasse');\n    if (!btn || !out || !komponenteFeld || !klasseFeld) { return; }<\/p>\n<p>    const KASTEN = 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 6px; padding: 14px; margin-bottom: 18px;';\n    const UEBERSCHRIFT = 'color: var(--text-primary, #e8e8ee); font-weight: 700; margin-bottom: 8px;';<\/p>\n<p>    function el(tag, css, inhalt) {\n        const knoten = document.createElement(tag);\n        if (css) { knoten.setAttribute('style', css); }\n        if (inhalt !== undefined && inhalt !== null) { knoten.textContent = inhalt; }\n        return knoten;\n    }<\/p>\n<p>    function wert(id) {\n        const knoten = document.getElementById(id);\n        return knoten ? knoten.value : '';\n    }<\/p>\n<p>    function klassenFuellen() {\n        const liste = komponenteFeld.value === 'binary_sensor' ? BINAER_KLASSEN : Object.keys(KLASSEN);\n        const vorher = klasseFeld.value;\n        klasseFeld.textContent = '';\n        liste.forEach(function (name) {\n            const option = document.createElement('option');\n            option.value = name;\n            option.textContent = name === '' ? T.klasse_keine : name;\n            klasseFeld.appendChild(option);\n        });\n        if (liste.indexOf(vorher) !== -1) { klasseFeld.value = vorher; }\n        else if (komponenteFeld.value === 'sensor') { klasseFeld.value = 'power'; }\n    }<\/p>\n<p>    komponenteFeld.addEventListener('change', klassenFuellen);\n    klassenFuellen();\n    klasseFeld.value = 'power';<\/p>\n<p>    btn.addEventListener('click', function () {\n        const erg = bauen({\n            komponente: komponenteFeld.value,\n            klasse: klasseFeld.value,\n            einheit: wert('mq-einheit'),\n            praefix: wert('mq-praefix'),\n            geraet: wert('mq-geraet'),\n            kennung: wert('mq-kennung'),\n            name: wert('mq-name'),\n            topic: wert('mq-topic'),\n            vorlage: wert('mq-vorlage'),\n            verfuegbar: wert('mq-verfuegbar'),\n            hersteller: wert('mq-hersteller')\n        });<\/p>\n<p>        out.textContent = '';\n        if (!erg.ok) {\n            out.appendChild(el('div', 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-left: 3px solid #ffb86c; color: var(--text-secondary, #a0a0b0); padding: 14px; border-radius: 6px;', erg.fehler));\n            return;\n        }<\/p>\n<p>        out.appendChild(el('div', UEBERSCHRIFT, T.h_topic));\n        out.appendChild(el('pre', 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--accent, #7ee787); padding: 14px; border-radius: 6px; overflow-x: auto; font-family: monospace; font-size: 0.85rem; white-space: pre-wrap; word-break: break-all; margin-bottom: 18px;', erg.topic));<\/p>\n<p>        out.appendChild(el('div', UEBERSCHRIFT, T.h_nutzlast));\n        out.appendChild(el('pre', 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); padding: 16px; border-radius: 6px; overflow-x: auto; font-family: monospace; font-size: 0.82rem; line-height: 1.5; white-space: pre; margin-bottom: 18px;', erg.json));<\/p>\n<p>        out.appendChild(el('div', UEBERSCHRIFT, T.h_befehl));\n        out.appendChild(el('pre', 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); padding: 16px; border-radius: 6px; overflow-x: auto; font-family: monospace; font-size: 0.8rem; line-height: 1.5; white-space: pre; margin-bottom: 18px;', erg.befehl));<\/p>\n<p>        out.appendChild(el('div', UEBERSCHRIFT, T.h_befunde));\n        const kasten = el('div', KASTEN);\n        erg.befunde.forEach(function (b) {\n            const z = el('div', 'display: flex; gap: 10px; padding: 5px 0;');\n            z.appendChild(el('span', 'color: ' + (b.schwer ? '#ffb86c' : 'var(--text-secondary, #a0a0b0)') + '; font-family: monospace;', b.schwer ? '!' : '-'));\n            z.appendChild(el('span', 'color: var(--text-secondary, #a0a0b0); font-size: 0.9rem; line-height: 1.5;', b.text));\n            kasten.appendChild(z);\n        });\n        out.appendChild(kasten);\n    });\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Builds the discovery topic and the config payload for an MQTT entity, and catches the mismatches between device class, unit and state class that keep a sensor out of the energy dashboard.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":38,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[91106,91237,91626],"class_list":["post-10259","page","type-page","status-publish","hentry","tag-automation","tag-self-hosting","tag-smart-home-de"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>MQTT Discovery Payload Builder for Home Assistant - Lukas Wojcik - Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/mqtt-home-assistant-discovery-builder\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"MQTT Discovery Payload Builder for Home Assistant - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Builds the discovery topic and the config payload for an MQTT entity, and catches the mismatches between device class, unit and state class that keep a sensor out of the energy dashboard.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/mqtt-home-assistant-discovery-builder\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/mqtt-home-assistant-discovery-builder\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/mqtt-home-assistant-discovery-builder\\\/\",\"name\":\"MQTT Discovery Payload Builder for Home Assistant - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-08-14T22:49:50+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/mqtt-home-assistant-discovery-builder\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/mqtt-home-assistant-discovery-builder\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/mqtt-home-assistant-discovery-builder\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"MQTT Discovery Payload Builder for Home Assistant\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"luky\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"luky\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"MQTT Discovery Payload Builder for Home Assistant - Lukas Wojcik - Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/mqtt-home-assistant-discovery-builder\/","og_locale":"en_US","og_type":"article","og_title":"MQTT Discovery Payload Builder for Home Assistant - Lukas Wojcik - Blog","og_description":"Builds the discovery topic and the config payload for an MQTT entity, and catches the mismatches between device class, unit and state class that keep a sensor out of the energy dashboard.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/mqtt-home-assistant-discovery-builder\/","og_site_name":"Lukas Wojcik - Blog","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/mqtt-home-assistant-discovery-builder\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/mqtt-home-assistant-discovery-builder\/","name":"MQTT Discovery Payload Builder for Home Assistant - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-08-14T22:49:50+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/mqtt-home-assistant-discovery-builder\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/mqtt-home-assistant-discovery-builder\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/mqtt-home-assistant-discovery-builder\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/"},{"@type":"ListItem","position":3,"name":"MQTT Discovery Payload Builder for Home Assistant"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"luky","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"luky"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/10259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=10259"}],"version-history":[{"count":0,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/10259\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/38"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=10259"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=10259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}