diff --git a/_static/webserver-v1.js b/_static/webserver-v1.js index 1f66a04a9..f82011ae5 100644 --- a/_static/webserver-v1.js +++ b/_static/webserver-v1.js @@ -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 += '' + e.data.substr(7, e.data.length - 10) + "\n"; + log.innerHTML += '' + e.data.substr(7, e.data.length - 11) + "\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'+t.data.substr(7,t.data.length-10)+"\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+=''+t.data.substr(7,t.data.length-11)+"\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`): An automation to perform + when the lock is locked. See :ref:`lock-on_lock_unlock_trigger`. +- **on_unlock** (*Optional*, :ref:`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 `. + +.. _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 ` 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 `, 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: + + * diff --git a/components/lock/output.rst b/components/lock/output.rst new file mode 100644 index 000000000..282763eb7 --- /dev/null +++ b/components/lock/output.rst @@ -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 `. + +See Also +-------- + +- :doc:`/components/output/index` +- :apiref:`output/lock/output_lock.h` +- :ghedit:`Edit` diff --git a/components/lock/template.rst b/components/lock/template.rst new file mode 100644 index 000000000..da81965c1 --- /dev/null +++ b/components/lock/template.rst @@ -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 `): + Lambda to be evaluated repeatedly to get the current state of the lock. +- **lock_action** (*Optional*, :ref:`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 `): 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 `. + +.. _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 `): + 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` diff --git a/index.rst b/index.rst index 2a2848dd3..7d3ced8d9 100644 --- a/index.rst +++ b/index.rst @@ -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 ---------------