@@ -141,6 +141,7 @@ def test_parse_meta_plain_values() -> None:
141141 "My Device" ,
142142 "A useful little box" ,
143143 None ,
144+ None ,
144145 )
145146
146147
@@ -150,7 +151,7 @@ def test_parse_meta_missing_keys_return_none() -> None:
150151esphome:
151152 name: my-device
152153"""
153- assert parse_esphome_meta (yaml_content ) == ("my-device" , None , None , None )
154+ assert parse_esphome_meta (yaml_content ) == ("my-device" , None , None , None , None )
154155
155156
156157def test_parse_meta_resolves_dollar_substitution () -> None :
@@ -162,7 +163,7 @@ def test_parse_meta_resolves_dollar_substitution() -> None:
162163 name: living-room-lamp
163164 friendly_name: $friendly_name
164165"""
165- _ , friendly_name , _ , _ = parse_esphome_meta (yaml_content )
166+ _ , friendly_name , _ , _ , _ = parse_esphome_meta (yaml_content )
166167 assert friendly_name == "Living Room Lamp"
167168
168169
@@ -175,7 +176,7 @@ def test_parse_meta_resolves_brace_substitution() -> None:
175176 name: kitchen
176177 friendly_name: ${friendly_name}
177178"""
178- _ , friendly_name , _ , _ = parse_esphome_meta (yaml_content )
179+ _ , friendly_name , _ , _ , _ = parse_esphome_meta (yaml_content )
179180 assert friendly_name == "Kitchen"
180181
181182
@@ -187,7 +188,7 @@ def test_parse_meta_resolves_substitution_inside_string() -> None:
187188esphome:
188189 friendly_name: "${room} Lamp"
189190"""
190- _ , friendly_name , _ , _ = parse_esphome_meta (yaml_content )
191+ _ , friendly_name , _ , _ , _ = parse_esphome_meta (yaml_content )
191192 assert friendly_name == "Bedroom Lamp"
192193
193194
@@ -199,7 +200,7 @@ def test_parse_meta_substitutions_block_after_esphome() -> None:
199200substitutions:
200201 friendly_name: "Office"
201202"""
202- _ , friendly_name , _ , _ = parse_esphome_meta (yaml_content )
203+ _ , friendly_name , _ , _ , _ = parse_esphome_meta (yaml_content )
203204 assert friendly_name == "Office"
204205
205206
@@ -211,7 +212,7 @@ def test_parse_meta_unknown_reference_left_untouched() -> None:
211212esphome:
212213 friendly_name: $missing
213214"""
214- _ , friendly_name , _ , _ = parse_esphome_meta (yaml_content )
215+ _ , friendly_name , _ , _ , _ = parse_esphome_meta (yaml_content )
215216 assert friendly_name == "$missing"
216217
217218
@@ -224,7 +225,7 @@ def test_parse_meta_resolves_substitution_in_comment() -> None:
224225 name: well
225226 comment: "${area} sensor"
226227"""
227- _ , _ , comment , _ = parse_esphome_meta (yaml_content )
228+ _ , _ , comment , _ , _ = parse_esphome_meta (yaml_content )
228229 assert comment == "Outside sensor"
229230
230231
@@ -243,7 +244,7 @@ def test_parse_meta_resolves_chained_substitutions() -> None:
243244 name: well
244245 comment: ${comment}
245246"""
246- _ , _ , comment , _ = parse_esphome_meta (yaml_content )
247+ _ , _ , comment , _ , _ = parse_esphome_meta (yaml_content )
247248 assert comment == "Outside, Well | Irrigation A"
248249
249250
@@ -259,7 +260,7 @@ def test_parse_meta_circular_substitutions_terminate() -> None:
259260"""
260261 # Should return without hanging; the exact stuck value is irrelevant
261262 # — what matters is that the resolver terminates safely.
262- _ , friendly_name , _ , _ = parse_esphome_meta (yaml_content )
263+ _ , friendly_name , _ , _ , _ = parse_esphome_meta (yaml_content )
263264 assert friendly_name in {"${a}" , "${b}" }
264265
265266
@@ -276,7 +277,7 @@ def test_parse_meta_resolves_substitution_from_extras() -> None:
276277 name: living-room-lamp
277278 friendly_name: $room
278279"""
279- _ , friendly_name , _ , _ = parse_esphome_meta (
280+ _ , friendly_name , _ , _ , _ = parse_esphome_meta (
280281 yaml_content ,
281282 extra_substitutions = {"room" : "Living Room" },
282283 )
@@ -295,7 +296,7 @@ def test_parse_meta_file_substitution_overrides_extras() -> None:
295296esphome:
296297 friendly_name: $room
297298"""
298- _ , friendly_name , _ , _ = parse_esphome_meta (
299+ _ , friendly_name , _ , _ , _ = parse_esphome_meta (
299300 yaml_content ,
300301 extra_substitutions = {"room" : "Living Room" },
301302 )
@@ -314,7 +315,7 @@ def test_parse_meta_extras_fill_gaps_in_file_substitutions() -> None:
314315esphome:
315316 friendly_name: "${room} ${suffix}"
316317"""
317- _ , friendly_name , _ , _ = parse_esphome_meta (
318+ _ , friendly_name , _ , _ , _ = parse_esphome_meta (
318319 yaml_content ,
319320 extra_substitutions = {"suffix" : "Lamp" },
320321 )
@@ -503,7 +504,7 @@ def test_parse_meta_comment_field() -> None:
503504 name: my-device
504505 comment: Hand-built controller
505506"""
506- name , friendly_name , comment , _ = parse_esphome_meta (yaml_content )
507+ name , friendly_name , comment , _ , _ = parse_esphome_meta (yaml_content )
507508 assert name == "my-device"
508509 assert friendly_name is None
509510 assert comment == "Hand-built controller"
@@ -523,7 +524,7 @@ def test_parse_meta_skips_blank_and_comment_lines_inside_block() -> None:
523524 # friendly_name: this is just a comment, ignore me
524525 comment: real comment
525526"""
526- name , friendly_name , comment , _ = parse_esphome_meta (yaml_content )
527+ name , friendly_name , comment , _ , _ = parse_esphome_meta (yaml_content )
527528 assert name == "my-device"
528529 assert friendly_name is None # comment line wasn't picked up
529530 assert comment == "real comment"
@@ -542,7 +543,7 @@ def test_parse_meta_area_field() -> None:
542543 friendly_name: Kitchen Lamp
543544 area: Kitchen
544545"""
545- name , friendly_name , _ , area = parse_esphome_meta (yaml_content )
546+ name , friendly_name , _ , area , _ = parse_esphome_meta (yaml_content )
546547 assert name == "kitchen-lamp"
547548 assert friendly_name == "Kitchen Lamp"
548549 assert area == "Kitchen"
@@ -554,7 +555,7 @@ def test_parse_meta_area_absent_returns_none() -> None:
554555esphome:
555556 name: my-device
556557"""
557- * _ , area = parse_esphome_meta (yaml_content )
558+ _ , _ , _ , area , _ = parse_esphome_meta (yaml_content )
558559 assert area is None
559560
560561
@@ -567,7 +568,7 @@ def test_parse_meta_area_resolves_substitution() -> None:
567568 name: lamp
568569 area: ${room}
569570"""
570- * _ , area = parse_esphome_meta (yaml_content )
571+ _ , _ , _ , area , _ = parse_esphome_meta (yaml_content )
571572 assert area == "Living Room"
572573
573574
@@ -679,7 +680,7 @@ def test_parse_meta_area_resolves_substitution() -> None:
679680)
680681def test_parse_meta_area_object_shapes (yaml_content : str , expected_area : str | None ) -> None :
681682 """``esphome.area`` resolves across every shape the schema accepts."""
682- * _ , area = parse_esphome_meta (yaml_content )
683+ _ , _ , _ , area , _ = parse_esphome_meta (yaml_content )
683684 assert area == expected_area
684685
685686
@@ -693,31 +694,66 @@ def test_parse_meta_area_block_form_isolates_nested_keys() -> None:
693694 name: "Kitchen"
694695 comment: "real comment"
695696"""
696- name , _ , comment , area = parse_esphome_meta (yaml_content )
697+ name , _ , comment , area , _ = parse_esphome_meta (yaml_content )
697698 assert name == "lamp"
698699 assert area == "Kitchen"
699700 assert comment == "real comment"
700701
701702
703+ # parse_esphome_meta — publish_shell_command field
704+ # ----------------------------------------------------------------------
705+
706+
707+ def test_parse_meta_publish_shell_command_field () -> None :
708+ """``esphome.publish_shell_command`` is captured as a plain string."""
709+ yaml_content = """
710+ esphome:
711+ name: my-device
712+ publish_shell_command: python3 publish.py
713+ """
714+ * _ , publish_shell_command = parse_esphome_meta (yaml_content )
715+ assert publish_shell_command == "python3 publish.py"
716+
717+
718+ def test_parse_meta_publish_shell_command_absent_returns_none () -> None :
719+ """Without a ``publish_shell_command:`` line, the field is ``None``."""
720+ yaml_content = """
721+ esphome:
722+ name: my-device
723+ """
724+ * _ , publish_shell_command = parse_esphome_meta (yaml_content )
725+ assert publish_shell_command is None
726+
727+
728+ def test_extract_meta_from_config_publish_shell_command () -> None :
729+ """``publish_shell_command`` is read from the resolved config."""
730+ config = {"esphome" : {"name" : "my-device" , "publish_shell_command" : "python3 publish.py" }}
731+ * _ , publish_shell_command = extract_esphome_meta_from_config (config )
732+ assert publish_shell_command == "python3 publish.py"
733+
734+
735+ # ----------------------------------------------------------------------
736+
737+
702738def test_extract_meta_from_config_string_area () -> None :
703739 """String-form ``area`` resolves against the supplied substitutions."""
704740 config = {"esphome" : {"name" : "lamp" , "area" : "${room}" }}
705- name , _ , _ , area = extract_esphome_meta_from_config (config , {"room" : "Kitchen" })
741+ name , _ , _ , area , _ = extract_esphome_meta_from_config (config , {"room" : "Kitchen" })
706742 assert name == "lamp"
707743 assert area == "Kitchen"
708744
709745
710746def test_extract_meta_from_config_dict_area_uses_name () -> None :
711747 """The ``{id, name}`` mapping form surfaces its ``name``, resolved."""
712748 config = {"esphome" : {"area" : {"id" : "${aid}" , "name" : "${room}" }}}
713- * _ , area = extract_esphome_meta_from_config (config , {"aid" : "k" , "room" : "Kitchen" })
749+ _ , _ , _ , area , _ = extract_esphome_meta_from_config (config , {"aid" : "k" , "room" : "Kitchen" })
714750 assert area == "Kitchen"
715751
716752
717753@pytest .mark .parametrize ("config" , [None , {}, {"esphome" : "not-a-dict" }, "nope" ])
718754def test_extract_meta_from_config_no_esphome_block (config : Any ) -> None :
719755 """Missing / malformed config or ``esphome:`` block yields all-``None``."""
720- assert extract_esphome_meta_from_config (config ) == (None , None , None , None )
756+ assert extract_esphome_meta_from_config (config ) == (None , None , None , None , None )
721757
722758
723759def test_parse_meta_top_level_comment_does_not_close_esphome_block () -> None :
@@ -729,7 +765,7 @@ def test_parse_meta_top_level_comment_does_not_close_esphome_block() -> None:
729765 friendly_name: Kitchen Lamp
730766 area: Kitchen
731767"""
732- name , friendly_name , _ , area = parse_esphome_meta (yaml_content )
768+ name , friendly_name , _ , area , _ = parse_esphome_meta (yaml_content )
733769 assert name == "lamp"
734770 assert friendly_name == "Kitchen Lamp"
735771 assert area == "Kitchen"
@@ -746,7 +782,7 @@ def test_parse_meta_ignores_unknown_esphome_keys_and_their_children() -> None:
746782 - -DSOMETHING
747783 comment: after-block comment
748784"""
749- name , _ , comment , area = parse_esphome_meta (yaml_content )
785+ name , _ , comment , area , _ = parse_esphome_meta (yaml_content )
750786 assert name == "lamp"
751787 assert comment == "after-block comment"
752788 assert area is None
0 commit comments