=====1. The Specific Topic Format=====
To make Home Assistant automatically discover your entity, you must publish the configuration message to a topic structured exactly like this:
discovery_prefix/component/node_id/object_id/config
* : By default, this is homeassistant.
* : The type of entity you are creating (e.g., sensor, binary_sensor, switch, light, number, button).
* : (Optional) An ID for the hosting device (e.g., windows_pc).
* : The specific ID for this entity (e.g., cpu_temperature).
//Example Topic: homeassistant/sensor/windows_pc/cpu_temperature/config//
=====2. The Specific JSON Payload Format=====
The JSON payload sent to that **/config** topic tells Home Assistant how to build the entity.
To make it show up as a Virtual Device (and not just a loose standalone entity), you must group your entity configurations using the **device** object.
{
"name": "CPU Temperature",
"unique_id": "win_pc_cpu_temp_001",
"state_topic": "windows_pc/sensor/cpu_temperature/state",
"unit_of_measurement": "°C",
"state_class": "measurement",
"device_class": "temperature",
"device": {
"identifiers": ["my_unique_windows_pc_id"],
"name": "My Windows PC",
"model": "Windows 11 Pro PC",
"manufacturer": "Custom App"
}
}
* **unique_id**: A completely unique string for this entity. If you don't provide this, you won't be able to change its icon, entity ID, or area inside the Home Assistant UI.
* **state_topic**: The separate MQTT topic where your Windows program will broadcast the actual number/value (e.g., publishing 45 to windows_pc/sensor/cpu_temperature/state).
* **device**: This is the magic key. Every entity you create that shares the exact same identifiers array will automatically get grouped together into a single, clean "Device" card in Home Assistant.
=====3. How to Send the Actual Data=====
Once the configuration is sent to the **config** topic (ideally with the MQTT retain flag set to true so Home Assistant remembers it), you simply publish raw values or simple strings directly to your defined **state_topic**.
* To update the temperature, you just publish the raw text payload: **52.5** to **windows_pc/sensor/cpu_temperature/state**.
* No JSON wrapping is needed for the state updates themselves unless you specifically configure value templates.
=====Add for Online/Offline=====
===Tell your MQTT Client to set a "Last Will"===
Before your client connects to the MQTT broker, you configure a "Will". This is a message stored by the MQTT broker. If your Delphi app crashes or loses internet, the broker will automatically publish this message on your behalf.
// Do this BEFORE calling MQTTClient.Connect;
MQTTClient.WillTopic := 'delphi_device/sensor/cpu_temp/availability';
MQTTClient.WillPayload := 'offline';
MQTTClient.WillRetain := True;
===Add availability to your Discovery JSON===
You must update your original discovery payload to let Home Assistant know it should look at this availability topic:
{
"name": "Delphi CPU Temp",
"unique_id": "delphi_virtual_cpu_01",
"state_topic": "delphi_device/sensor/cpu_temp/state",
"availability_topic": "delphi_device/sensor/cpu_temp/availability",
"payload_available": "online",
"payload_not_available": "offline"
}
===Publish "online" when you start===
Right after your app successfully connects, publish online:
MQTTClient.Publish('delphi_device/sensor/cpu_temp/availability', 'online', True);
__**The Result:**__
* When your app is running: It sends online, and Home Assistant shows the live graph data.
* When you close/crash your app: The MQTT broker realizes your app disappeared and instantly broadcasts offline to the availability topic. Home Assistant will immediately grey out the entity and display "Unavailable", letting you know the Windows app isn't running.
=====Bidirectional Communication (ie: Slider)=====
To handle a sliding value from 0 to 100 that a user can interact with (like a volume slider, dimmer, or custom threshold adjustment), you use Home Assistant's number component instead of a sensor.
A sensor is read-only, but a number component renders as a slider in the Home Assistant UI and supports bi-directional communication.
===The Topic Structure===
You will need two communication topics for the state:
* state_topic: Where your Delphi app publishes the current value so HA can display it.
* command_topic: The topic Home Assistant will publish to when a user slides the bar in the UI. Your Delphi app must subscribe to this topic to receive the new value.
* Discovery Topic: homeassistant/number/delphi_virt_device/slider_value/config
===The JSON Discovery Payload===
You need to tell Home Assistant the minimum, maximum, and step values for the slider using min, max, and step
{
"name": "Custom Slider",
"unique_id": "delphi_virtual_slider_01",
"state_topic": "delphi_device/number/slider_value/state",
"command_topic": "delphi_device/number/slider_value/set",
"min": 0,
"max": 100,
"step": 1,
"mode": "slider",
"device": {
"identifiers": ["my_custom_delphi_virtual_mainframe"],
"name": "Delphi Virtual Mainframe",
"model": "VCL Virtual Device v1.0",
"manufacturer": "Embarcadero Delphi"
}
}
===How to Handle It===
application needs to do two things now: Publish updates and Listen for changes.
===Publishing a New Value (App -> HA)===
If your Windows app changes the value internally, send the raw number string to the state_topic
MQTTClient.Publish('delphi_device/number/slider_value/state', '42', True);
===Listening for UI Changes (HA <- App)===
When your app starts up, make sure it subscribes to the command_topic:
MQTTClient.Subscribe('delphi_device/number/slider_value/set');
Then, look inside your MQTT client's message received event handler (usually named something like OnMessage or OnPublishReceived). When Home Assistant sends a message there, extract the string payload and convert it back to an integer:
procedure TForm1.MQTTClientMessageReceived(Sender: TObject; const Topic, Payload: string);
var
NewSliderValue: Integer;
begin
if Topic = 'delphi_device/number/slider_value/set' then
begin
NewSliderValue := StrToIntDef(Payload, 0);
// Update your internal Windows application state or UI here!
Writeln('User moved the slider in Home Assistant to: ', NewSliderValue);
// Echo the value back to the state topic to confirm receipt
MQTTClient.Publish('delphi_device/number/slider_value/state', Payload, True);
end;
end;
=====SUMMARY=====
__**Creating a Virtual Device in Delphi**__
You can programmatically build custom, interactive hardware devices directly inside Windows using Delphi. The standard, native way to do this without writing custom YAML configurations is via MQTT Discovery.
* How it Works: Your Delphi app acts as a standalone machine communicating entirely through an MQTT broker.
* The Discovery Payload: You publish a specific, highly structured JSON configuration message to a predefined topic (homeassistant//.../config).
* Device Grouping: By including a matching device block with a shared identifiers string inside your JSON payloads, Home Assistant will cleanly group all your distinct virtual sensors, numbers, and switches together under a single, beautiful "Device" card in the UI.
__**Managing the Device Lifecycle & Interaction**__
* Asynchronous Design: Home Assistant operates completely asynchronously; it will not send a confirmation reply when you register a discovery device. You simply verify its presence in your HA Devices dashboard or HA logs.
* Unregistering Devices: If you want to permanently delete a virtual device from Home Assistant, your app just needs to publish an empty string payload ("") to the original config topic with the MQTT Retain flag set to true.
* Availability Tracking: To gracefully handle app crashes or network disconnects, you can configure an MQTT Last Will and Testament. If your Delphi app suddenly drops offline, the MQTT broker automatically tells Home Assistant to grey out the device as "Unavailable".
* Bi-directional Controls (Sliders): To create a sliding value from 0 to 100, you use the number component instead of a read-only sensor. This configuration utilizes a state_topic (to push updates to HA) and a command_topic (which your Delphi app subscribes to, allowing it to execute changes when a user moves the slider UI inside HA).