From a34a0083185f1e0252f83ec67835c88026569227 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 4 Dec 2020 11:13:58 +0100 Subject: [PATCH] Initial creating blueprint docs (#15816) Co-authored-by: Philip Allgaier Co-authored-by: Franck Nijhof --- source/_docs/blueprint.markdown | 22 ++ source/_docs/blueprint/schema.markdown | 83 +++++++ source/_docs/blueprint/selectors.markdown | 114 ++++++++++ source/_docs/blueprint/tutorial.markdown | 223 +++++++++++++++++++ source/_includes/asides/docs_navigation.html | 8 + source/images/blueprints/tutorial-ui.png | Bin 0 -> 17498 bytes 6 files changed, 450 insertions(+) create mode 100644 source/_docs/blueprint.markdown create mode 100644 source/_docs/blueprint/schema.markdown create mode 100644 source/_docs/blueprint/selectors.markdown create mode 100644 source/_docs/blueprint/tutorial.markdown create mode 100644 source/images/blueprints/tutorial-ui.png diff --git a/source/_docs/blueprint.markdown b/source/_docs/blueprint.markdown new file mode 100644 index 00000000000..d97d4f95c24 --- /dev/null +++ b/source/_docs/blueprint.markdown @@ -0,0 +1,22 @@ +--- +title: "Creating blueprints" +description: "Documentation on how to get started creating blueprints." +--- + +
+ +If you're looking on how to use blueprints, see the [automation documentation](/docs/automation/using_blueprints/). + +
+ +An automation blueprint is an automation configuration with certain parts marked as configurable. This allows users to create multiple automations based on the same blueprint, with each having its own configuration. + +Imagine a blueprint that controls a light based on motion, that allows you to configure the motion sensor to trigger on, and the light to control. It is now possible to create two automations that each have their own configuration for this blueprint and that act completely independent, yet are based on the same automation configuration. + +
+ +This is an advanced feature and requires knowledge of writing [automations in YAML](/docs/automation/yaml/). + +
+ +### [Tutorial: Create a blueprint »](/docs/blueprint/tutorial/) diff --git a/source/_docs/blueprint/schema.markdown b/source/_docs/blueprint/schema.markdown new file mode 100644 index 00000000000..9bc993f26b1 --- /dev/null +++ b/source/_docs/blueprint/schema.markdown @@ -0,0 +1,83 @@ +--- +title: "Blueprint schema" +description: "The schema for a valid blueprint." +--- + +Schema of the blueprint metadata: + +```ts +interface BlueprintInput { + name?: string; + description?: string; + selector?: Selector; + default?: any; +} + +interface Blueprint { + blueprint: { + domain: string; + name: string; + input?: Record; + description?: string; + source_url?: string; + } +} +``` + +The [built-in blueprints](https://github.com/home-assistant/core/tree/dev/homeassistant/components/automation/blueprints) are great examples. + +Here is the built-in motion light blueprint: + +```yaml +blueprint: + name: Motion-activated Light + description: Turn on a light when motion is detected. + domain: automation + source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/motion_light.yaml + input: + motion_entity: + name: Motion Sensor + selector: + entity: + domain: binary_sensor + device_class: motion + light_target: + name: Light + selector: + target: + entity: + domain: light + no_motion_wait: + name: Wait time + description: Time to leave the light on after last motion is detected. + default: 120 + selector: + number: + min: 0 + max: 3600 + unit_of_measurement: seconds + +# If motion is detected within the delay, +# we restart the script. +mode: restart +max_exceeded: silent + +trigger: + platform: state + entity_id: !input motion_entity + from: "off" + to: "on" + +action: + - service: light.turn_on + target: !input light_target + - wait_for_trigger: + platform: state + entity_id: !input motion_entity + from: "on" + to: "off" + - delay: !input no_motion_wait + - service: light.turn_off + target: !input light_target + +``` diff --git a/source/_docs/blueprint/selectors.markdown b/source/_docs/blueprint/selectors.markdown new file mode 100644 index 00000000000..1e26730569a --- /dev/null +++ b/source/_docs/blueprint/selectors.markdown @@ -0,0 +1,114 @@ +--- +title: "Selectors" +description: "Documentation on available selectors." +--- + +Selectors can be used to specify what values are accepted for an input. + +## Entity Selector + +Pick an entity. The chosen value will be an entity ID. + +```yaml +entity: + # All fields are optional + integration: zha + domain: binary_sensor + device_class: motion +``` + +## Device Selector + +Pick a device. The chosen value will be a device ID. + +```yaml +device: + # All fields are optional + integration: zha + manufacturer: Philips + model: Remote X1 + entity: +``` + +The `entity` option will limit devices that have an entity matching the entity selector. + + +## Target Selector + +Pick a target for service calls. Will be a dictionary containing optionally `entity_id`, `device_id` or `area_id` keys with the picked values. + +Meant to be specified as the `target` property in a call service step in a script sequence. + +```yaml +target: + # All fields are optional + entity: + integration: zha + domain: binary_sensor + device_class: motion + + device: + integration: zha + manufacturer: Philips + model: Remote X1 +``` + +## Number Selector + +Pick a number. + +```yaml +number: + # Required + min: 0 + max: 100 + # Optional + step: 5 + unit_of_measurement: seconds + mode: slider # other valid value 'box' +``` + +## Boolean Selector + +Pick true or false. + +```yaml +boolean: +``` + +## Time Selector + +Pick a time. + +```yaml +time: +``` + +## Action Selector + +Enter a script action. Will be a list of action steps. + +Meant to be specified as the `action` of an automation or any syntax that accepts actions, like `choose`. + +```yaml +action: +``` + + +## Area Selector + +Pick an area. The chosen value will be an area ID. + +```yaml +area: + # All fields are optional + entity: + integration: zha + domain: binary_sensor + device_class: motion + + device: + integration: zha + manufacturer: Philips + model: Remote X1 +``` diff --git a/source/_docs/blueprint/tutorial.markdown b/source/_docs/blueprint/tutorial.markdown new file mode 100644 index 00000000000..318d763f1a8 --- /dev/null +++ b/source/_docs/blueprint/tutorial.markdown @@ -0,0 +1,223 @@ +--- +title: "Blueprint tutorial" +description: "Tutorial on creating a blueprint." +--- + +In this tutorial, we're going to create a blueprint that controls a light based on a motion sensor. We will do this by taking an existing automation and converting it to a blueprint. + +For this tutorial, we use a simple automation. The process for converting a complex automation is not any different. + +## Our automation + +To create a blueprint, we first need to have a working automation. The automation we're going to use in this tutorial, which controls a light based on a motion sensor, looks like this: + +{% raw %} +```yaml +trigger: + platform: state + entity_id: binary_sensor.motion_kitchen + +action: + service: > + {% if trigger.to_state.state == "on" %} + light.turn_on + {% else %} + light.turn_off + {% endif %} + target: + entity_id: light.kitchen +``` +{% endraw %} + +## Create the blueprint file + +Automation blueprints are YAML files (with the `.yaml` extension) and live in the `/blueprints/automation/` folder. You can create as many subdirectories in this folder as you want. + +To get started with our blueprint, we're going to copy the above automation YAML and save it in that directory with the name `motion_light_tutorial.yaml`. + +## Add basic blueprint metadata + +Home Assistant needs to know about the blueprint. This is achieved by adding a `blueprint:` section. It should contain the `domain` of the integration it is for (`automation`) and `name`, the name of your blueprint. Optionally, you can also include a `description` for your blueprint. + +Add this to the top of the file: + +```yaml +blueprint: + name: Motion Light Tutorial + description: Turn a light on based on detected motion + domain: automation +``` + +## Define the configurable parts as inputs + +Now we have to decide what steps we want to make configurable. We want to make it as re-usable as possible, without losing its original intent of turning on a light-based on a motion sensor. + +Configurable parts in blueprints are called inputs. To make the motion sensor entity configurable, we're replacing the entity ID with a custom YAML tag `!input`. This YAML tag has to be combined with the name of the input: + +```yaml +trigger: + platform: state + entity_id: !input motion_sensor +``` + +For the light, we can offer some more flexibility. We want to allow the user to be able to define any device or area as the target. The `target` property in the service action can contain references to areas, devices and/or entities, so that's what we will use. + +Inputs are not limited to strings. They can contain complex objects too. So in this case, we're going to mark the whole `target` as input: + +{% raw %} +```yaml +action: + service: > + {% if trigger.to_state.state == "on" %} + light.turn_on + {% else %} + light.turn_off + {% endif %} + target: !input target_light +``` +{% endraw %} + +## Add the inputs to the metadata + +All parts that are marked as inputs need to be added to the metadata. The minimum is that we add their names as used in the automation: + +```yaml +blueprint: + name: Motion Light Tutorial + description: Turn a light on based on detected motion + domain: automation + input: + motion_sensor: + target_light: +``` + +## Use it via `configuration.yaml` + +With the bare minimum metadata added, your blueprint is ready to use. + +Open your `configuration.yaml` and add the following: + +```yaml +automation tutorial: + use_blueprint: + path: motion_light_tutorial.yaml + input: + motion_sensor: binary_sensor.kitchen + target_light: + entity_id: light.kitchen +``` + +Reload automations and your new automation should popup. Because we configured the exact values as the original automation, they should work exactly the same. + +## Adding user friendly names to the inputs + +Blueprints are easier to use if it's easy to see what each field is used for. We can improve this experience by adding names and descriptions to our inputs: + +```yaml +blueprint: + name: Motion Light Tutorial + domain: automation + input: + motion_sensor: + name: Motion Sensor + description: This sensor will be synchronized with the light. + target_light: + name: Lights + description: The lights to keep in sync. +``` + +## Describing the inputs + +Our blueprint doesn't currently describe what the inputs should contain. Without this information, Home Assistant will offer the user an empty text box. + +To instead allow Home Assistant to offer more assistance, we will use [selectors](/docs/blueprint/selectors/). Selectors describe a type and can be used to help the user pick a matching value. + +The selector for the motion sensor entity should describe that we want entities from the binary sensor domain that have the device class `motion`. + +The selector for the target light should describe that we want to target light entities. + +```yaml +blueprint: + name: Motion Light Tutorial + domain: automation + input: + motion_sensor: + name: Motion Sensor + description: This sensor will be synchronized with the light. + selector: + entity: + domain: binary_sensor + device_class: motion + target_light: + name: Lights + description: The lights to keep in sync. + selector: + target: + entity: + domain: light +``` + +By limiting our blueprint to working with lights and motion sensors, we unlock a couple of benefits: the UI will be able to limit suggested values to lights and motion sensors instead of all devices. It will also allow the user to pick an area to control the lights in. + +## The final blueprint + +After we have added all the steps, our blueprint will look like this: + +{% raw %} +```yaml +blueprint: + name: Motion Light Tutorial + description: Turn a light on based on detected motion + domain: automation + input: + motion_sensor: + name: Motion Sensor + description: This sensor will be synchronized with the light. + selector: + entity: + domain: binary_sensor + device_class: motion + target_light: + name: Lights + description: The lights to keep in sync. + selector: + target: + entity: + domain: light + +trigger: + platform: state + entity_id: !input motion_sensor + +action: + service: > + {% if trigger.to_state.state == "on" %} + light.turn_on + {% else %} + light.turn_off + {% endif %} + target: !input target_light +``` +{% endraw %} + +## Use it via the UI + +To configure it via the UI, go to **Configuration** and then **Blueprints**. Find the "Motion Light Tutorial" blueprint and click on "Create Automation". + +
+Don't forget to reload automations after you make changes to your blueprint to have the UI and the automation integration pick up the latest blueprint changes. +
+ +![Screenshot of the blueprint UI](/images/blueprints/tutorial-ui.png) + +## Share the love + +The final step is to share this blueprint with others. For this tutorial we're going to share it on GitHub Gists. + +- Go to [GitHub Gists](https://gist.github.com/) +- Gist description: blueprint tutorial +- Filename including extension: `motion_light_tutorial.yaml` +- Content is the content of the blueprint file. +- Click the "Create Gist" button + +You can now copy the URL of your new Gist and share it with other people. They can import it by going to **Configuration**, **Blueprints** and clicking on the blue "Import Blueprint" button. diff --git a/source/_includes/asides/docs_navigation.html b/source/_includes/asides/docs_navigation.html index 416eaae3fb8..f70674aa17c 100644 --- a/source/_includes/asides/docs_navigation.html +++ b/source/_includes/asides/docs_navigation.html @@ -100,6 +100,14 @@ +
  • + {% active_link /docs/blueprint/ Blueprints %} +
      +
    • {% active_link /docs/blueprint/tutorial/ Tutorial %}
    • +
    • {% active_link /docs/blueprint/schema/ Schema %}
    • +
    • {% active_link /docs/blueprint/selectors/ Selectors %}
    • +
    +
  • {% active_link /docs/frontend/ Frontend %}
      diff --git a/source/images/blueprints/tutorial-ui.png b/source/images/blueprints/tutorial-ui.png new file mode 100644 index 0000000000000000000000000000000000000000..1a84c5718f39cb640ff6d47ff53b0aa9130ad651 GIT binary patch literal 17498 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