跳转到内容

数据包传输组件

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

节点可以是 提供者(发送或广播传感器数据),也可以是 消费者(从一个或多个 提供者接收传感器数据)。一个节点可以同时是提供者和消费者。可选的安全由以下一种或多种方式提供:

  • 使用共享密钥的加密
  • 滚动码
  • 挑战-响应(ping-pong)密钥
# Example configuration entry
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 # Optional, defaults to 'data'
id: other_binary_sensor_id # also used as remote_id
- platform: packet_transport
provider: device1-name
type: status
name: Device 1 connection status
  • id (可选, ID): 手动指定用于代码生成的 ID。

  • update_interval (可选, Time): 全量广播之间的间隔。默认为 15s。

  • sensors (可选, list): 要广播的传感器 ID 列表。每个条目可以仅为传感器 id,也可以设置不同的广播 id。

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

    • id (必填, ID): 要使用的二进制传感器的 id
    • broadcast_id (可选, string): 在广播中用于此二进制传感器的 id。默认为与内部 id 相同。
  • encryption (可选, string): 广播时使用的加密密钥。默认为不加密。它可以是 任何字符串,将经过哈希处理形成 256 位密钥。

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

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

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

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

    • name (必填, string): 提供者的设备名称。
    • encryption (可选, string): 提供者的加密密钥。

在需要提供者名称的地方,这应该是 esphome: 块中配置的设备名称。

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

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

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

仅加密可确保数据在传输过程中无法被读取,并防止数据伪造,但不能防止 重放攻击(威胁行为者记录传输并稍后重放,例如重复某个操作。)

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

NOTE

滚动码的上部 32 位字段在提供者节点重启时递增并写入闪存 一次。 当下部 32 位字段溢出时(只能在 很长时间后发生),它也会递增并写入闪存。消费者端不在闪存中存储滚动码。

为进一步保护,提供了一种 ping-pong(或挑战-响应)机制,可以在 消费者配置中启用。消费者定期生成一个 32 位随机数(一个 nonce,即”Number used Once”) 并将其作为 ping 广播。任何收到此 nonce 的提供者都将在未来的加密广播中将其包含为 pong。消费者期望在收到的任何数据包中获取最近发送的 ping,并将拒绝 任何不包含它的数据包。

使用 ping-pong 功能将增加网络流量和传输数据包的大小(单个数据包最多可能 包含来自不同设备的 4 个 nonce),但提供高水平的重放攻击保护。它 需要双向网络连接,并且仅适用于本地网络,因为消费者只能 广播 nonce 给提供者。

此外,在使用 ping-pong 时,可以创建连接状态二进制传感器。状态传感器将在消费者收到提供者发送的 pong 后 ping_pong_recycle_time 内报告 connected。如果未 收到,将报告 disconnected。这可用于检测提供者何时不可用,或者 加密密钥何时发生变化。

NOTE

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

因此,ping-pong 仅推荐用于状态传输,这些传输会在 update_interval 时定期更新。

使用的加密是 XXTEA,速度快且紧凑。虽然 XXTEA 已知 容易受到选择明文攻击,但在这种应用中这种攻击是不可能的,并且它没有 其他已公布的弱点 1。此处使用的实现经过略微修改,以使用 256 位密钥, 这将比原始的 128 位密钥提供更强的安全性。

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

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

此示例将两个不同设备中的两个灯开关耦合在一起,使得打开或关闭任何一个都会导致 另一个随之行动。每种情况都使用模板二进制传感器来镜像开关状态。

# Device 1
esphome:
name: device-1
packet_transport:
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: packet_transport
provider: device-2
id: relay2_sensor
on_press:
switch.turn_on: relay1
on_release:
switch.turn_off: relay1
# Device 2
esphome:
name: device-2
packet_transport:
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: packet_transport
provider: device-1
id: relay1_sensor
on_press:
switch.turn_on: relay2
on_release:
switch.turn_off: relay2

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

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 # Not required here since no encryption
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 # the IP of the specific node to provide the sensor value to
- 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 已知。 ↩