Fix Home Assistant MQTT discovery compliance
This commit fixes several issues with the MQTT discovery payload to ensure
full compliance with Home Assistant's MQTT discovery specification and match
the working configuration from the v1 implementation.
Changes:
- Add payload_available ("online") and payload_not_available ("offline") fields
to EntityConfig struct for proper availability handling in HA
- Make device_info parameters (manufacturer, model, sw_version, suggested_area)
configurable via config.yaml instead of hardcoded values
- Remove incorrect device_class "power" from cpu_usage sensor (power is for Watts, not %)
- Update config.example.yaml with documented device_info fields
The discovery payload now correctly includes all required fields for HA to
properly register and display the device with its sensors and switches.
Tested on physical PC (asus) with MQTT broker at 10.0.0.3:1883.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,10 @@
|
|||||||
device:
|
device:
|
||||||
name: $hostname # Use $hostname for automatic hostname, or a custom name like "my-pc"
|
name: $hostname # Use $hostname for automatic hostname, or a custom name like "my-pc"
|
||||||
identifiers: ["$hostname"]
|
identifiers: ["$hostname"]
|
||||||
|
manufacturer: "Pilot" # Manufacturer name shown in Home Assistant
|
||||||
|
model: "PC Agent" # Model name (e.g., "Laptop", "Desktop", "Server")
|
||||||
|
sw_version: "2.0.0" # Software version
|
||||||
|
suggested_area: "Bureau" # Suggested area in Home Assistant (optional)
|
||||||
|
|
||||||
mqtt:
|
mqtt:
|
||||||
host: "127.0.0.1"
|
host: "127.0.0.1"
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
device:
|
device:
|
||||||
name: $hostname
|
name: $hostname
|
||||||
identifiers: ["$hostname"]
|
identifiers: ["$hostname"]
|
||||||
|
manufacturer: "Asus"
|
||||||
|
model: "Laptop"
|
||||||
|
sw_version: "2.0.0"
|
||||||
|
suggested_area: "Bureau"
|
||||||
|
|
||||||
mqtt:
|
mqtt:
|
||||||
host: "10.0.0.3"
|
host: "10.0.0.3"
|
||||||
|
|||||||
@@ -20,6 +20,25 @@ pub struct Config {
|
|||||||
pub struct Device {
|
pub struct Device {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub identifiers: Vec<String>,
|
pub identifiers: Vec<String>,
|
||||||
|
#[serde(default = "default_manufacturer")]
|
||||||
|
pub manufacturer: String,
|
||||||
|
#[serde(default = "default_model")]
|
||||||
|
pub model: String,
|
||||||
|
#[serde(default = "default_sw_version")]
|
||||||
|
pub sw_version: String,
|
||||||
|
pub suggested_area: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_manufacturer() -> String {
|
||||||
|
"Pilot".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_model() -> String {
|
||||||
|
"PC Agent".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_sw_version() -> String {
|
||||||
|
"2.0.0".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ struct DeviceInfo {
|
|||||||
manufacturer: String,
|
manufacturer: String,
|
||||||
model: String,
|
model: String,
|
||||||
sw_version: String,
|
sw_version: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
suggested_area: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@@ -20,6 +22,8 @@ struct EntityConfig<'a> {
|
|||||||
unique_id: String,
|
unique_id: String,
|
||||||
state_topic: String,
|
state_topic: String,
|
||||||
availability_topic: String,
|
availability_topic: String,
|
||||||
|
payload_available: &'a str,
|
||||||
|
payload_not_available: &'a str,
|
||||||
device: DeviceInfo,
|
device: DeviceInfo,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
command_topic: Option<String>,
|
command_topic: Option<String>,
|
||||||
@@ -42,15 +46,16 @@ pub async fn publish_all(client: &AsyncClient, cfg: &Config) -> Result<()> {
|
|||||||
let device = DeviceInfo {
|
let device = DeviceInfo {
|
||||||
identifiers: cfg.device.identifiers.clone(),
|
identifiers: cfg.device.identifiers.clone(),
|
||||||
name: cfg.device.name.clone(),
|
name: cfg.device.name.clone(),
|
||||||
manufacturer: "Pilot".to_string(),
|
manufacturer: cfg.device.manufacturer.clone(),
|
||||||
model: "v2".to_string(),
|
model: cfg.device.model.clone(),
|
||||||
sw_version: "2.0.0".to_string(),
|
sw_version: cfg.device.sw_version.clone(),
|
||||||
|
suggested_area: cfg.device.suggested_area.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let availability = format!("{}/availability", base);
|
let availability = format!("{}/availability", base);
|
||||||
|
|
||||||
let sensors = vec![
|
let sensors = vec![
|
||||||
("cpu_usage", "CPU Usage", Some("%"), Some("power"), Some("mdi:chip")),
|
("cpu_usage", "CPU Usage", Some("%"), None, Some("mdi:chip")),
|
||||||
("memory_used_mb", "Memory Used", Some("MB"), None, Some("mdi:memory")),
|
("memory_used_mb", "Memory Used", Some("MB"), None, Some("mdi:memory")),
|
||||||
("memory_total_mb", "Memory Total", Some("MB"), None, Some("mdi:memory")),
|
("memory_total_mb", "Memory Total", Some("MB"), None, Some("mdi:memory")),
|
||||||
("ip_address", "IP Address", None, None, Some("mdi:ip")),
|
("ip_address", "IP Address", None, None, Some("mdi:ip")),
|
||||||
@@ -65,6 +70,8 @@ pub async fn publish_all(client: &AsyncClient, cfg: &Config) -> Result<()> {
|
|||||||
unique_id: format!("{}_{}", cfg.device.name, key),
|
unique_id: format!("{}_{}", cfg.device.name, key),
|
||||||
state_topic: format!("{}/state/{}", base, key),
|
state_topic: format!("{}/state/{}", base, key),
|
||||||
availability_topic: availability.clone(),
|
availability_topic: availability.clone(),
|
||||||
|
payload_available: "online",
|
||||||
|
payload_not_available: "offline",
|
||||||
device: DeviceInfo { ..device.clone() },
|
device: DeviceInfo { ..device.clone() },
|
||||||
command_topic: None,
|
command_topic: None,
|
||||||
payload_on: None,
|
payload_on: None,
|
||||||
@@ -90,6 +97,8 @@ pub async fn publish_all(client: &AsyncClient, cfg: &Config) -> Result<()> {
|
|||||||
unique_id: format!("{}_{}", cfg.device.name, key),
|
unique_id: format!("{}_{}", cfg.device.name, key),
|
||||||
state_topic: format!("{}/state/{}", base, key),
|
state_topic: format!("{}/state/{}", base, key),
|
||||||
availability_topic: availability.clone(),
|
availability_topic: availability.clone(),
|
||||||
|
payload_available: "online",
|
||||||
|
payload_not_available: "offline",
|
||||||
device: DeviceInfo { ..device.clone() },
|
device: DeviceInfo { ..device.clone() },
|
||||||
command_topic: Some(format!("{}/{}", base, cmd)),
|
command_topic: Some(format!("{}/{}", base, cmd)),
|
||||||
payload_on: Some("ON"),
|
payload_on: Some("ON"),
|
||||||
|
|||||||
Reference in New Issue
Block a user