Add lock component documentation (#1867)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Keilin Bickar 2022-02-03 13:24:35 -05:00 committed by GitHub
parent 5934c83969
commit 7fd533fe17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 379 additions and 80 deletions

View File

@ -2,25 +2,40 @@ const source = new EventSource("/events");
source.addEventListener('log', function (e) {
const log = document.getElementById("log");
let log_prefs = [
["\u001b[1;31m", 'e'],
["\u001b[0;33m", 'w'],
["\u001b[0;32m", 'i'],
["\u001b[0;35m", 'c'],
["\u001b[0;36m", 'd'],
["\u001b[0;37m", 'v'],
];
let klass = '';
if (e.data.startsWith("")) {
klass = 'e';
} else if (e.data.startsWith("")) {
klass = 'w';
} else if (e.data.startsWith("")) {
klass = 'i';
} else if (e.data.startsWith("")) {
klass = 'c';
} else if (e.data.startsWith("")) {
klass = 'd';
} else if (e.data.startsWith("")) {
klass = 'v';
} else {
for (const log_pref of log_prefs){
if (e.data.startsWith(log_pref[0])) {
klass = log_pref[1];
}
}
if (klass == ''){
log.innerHTML += e.data + '\n';
}
log.innerHTML += '<span class="' + klass + '">' + e.data.substr(7, e.data.length - 10) + "</span>\n";
log.innerHTML += '<span class="' + klass + '">' + e.data.substr(7, e.data.length - 11) + "</span>\n";
});
actions = [
["switch", ["toggle"]],
["light", ["toggle"]],
["fan", ["toggle"]],
["cover", ["open", "close"]],
["button", ["press"]],
["lock", ["lock", "unlock", "open"]],
];
multi_actions = [
["select", "option"],
["number", "value"],
];
source.addEventListener('state', function (e) {
const data = JSON.parse(e.data);
document.getElementById(data.id).children[1].innerText = data.state;
@ -32,73 +47,27 @@ for (; row = states.rows[i]; i++) {
if (!row.children[2].children.length) {
continue;
}
if (row.classList.contains("switch")) {
(function(id) {
row.children[2].children[0].addEventListener('click', function () {
const xhr = new XMLHttpRequest();
xhr.open("POST", '/switch/' + id.substr(7) + '/toggle', true);
xhr.send();
});
})(row.id);
}
if (row.classList.contains("fan")) {
(function(id) {
row.children[2].children[0].addEventListener('click', function () {
const xhr = new XMLHttpRequest();
xhr.open("POST", '/fan/' + id.substr(4) + '/toggle', true);
xhr.send();
});
})(row.id);
}
if (row.classList.contains("light")) {
(function(id) {
row.children[2].children[0].addEventListener('click', function () {
const xhr = new XMLHttpRequest();
xhr.open("POST", '/light/' + id.substr(6) + '/toggle', true);
xhr.send();
});
})(row.id);
}
if (row.classList.contains("cover")) {
(function(id) {
row.children[2].children[0].addEventListener('click', function () {
const xhr = new XMLHttpRequest();
xhr.open("POST", '/cover/' + id.substr(6) + '/open', true);
xhr.send();
});
row.children[2].children[1].addEventListener('click', function () {
const xhr = new XMLHttpRequest();
xhr.open("POST", '/cover/' + id.substr(6) + '/close', true);
xhr.send();
});
})(row.id);
}
if (row.classList.contains("select")) {
(function(id) {
for (const domain of actions){
if (row.classList.contains(domain[0])) {
let id = row.id.substr(domain[0].length+1);
for (let j=0;j<row.children[2].children.length && j < domain[1].length; j++){
row.children[2].children[j].addEventListener('click', function () {
const xhr = new XMLHttpRequest();
xhr.open("POST", '/'+domain[0]+'/' + id + '/' + domain[1][j], true);
xhr.send();
});
}
}
}
for (const domain of multi_actions){
if (row.classList.contains(domain[0])) {
let id = row.id.substr(domain[0].length+1);
row.children[2].children[0].addEventListener('change', function () {
const xhr = new XMLHttpRequest();
xhr.open("POST", '/select/' + id.substr(7) + '/set?option=' + encodeURIComponent(this.value), true);
xhr.open("POST", '/'+domain[0]+'/' + id + '/set?'+domain[1]+'=' + encodeURIComponent(this.value), true);
xhr.send();
});
})(row.id);
}
if (row.classList.contains("number")) {
(function(id) {
row.children[2].children[0].addEventListener('change', function () {
const xhr = new XMLHttpRequest();
xhr.open("POST", '/number/' + id.substr(7) + '/set?value=' + encodeURIComponent(this.value), true);
xhr.send();
});
})(row.id);
}
if (row.classList.contains("button")) {
(function(id) {
row.children[2].children[0].addEventListener('click', function () {
const xhr = new XMLHttpRequest();
xhr.open("POST", '/button/' + id.substr(7) + '/press', true);
xhr.send();
});
})(row.id);
}
}
}
}

View File

@ -1 +1 @@
const source=new EventSource("/events");source.addEventListener("log",(function(t){const n=document.getElementById("log");let e="";t.data.startsWith("")?e="e":t.data.startsWith("")?e="w":t.data.startsWith("")?e="i":t.data.startsWith("")?e="c":t.data.startsWith("")?e="d":t.data.startsWith("")?e="v":n.innerHTML+=t.data+"\n",n.innerHTML+='<span class="'+e+'">'+t.data.substr(7,t.data.length-10)+"</span>\n"})),source.addEventListener("state",(function(t){const n=JSON.parse(t.data);document.getElementById(n.id).children[1].innerText=n.state}));const states=document.getElementById("states");let row,i=0;for(;row=states.rows[i];i++)row.children[2].children.length&&(row.classList.contains("switch")&&function(t){row.children[2].children[0].addEventListener("click",(function(){const n=new XMLHttpRequest;n.open("POST","/switch/"+t.substr(7)+"/toggle",!0),n.send()}))}(row.id),row.classList.contains("fan")&&function(t){row.children[2].children[0].addEventListener("click",(function(){const n=new XMLHttpRequest;n.open("POST","/fan/"+t.substr(4)+"/toggle",!0),n.send()}))}(row.id),row.classList.contains("light")&&function(t){row.children[2].children[0].addEventListener("click",(function(){const n=new XMLHttpRequest;n.open("POST","/light/"+t.substr(6)+"/toggle",!0),n.send()}))}(row.id),row.classList.contains("cover")&&function(t){row.children[2].children[0].addEventListener("click",(function(){const n=new XMLHttpRequest;n.open("POST","/cover/"+t.substr(6)+"/open",!0),n.send()})),row.children[2].children[1].addEventListener("click",(function(){const n=new XMLHttpRequest;n.open("POST","/cover/"+t.substr(6)+"/close",!0),n.send()}))}(row.id),row.classList.contains("select")&&function(t){row.children[2].children[0].addEventListener("change",(function(){const n=new XMLHttpRequest;n.open("POST","/select/"+t.substr(7)+"/set?option="+encodeURIComponent(this.value),!0),n.send()}))}(row.id),row.classList.contains("number")&&function(t){row.children[2].children[0].addEventListener("change",(function(){const n=new XMLHttpRequest;n.open("POST","/number/"+t.substr(7)+"/set?value="+encodeURIComponent(this.value),!0),n.send()}))}(row.id),row.classList.contains("button")&&function(t){row.children[2].children[0].addEventListener("click",(function(){const n=new XMLHttpRequest;n.open("POST","/button/"+t.substr(7)+"/press",!0),n.send()}))}(row.id));
const source=new EventSource("/events");source.addEventListener("log",function(t){const e=document.getElementById("log");let n=[["","e"],["","w"],["","i"],["","c"],["","d"],["","v"]],o="";for(const e of n)t.data.startsWith(e[0])&&(o=e[1]);""==o&&(e.innerHTML+=t.data+"\n"),e.innerHTML+='<span class="'+o+'">'+t.data.substr(7,t.data.length-11)+"</span>\n"}),actions=[["switch",["toggle"]],["light",["toggle"]],["fan",["toggle"]],["cover",["open","close"]],["button",["press"]],["lock",["lock","unlock","open"]]],multi_actions=[["select","option"],["number","value"]],source.addEventListener("state",function(t){const e=JSON.parse(t.data);document.getElementById(e.id).children[1].innerText=e.state});const states=document.getElementById("states");let row,i=0;for(;row=states.rows[i];i++)if(row.children[2].children.length){for(const t of actions)if(row.classList.contains(t[0])){let e=row.id.substr(t[0].length+1);for(let n=0;n<row.children[2].children.length&&n<t[1].length;n++)row.children[2].children[n].addEventListener("click",function(){const o=new XMLHttpRequest;o.open("POST","/"+t[0]+"/"+e+"/"+t[1][n],!0),o.send()})}for(const t of multi_actions)if(row.classList.contains(t[0])){let e=row.id.substr(t[0].length+1);row.children[2].children[0].addEventListener("change",function(){const n=new XMLHttpRequest;n.open("POST","/"+t[0]+"/"+e+"/set?"+t[1]+"="+encodeURIComponent(this.value),!0),n.send()})}}

View File

@ -19,4 +19,5 @@ Components
text_sensor/index
stepper/index
touchscreen/index
lock/index
*

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

166
components/lock/index.rst Normal file
View File

@ -0,0 +1,166 @@
Lock Component
================
.. seo::
:description: Instructions for setting up generic locks in ESPHome.
:image: folder-open.svg
The ``lock`` domain includes all platforms that should function like a lock
with lock/unlock actions.
.. note::
ESPHome lock components requires Home Assistant 2022.3 or newer
.. _config-lock:
Base Lock Configuration
-------------------------
.. code-block:: yaml
lock:
- platform: ...
name: "Lock Name"
Configuration variables:
- **name** (**Required**, string): The name of the lock.
- **icon** (*Optional*, icon): Manually set the icon to use for the
lock in the frontend.
- **internal** (*Optional*, boolean): Mark this component as internal. Internal components will
not be exposed to the frontend (like Home Assistant). Only specifying an ``id`` without
a ``name`` will implicitly set this to true.
- **on_lock** (*Optional*, :ref:`Action <config-action>`): An automation to perform
when the lock is locked. See :ref:`lock-on_lock_unlock_trigger`.
- **on_unlock** (*Optional*, :ref:`Action <config-action>`): An automation to perform
when the lock is unlocked. See :ref:`lock-on_lock_unlock_trigger`..
- **disabled_by_default** (*Optional*, boolean): If true, then this entity should not be added to any client's frontend,
(usually Home Assistant) without the user manually enabling it (via the Home Assistant UI).
Defaults to ``false``.
- **entity_category** (*Optional*, string): The category of the entity.
See https://developers.home-assistant.io/docs/core/entity/#generic-properties
for a list of available options. Set to ``""`` to remove the default entity category.
- If MQTT enabled, All other options from :ref:`MQTT Component <config-mqtt-component>`.
.. _lock-lock_action:
``lock.lock`` Action
*************************
This action locks a lock with the given ID on when executed.
.. code-block:: yaml
on_...:
then:
- lock.lock: deadbolt_1
.. _lock-unlock_action:
``lock.unlock`` Action
**************************
This action unlocks a lock with the given ID off when executed.
.. code-block:: yaml
on_...:
then:
- lock.unlock: deadbolt_1
.. _lock-open_action:
``lock.open`` Action
************************
This action opens (e.g. unlatch) a lock with the given ID off when executed.
.. code-block:: yaml
on_...:
then:
- lock.open: doorlock_1
.. _lock-is_locked_condition:
.. _lock-is_unlocked_condition:
``lock.is_locked`` / ``lock.is_unlocked`` Condition
***************************************************
This :ref:`Condition <config-condition>` checks if the given lock is LOCKED (or UNLOCKED).
.. code-block:: yaml
# In some trigger:
on_...:
if:
condition:
# Same syntax for is_unlocked
lock.is_locked: my_lock
.. _lock-lambda_calls:
lambda calls
************
From :ref:`lambdas <config-lambda>`, you can call several methods on all locks to do some
advanced stuff (see the full API Reference for more info).
- ``publish_state()``: Manually cause the lock to publish a new state and store it internally.
If it's different from the last internal state, it's additionally published to the frontend.
.. code-block:: yaml
// Within lambda, make the lock report a specific state
id(my_lock).publish_state(LOCK_STATE_LOCKED);
id(my_lock).publish_state(LOCK_STATE_UNLOCKED);
- ``state``: Retrieve the current state of the lock.
.. code-block:: yaml
// Within lambda, get the lock state and conditionally do something
if (id(my_lock).state == LOCK_STATE_LOCKED) {
// Lock is LOCKED, do something here
}
- ``unlock()``/``lock()``/``open()``: Manually lock/unlock/open a lock from code.
Similar to the ``lock.lock``, ``lock.unlock``, and ``lock.open`` actions,
but can be used in complex lambda expressions.
.. code-block:: yaml
id(my_lock).unlock();
id(my_lock).lock();
id(my_lock).open();
.. _lock-on_lock_unlock_trigger:
``lock.on_lock`` / ``lock.on_unlock`` Trigger
****************************************************************
This trigger is activated each time the lock is locked/unlocked. It becomes active
right after the lock component has acknowledged the state (e.g. after it LOCKED/UNLOCKED itself).
.. code-block:: yaml
lock:
- platform: template # or any other platform
# ...
on_lock:
- logger.log: "Door Locked!"
on_unlock:
- logger.log: "Door Unlocked!"
See Also
--------
- :apiref:`lock/lock.h`
- :ghedit:`Edit`
.. toctree::
:maxdepth: 1
:glob:
*

View File

@ -0,0 +1,39 @@
Generic Output Lock
=====================
.. seo::
:description: Instructions for setting up generic output locks in ESPHome that control an output component.
:image: upload.svg
The ``output`` lock platform allows you to use any output component as a lock.
.. figure:: images/output-ui.png
:align: center
:width: 80.0%
.. code-block:: yaml
# Example configuration entry
output:
- platform: gpio
pin: 25
id: 'generic_out'
lock:
- platform: output
name: "Generic Output"
output: 'generic_out'
Configuration variables:
------------------------
- **output** (**Required**, :ref:`config-id`): The ID of the output component to use.
- **name** (**Required**, string): The name for the lock.
- **id** (*Optional*, :ref:`config-id`): Manually specify the ID used for code generation.
- All other options from :ref:`Lock <config-lock>`.
See Also
--------
- :doc:`/components/output/index`
- :apiref:`output/lock/output_lock.h`
- :ghedit:`Edit`

View File

@ -0,0 +1,115 @@
Template Lock
===============
.. seo::
:description: Instructions for setting up template locks that can execute arbitrary actions when locked, unlocked, or opened
:image: description.svg
The ``template`` lock platform allows you to create simple locks out of just actions and
an optional value lambda. Once defined, it will automatically appear in Home Assistant
as a lock and can be controlled through the frontend.
.. code-block:: yaml
# Example configuration entry
lock:
- platform: template
name: "Template Lock"
lambda: |-
if (id(some_binary_sensor).state) {
return LOCK_STATE_LOCKED;
} else {
return LOCK_STATE_UNLOCKED;
}
lock_action:
- switch.turn_on: switch1
unlock_action:
- switch.turn_off: switch1
open_action:
- button.press: button1
Possible return values for the optional lambda:
- ``return LOCK_STATE_LOCKED;`` if the lock should be reported as LOCKED.
- ``return LOCK_STATE_UNLOCKED;`` if the lock should be reported as UNLOCKED.
- ``return LOCK_STATE_JAMMED;`` if the lock should be reported as JAMMED.
- ``return LOCK_STATE_LOCKING;`` if the lock should be reported as LOCKING.
- ``return LOCK_STATE_UNLOCKING;`` if the lock should be reported as UNLOCKING.
- ``return {};`` if the last state should be repeated.
.. note::
Only ``LOCK_STATE_LOCKED`` and ``LOCK_STATE_UNLOCKED`` are supported by the MQTT component in Home Assistant
Configuration variables:
------------------------
- **name** (**Required**, string): The name of the lock.
- **lambda** (*Optional*, :ref:`lambda <config-lambda>`):
Lambda to be evaluated repeatedly to get the current state of the lock.
- **lock_action** (*Optional*, :ref:`Action <config-action>`): The action that should
be performed when the remote (like Home Assistant's frontend) requests the lock to be locked.
- **unlock_action** (*Optional*, :ref:`Action <config-action>`): The action that should
be performed when the remote (like Home Assistant's frontend) requests the lock to be unlocked.
- **restore_state** (*Optional*, boolean): Sets whether ESPHome should attempt to restore the
state on boot-up and call the lock/unlock actions with the recovered values. Defaults to ``no``.
- **optimistic** (*Optional*, boolean): Whether to operate in optimistic mode - when in this mode,
any command sent to the template lock will immediately update the reported state.
Defaults to ``false``.
- **assumed_state** (*Optional*, boolean): Whether the true state of the lock is not known.
This will make the Home Assistant frontend show buttons for both LOCK and UNLOCK actions, instead
of hiding one of them when the lock is LOCKED/UNLOCKED. Defaults to ``false``.
- **id** (*Optional*, :ref:`config-id`): Manually specify the ID used for code generation.
- All other options from :ref:`Lock <config-lock>`.
.. _lock-template-publish_action:
``lock.template.publish`` Action
----------------------------------
You can also publish a state to a template lock from elsewhere in your YAML file
with the ``lock.template.publish`` action.
.. code-block:: yaml
# Example configuration entry
lock:
- platform: template
name: "Template Lock"
id: template_lock1
# in some trigger
on_...:
- lock.template.publish:
id: template_lock1
state: LOCK_STATE_LOCKED
# Templated
- lock.template.publish:
id: template_lock1
state: !lambda 'return LOCK_STATE_LOCKED;'
Configuration options:
- **id** (**Required**, :ref:`config-id`): The ID of the template lock.
- **state** (**Required**, boolean, :ref:`templatable <config-templatable>`):
The state to publish.
.. note::
This action can also be written in lambdas, the parameter of the `publish_state` method denotes the state the
lock should become:
.. code-block:: cpp
id(template_lock1).publish_state(lock::LOCK_STATE_LOCKED);
See Also
--------
- :doc:`/guides/automations`
- :doc:`/components/lock/index`
- :doc:`/components/binary_sensor/index`
- :apiref:`template/lock/template_lock.h`
- :ghedit:`Edit`

View File

@ -598,6 +598,15 @@ Select Components
Select Core, components/select/index, folder-open.svg
Template Select, components/select/template, description.svg
Lock Components
-----------------
.. imgtable::
Lock Core, components/lock/index, folder-open.svg
Generic Output Lock, components/lock/output, upload.svg
Template Lock, components/lock/template, description.svg
Misc Components
---------------