From 945a55b91a3c64d6a3b595bdd2e28c5b2149d4b6 Mon Sep 17 00:00:00 2001 From: karwosts Date: Tue, 13 Feb 2024 17:26:40 +0000 Subject: [PATCH 01/11] initial commit for sections --- homeassistant/components/blueprint/const.py | 3 +++ homeassistant/components/blueprint/schemas.py | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/homeassistant/components/blueprint/const.py b/homeassistant/components/blueprint/const.py index 1ca6117f153b1e..dd76a48cc7c894 100644 --- a/homeassistant/components/blueprint/const.py +++ b/homeassistant/components/blueprint/const.py @@ -8,5 +8,8 @@ CONF_HOMEASSISTANT = "homeassistant" CONF_MIN_VERSION = "min_version" CONF_AUTHOR = "author" +CONF_INPUT_SECTIONS = "input_sections" +CONF_SECTION = "section" +CONF_COLLAPSE = "collapse" DOMAIN = "blueprint" diff --git a/homeassistant/components/blueprint/schemas.py b/homeassistant/components/blueprint/schemas.py index fd3aa967336e30..b088bdeba00a7b 100644 --- a/homeassistant/components/blueprint/schemas.py +++ b/homeassistant/components/blueprint/schemas.py @@ -17,9 +17,12 @@ from .const import ( CONF_AUTHOR, CONF_BLUEPRINT, + CONF_COLLAPSE, CONF_HOMEASSISTANT, CONF_INPUT, + CONF_INPUT_SECTIONS, CONF_MIN_VERSION, + CONF_SECTION, CONF_SOURCE_URL, CONF_USE_BLUEPRINT, ) @@ -63,6 +66,17 @@ def is_blueprint_instance_config(config: Any) -> bool: vol.Optional(CONF_DESCRIPTION): str, vol.Optional(CONF_DEFAULT): cv.match_all, vol.Optional(CONF_SELECTOR): selector.validate_selector, + vol.Optional(CONF_SECTION): str, + } +) + +BLUEPRINT_INPUT_SECTION_SCHEMA = vol.Schema( + { + vol.Required(CONF_NAME): str, + vol.Optional(CONF_COLLAPSE): bool, + vol.Optional(CONF_INPUT_SECTIONS, default=dict): { + str: vol.Self, + }, } ) @@ -84,6 +98,9 @@ def is_blueprint_instance_config(config: Any) -> bool: BLUEPRINT_INPUT_SCHEMA, ) }, + vol.Optional(CONF_INPUT_SECTIONS, default=dict): { + str: BLUEPRINT_INPUT_SECTION_SCHEMA, + }, } ), }, From e1e0605f95fbbbf84d2e2b4846f2a76e6b37f24c Mon Sep 17 00:00:00 2001 From: karwosts Date: Tue, 13 Feb 2024 16:40:05 -0800 Subject: [PATCH 02/11] updates --- homeassistant/components/blueprint/const.py | 2 +- homeassistant/components/blueprint/schemas.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/blueprint/const.py b/homeassistant/components/blueprint/const.py index dd76a48cc7c894..1a7603874e87e9 100644 --- a/homeassistant/components/blueprint/const.py +++ b/homeassistant/components/blueprint/const.py @@ -10,6 +10,6 @@ CONF_AUTHOR = "author" CONF_INPUT_SECTIONS = "input_sections" CONF_SECTION = "section" -CONF_COLLAPSE = "collapse" +CONF_DEFAULT_COLLAPSED = "default_collapsed" DOMAIN = "blueprint" diff --git a/homeassistant/components/blueprint/schemas.py b/homeassistant/components/blueprint/schemas.py index b088bdeba00a7b..3ea6d70bfb977f 100644 --- a/homeassistant/components/blueprint/schemas.py +++ b/homeassistant/components/blueprint/schemas.py @@ -7,6 +7,7 @@ CONF_DEFAULT, CONF_DESCRIPTION, CONF_DOMAIN, + CONF_ICON, CONF_NAME, CONF_PATH, CONF_SELECTOR, @@ -17,7 +18,7 @@ from .const import ( CONF_AUTHOR, CONF_BLUEPRINT, - CONF_COLLAPSE, + CONF_DEFAULT_COLLAPSED, CONF_HOMEASSISTANT, CONF_INPUT, CONF_INPUT_SECTIONS, @@ -73,10 +74,8 @@ def is_blueprint_instance_config(config: Any) -> bool: BLUEPRINT_INPUT_SECTION_SCHEMA = vol.Schema( { vol.Required(CONF_NAME): str, - vol.Optional(CONF_COLLAPSE): bool, - vol.Optional(CONF_INPUT_SECTIONS, default=dict): { - str: vol.Self, - }, + vol.Optional(CONF_ICON): str, + vol.Optional(CONF_DEFAULT_COLLAPSED): bool, } ) From 5fe2888de5135434b7fc313899b7fe690126c814 Mon Sep 17 00:00:00 2001 From: karwosts Date: Tue, 13 Feb 2024 17:31:22 -0800 Subject: [PATCH 03/11] add description --- homeassistant/components/blueprint/schemas.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/blueprint/schemas.py b/homeassistant/components/blueprint/schemas.py index 3ea6d70bfb977f..aa003d6a70dba5 100644 --- a/homeassistant/components/blueprint/schemas.py +++ b/homeassistant/components/blueprint/schemas.py @@ -75,6 +75,7 @@ def is_blueprint_instance_config(config: Any) -> bool: { vol.Required(CONF_NAME): str, vol.Optional(CONF_ICON): str, + vol.Optional(CONF_DESCRIPTION): str, vol.Optional(CONF_DEFAULT_COLLAPSED): bool, } ) From 2c9d1a0c520502ed6d5afb0135d73901cc022e46 Mon Sep 17 00:00:00 2001 From: karwosts Date: Tue, 13 Feb 2024 19:25:29 -0800 Subject: [PATCH 04/11] fix test --- homeassistant/components/blueprint/schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/blueprint/schemas.py b/homeassistant/components/blueprint/schemas.py index aa003d6a70dba5..322b610fcf61c6 100644 --- a/homeassistant/components/blueprint/schemas.py +++ b/homeassistant/components/blueprint/schemas.py @@ -98,7 +98,7 @@ def is_blueprint_instance_config(config: Any) -> bool: BLUEPRINT_INPUT_SCHEMA, ) }, - vol.Optional(CONF_INPUT_SECTIONS, default=dict): { + vol.Optional(CONF_INPUT_SECTIONS): { str: BLUEPRINT_INPUT_SECTION_SCHEMA, }, } From 7cca7a157821545a98578cdf53181b57da6cb05e Mon Sep 17 00:00:00 2001 From: karwosts Date: Fri, 1 Mar 2024 14:31:12 -0800 Subject: [PATCH 05/11] rename collapsed key --- homeassistant/components/blueprint/const.py | 2 +- homeassistant/components/blueprint/schemas.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/blueprint/const.py b/homeassistant/components/blueprint/const.py index 1a7603874e87e9..bab18e17fc6f11 100644 --- a/homeassistant/components/blueprint/const.py +++ b/homeassistant/components/blueprint/const.py @@ -10,6 +10,6 @@ CONF_AUTHOR = "author" CONF_INPUT_SECTIONS = "input_sections" CONF_SECTION = "section" -CONF_DEFAULT_COLLAPSED = "default_collapsed" +CONF_COLLAPSED = "collapsed" DOMAIN = "blueprint" diff --git a/homeassistant/components/blueprint/schemas.py b/homeassistant/components/blueprint/schemas.py index 322b610fcf61c6..9c77d997dd5ac1 100644 --- a/homeassistant/components/blueprint/schemas.py +++ b/homeassistant/components/blueprint/schemas.py @@ -18,7 +18,7 @@ from .const import ( CONF_AUTHOR, CONF_BLUEPRINT, - CONF_DEFAULT_COLLAPSED, + CONF_COLLAPSED, CONF_HOMEASSISTANT, CONF_INPUT, CONF_INPUT_SECTIONS, @@ -73,10 +73,10 @@ def is_blueprint_instance_config(config: Any) -> bool: BLUEPRINT_INPUT_SECTION_SCHEMA = vol.Schema( { - vol.Required(CONF_NAME): str, + vol.Optional(CONF_NAME): str, vol.Optional(CONF_ICON): str, vol.Optional(CONF_DESCRIPTION): str, - vol.Optional(CONF_DEFAULT_COLLAPSED): bool, + vol.Optional(CONF_COLLAPSED): bool, } ) From 703f445850fdee77088ab21bae827307f26e5e15 Mon Sep 17 00:00:00 2001 From: karwosts Date: Fri, 29 Mar 2024 09:50:49 -0700 Subject: [PATCH 06/11] New schema --- homeassistant/components/blueprint/const.py | 2 -- homeassistant/components/blueprint/models.py | 11 +++++++++-- homeassistant/components/blueprint/schemas.py | 13 +++++++------ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/blueprint/const.py b/homeassistant/components/blueprint/const.py index b0f5b0627702e4..ccbcd7a9d8084e 100644 --- a/homeassistant/components/blueprint/const.py +++ b/homeassistant/components/blueprint/const.py @@ -9,8 +9,6 @@ CONF_HOMEASSISTANT = "homeassistant" CONF_MIN_VERSION = "min_version" CONF_AUTHOR = "author" -CONF_INPUT_SECTIONS = "input_sections" -CONF_SECTION = "section" CONF_COLLAPSED = "collapsed" DOMAIN = "blueprint" diff --git a/homeassistant/components/blueprint/models.py b/homeassistant/components/blueprint/models.py index 2475ccf8d14757..585c904241e352 100644 --- a/homeassistant/components/blueprint/models.py +++ b/homeassistant/components/blueprint/models.py @@ -78,7 +78,7 @@ def __init__( self.domain = data_domain - missing = yaml.extract_inputs(data) - set(data[CONF_BLUEPRINT][CONF_INPUT]) + missing = yaml.extract_inputs(data) - set(self.inputs) if missing: raise InvalidBlueprint( @@ -96,7 +96,14 @@ def name(self) -> str: @property def inputs(self) -> dict[str, Any]: """Return blueprint inputs.""" - return self.data[CONF_BLUEPRINT][CONF_INPUT] # type: ignore[no-any-return] + inputs = {} + for key, value in self.data[CONF_BLUEPRINT][CONF_INPUT].items(): + if value and CONF_INPUT in value: + for key, value in value[CONF_INPUT].items(): + inputs[key] = value + else: + inputs[key] = value + return inputs @property def metadata(self) -> dict[str, Any]: diff --git a/homeassistant/components/blueprint/schemas.py b/homeassistant/components/blueprint/schemas.py index a612172c212755..b4ad4c7380c880 100644 --- a/homeassistant/components/blueprint/schemas.py +++ b/homeassistant/components/blueprint/schemas.py @@ -22,9 +22,7 @@ CONF_COLLAPSED, CONF_HOMEASSISTANT, CONF_INPUT, - CONF_INPUT_SECTIONS, CONF_MIN_VERSION, - CONF_SECTION, CONF_SOURCE_URL, CONF_USE_BLUEPRINT, ) @@ -68,7 +66,6 @@ def is_blueprint_instance_config(config: Any) -> bool: vol.Optional(CONF_DESCRIPTION): str, vol.Optional(CONF_DEFAULT): cv.match_all, vol.Optional(CONF_SELECTOR): selector.validate_selector, - vol.Optional(CONF_SECTION): str, } ) @@ -78,6 +75,12 @@ def is_blueprint_instance_config(config: Any) -> bool: vol.Optional(CONF_ICON): str, vol.Optional(CONF_DESCRIPTION): str, vol.Optional(CONF_COLLAPSED): bool, + vol.Required(CONF_INPUT, default=dict): { + str: vol.Any( + None, + BLUEPRINT_INPUT_SCHEMA, + ) + }, } ) @@ -97,11 +100,9 @@ def is_blueprint_instance_config(config: Any) -> bool: str: vol.Any( None, BLUEPRINT_INPUT_SCHEMA, + BLUEPRINT_INPUT_SECTION_SCHEMA, ) }, - vol.Optional(CONF_INPUT_SECTIONS): { - str: BLUEPRINT_INPUT_SECTION_SCHEMA, - }, } ), }, From ef031cd91727796c6fea4aee611f886aee4bb1b6 Mon Sep 17 00:00:00 2001 From: karwosts Date: Sat, 30 Mar 2024 06:13:23 -0700 Subject: [PATCH 07/11] update snapshots --- tests/components/blueprint/snapshots/test_importer.ambr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/components/blueprint/snapshots/test_importer.ambr b/tests/components/blueprint/snapshots/test_importer.ambr index 002d5204dc80bc..38cb3b485d407b 100644 --- a/tests/components/blueprint/snapshots/test_importer.ambr +++ b/tests/components/blueprint/snapshots/test_importer.ambr @@ -1,6 +1,6 @@ # serializer version: 1 # name: test_extract_blueprint_from_community_topic - NodeDictClass({ + dict({ 'brightness': NodeDictClass({ 'default': 50, 'description': 'Brightness of the light(s) when turning on', @@ -97,7 +97,7 @@ }) # --- # name: test_fetch_blueprint_from_community_url - NodeDictClass({ + dict({ 'brightness': NodeDictClass({ 'default': 50, 'description': 'Brightness of the light(s) when turning on', @@ -194,7 +194,7 @@ }) # --- # name: test_fetch_blueprint_from_github_gist_url - NodeDictClass({ + dict({ 'light_entity': NodeDictClass({ 'name': 'Light', 'selector': dict({ From 58f68d9c2bbe675d9b3c7d76d49a4e02da03b9a9 Mon Sep 17 00:00:00 2001 From: karwosts Date: Sat, 30 Mar 2024 07:08:47 -0700 Subject: [PATCH 08/11] Testing for sections --- tests/components/blueprint/test_models.py | 38 ++++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/tests/components/blueprint/test_models.py b/tests/components/blueprint/test_models.py index 96e72e2b4cca1f..ea811d8485b006 100644 --- a/tests/components/blueprint/test_models.py +++ b/tests/components/blueprint/test_models.py @@ -26,24 +26,38 @@ def blueprint_1(): ) -@pytest.fixture -def blueprint_2(): +@pytest.fixture(params=[False, True]) +def blueprint_2(request): """Blueprint fixture with default inputs.""" - return models.Blueprint( - { - "blueprint": { - "name": "Hello", - "domain": "automation", - "source_url": "https://github.com/balloob/home-assistant-config/blob/main/blueprints/automation/motion_light.yaml", + blueprint = { + "blueprint": { + "name": "Hello", + "domain": "automation", + "source_url": "https://github.com/balloob/home-assistant-config/blob/main/blueprints/automation/motion_light.yaml", + "input": { + "test-input": {"name": "Name", "description": "Description"}, + "test-input-default": {"default": "test"}, + }, + }, + "example": Input("test-input"), + "example-default": Input("test-input-default"), + } + if request.param: + # Replace the inputs with inputs in sections. Test should otherwise behave the same. + blueprint["blueprint"]["input"] = { + "section-1": { + "name": "Section 1", "input": { "test-input": {"name": "Name", "description": "Description"}, - "test-input-default": {"default": "test"}, }, }, - "example": Input("test-input"), - "example-default": Input("test-input-default"), + "section-2": { + "input": { + "test-input-default": {"default": "test"}, + } + }, } - ) + return models.Blueprint(blueprint) @pytest.fixture From c06074c59b7b0e553dd32f786e43515ad32d8943 Mon Sep 17 00:00:00 2001 From: karwosts Date: Sat, 6 Apr 2024 21:50:59 -0700 Subject: [PATCH 09/11] Validate no duplicate input keys across sections --- homeassistant/components/blueprint/schemas.py | 34 +++++++++++--- tests/components/blueprint/test_schemas.py | 46 +++++++++++++++++++ 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/blueprint/schemas.py b/homeassistant/components/blueprint/schemas.py index b4ad4c7380c880..5bf93e82d2f7e7 100644 --- a/homeassistant/components/blueprint/schemas.py +++ b/homeassistant/components/blueprint/schemas.py @@ -48,6 +48,23 @@ def version_validator(value: Any) -> str: return value +def unique_input_validator(inputs: Any) -> Any: + """Validate the inputs don't have duplicate keys under different sections.""" + allinputs = set() + for key, value in inputs.items(): + if value and CONF_INPUT in value: + for key in value[CONF_INPUT]: + if key in allinputs: + raise vol.Invalid(f"Duplicate use of input key {key} in blueprint.") + allinputs.add(key) + else: + if key in allinputs: + raise vol.Invalid(f"Duplicate use of input key {key} in blueprint.") + allinputs.add(key) + + return inputs + + @callback def is_blueprint_config(config: Any) -> bool: """Return if it is a blueprint config.""" @@ -96,13 +113,16 @@ def is_blueprint_instance_config(config: Any) -> bool: vol.Optional(CONF_HOMEASSISTANT): { vol.Optional(CONF_MIN_VERSION): version_validator }, - vol.Optional(CONF_INPUT, default=dict): { - str: vol.Any( - None, - BLUEPRINT_INPUT_SCHEMA, - BLUEPRINT_INPUT_SECTION_SCHEMA, - ) - }, + vol.Optional(CONF_INPUT, default=dict): vol.All( + { + str: vol.Any( + None, + BLUEPRINT_INPUT_SCHEMA, + BLUEPRINT_INPUT_SECTION_SCHEMA, + ) + }, + unique_input_validator, + ), } ), }, diff --git a/tests/components/blueprint/test_schemas.py b/tests/components/blueprint/test_schemas.py index 0440a759f2f207..70d599c9d01ec6 100644 --- a/tests/components/blueprint/test_schemas.py +++ b/tests/components/blueprint/test_schemas.py @@ -52,6 +52,24 @@ }, } }, + # With input sections + { + "blueprint": { + "name": "Test Name", + "domain": "automation", + "input": { + "section_a": { + "input": {"some_placeholder": None}, + }, + "section_b": { + "name": "Section", + "description": "A section with no inputs", + "input": {}, + }, + "some_placeholder_2": None, + }, + } + }, ], ) def test_blueprint_schema(blueprint) -> None: @@ -94,6 +112,34 @@ def test_blueprint_schema(blueprint) -> None: }, } }, + # Duplicate inputs in sections (1 of 2) + { + "blueprint": { + "name": "Test Name", + "domain": "automation", + "input": { + "section_a": { + "input": {"some_placeholder": None}, + }, + "section_b": { + "input": {"some_placeholder": None}, + }, + }, + } + }, + # Duplicate inputs in sections (2 of 2) + { + "blueprint": { + "name": "Test Name", + "domain": "automation", + "input": { + "section_a": { + "input": {"some_placeholder": None}, + }, + "some_placeholder": None, + }, + } + }, ], ) def test_blueprint_schema_invalid(blueprint) -> None: From 312a27494893bdca357984ce0cc0855c42ad56c2 Mon Sep 17 00:00:00 2001 From: karwosts Date: Wed, 24 Apr 2024 05:36:42 -0700 Subject: [PATCH 10/11] rename all_inputs --- homeassistant/components/blueprint/schemas.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/blueprint/schemas.py b/homeassistant/components/blueprint/schemas.py index 5bf93e82d2f7e7..6aaa4091e07dfb 100644 --- a/homeassistant/components/blueprint/schemas.py +++ b/homeassistant/components/blueprint/schemas.py @@ -50,17 +50,17 @@ def version_validator(value: Any) -> str: def unique_input_validator(inputs: Any) -> Any: """Validate the inputs don't have duplicate keys under different sections.""" - allinputs = set() + all_inputs = set() for key, value in inputs.items(): if value and CONF_INPUT in value: for key in value[CONF_INPUT]: - if key in allinputs: + if key in all_inputs: raise vol.Invalid(f"Duplicate use of input key {key} in blueprint.") - allinputs.add(key) + all_inputs.add(key) else: - if key in allinputs: + if key in all_inputs: raise vol.Invalid(f"Duplicate use of input key {key} in blueprint.") - allinputs.add(key) + all_inputs.add(key) return inputs From b3520f9509c0c59941182bae52e4d4f69e28549e Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 29 May 2024 12:59:26 +0200 Subject: [PATCH 11/11] Update homeassistant/components/blueprint/models.py --- homeassistant/components/blueprint/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/blueprint/models.py b/homeassistant/components/blueprint/models.py index 585c904241e352..414d4e55a9b192 100644 --- a/homeassistant/components/blueprint/models.py +++ b/homeassistant/components/blueprint/models.py @@ -95,7 +95,7 @@ def name(self) -> str: @property def inputs(self) -> dict[str, Any]: - """Return blueprint inputs.""" + """Return flattened blueprint inputs.""" inputs = {} for key, value in self.data[CONF_BLUEPRINT][CONF_INPUT].items(): if value and CONF_INPUT in value: