1616logger = logging .getLogger (__name__ )
1717
1818
19+ def _normalize_automation_config (config : dict [str , Any ]) -> dict [str , Any ]:
20+ """
21+ Normalize automation config field names to HA API format.
22+
23+ Home Assistant accepts both singular ('trigger', 'action', 'condition')
24+ and plural ('triggers', 'actions', 'conditions') field names in YAML,
25+ but the API expects singular forms. This function normalizes plural
26+ to singular for consistency.
27+
28+ Args:
29+ config: Automation configuration dict
30+
31+ Returns:
32+ Normalized configuration with singular field names
33+ """
34+ normalized = config .copy ()
35+
36+ # Map plural field names to singular (HA API format)
37+ field_mappings = {
38+ "triggers" : "trigger" ,
39+ "actions" : "action" ,
40+ "conditions" : "condition" ,
41+ }
42+
43+ for plural , singular in field_mappings .items ():
44+ if plural in normalized and singular not in normalized :
45+ normalized [singular ] = normalized .pop (plural )
46+ elif plural in normalized and singular in normalized :
47+ # Both exist - prefer singular, remove plural
48+ del normalized [plural ]
49+
50+ return normalized
51+
52+
53+ def _normalize_trigger_keys (triggers : list [dict [str , Any ]]) -> list [dict [str , Any ]]:
54+ """
55+ Normalize trigger objects for round-trip compatibility.
56+
57+ Home Assistant GET API returns triggers with 'trigger' key for the platform type,
58+ but the SET API expects 'platform' key. This function converts between formats.
59+
60+ Args:
61+ triggers: List of trigger configuration dicts
62+
63+ Returns:
64+ List of triggers with 'platform' key instead of 'trigger' key
65+ """
66+ normalized_triggers = []
67+ for trigger in triggers :
68+ normalized_trigger = trigger .copy ()
69+ # Convert 'trigger' key to 'platform' if present and 'platform' is not
70+ if "trigger" in normalized_trigger and "platform" not in normalized_trigger :
71+ normalized_trigger ["platform" ] = normalized_trigger .pop ("trigger" )
72+ normalized_triggers .append (normalized_trigger )
73+ return normalized_triggers
74+
75+
76+ def _normalize_config_for_roundtrip (config : dict [str , Any ]) -> dict [str , Any ]:
77+ """
78+ Normalize automation config from GET response for direct use in SET.
79+
80+ This ensures a config retrieved via ha_config_get_automation can be
81+ directly passed to ha_config_set_automation without modification.
82+
83+ Transformations:
84+ 1. Field names: triggers -> trigger, actions -> action, conditions -> condition
85+ 2. Trigger keys: trigger -> platform (inside each trigger object)
86+
87+ Args:
88+ config: Raw automation configuration from HA API
89+
90+ Returns:
91+ Normalized configuration compatible with SET API
92+ """
93+ # First normalize field names (plural -> singular)
94+ normalized = _normalize_automation_config (config )
95+
96+ # Then normalize trigger keys (trigger -> platform)
97+ if "trigger" in normalized and isinstance (normalized ["trigger" ], list ):
98+ normalized ["trigger" ] = _normalize_trigger_keys (normalized ["trigger" ])
99+
100+ return normalized
101+
102+
19103def register_config_automation_tools (mcp : Any , client : Any , ** kwargs : Any ) -> None :
20104 """Register Home Assistant automation configuration tools."""
21105
@@ -42,11 +126,13 @@ async def ha_config_get_automation(
42126 """
43127 try :
44128 config_result = await client .get_automation_config (identifier )
129+ # Normalize config for round-trip compatibility (GET → SET)
130+ normalized_config = _normalize_config_for_roundtrip (config_result )
45131 return {
46132 "success" : True ,
47133 "action" : "get" ,
48134 "identifier" : identifier ,
49- "config" : config_result ,
135+ "config" : normalized_config ,
50136 }
51137 except Exception as e :
52138 # Handle 404 errors gracefully (often used to verify deletion)
@@ -186,6 +272,9 @@ async def ha_config_set_automation(
186272
187273 config_dict = cast (dict [str , Any ], parsed_config )
188274
275+ # Normalize field names (triggers -> trigger, actions -> action, etc.)
276+ config_dict = _normalize_automation_config (config_dict )
277+
189278 # Validate required fields
190279 required_fields = ["alias" , "trigger" , "action" ]
191280 missing_fields = [f for f in required_fields if f not in config_dict ]
0 commit comments