跳转到内容

数据包传输组件

此组件的目的是允许 ESPHome 节点通过通信通道直接相互通信。它允许从一个节点向另一个节点传输传感器和二进制传感器的状态,无需中央服务器或代理。实际的传输通道由另一个组件提供。目前支持的传输方式有 EspnowSx126XSx127XUartUdp

节点可以是传输或广播传感器数据的 提供者,也可以是从一个或多个提供者接收传感器数据的 消费者。一个节点可以既是提供者又是消费者。可选的安全保护通过以下一种或多种方式提供:

  • 使用共享密钥加密
  • 滚动码
  • 挑战-响应(乒乓)密钥
# 示例配置
packet_transport:
platform: ...
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: device1-name
encryption: "REPLACEME"
sensor:
- platform: packet_transport
provider: device1-name
id: local_sensor_id
remote_id: some_sensor_id
binary_sensor:
- platform: packet_transport
provider: device2-name
type: data # 可选,默认为 'data'
id: other_binary_sensor_id # 也用作 remote_id
- platform: packet_transport
provider: device1-name
type: status
name: 设备 1 连接状态
  • id (可选, ID):手动指定用于代码生成的 ID。

  • update_interval (可选, 时间):完整广播之间的间隔。默认为 15s。

  • sensors (可选, 列表):要广播的传感器 ID 列表。每项可以只是传感器 id,也可以设置不同的 id 进行广播。

    • id (*必需, ID):要使用的传感器 id
    • broadcast_id (可选, 字符串):在广播中用于此传感器的 id。默认与内部 id 相同。
  • binary_sensors (可选, 列表):要广播的二进制传感器 ID 列表。

    • id (*必需, ID):要使用的二进制传感器 id
    • broadcast_id (可选, 字符串):在广播中用于此二进制传感器的 id。默认与内部 id 相同。
  • encryption (可选, 字符串):广播时使用的加密密钥。默认为无加密。这可以是任何字符串,并将被哈希为 256 位密钥。

  • rolling_code_enable (可选, 布尔值):启用在所有广播中包含滚动码。需要设置 encryption。默认为 false。只能在提供者端设置。

  • ping_pong_enable (可选, 布尔值):设置后,要求加密的提供者在广播中包含由此设备生成的 nonce。默认为 false。只能在消费者端设置。

  • ping_pong_recycle_time (可选, 时间):控制乒乓密钥重新生成的频率。需要设置 ping_pong_enable。默认为 10 分钟。只能在消费者端设置。

  • providers (可选, 列表):提供者设备名称及其可选的加密密钥列表。

    • name (*必需, 字符串):提供者的设备名称。
    • encryption (可选, 字符串):提供者的加密密钥。

无论何处需要提供者名称,都应该是 esphome: 块中配置的节点名称。

此组件支持多个配置,使得在向消费者提供数据时可以区分消费者,或者如果有多个提供者时可以区分提供者。在此类配置中接收数据时,传感器需要一个 transport_id 配置项来知道预期数据来自哪里。

传输的可靠性取决于底层传输方式。

默认情况下没有安全性 - 所有数据以明文形式在网络中传输。这对于非敏感传感器数据或在完全安全的有线网络上可能是合适的。对于其他情况,可以通过提供在提供者和消费者之间共享的加密密钥来加密数据。

仅加密确保数据在传输中无法被读取,并防止数据欺骗,但不能防止重放攻击(攻击者记录传输并在稍后重播,例如重复某个动作。)

可以启用滚动码来减轻重放攻击 - 每次传输包含一个保证单调递增的 64 位值,因此消费者将拒绝任何包含已见过的滚动码的数据。滚动码还确保每个数据包中的数据都不同,这使得对加密的暴力攻击变得更加困难。这在提供者配置中启用,会增加少量开销。

NOTE

滚动码的高 32 位字段在提供者节点重启时递增并写入 flash 一次。 当低 32 位字段溢出时也会递增并写入 flash,这只能在很长时间后发生。 消费者端不会将滚动码存储在 flash 中。

为了进一步保护,可以使用 ping-pong(或挑战-响应)功能,可以在消费者配置中启用。消费者定期生成一个 32 位随机数(一个 nonce,即”使用一次的数字”)并将其作为 ping 广播。任何接收此 nonce 的提供者将在未来的任何加密广播中包含它作为 pong。消费者期望在其接收的任何数据包中收到最近传输的 ping,并将拒绝任何不包含它的数据包。

