// from: https://forum.iobroker.net/topic/78685/iobroker-mqtt-homeassistent-autodiscovery // Required libraries const mqtt = require('mqtt'); const util = require('util'); // Configuration const MQTT_BROKER_URL = 'mqtt://192.168.178.150:1883'; // Replace with your broker's address const MQTT_USERNAME = ''; // Replace with your MQTT username const MQTT_PASSWORD = ''; // Replace with your MQTT password const DEBUG = false; // Set to true for debugging output const SENDFLAG = true; // acutally send packages // MQTT client setup const mqttClient = mqtt.connect(MQTT_BROKER_URL, { username: MQTT_USERNAME, password: MQTT_PASSWORD }); mqttClient.on('connect', () => { console.log('Connected to MQTT broker'); }); mqttClient.on('error', (err) => { console.error('MQTT connection error:', err); }); // Function to log debug information function debugLog(message) { if (DEBUG) { console.log(message); } } // Function to determine the type of a state function determineStateType(stateObj, id) { if (typeof stateObj.val === 'boolean') { if( getObject(id).common.role == 'sensor') { return 'binary_sensor'; } return 'switch'; } else if (typeof stateObj.val === 'number') { return 'sensor'; } else if (typeof stateObj.val === 'string') { return 'text'; } else { return 'unknown'; } } // Function to publish MQTT discovery messages function publishMQTTDiscovery(id, stateObj, deviceName, fromsub) { const baseTopic = `homeassistant`; const deviceId = id.replace(/\./g, '_').replace(/รค/g, 'ae'); const stateType = determineStateType(stateObj, id); const model = getObject(id).common.name; let configTopic; let configPayload; switch (stateType) { case 'switch': configTopic = `${baseTopic}/switch/${deviceId}/config`; configPayload = { name: deviceName || id, // Use the provided deviceName or fallback to id state_topic: `${baseTopic}/switch/${deviceId}/state`, command_topic: `${baseTopic}/switch/${deviceId}/set`, payload_on: true, payload_off: false, unique_id: deviceId, device: { identifiers: [deviceId], name: deviceName || id, // Use deviceName for the device itself manufacturer: `ioBroker`, model: `${model}`, }, }; break; case 'binary_sensor': var devclass = "window"; configTopic = `${baseTopic}/binary_sensor/${deviceId}/config`; configPayload = { name: deviceName || id, state_topic: `${baseTopic}/binary_sensor/${deviceId}/state`, unique_id: deviceId, device_class: `${devclass}`, device: { identifiers: [deviceId], name: deviceName || id, manufacturer: `ioBroker`, model: `${model}`, }, }; break; case 'sensor': configTopic = `${baseTopic}/sensor/${deviceId}/config`; configPayload = { name: deviceName || id, state_topic: `${baseTopic}/sensor/${deviceId}/state`, unique_id: deviceId, device: { identifiers: [deviceId], name: deviceName || id, manufacturer: `ioBroker`, model: `${model}`, }, }; break; case 'text': configTopic = `${baseTopic}/text/${deviceId}/config`; configPayload = { name: deviceName || id, state_topic: `${baseTopic}/text/${deviceId}/state`, command_topic: `${baseTopic}/text/${deviceId}/set`, unique_id: deviceId, device: { identifiers: [deviceId], name: deviceName || id, manufacturer: `ioBroker`, model: `${model}`, }, }; break; default: debugLog(`Unknown state type for ${id}`); return; } if(SENDFLAG) mqttClient.publish(configTopic, JSON.stringify(configPayload), { retain: true }); // Publish initial state const stateTopic = configPayload.state_topic; var stateVal = JSON.stringify(stateObj.val); if( stateType == "binary_sensor") { stateVal="OFF"; if(stateObj.val == true) { stateVal = "ON"; } } if(!fromsub) { // subscribe for changes debugLog( 'SUBSCRIBE ' + getObject(id).common.name + ' (' + deviceName+ ')' ); on({ id: id, change: 'ne' }, async (obj) => { publishSingleDevice( obj.id, true ); }); } if(SENDFLAG) { mqttClient.publish(stateTopic, stateVal ); if(DEBUG) console.log('Publish to ' + stateTopic + ' -> ' + stateVal ); } // Subscribe to command topic if applicable if (configPayload.command_topic && SENDFLAG ) { mqttClient.subscribe(configPayload.command_topic); mqttClient.on('message', (topic, message) => { if (topic === configPayload.command_topic) { try { // Check if the message is JSON let newValue; if (message.toString().startsWith('{') || message.toString().startsWith('[')) { newValue = JSON.parse(message.toString()); } else { // Handle non-JSON payloads (e.g., "true", "false", "42") newValue = message.toString().trim().toLowerCase(); if (newValue === 'true') newValue = true; else if (newValue === 'false') newValue = false; else if (!isNaN(newValue)) newValue = parseFloat(newValue); } setState(id, newValue); // Update ioBroker state } catch (err) { console.error(`Failed to process MQTT message on ${topic}:`, err); } } }); /* */ } debugLog(`Published MQTT discovery for ${id}: ${JSON.stringify(configPayload)}`); } function publishSingleDevice(deviceId, fromsub) { const deviceObj = getObject(deviceId); // Fetch the device object const stateObj = getState(deviceId); // Fetch the state of the device var myarray = deviceId.split("."); myarray.pop(); const parentId = myarray.join("."); const parentObj = getObject(parentId); if (deviceObj && stateObj) { if(DEBUG) console.log(`- ${deviceObj.common.name} - ${parentObj.common.name} (${deviceId})`); // Pass the device name as an additional argument publishMQTTDiscovery(deviceId, stateObj, parentObj.common.name, fromsub); } else { console.log(`Skipping device ${deviceId}: Unable to retrieve object or state.`); } } function scanAndPublish() { const homeAssistantDevices = getObject('enum.functions.homeassistent_enabled'); if (homeAssistantDevices) { console.log('Devices with homeassistent_enabled function:'); homeAssistantDevices.common.members.forEach(deviceId => { publishSingleDevice( deviceId, false ); }); } else { console.log('No devices found with homeassistent_enabled function'); } } // Run the script periodically schedule('0 * * * *', () => { debugLog('Scanning for states with homeassistent_enabled...'); scanAndPublish(); }); /* */ scanAndPublish();