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
|
||||
# Configuration example for Pilot v2
|
||||
# Special variables:
|
||||
# $hostname - Will be replaced by the system hostname at runtime
|
||||
|
||||
device:
|
||||
name: pilot-device
|
||||
identifiers: ["pilot-device"]
|
||||
name: $hostname # Use $hostname for automatic hostname, or a custom name like "my-pc"
|
||||
identifiers: ["$hostname"]
|
||||
|
||||
mqtt:
|
||||
host: "127.0.0.1"
|
||||
@@ -10,7 +14,7 @@ mqtt:
|
||||
password: ""
|
||||
base_topic: "pilot"
|
||||
discovery_prefix: "homeassistant"
|
||||
client_id: "pilot-device"
|
||||
client_id: "$hostname" # MQTT client ID - use $hostname or a custom ID
|
||||
keepalive_s: 60
|
||||
qos: 0
|
||||
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"
|
||||
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]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
@@ -930,6 +941,7 @@ name = "pilot-v2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hostname",
|
||||
"local-ip-address",
|
||||
"rumqttc",
|
||||
"serde",
|
||||
|
||||
@@ -17,3 +17,4 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "signa
|
||||
sysinfo = "0.30"
|
||||
local-ip-address = "0.6"
|
||||
zbus = "3"
|
||||
hostname = "0.4"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Codex created 2025-12-29_0224
|
||||
device:
|
||||
name: pilot-device
|
||||
identifiers: ["pilot-device"]
|
||||
name: $hostname
|
||||
identifiers: ["$hostname"]
|
||||
|
||||
mqtt:
|
||||
host: "127.0.0.1"
|
||||
host: "10.0.0.3"
|
||||
port: 1883
|
||||
username: ""
|
||||
password: ""
|
||||
base_topic: "pilot"
|
||||
discovery_prefix: "homeassistant"
|
||||
client_id: "pilot-device"
|
||||
client_id: "$hostname"
|
||||
keepalive_s: 60
|
||||
qos: 0
|
||||
retain_states: true
|
||||
|
||||
@@ -87,8 +87,12 @@ pub fn load() -> Result<Config> {
|
||||
if path.exists() {
|
||||
let raw = fs::read_to_string(&path)
|
||||
.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()))?;
|
||||
|
||||
// Substituer $hostname par le vrai hostname
|
||||
expand_variables(&mut cfg)?;
|
||||
|
||||
validate(&cfg)?;
|
||||
return Ok(cfg);
|
||||
}
|
||||
@@ -116,6 +120,39 @@ pub fn base_device_topic(cfg: &Config) -> String {
|
||||
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.
|
||||
fn validate(cfg: &Config) -> Result<()> {
|
||||
if cfg.device.name.trim().is_empty() {
|
||||
@@ -180,4 +217,51 @@ publish:
|
||||
assert_eq!(cfg.mqtt.port, 1883);
|
||||
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