使用乒乓功能会增加网络流量和传输数据包的大小(单个数据包可能包含来自不同设备的最多 4 个 nonce),但提供了高度的重放攻击保护。它确实需要双向网络连接,并且仅在本地网络上有效,因为消费者只能向提供者 广播 nonce。

此外,使用乒乓时,可以创建连接状态二进制传感器。当消费者在最后 ping_pong_recycle_time 内收到来自提供者的 pong 时,状态传感器将报告 connected。如果未收到,将报告 disconnected。这可用于检测提供者何时不再可用,或加密密钥何时已更改。

NOTE

偶尔可能会在设备日志中出现 Ping key not seen 警告消息。这是预期的,因为可能发生的情况是,当消费者重新生成 ping 密钥后,它随后收到了包含先前密钥的 pong,最可能是因为消息在传输中交叉。在这种情况下,消息将被拒绝,但下一条消息将包含正确的 pong

因此,ping-pong 仅建议用于状态传输,这些状态按 update_interval 定期更新。

使用的加密是 XXTEA,它快速且紧凑。虽然已知 XXTEA 容易受到选择明文攻击,但这种攻击在本文应用中是不可能的,除此之外它没有已公布的弱点 1。此处使用的实现稍有修改,使用 256 位密钥,与原始 128 位密钥相比将增强安全性。

使用加密时,所有数据都被加密,除了发送者节点名称和初始的乒乓密钥请求。广播名称不会损害安全性,因为这些信息已经可以通过 mDNS 获得以明文形式请求密钥不会降低密钥的安全性,因为使用共享密钥加密此密钥的能力提供了安全保证。

然而,这意味着恶意节点覆盖有效的乒乓密钥可能导致拒绝服务攻击,这将导致数据包被合法消费者拒绝。

此示例将两个不同设备中的两个电灯开关耦合,以便切换任一个开关将导致另一个跟随切换。在每种情况下,使用模板 binary_sensor 来镜像开关状态。

# 设备 1
esphome:
name: device-1
packet_transport:
binary_sensors:
- relay1_sensor
switch:
- platform: gpio
pin: GPIO6
id: relay1
name: "设备 1 开关"
binary_sensor:
- platform: template
id: relay1_sensor
lambda: "return id(relay1).state;"
- platform: packet_transport
provider: device-2
id: relay2_sensor
on_press:
switch.turn_on: relay1
on_release:
switch.turn_off: relay1
# 设备 2
esphome:
name: device-2
packet_transport:
binary_sensors:
- relay2_sensor
switch:
- platform: gpio
pin: GPIO6
id: relay2
name: "设备 2 开关"
binary_sensor:
- platform: template
id: relay2_sensor
lambda: "return id(relay2).state;"
- platform: packet_transport
provider: device-1
id: relay1_sensor
on_press:
switch.turn_on: relay2
on_release:
switch.turn_off: relay2

以下示例显示一个设备使用加密从两个不同的设备读取一个传感器和两个二进制传感器,一个使用加密和乒乓,另一个不使用。它还用自己的加密和滚动码将其中一个二进制传感器重新广播到远程主机。

packet_transport:
update_interval: 60s
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 # 此处不需要,因为没有加密
binary_sensor:
- platform: packet_transport
provider: st7735s
id: tick_tock
- platform: packet_transport
provider: room-lights
id: relay1_sensor
sensor:
- platform: packet_transport
provider: st7735s
id: wifi_signal_sensor

在下面的示例中,我们通过提供多个配置键来区分通过 UDP 建立的通道。我们将 sensor_to_provide 的值加密提供给具有特定 IP 地址的节点,并从名为 device-1 的节点接收 relay1_sensor 的状态,不使用任何加密。

udp:
- id: udp_output
addresses:
- 192.168.1.78 # 要提供传感器值的特定节点的 IP
- id: udp_input
packet_transport:
- platform: udp
id: transport_output
udp_id: udp_output
encryption: !secret your_encryption_key
sensors:
- sensor_to_provide
- platform: udp
id: transport_input
udp_id: udp_input
providers:
- name: device-1
sensor:
- platform: ...
id: sensor_to_provide
binary_sensor:
- platform: packet_transport
transport_id: transport_input
provider: device-1
remote_id: relay1_sensor
  1. 截至 2025.02 的已知情况。