Add $hostname variable substitution in config
This commit adds support for automatic hostname substitution in configuration files. Users can now use $hostname in device.name, device.identifiers, and mqtt.client_id to automatically use the system's hostname. Changes: - Add hostname crate dependency (v0.4) - Implement expand_variables() to replace $hostname with actual hostname - Add get_hostname() helper function - Update config.example.yaml to demonstrate $hostname usage - Add test for hostname substitution - Update config.yaml to use $hostname by default - Add test_command.sh script for testing MQTT commands This makes deployment easier across multiple machines without manual config changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
# Codex created 2025-12-29_0224
|
# Codex created 2025-12-29_0224
|
||||||
|
# Configuration example for Pilot v2
|
||||||
|
# Special variables:
|
||||||
|
# $hostname - Will be replaced by the system hostname at runtime
|
||||||
|
|
||||||
device:
|
device:
|
||||||
name: pilot-device
|
name: $hostname # Use $hostname for automatic hostname, or a custom name like "my-pc"
|
||||||
identifiers: ["pilot-device"]
|
identifiers: ["$hostname"]
|
||||||
|
|
||||||
mqtt:
|
mqtt:
|
||||||
host: "127.0.0.1"
|
host: "127.0.0.1"
|
||||||
@@ -10,7 +14,7 @@ mqtt:
|
|||||||
password: ""
|
password: ""
|
||||||
base_topic: "pilot"
|
base_topic: "pilot"
|
||||||
discovery_prefix: "homeassistant"
|
discovery_prefix: "homeassistant"
|
||||||
client_id: "pilot-device"
|
client_id: "$hostname" # MQTT client ID - use $hostname or a custom ID
|
||||||
keepalive_s: 60
|
keepalive_s: 60
|
||||||
qos: 0
|
qos: 0
|
||||||
retain_states: true
|
retain_states: true
|
||||||
|
|||||||
12
pilot-v2/Cargo.lock
generated
12
pilot-v2/Cargo.lock
generated
@@ -672,6 +672,17 @@ version = "0.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hostname"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ident_case"
|
name = "ident_case"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -930,6 +941,7 @@ name = "pilot-v2"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"hostname",
|
||||||
"local-ip-address",
|
"local-ip-address",
|
||||||
"rumqttc",
|
"rumqttc",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "signa
|
|||||||
sysinfo = "0.30"
|
sysinfo = "0.30"
|
||||||
local-ip-address = "0.6"
|
local-ip-address = "0.6"
|
||||||
zbus = "3"
|
zbus = "3"
|
||||||
|
hostname = "0.4"
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
# Codex created 2025-12-29_0224
|
# Codex created 2025-12-29_0224
|
||||||
device:
|
device:
|
||||||
name: pilot-device
|
name: $hostname
|
||||||
identifiers: ["pilot-device"]
|
identifiers: ["$hostname"]
|
||||||
|
|
||||||
mqtt:
|
mqtt:
|
||||||
host: "127.0.0.1"
|
host: "10.0.0.3"
|
||||||
port: 1883
|
port: 1883
|
||||||
username: ""
|
username: ""
|
||||||
password: ""
|
password: ""
|
||||||
base_topic: "pilot"
|
base_topic: "pilot"
|
||||||
discovery_prefix: "homeassistant"
|
discovery_prefix: "homeassistant"
|
||||||
client_id: "pilot-device"
|
client_id: "$hostname"
|
||||||
keepalive_s: 60
|
keepalive_s: 60
|
||||||
qos: 0
|
qos: 0
|
||||||
retain_states: true
|
retain_states: true
|
||||||
|
|||||||
@@ -87,8 +87,12 @@ pub fn load() -> Result<Config> {
|
|||||||
if path.exists() {
|
if path.exists() {
|
||||||
let raw = fs::read_to_string(&path)
|
let raw = fs::read_to_string(&path)
|
||||||
.with_context(|| format!("failed reading config {}", path.display()))?;
|
.with_context(|| format!("failed reading config {}", path.display()))?;
|
||||||
let cfg: Config = serde_yaml::from_str(&raw)
|
let mut cfg: Config = serde_yaml::from_str(&raw)
|
||||||
.with_context(|| format!("failed parsing config {}", path.display()))?;
|
.with_context(|| format!("failed parsing config {}", path.display()))?;
|
||||||
|
|
||||||
|
// Substituer $hostname par le vrai hostname
|
||||||
|
expand_variables(&mut cfg)?;
|
||||||
|
|
||||||
validate(&cfg)?;
|
validate(&cfg)?;
|
||||||
return Ok(cfg);
|
return Ok(cfg);
|
||||||
}
|
}
|
||||||
@@ -116,6 +120,39 @@ pub fn base_device_topic(cfg: &Config) -> String {
|
|||||||
format!("{}/{}", base, cfg.device.name)
|
format!("{}/{}", base, cfg.device.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Obtient le hostname du système
|
||||||
|
fn get_hostname() -> Result<String> {
|
||||||
|
let hostname = hostname::get()
|
||||||
|
.context("failed to get system hostname")?
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
Ok(hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remplace les variables comme $hostname dans la config
|
||||||
|
fn expand_variables(cfg: &mut Config) -> Result<()> {
|
||||||
|
let hostname = get_hostname()?;
|
||||||
|
|
||||||
|
// Remplacer dans device.name
|
||||||
|
if cfg.device.name == "$hostname" {
|
||||||
|
cfg.device.name = hostname.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remplacer dans device.identifiers
|
||||||
|
for identifier in &mut cfg.device.identifiers {
|
||||||
|
if identifier == "$hostname" {
|
||||||
|
*identifier = hostname.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remplacer dans client_id
|
||||||
|
if cfg.mqtt.client_id == "$hostname" {
|
||||||
|
cfg.mqtt.client_id = hostname.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Verifie les champs minimum pour eviter les erreurs au demarrage.
|
// Verifie les champs minimum pour eviter les erreurs au demarrage.
|
||||||
fn validate(cfg: &Config) -> Result<()> {
|
fn validate(cfg: &Config) -> Result<()> {
|
||||||
if cfg.device.name.trim().is_empty() {
|
if cfg.device.name.trim().is_empty() {
|
||||||
@@ -180,4 +217,51 @@ publish:
|
|||||||
assert_eq!(cfg.mqtt.port, 1883);
|
assert_eq!(cfg.mqtt.port, 1883);
|
||||||
assert!(cfg.features.commands.dry_run);
|
assert!(cfg.features.commands.dry_run);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hostname_substitution() {
|
||||||
|
let raw = r#"
|
||||||
|
device:
|
||||||
|
name: "$hostname"
|
||||||
|
identifiers: ["$hostname"]
|
||||||
|
mqtt:
|
||||||
|
host: "127.0.0.1"
|
||||||
|
port: 1883
|
||||||
|
username: ""
|
||||||
|
password: ""
|
||||||
|
base_topic: "pilot"
|
||||||
|
discovery_prefix: "homeassistant"
|
||||||
|
client_id: "$hostname"
|
||||||
|
keepalive_s: 60
|
||||||
|
qos: 0
|
||||||
|
retain_states: true
|
||||||
|
features:
|
||||||
|
telemetry:
|
||||||
|
enabled: true
|
||||||
|
interval_s: 5
|
||||||
|
commands:
|
||||||
|
enabled: true
|
||||||
|
cooldown_s: 2
|
||||||
|
dry_run: true
|
||||||
|
allowlist: ["shutdown"]
|
||||||
|
power_backend:
|
||||||
|
linux: "linux_logind_polkit"
|
||||||
|
windows: "windows_service"
|
||||||
|
screen_backend:
|
||||||
|
linux: "gnome_busctl"
|
||||||
|
windows: "winapi_session"
|
||||||
|
publish:
|
||||||
|
heartbeat_s: 10
|
||||||
|
availability: true
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let mut cfg: Config = serde_yaml::from_str(raw).unwrap();
|
||||||
|
expand_variables(&mut cfg).unwrap();
|
||||||
|
|
||||||
|
let hostname = get_hostname().unwrap();
|
||||||
|
assert_eq!(cfg.device.name, hostname);
|
||||||
|
assert_eq!(cfg.device.identifiers[0], hostname);
|
||||||
|
assert_eq!(cfg.mqtt.client_id, hostname);
|
||||||
|
assert_ne!(cfg.device.name, "$hostname");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
44
scripts/test_command.sh
Executable file
44
scripts/test_command.sh
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Script simple pour tester les commandes MQTT avec Pilot v2
|
||||||
|
# Usage: ./test_command.sh <action> <value>
|
||||||
|
# Exemple: ./test_command.sh screen OFF
|
||||||
|
|
||||||
|
MQTT_HOST="10.0.0.3"
|
||||||
|
MQTT_PORT="1883"
|
||||||
|
DEVICE="asus"
|
||||||
|
|
||||||
|
if [ $# -ne 2 ]; then
|
||||||
|
echo "Usage: $0 <action> <value>"
|
||||||
|
echo "Actions: shutdown, reboot, sleep, screen"
|
||||||
|
echo "Values: ON, OFF"
|
||||||
|
echo "Exemple: $0 screen OFF"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTION=$1
|
||||||
|
VALUE=$2
|
||||||
|
TOPIC="pilot/${DEVICE}/cmd/${ACTION}/set"
|
||||||
|
|
||||||
|
# Vérifier si mosquitto_pub est installé
|
||||||
|
if ! command -v mosquitto_pub &> /dev/null; then
|
||||||
|
echo "❌ mosquitto_pub n'est pas installé"
|
||||||
|
echo "Installer avec: sudo apt install mosquitto-clients"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "📡 Envoi commande MQTT:"
|
||||||
|
echo " Topic: $TOPIC"
|
||||||
|
echo " Message: $VALUE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
mosquitto_pub -h "$MQTT_HOST" -p "$MQTT_PORT" -t "$TOPIC" -m "$VALUE"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "✅ Commande envoyée avec succès!"
|
||||||
|
echo ""
|
||||||
|
echo "Vérifiez les logs de Pilot pour voir le résultat:"
|
||||||
|
echo " En dry-run mode, la commande sera loggée mais pas exécutée"
|
||||||
|
else
|
||||||
|
echo "❌ Erreur lors de l'envoi de la commande"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user