esphome / issues

Issue Tracker for ESPHome
https://esphome.io/
289 stars 36 forks source link

State updates not retried when TCP send buffer full; camera exhausts it and triggers lost updates #1998

Closed skandalfo closed 3 years ago

skandalfo commented 3 years ago

Operating environment/Installation (Hass.io/Docker/pip/etc.):

pip, git clone dev branch

ESP (ESP32/ESP8266, Board/Sonoff):

esp32-cam

ESPHome version (latest production, beta, dev branch)

dev branch

Affected component:

https://esphome.io/components/api.html

Description of problem:

Sensor or light updates to Home Assistant get lost when there's a lot of traffic towards Home Assistant, especially when streaming pictures.

While https://github.com/esphome/issues/issues/877#issuecomment-558745873 says important messages are retried, that doesn't seem to be the case:

I've been able to reproduce the lost state (accompanied of the Cannot send message because of TCP buffer space message in serial logs) quite reliably while streaming pictures. Pressing the button of my "doorbell camera" briefly would keep the state remain stuck in ON in Home Assistant when the OFF transiton was lost, or sending commands to turn on/off a light would be "rolled back" in HA's UI when it timed out waiting for a "light state change" coming back to confirm, but it being lost.

Problem-relevant YAML-configuration entries:

substitutions:
  status_led_pin: GPIO15
  status_led_active_low: 'true'
  camera_resolution: 800x600
  camera_idle_framerate: 0.1fps
  camera_max_framerate: 10fps
  camera_horizontal_mirror: 'true'
  camera_vertical_flip: 'true'
  uart_baud_rate: '0'
  log_level: INFO
  dev_name: front_doorbell_cam
  dev_title: Timbre Delantero con Cámara
esphome:
  platform: ESP32
  board: esp32cam
  name: front_doorbell_cam
  comment: Timbre Delantero con Cámara
  platformio_options:
    lib_ldf_mode: chain+
  arduino_version: platformio/espressif32@3.0.0
  build_path: front_doorbell_cam
  includes: []
  libraries: []
  name_add_mac_suffix: false
status_led:
  pin:
    number: 15
    inverted: true
    mode: OUTPUT
output:
- platform: ledc
  id: flash_led_gpio
  pin:
    number: 4
    mode: OUTPUT
    inverted: false
  inverted: false
  frequency: 1000.0
  channel: 2
- platform: ledc
  id: button_led_gpio
  pin:
    number: 12
    mode: OUTPUT
    inverted: false
  inverted: false
  frequency: 1000.0
  channel: 3
light:
- platform: monochromatic
  id: flash_led_light
  output: flash_led_gpio
  name: Flash LED en Timbre Delantero con Cámara
  effects:
  - strobe:
      colors:
      - state: true
        duration: 500ms
        brightness: 1.0
        red: 1.0
        green: 1.0
        blue: 1.0
        white: 1.0
      - state: false
        duration: 500ms
        brightness: 1.0
        red: 1.0
        green: 1.0
        blue: 1.0
        white: 1.0
      name: Strobe
  restore_mode: RESTORE_DEFAULT_OFF
  gamma_correct: 2.8
  default_transition_length: 1s
- platform: monochromatic
  id: button_led_light
  output: button_led_gpio
  name: LED del Pulsador en Timbre Delantero con Cámara
  effects:
  - strobe:
      colors:
      - state: true
        duration: 500ms
        brightness: 1.0
        red: 1.0
        green: 1.0
        blue: 1.0
        white: 1.0
      - state: false
        duration: 500ms
        brightness: 1.0
        red: 1.0
        green: 1.0
        blue: 1.0
        white: 1.0
      name: Strobe
  - lambda:
      name: Pulse
      update_interval: 1s
      lambda: !lambda |-
        static bool on = true;
        if(on) {
          auto call = id(button_led_light).turn_on();
          call.set_brightness(1.0);
          call.set_transition_length(1000);
          call.perform();
        } else {
          auto call = id(button_led_light).turn_on();
          call.set_brightness(0.1);
          call.set_transition_length(1000);
          call.perform();
        }
        on = !on;
  restore_mode: RESTORE_DEFAULT_OFF
  gamma_correct: 2.8
  default_transition_length: 1s
