This commit is contained in:
Mateusz Gruszczyński
2026-09-03 13:08:28 +02:00
parent d7b6d0478f
commit b8ff31f205
52 changed files with 3874 additions and 1912 deletions
+53 -18
View File
@@ -61,50 +61,85 @@ fn shared_input_comparison_kind(kind: &str) -> bool {
)
}
fn validate_shared_input_source(kind: &str, config: &Value, state: &AppState) -> Result<(), AppError> {
match kind {
"outdoor_temperature" | "house_mode" | "night_mode" => {}
enum SharedInputResourceReference {
Device(String),
Zone(String),
Group(String),
}
fn shared_input_resource_reference(kind: &str, config: &Value) -> Result<Option<SharedInputResourceReference>, AppError> {
let reference = match kind {
"outdoor_temperature" | "house_mode" | "night_mode" => None,
"device_temperature" => {
let id = flow_string(config, "device_id").ok_or_else(|| AppError::BadRequest("shared device temperature input needs device".into()))?;
if state.db.get_device(&id)?.is_none() { return Err(AppError::BadRequest("shared Flow input references a missing device".into())); }
let id = flow_string(config, "device_id")
.ok_or_else(|| AppError::BadRequest("shared device temperature input needs device".into()))?;
Some(SharedInputResourceReference::Device(id))
}
"zone_temperature" => {
let id = flow_string(config, "zone_id").ok_or_else(|| AppError::BadRequest("shared zone temperature input needs zone".into()))?;
if state.db.get_zone(&id)?.is_none() { return Err(AppError::BadRequest("shared Flow input references a missing zone".into())); }
let id = flow_string(config, "zone_id")
.ok_or_else(|| AppError::BadRequest("shared zone temperature input needs zone".into()))?;
Some(SharedInputResourceReference::Zone(id))
}
"ha_state" | "ha_numeric" | "ha_available" => {
if flow_string(config, "entity_id").is_none() { return Err(AppError::BadRequest("shared Home Assistant input needs entity_id".into())); }
if flow_string(config, "entity_id").is_none() {
return Err(AppError::BadRequest("shared Home Assistant input needs entity_id".into()));
}
None
}
"ha_attribute" => {
if flow_string(config, "entity_id").is_none() || flow_string(config, "attribute").is_none() {
return Err(AppError::BadRequest("shared Home Assistant attribute input needs entity_id and attribute".into()));
}
None
}
"device_state" => {
let id = flow_string(config, "device_id").ok_or_else(|| AppError::BadRequest("shared device state input needs device".into()))?;
if state.db.get_device(&id)?.is_none() { return Err(AppError::BadRequest("shared Flow input references a missing device".into())); }
let field = flow_string(config, "field").ok_or_else(|| AppError::BadRequest("shared device state input needs a field".into()))?;
let id = flow_string(config, "device_id")
.ok_or_else(|| AppError::BadRequest("shared device state input needs device".into()))?;
let field = flow_string(config, "field")
.ok_or_else(|| AppError::BadRequest("shared device state input needs a field".into()))?;
if !matches!(field.as_str(), "enabled" | "online" | "power" | "mode" | "fan_speed" | "swing_vertical" | "swing_horizontal" | "quiet" | "turbo" | "light" | "air" | "xfan" | "health" | "sleep") {
return Err(AppError::BadRequest("unsupported shared device state field".into()));
}
Some(SharedInputResourceReference::Device(id))
}
"zone_state" => {
let id = flow_string(config, "zone_id").ok_or_else(|| AppError::BadRequest("shared zone state input needs zone".into()))?;
if state.db.get_zone(&id)?.is_none() { return Err(AppError::BadRequest("shared Flow input references a missing zone".into())); }
let field = flow_string(config, "field").ok_or_else(|| AppError::BadRequest("shared zone state input needs a field".into()))?;
let id = flow_string(config, "zone_id")
.ok_or_else(|| AppError::BadRequest("shared zone state input needs zone".into()))?;
let field = flow_string(config, "field")
.ok_or_else(|| AppError::BadRequest("shared zone state input needs a field".into()))?;
if !matches!(field.as_str(), "enabled" | "mode" | "active_preset" | "demand" | "control_owner" | "device_manual_override" | "local_thermostat_power") {
return Err(AppError::BadRequest("unsupported shared zone state field".into()));
}
Some(SharedInputResourceReference::Zone(id))
}
"group_state" => {
let id = flow_string(config, "group_id").ok_or_else(|| AppError::BadRequest("shared group state input needs group".into()))?;
if state.db.get_group(&id)?.is_none() { return Err(AppError::BadRequest("shared Flow input references a missing group".into())); }
if flow_string(config, "field").as_deref() != Some("power_enabled") { return Err(AppError::BadRequest("unsupported shared group state field".into())); }
let id = flow_string(config, "group_id")
.ok_or_else(|| AppError::BadRequest("shared group state input needs group".into()))?;
if flow_string(config, "field").as_deref() != Some("power_enabled") {
return Err(AppError::BadRequest("unsupported shared group state field".into()));
}
Some(SharedInputResourceReference::Group(id))
}
"constant" => {
if config.get("value").and_then(Value::as_bool).is_none() { return Err(AppError::BadRequest("shared constant input needs a boolean value".into())); }
if config.get("value").and_then(Value::as_bool).is_none() {
return Err(AppError::BadRequest("shared constant input needs a boolean value".into()));
}
None
}
_ => return Err(AppError::BadRequest(format!("unsupported shared Flow input kind: {kind}"))),
};
Ok(reference)
}
fn validate_shared_input_source(kind: &str, config: &Value, state: &AppState) -> Result<(), AppError> {
let Some(reference) = shared_input_resource_reference(kind, config)? else { return Ok(()); };
let exists = match reference {
SharedInputResourceReference::Device(id) => state.db.get_device(&id)?.is_some(),
SharedInputResourceReference::Zone(id) => state.db.get_zone(&id)?.is_some(),
SharedInputResourceReference::Group(id) => state.db.get_group(&id)?.is_some(),
};
if !exists {
return Err(AppError::BadRequest("shared Flow input references a missing resource".into()));
}
Ok(())
}