Change Home Assistant device name and manufacturer/model

This commit is contained in:
ryan 2025-08-08 21:17:46 -07:00
parent bc7ac0c854
commit 8bbf91f25f

View File

@ -1,17 +1,17 @@
defmodule Systant.HaDiscovery do
@moduledoc """
Home Assistant MQTT Discovery integration for Systant.
Publishes device and entity discovery configurations to Home Assistant
via MQTT following the HA discovery protocol.
Discovery topic format: homeassistant/<component>/<node_id>/<object_id>/config
"""
require Logger
@manufacturer "Systant"
@model "System Monitor"
@model "Systant"
@doc """
Publish all discovery configurations for a host
@ -19,17 +19,17 @@ defmodule Systant.HaDiscovery do
def publish_discovery(client_pid, hostname, config \\ nil) do
app_config = config || Systant.Config.load_config()
ha_config = Systant.Config.get(app_config, ["homeassistant"]) || %{}
if ha_config["discovery_enabled"] != false do
discovery_prefix = ha_config["discovery_prefix"] || "homeassistant"
device_config = build_device_config(hostname)
# Publish device discovery first
publish_device_discovery(client_pid, hostname, device_config, discovery_prefix)
# Publish sensor discoveries
publish_sensor_discoveries(client_pid, hostname, device_config, discovery_prefix)
Logger.info("Published Home Assistant discovery for #{hostname}")
else
Logger.info("Home Assistant discovery disabled in configuration")
@ -43,15 +43,15 @@ defmodule Systant.HaDiscovery do
app_config = config || Systant.Config.load_config()
ha_config = Systant.Config.get(app_config, ["homeassistant"]) || %{}
discovery_prefix = ha_config["discovery_prefix"] || "homeassistant"
# Remove by publishing empty payloads to discovery topics
sensors = get_sensor_definitions(hostname)
Enum.each(sensors, fn {component, object_id, _config} ->
topic = "#{discovery_prefix}/#{component}/#{hostname}/#{object_id}/config"
Tortoise.publish(client_pid, topic, "", retain: true)
end)
Logger.info("Removed Home Assistant discovery for #{hostname}")
end
@ -63,21 +63,21 @@ defmodule Systant.HaDiscovery do
device: device_config,
components: build_all_components(hostname, device_config)
}
topic = "#{discovery_prefix}/device/#{hostname}/config"
payload = Jason.encode!(components_config)
Tortoise.publish(client_pid, topic, payload, retain: true)
end
defp publish_sensor_discoveries(client_pid, hostname, device_config, discovery_prefix) do
sensors = get_sensor_definitions(hostname)
Enum.each(sensors, fn {component, object_id, config} ->
full_config = Map.merge(config, %{device: device_config})
topic = "#{discovery_prefix}/#{component}/#{hostname}/#{object_id}/config"
payload = Jason.encode!(full_config)
Tortoise.publish(client_pid, topic, payload, retain: true)
end)
end
@ -85,7 +85,7 @@ defmodule Systant.HaDiscovery do
defp build_device_config(hostname) do
%{
identifiers: ["systant_#{hostname}"],
name: "Systant #{hostname}",
name: hostname |> String.capitalize(),
manufacturer: @manufacturer,
model: @model,
sw_version: Application.spec(:systant, :vsn) |> to_string()
@ -102,61 +102,196 @@ defmodule Systant.HaDiscovery do
defp get_sensor_definitions(hostname) do
base_topic = "systant/#{hostname}/stats"
[
# CPU Sensors
{"sensor", "cpu_load_1m", build_sensor_config("CPU Load 1m", "#{base_topic}", "cpu.avg1", "load", "mdi:speedometer")},
{"sensor", "cpu_load_5m", build_sensor_config("CPU Load 5m", "#{base_topic}", "cpu.avg5", "load", "mdi:speedometer")},
{"sensor", "cpu_load_15m", build_sensor_config("CPU Load 15m", "#{base_topic}", "cpu.avg15", "load", "mdi:speedometer")},
{"sensor", "cpu_load_1m",
build_sensor_config("CPU Load 1m", "#{base_topic}", "cpu.avg1", "load", "mdi:speedometer")},
{"sensor", "cpu_load_5m",
build_sensor_config("CPU Load 5m", "#{base_topic}", "cpu.avg5", "load", "mdi:speedometer")},
{"sensor", "cpu_load_15m",
build_sensor_config(
"CPU Load 15m",
"#{base_topic}",
"cpu.avg15",
"load",
"mdi:speedometer"
)},
# Memory Sensors
{"sensor", "memory_used_percent", build_sensor_config("Memory Used", "#{base_topic}", "memory.used_percent", "%", "mdi:memory")},
{"sensor", "memory_used_gb", build_sensor_config("Memory Used GB", "#{base_topic}", "memory.used_kb", "GB", "mdi:memory", "{{ (value_json.memory.used_kb | float / 1024 / 1024) | round(2) }}")},
{"sensor", "memory_total_gb", build_sensor_config("Memory Total GB", "#{base_topic}", "memory.total_kb", "GB", "mdi:memory", "{{ (value_json.memory.total_kb | float / 1024 / 1024) | round(2) }}")},
{"sensor", "memory_used_percent",
build_sensor_config(
"Memory Used",
"#{base_topic}",
"memory.used_percent",
"%",
"mdi:memory"
)},
{"sensor", "memory_used_gb",
build_sensor_config(
"Memory Used GB",
"#{base_topic}",
"memory.used_kb",
"GB",
"mdi:memory",
"{{ (value_json.memory.used_kb | float / 1024 / 1024) | round(2) }}"
)},
{"sensor", "memory_total_gb",
build_sensor_config(
"Memory Total GB",
"#{base_topic}",
"memory.total_kb",
"GB",
"mdi:memory",
"{{ (value_json.memory.total_kb | float / 1024 / 1024) | round(2) }}"
)},
# System Sensors
{"sensor", "uptime_hours", build_sensor_config("Uptime", "#{base_topic}", "system.uptime_seconds", "h", "mdi:clock-outline", "{{ (value_json.system.uptime_seconds | float / 3600) | round(1) }}")},
{"sensor", "kernel_version", build_sensor_config("Kernel Version", "#{base_topic}", "system.kernel_version", nil, "mdi:linux")},
{"sensor", "uptime_hours",
build_sensor_config(
"Uptime",
"#{base_topic}",
"system.uptime_seconds",
"h",
"mdi:clock-outline",
"{{ (value_json.system.uptime_seconds | float / 3600) | round(1) }}"
)},
{"sensor", "kernel_version",
build_sensor_config(
"Kernel Version",
"#{base_topic}",
"system.kernel_version",
nil,
"mdi:linux"
)},
# Temperature Sensors
{"sensor", "cpu_temperature", build_sensor_config("CPU Temperature", "#{base_topic}", "temperature.cpu", "°C", "mdi:thermometer")},
{"sensor", "cpu_temperature",
build_sensor_config(
"CPU Temperature",
"#{base_topic}",
"temperature.cpu",
"°C",
"mdi:thermometer"
)},
# GPU Sensors - NVIDIA
{"sensor", "gpu_nvidia_utilization", build_sensor_config("NVIDIA GPU Utilization", "#{base_topic}", "gpu.nvidia[0].utilization_percent", "%", "mdi:expansion-card", "{{ value_json.gpu.nvidia[0].utilization_percent if value_json.gpu.nvidia and value_json.gpu.nvidia|length > 0 else 0 }}")},
{"sensor", "gpu_nvidia_temperature", build_sensor_config("NVIDIA GPU Temperature", "#{base_topic}", "gpu.nvidia[0].temperature_c", "°C", "mdi:thermometer", "{{ value_json.gpu.nvidia[0].temperature_c if value_json.gpu.nvidia and value_json.gpu.nvidia|length > 0 else none }}")},
{"sensor", "gpu_nvidia_memory", build_sensor_config("NVIDIA GPU Memory", "#{base_topic}", "gpu.nvidia[0].memory_used_mb", "MB", "mdi:memory", "{{ value_json.gpu.nvidia[0].memory_used_mb if value_json.gpu.nvidia and value_json.gpu.nvidia|length > 0 else none }}")},
{"sensor", "gpu_nvidia_utilization",
build_sensor_config(
"NVIDIA GPU Utilization",
"#{base_topic}",
"gpu.nvidia[0].utilization_percent",
"%",
"mdi:expansion-card",
"{{ value_json.gpu.nvidia[0].utilization_percent if value_json.gpu.nvidia and value_json.gpu.nvidia|length > 0 else 0 }}"
)},
{"sensor", "gpu_nvidia_temperature",
build_sensor_config(
"NVIDIA GPU Temperature",
"#{base_topic}",
"gpu.nvidia[0].temperature_c",
"°C",
"mdi:thermometer",
"{{ value_json.gpu.nvidia[0].temperature_c if value_json.gpu.nvidia and value_json.gpu.nvidia|length > 0 else none }}"
)},
{"sensor", "gpu_nvidia_memory",
build_sensor_config(
"NVIDIA GPU Memory",
"#{base_topic}",
"gpu.nvidia[0].memory_used_mb",
"MB",
"mdi:memory",
"{{ value_json.gpu.nvidia[0].memory_used_mb if value_json.gpu.nvidia and value_json.gpu.nvidia|length > 0 else none }}"
)},
# GPU Sensors - AMD
{"sensor", "gpu_amd_utilization", build_sensor_config("AMD GPU Utilization", "#{base_topic}", "gpu.amd[0].utilization_percent", "%", "mdi:expansion-card", "{{ value_json.gpu.amd[0].utilization_percent if value_json.gpu.amd and value_json.gpu.amd|length > 0 else 0 }}")},
{"sensor", "gpu_amd_temperature", build_sensor_config("AMD GPU Temperature", "#{base_topic}", "gpu.amd[0].temperature_c", "°C", "mdi:thermometer", "{{ value_json.gpu.amd[0].temperature_c if value_json.gpu.amd and value_json.gpu.amd|length > 0 else none }}")},
{"sensor", "gpu_amd_utilization",
build_sensor_config(
"AMD GPU Utilization",
"#{base_topic}",
"gpu.amd[0].utilization_percent",
"%",
"mdi:expansion-card",
"{{ value_json.gpu.amd[0].utilization_percent if value_json.gpu.amd and value_json.gpu.amd|length > 0 else 0 }}"
)},
{"sensor", "gpu_amd_temperature",
build_sensor_config(
"AMD GPU Temperature",
"#{base_topic}",
"gpu.amd[0].temperature_c",
"°C",
"mdi:thermometer",
"{{ value_json.gpu.amd[0].temperature_c if value_json.gpu.amd and value_json.gpu.amd|length > 0 else none }}"
)},
# Disk Sensors - Main filesystem usage
{"sensor", "disk_root_usage", build_sensor_config("Root Disk Usage", "#{base_topic}", "disk.disks", "%", "mdi:harddisk", "{{ (value_json.disk.disks | selectattr('mounted_on', 'equalto', '/') | list | first).use_percent if value_json.disk.disks else 0 }}")},
{"sensor", "disk_home_usage", build_sensor_config("Home Disk Usage", "#{base_topic}", "disk.disks", "%", "mdi:harddisk", "{{ (value_json.disk.disks | selectattr('mounted_on', 'equalto', '/home') | list | first).use_percent if (value_json.disk.disks | selectattr('mounted_on', 'equalto', '/home') | list) else 0 }}")},
{"sensor", "disk_root_usage",
build_sensor_config(
"Root Disk Usage",
"#{base_topic}",
"disk.disks",
"%",
"mdi:harddisk",
"{{ (value_json.disk.disks | selectattr('mounted_on', 'equalto', '/') | list | first).use_percent if value_json.disk.disks else 0 }}"
)},
{"sensor", "disk_home_usage",
build_sensor_config(
"Home Disk Usage",
"#{base_topic}",
"disk.disks",
"%",
"mdi:harddisk",
"{{ (value_json.disk.disks | selectattr('mounted_on', 'equalto', '/home') | list | first).use_percent if (value_json.disk.disks | selectattr('mounted_on', 'equalto', '/home') | list) else 0 }}"
)},
# Network Sensors - Primary interface throughput
{"sensor", "network_rx_throughput", build_sensor_config("Network RX Throughput", "#{base_topic}", "network.rx_throughput", "MB/s", "mdi:download-network", "{{ (value_json.network[0].rx_throughput_bps | float / 1024 / 1024) | round(2) if value_json.network and value_json.network|length > 0 else 0 }}")},
{"sensor", "network_tx_throughput", build_sensor_config("Network TX Throughput", "#{base_topic}", "network.tx_throughput", "MB/s", "mdi:upload-network", "{{ (value_json.network[0].tx_throughput_bps | float / 1024 / 1024) | round(2) if value_json.network and value_json.network|length > 0 else 0 }}")},
{"sensor", "network_rx_throughput",
build_sensor_config(
"Network RX Throughput",
"#{base_topic}",
"network.rx_throughput",
"MB/s",
"mdi:download-network",
"{{ (value_json.network[0].rx_throughput_bps | float / 1024 / 1024) | round(2) if value_json.network and value_json.network|length > 0 else 0 }}"
)},
{"sensor", "network_tx_throughput",
build_sensor_config(
"Network TX Throughput",
"#{base_topic}",
"network.tx_throughput",
"MB/s",
"mdi:upload-network",
"{{ (value_json.network[0].tx_throughput_bps | float / 1024 / 1024) | round(2) if value_json.network and value_json.network|length > 0 else 0 }}"
)},
# Binary Sensors for status
{"binary_sensor", "system_online", build_binary_sensor_config("System Online", "#{base_topic}", "mdi:server", "connectivity")}
{"binary_sensor", "system_online",
build_binary_sensor_config("System Online", "#{base_topic}", "mdi:server", "connectivity")}
]
end
defp build_sensor_config(name, state_topic, value_template_path, unit, icon, custom_template \\ nil) do
defp build_sensor_config(
name,
state_topic,
value_template_path,
unit,
icon,
custom_template \\ nil
) do
base_config = %{
name: name,
state_topic: state_topic,
value_template: custom_template || "{{ value_json.#{value_template_path} }}",
icon: icon,
unique_id: "systant_#{String.replace(state_topic, "/", "_")}_#{String.replace(value_template_path, ".", "_")}",
unique_id:
"systant_#{String.replace(state_topic, "/", "_")}_#{String.replace(value_template_path, ".", "_")}",
origin: %{
name: "Systant",
sw_version: Application.spec(:systant, :vsn) |> to_string(),
support_url: "https://github.com/user/systant"
}
}
if unit do
Map.put(base_config, :unit_of_measurement, unit)
else
@ -179,4 +314,4 @@ defmodule Systant.HaDiscovery do
}
}
end
end
end