301 lines
12 KiB
ReStructuredText
301 lines
12 KiB
ReStructuredText
.. _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`
|