esp32_camera:
  id: camera
  external_clock:
    pin: 0
    frequency: 10000000.0
  i2c_pins:
    sda: 26
    scl: 27
  data_pins:
  - 5
  - 18
  - 19
  - 21
  - 36
  - 39
  - 34
  - 35
  vsync_pin: 25
  href_pin: 23
  pixel_clock_pin: 22
  power_down_pin: 32
  name: Cámara en Timbre Delantero con Cámara
  horizontal_mirror: true
  vertical_flip: true
  resolution: 800X600
  idle_framerate: 0.1
  max_framerate: 10.0
  jpeg_quality: 10
  contrast: 0
  brightness: 0
  saturation: 0
  test_pattern: false
wifi:
  ap:
    ssid: front_doorbell_cam fallback
    password: !secret 'fallback_ap_password'
    ap_timeout: 1min
  domain: .local
  reboot_timeout: 15min
  power_save_mode: LIGHT
  fast_connect: false
  networks:
  - ssid: !secret 'wifi_ssid'
    password: !secret 'wifi_password'
    priority: 0.0
  use_address: front_doorbell_cam.local
captive_portal: {}
logger:
  baud_rate: 0
  level: INFO
  tx_buffer_size: 512
  hardware_uart: UART0
  logs: {}
api:
  password: !secret 'api_password'
  port: 6053
  reboot_timeout: 15min
ota:
  password: !secret 'ota_password'
  safe_mode: true
  port: 3232
  reboot_timeout: 5min
  num_attempts: 10
time:
- platform: homeassistant
  timezone: IST-1GMT0,M10.5.0/2,M3.4.0/1
  update_interval: 15min
sensor:
- platform: uptime
  name: Tiempo desde el Inicio en Timbre Delantero con Cámara
  force_update: false
  unit_of_measurement: s
  icon: mdi:timer-outline
  update_interval: 60s
- platform: dallas
  address: 0xA8011933D0D7B728
  name: Temperatura en Timbre Delantero con Cámara
  filters:
  - or:
    - delta: 1.0
    - throttle: 60s
  force_update: false
  unit_of_measurement: °C
  accuracy_decimals: 1
  device_class: temperature
  resolution: 12
- platform: adc
  pin: 33
  name: Luz ambiente en Timbre Delantero con Cámara
  attenuation: 11db
  update_interval: 1s
  filters:
  - sliding_window_moving_average:
      window_size: 5
      send_every: 1
      send_first_at: 1
  - or:
    - delta: 0.1
    - throttle: 60s
  - calibrate_polynomial:
      degree: 7
      datapoints:
      - from: 0.0
        to: 0.12
      - from: 0.1
        to: 0.22
      - from: 0.2
        to: 0.31
      - from: 0.3
        to: 0.4
      - from: 0.4
        to: 0.48
      - from: 0.5
        to: 0.57
      - from: 1.0
        to: 1.0
      - from: 1.5
        to: 1.42
      - from: 2.0
        to: 1.84
      - from: 2.5
        to: 2.27
      - from: 3.0
        to: 2.67
      - from: 3.4
        to: 2.91
      - from: 3.5
        to: 2.96
      - from: 3.6
        to: 3.01
      - from: 3.7
        to: 3.06
      - from: 3.8
        to: 3.11
      - from: 3.9
        to: 3.16
  - lambda: !lambda |-
      return 10.0f * pow((100.0f / 47.0f) * (x / (3.3f - x)), 1.25f);
  unit_of_measurement: lx
  icon: mdi:brightness-6
  force_update: false
  accuracy_decimals: 2
  device_class: voltage
switch:
- platform: restart
  name: Reiniciar Timbre Delantero con Cámara
  icon: mdi:restart
binary_sensor:
- platform: gpio
  id: pir_sensor
  name: Detección de movimiento en Timbre Delantero con Cámara
  device_class: motion
  pin:
    number: 14
    inverted: false
    mode: INPUT
- platform: gpio
  id: button_sensor
  name: Pulsador en Timbre Delantero con Cámara
  pin:
    number: 16
    inverted: true
    mode: INPUT_PULLUP
dallas:
- pin:
    number: 13
    mode: INPUT
    inverted: false
  update_interval: 1s

Logs (if applicable):

Additional information and things you've tried:

I've managed to greatly mitigate this by ensuring the camera data leaves at least 64 unused bytes in the TCP send window. Then when I press a button or send a light change there's always some space for a single state update back, even when streaming pictures back.

In addition to this, I think it'd be necessary to actually loop in APIConnection until there's enough free send buffer, rather than trying only twice.

I'll link a PR with these changes.

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.