Document UDP component (#3918)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: H. Árkosi Róbert <robreg@zsurob.hu>
This commit is contained in:
Clyde Stubbs 2024-08-30 19:00:00 +10:00 committed by GitHub
parent f1b0f2e58f
commit 31f188f262
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 400 additions and 1 deletions

View File

@ -0,0 +1,49 @@
UDP Binary Sensor
=================
.. seo::
:description: Instructions for setting up a UDP binary sensor.
:image: udp.svg
The ``udp`` binary sensor platform allows you to receive binary sensor data directly from another ESPHome node.
.. code-block:: yaml
# Example configuration entry
binary_sensor:
- platform: udp
id: switch_status
provider: light-switch
remote_id: light_switch
Configuration variables
-----------------------
- **id** (*Optional*, :ref:`config-id`): Manually specify the ID used for code generation.
- **provider** (**Required**, string): The name of the provider node.
- **remote_id** (*Optional*, :ref:`config-id`): The ID of the original binary sensor in the provider device. If not specified defaults to the ID configured with ``id:``.
- **name** (*Optional*, string): The name of the binary sensor.
- **internal** (*Optional*, boolean): Whether the sensor should be exposed via API (e.g. to Home Assistant.) Defaults to ``true`` if name is not set, required if name is provided.
- All other options from :ref:`Binary Sensor <config-binary_sensor>`.
At least one of ``id`` and ``remote_id`` must be configured.
Publishing to Home Assistant
----------------------------
Typically this type of binary sensor would be used for internal automation purposes rather than having it published back to
Home Assistant, since it would be a duplicate of the original sensor.
If it *is* desired to expose the binary sensor to Home Assistant, then the ``internal:`` configuration setting needs to be explicitly
set to ``false`` and a name provided.
Only the state (i.e. binary value) of the remote sensor is received by the consumer, so any other attributes must be explicitly
configured.
See Also
--------
- :doc:`/components/udp`
- :doc:`/components/sensor/index`
- :ref:`automation`
- :apiref:`udp/udp_component.h`
- :ghedit:`Edit`

49
components/sensor/udp.rst Normal file
View File

@ -0,0 +1,49 @@
UDP Sensor
==========
.. seo::
:description: Instructions for setting up a UDP sensor.
:image: udp.svg
The ``udp`` sensor platform allows you to receive numeric sensor data directly from another ESPHome node.
.. code-block:: yaml
# Example configuration entry
sensor:
- platform: udp
id: temperature_id
provider: thermometer
remote_id: temp_id
Configuration variables
-----------------------
- **id** (*Optional*, :ref:`config-id`): Manually specify the ID used for code generation.
- **provider** (**Required**, string): The name of the provider node.
- **remote_id** (*Optional*, :ref:`config-id`): The ID of the original sensor in the provider node. If not specified defaults to the ID configured with ``id:``.
- **name** (*Optional*, string): The name of the sensor.
- **internal** (*Optional*, boolean): Whether the sensor should be exposed via API (e.g. to Home Assistant.) Defaults to ``true`` if name is not set, required if name is provided.
- All other options from :ref:`Sensor <config-sensor>`.
At least one of ``id`` and ``remote_id`` must be configured.
Publishing to Home Assistant
----------------------------
Typically this type of sensor would be used for internal automation purposes rather than having it published back to
Home Assistant, since it would be a duplicate of the original sensor.
If it *is* desired to expose the sensor to Home Assistant, then the ``internal:`` configuration setting needs to be explicitly
set to ``false`` and a name provided.
Only the state (i.e. numeric value) of the remote sensor is received by the consumer, so any other attributes must be explicitly
configured.
See Also
--------
- :doc:`/components/udp`
- :doc:`/components/binary_sensor/index`
- :ref:`automation`
- :apiref:`udp/udp_component.h`
- :ghedit:`Edit`

300
components/udp.rst Normal file
View File

@ -0,0 +1,300 @@
.. _udp:
UDP Component
=============
.. seo::
:description: Instructions for setting up a UDP component on ESPHome
:image: udp.svg
:keywords: UDP
The purpose of this component is to allow ESPHome nodes to directly communicate with each over an IP network.
It permits the state of sensors and binary sensors to be broadcast via UDP packets
to other nodes on the same LAN, or to specific IP addresses (which may be in remote, but reachable networks).
Nodes may be *providers* which broadcast sensor data, or *consumers* which receive sensor data from one or more
providers. A node may be both a provider and a consumer. Optional security is provided by one or more of:
- encryption using a shared secret key
- a rolling code
- a challenge-response (ping-pong) key
.. code-block:: yaml
# Example configuration entry
udp:
update_interval: 5s
encryption: "REPLACEME"
rolling_code_enable: true
binary_sensors:
- binary_sensor_id1
sensors:
- sensor_id1
- id: sensor_id2
broadcast_id: different_id
providers:
- name: some-device-name
encryption: "REPLACEME with some key"
sensor:
- platform: udp
provider: some-device-name
id: local_sensor_id
remote_id: some_sensor_id
binary_sensor:
- platform: udp
provider: unencrypted-device
id: other_binary_sensor_id # also used as remote_id
Configuration variables:
------------------------
- **id** (*Optional*, :ref:`config-id`): Manually specify the ID used for code generation.
- **update_interval** (*Optional*, :ref:`config-time`): Interval between full broadcasts. Defaults to 15s.
- **port** (*Optional*, int): The destination UDP port number to use. Defaults to ``18511``.
- **addresses** (*Optional*, list of IPv4 addresses): One or more IP addresses to broadcast data to. Defaults to ``255.255.255.255``
which is the local network broadcast address.
- **sensors** (*Optional*, list): A list of sensor IDs to be broadcast. Each entry may be just the sensor id, or may set a different id to be broadcast.
- **id** (**Required**, :ref:`config-id`): The id of the sensor to be used
- **broadcast_id** (*Optional*, string): The id to be used for this sensor in the broadcast. Defaults to the same as the internal id.
- **binary_sensors** (*Optional*, list): A list of binary sensor IDs to be broadcast.
- **id** (**Required**, :ref:`config-id`): The id of the binary sensor to be used
- **broadcast_id** (*Optional*, string): The id to be used for this binary sensor in the broadcast. Defaults to the same as the internal id.
- **encryption** (*Optional*, string): The encryption key to use when broadcasting. Default is no encryption. This may be
any string, and will be hashed to form a 256 bit key.
- **rolling_code_enable** (*Optional*, boolean): Enables a rolling code to be included in all broadcasts. Requires ``encryption`` to be set. Defaults to ``false``. Can be set only on the provider side.
- **ping_pong_enable** (*Optional*, boolean): When set, requires encrypted providers to include a *nonce* generated by this device in broadcasts. Defaults to ``false``. Can be set only on the consumer side.
- **ping_pong_recycle_time** (*Optional*, :ref:`config-time`): Controls how often the ping-pong key is regenerated. Requires ``ping_pong_enable`` to be set. Defaults to 10 minutes. Can be set only on the consumer side.
- **providers** (*Optional*, list): A list of provider device names and optionally their secret encryption keys.
- **name** (**Required**, string): The device name of the provider.
- **encryption** (*Optional*, string): The provider's encryption key.
Wherever a provider name is required, this should be the node name configured in the ``esphome:`` block.
This component supports multiple configurations, making it possible to differentiate between consumers when providing data to them.
When receiving data in such a configuration, sensors need an ``udp_id`` configuration item to know where to expect data to come from.
Reliability
-----------
UDP, like any other network protocol, does not provide a guarantee that data will be delivered, but unlike TCP it does not
even provide any indication whether data has been successfully delivered or not. When any of the configured sensors changes state,
the component will broadcast that sensor's state, but since this may not be delivered to a consumer, the UDP component
also broadcasts *all* sensor data on a timed schedule, set by ``update_interval``. Even this does not guarantee
delivery, but in practice unless the network has failed, updates will eventually be delivered, albeit possibly after
some delay.
Security
--------
By default there is no security - all data is transmitted in clear text on the network. This would be appropriate
for non-sensitive sensor data or perhaps on a fully secured wired network. For other cases the data can be encrypted
by providing an encryption key, which is shared between the provider and consumer.
Encryption alone ensures that data cannot be read in transit and protects against spoofing of data, but does not protect
against replay attacks (where a threat actor records a transmission and replays it later, e.g. to repeat an action.)
A rolling code can be enabled which mitigates replay attacks - each transmission contains a 64 bit value which is
guaranteed to monotonically increase, so the consumer will reject any data which contains a rolling code
already seen. The rolling code also ensures that the data in every packet is different, which makes brute-force
attacks on the encryption much more difficult. This is enabled in the provider configuration and adds minor overhead.
.. note::
The rolling code's upper 32 bit field is incremented and written to flash *once* at reboot on the provider node.
It's also incremented and written to flash when the lower 32 bit field overflows, which can only happen after
a very long time. The consumer side does not store the d rolling codes in flash.
For further protection a ``ping-pong`` (or challenge-response) facility is available, which can be enabled in the
consumer configuration. The consumer periodically generates a 32 bit random number (a *nonce* aka "Number used Once")
and broadcasts it as a *ping*. Any provider receiving this nonce will include it in any future encrypted broadcasts as
*pong*. The consumer expects to get back its most recently transmitted *ping* in any packets it receives, and will reject
any that do not contain it.
Use of the ping-pong feature will add to network traffic and the size of the transmitted packets (a single packet may
include up to 4 nonces from different devices) but provides a high level of protection against replay attacks. It does
require a 2-way network connection, and it only works on local networks because the consumer can only *broadcast* the
nonce to the providers.
.. note::
Occasionally a ``Ping key not seen`` warning message may appear in the device log. This is expected, because it may
happen that while the consumer has regenerated the *ping* key, it subsequently received a *pong* with the previous key,
most likely because the messages crossed in transit. In such a case, the message will be rejected, but the next message
will contain the correct *pong*.
Because of this, ``ping-pong`` is only recommended to be used for state transmissions, which are updated periodically
at ``update_interval``.
**Security considerations**
The encryption used is `XXTEA <https://en.wikipedia.org/wiki/XXTEA>`_ which is fast and compact. Although XXTEA is known
to be susceptible to a chosen-plaintext attack, such an attack is not possible with this application, and it otherwise
has no published weaknesses [#f1]_. The implementation used here has been modified slightly to use a 256 bit key which
will strengthen security compared to the original 128 bit key.
When encryption is used, all data is encrypted except the sender node name, and the initial request for a ping-pong key.
Broadcasting names does not compromise security, since this information would already be available via mDNS.
Requesting a key in clear text does not reduce the security of the key, since it is the ability to encrypt this key
with the shared secret key that provides the security assurance.
This does mean however that there is a possible Denial of Service attack by a malicious node overwriting a valid
ping-pong key, which will result in packets being rejected by the legitimate consumer.
Configuration examples
----------------------
This example couples two light switches in two different devices, so that switching either one on or off will cause
the other to follow suit. In each case a template binary_sensor is used to mirror the switch state.
.. code-block:: yaml
# Device 1
esphome:
name: device-1
udp:
binary_sensors:
- relay1_sensor
switch:
- platform: gpio
pin: GPIO6
id: relay1
name: "Device 1 switch"
binary_sensor:
- platform: template
id: relay1_sensor
lambda: "return id(relay1).state;"
- platform: udp
provider: device-2
id: relay2_sensor
on_press:
switch.turn_on: relay1
on_release:
switch.turn_off: relay1
# Device 2
esphome:
name: device-2
udp:
binary_sensors:
- relay2_sensor
switch:
- platform: gpio
pin: GPIO6
id: relay2
name: "Device 2 switch"
binary_sensor:
- platform: template
id: relay2_sensor
lambda: "return id(relay2).state;"
- platform: udp
provider: device-1
id: relay1_sensor
on_press:
switch.turn_on: relay2
on_release:
switch.turn_off: relay2
The following example shows a device using encryption to read a sensor and two binary sensors from two different
devices, one with encryption and ping-pong and one without. It also rebroadcasts one of those binary sensors with its own
encryption and a rolling code to a remote host.
.. code-block:: yaml
udp:
update_interval: 60s
addresses: ["10.87.135.110"]
ping_pong_enable: true
rolling_code_enable: true
encryption: "Muddy Waters"
binary_sensors:
- tick_tock
providers:
- name: st7735s
encryption: "Blind Willie Johnson"
# - name: room-lights # Not required here since no encryption
binary_sensor:
- platform: udp
provider: st7735s
id: tick_tock
- platform: udp
provider: room-lights
id: relay1_sensor
sensor:
- platform: udp
provider: st7735s
id: wifi_signal_sensor
The example below shows a provider device separating data sent to different consumers. There are two provider confgurations, with different IDs.
The ``udp_internal`` provider broadcasts the selected sensor states in plain every 10 seconds to all the network members, while the ``udp_external``
provider sends other sensors data to an external IP address and port, with encryption. The node also listens to data from a ``remote-node`` through
the port specified in the ``udp_external`` configuration:
.. code-block:: yaml
udp:
- id: udp_internal
update_interval: 10s
sensors:
- temp_outdoor
- temp_rooma
- temp_roomb
- temp_roomc
- temp_garage
- temp_water
- humi_rooma
- humi_roomb
- humi_roomc
- id: udp_external
update_interval: 60s
encryption: "Muddy Waters"
ping_pong_enable: true
rolling_code_enable: true
port: 38512
addresses:
- 10.87.135.110
binary_sensors:
- binary_sensor_door
sensors:
- temp_outdoor
binary_sensor:
- platform: udp
id: binary_sensor_unlock
udp_id: udp_external
provider: remote-node
remote_id: binary_sensor_unlock_me
on_press:
- lambda: |-
ESP_LOGI("main", "d command to binary_sensor_unlock");
.. [#f1] As known in 2024.06.
See Also
--------
- :doc:`/components/binary_sensor/udp`
- :doc:`/components/sensor/udp`
- :ref:`automation`
- :apiref:`udp/udp_component.h`
- :ghedit:`Edit`

1
images/udp.svg Normal file
View File

@ -0,0 +1 @@
<svg viewBox="0 0 69 25" id="svg5" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><defs id="defs9"/><path d="M5 0H64a5 5 0 015 5v15a5 5 0 01-5 5H5a5 5 0 01-5-5V5a5 5 0 015-5z" style="fill:#000" id="path2"/><g aria-label="UDP" id="component-text" style="font-weight:900;font-size:25px;font-family:Montserrat;letter-spacing:1.1px;fill:#fffffc"><path d="m14.8 21.4q-4.025.0-6.275-2.175t-2.25-6.1V3.5h5.9v9.45q0 2 .725 2.85.725.825 1.95.825 1.25.0 1.95-.825.725-.85.725-2.85V3.5h5.8v9.625q0 3.925-2.25 6.1Q18.825 21.4 14.8 21.4z" id="path11"/><path d="M27.12501 21V3.5h8.625q2.925.0 5.15 1.075 2.225 1.05 3.475 3t1.25 4.65q0 2.725-1.25 4.7-1.25 1.95-3.475 3.025-2.225 1.05-5.15 1.05zm5.9-4.6h2.475q1.25.0 2.175-.475.95-.475 1.475-1.4.525-.95.525-2.3.0-1.325-.525-2.25t-1.475-1.4q-.925-.475-2.175-.475h-2.475z" id="path13"/><path d="M48.875016 21V3.5h8.425q2.45.0 4.225.8 1.8.8 2.775 2.3.975 1.475.975 3.5t-.975 3.5q-.975 1.475-2.775 2.3-1.775.8-4.225.8h-5.15l2.625-2.525V21zm5.9-6.175-2.625-2.675h4.775q1.225.0 1.8-.55.6-.55.6-1.5t-.6-1.5q-.575-.55-1.8-.55h-4.775l2.625-2.675z" id="path15"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -204,6 +204,7 @@ Network Protocols
HTTP Request, components/http_request, connection.svg, dark-invert
mDNS, components/mdns, radio-tower.svg, dark-invert
WireGuard, components/wireguard, wireguard_custom_logo.svg
UDP, components/udp, udp.svg
Bluetooth/BLE
-------------
@ -1156,7 +1157,6 @@ Cookbook
Sonoff Fishpond Pump, cookbook/sonoff-fishpond-pump, cookbook-sonoff-fishpond-pump.jpg
Arduino Port Extender, cookbook/arduino_port_extender, arduino_logo.svg
EHMTX a matrix status/text display, cookbook/ehmtx, ehmtx.jpg
Share data directly between ESPHome nodes, cookbook/http_request_sensor, connection.svg, dark-invert
Do you have other awesome automations or cool setups? Please feel free to add them to the
documentation for others to copy. See :doc:`Contributing </guides/contributing>`.