esphome 2024.8.2__py3-none-any.whl → 2024.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. esphome/__main__.py +6 -2
  2. esphome/components/api/api_connection.cpp +53 -0
  3. esphome/components/api/api_connection.h +4 -0
  4. esphome/components/api/api_pb2.cpp +280 -0
  5. esphome/components/api/api_pb2.h +91 -0
  6. esphome/components/api/api_pb2_service.cpp +85 -0
  7. esphome/components/api/api_pb2_service.h +28 -0
  8. esphome/components/async_tcp/__init__.py +3 -3
  9. esphome/components/atm90e26/sensor.py +10 -10
  10. esphome/components/atm90e32/sensor.py +1 -1
  11. esphome/components/bl0906/__init__.py +1 -0
  12. esphome/components/bl0906/bl0906.cpp +238 -0
  13. esphome/components/bl0906/bl0906.h +96 -0
  14. esphome/components/bl0906/const.py +4 -0
  15. esphome/components/bl0906/constants.h +122 -0
  16. esphome/components/bl0906/sensor.py +184 -0
  17. esphome/components/bl0942/__init__.py +1 -1
  18. esphome/components/bl0942/bl0942.cpp +127 -34
  19. esphome/components/bl0942/bl0942.h +87 -3
  20. esphome/components/bl0942/sensor.py +46 -8
  21. esphome/components/ble_client/__init__.py +1 -3
  22. esphome/components/ble_presence/binary_sensor.py +2 -2
  23. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +5 -0
  24. esphome/components/bmp280/sensor.py +2 -93
  25. esphome/components/bmp280_base/__init__.py +88 -0
  26. esphome/components/{bmp280/bmp280.cpp → bmp280_base/bmp280_base.cpp} +11 -4
  27. esphome/components/{bmp280/bmp280.h → bmp280_base/bmp280_base.h} +9 -5
  28. esphome/components/bmp280_i2c/__init__.py +0 -0
  29. esphome/components/bmp280_i2c/bmp280_i2c.cpp +27 -0
  30. esphome/components/bmp280_i2c/bmp280_i2c.h +22 -0
  31. esphome/components/bmp280_i2c/sensor.py +22 -0
  32. esphome/components/bmp280_spi/__init__.py +0 -0
  33. esphome/components/bmp280_spi/bmp280_spi.cpp +65 -0
  34. esphome/components/bmp280_spi/bmp280_spi.h +20 -0
  35. esphome/components/bmp280_spi/sensor.py +22 -0
  36. esphome/components/captive_portal/captive_portal.cpp +2 -0
  37. esphome/components/captive_portal/captive_portal.h +3 -1
  38. esphome/components/ch422g/__init__.py +67 -0
  39. esphome/components/ch422g/ch422g.cpp +122 -0
  40. esphome/components/ch422g/ch422g.h +70 -0
  41. esphome/components/debug/debug_esp32.cpp +3 -1
  42. esphome/components/display/__init__.py +5 -4
  43. esphome/components/dsmr/dsmr.cpp +6 -0
  44. esphome/components/dsmr/dsmr.h +6 -0
  45. esphome/components/dsmr/text_sensor.py +7 -2
  46. esphome/components/e131/e131.cpp +2 -0
  47. esphome/components/e131/e131.h +3 -1
  48. esphome/components/e131/e131_addressable_light_effect.cpp +2 -0
  49. esphome/components/e131/e131_addressable_light_effect.h +2 -1
  50. esphome/components/e131/e131_packet.cpp +2 -0
  51. esphome/components/esp32_ble/ble_uuid.cpp +7 -0
  52. esphome/components/esp32_ble/ble_uuid.h +1 -0
  53. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +11 -9
  54. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +3 -3
  55. esphome/components/esp32_camera/__init__.py +4 -0
  56. esphome/components/esp32_camera/esp32_camera.cpp +9 -1
  57. esphome/components/esp32_camera/esp32_camera.h +3 -0
  58. esphome/components/esp32_can/canbus.py +18 -7
  59. esphome/components/esp32_can/esp32_can.cpp +8 -0
  60. esphome/components/esp32_can/esp32_can.h +4 -0
  61. esphome/components/esp32_rmt_led_strip/led_strip.cpp +14 -2
  62. esphome/components/esp32_rmt_led_strip/led_strip.h +3 -2
  63. esphome/components/esp32_rmt_led_strip/light.py +21 -4
  64. esphome/components/esphome/ota/ota_esphome.cpp +2 -1
  65. esphome/components/esphome/ota/ota_esphome.h +2 -0
  66. esphome/components/font/__init__.py +11 -22
  67. esphome/components/font/font.cpp +3 -2
  68. esphome/components/font/font.h +12 -3
  69. esphome/components/gree/climate.py +2 -1
  70. esphome/components/gree/gree.cpp +54 -3
  71. esphome/components/gree/gree.h +10 -2
  72. esphome/components/gt911/touchscreen/__init__.py +6 -4
  73. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +17 -0
  74. esphome/components/gt911/touchscreen/gt911_touchscreen.h +2 -0
  75. esphome/components/hmac_md5/__init__.py +2 -0
  76. esphome/components/hmac_md5/hmac_md5.cpp +56 -0
  77. esphome/components/hmac_md5/hmac_md5.h +48 -0
  78. esphome/components/homeassistant/__init__.py +13 -0
  79. esphome/components/homeassistant/switch/__init__.py +15 -2
  80. esphome/components/homeassistant/switch/homeassistant_switch.cpp +2 -2
  81. esphome/components/i2s_audio/__init__.py +88 -9
  82. esphome/components/i2s_audio/i2s_audio.h +20 -2
  83. esphome/components/i2s_audio/media_player/__init__.py +8 -4
  84. esphome/components/i2s_audio/media_player/i2s_audio_media_player.h +1 -1
  85. esphome/components/i2s_audio/microphone/__init__.py +19 -51
  86. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +18 -15
  87. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +0 -12
  88. esphome/components/i2s_audio/speaker/__init__.py +39 -27
  89. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +49 -37
  90. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +3 -4
  91. esphome/components/ili9xxx/display.py +16 -17
  92. esphome/components/ili9xxx/ili9xxx_display.cpp +1 -1
  93. esphome/components/ili9xxx/ili9xxx_display.h +18 -18
  94. esphome/components/ili9xxx/ili9xxx_init.h +0 -3
  95. esphome/components/improv_serial/improv_serial_component.cpp +2 -1
  96. esphome/components/improv_serial/improv_serial_component.h +2 -1
  97. esphome/components/ledc/ledc_output.cpp +11 -7
  98. esphome/components/libretiny/__init__.py +8 -13
  99. esphome/components/ltr501/__init__.py +1 -0
  100. esphome/components/ltr501/ltr501.cpp +542 -0
  101. esphome/components/ltr501/ltr501.h +184 -0
  102. esphome/components/ltr501/ltr_definitions_501.h +260 -0
  103. esphome/components/ltr501/sensor.py +274 -0
  104. esphome/components/ltr_als_ps/sensor.py +2 -2
  105. esphome/components/lvgl/__init__.py +19 -16
  106. esphome/components/lvgl/automation.py +90 -9
  107. esphome/components/lvgl/defines.py +29 -2
  108. esphome/components/lvgl/gradient.py +61 -0
  109. esphome/components/lvgl/lv_validation.py +45 -27
  110. esphome/components/lvgl/lvcode.py +8 -3
  111. esphome/components/lvgl/lvgl_esphome.cpp +54 -0
  112. esphome/components/lvgl/lvgl_esphome.h +9 -3
  113. esphome/components/lvgl/number/__init__.py +1 -0
  114. esphome/components/lvgl/number/lvgl_number.h +3 -1
  115. esphome/components/lvgl/schemas.py +16 -11
  116. esphome/components/lvgl/select/__init__.py +1 -0
  117. esphome/components/lvgl/select/lvgl_select.h +3 -1
  118. esphome/components/lvgl/switch/__init__.py +2 -1
  119. esphome/components/lvgl/switch/lvgl_switch.h +3 -1
  120. esphome/components/lvgl/text/__init__.py +1 -0
  121. esphome/components/lvgl/text/lvgl_text.h +3 -1
  122. esphome/components/lvgl/trigger.py +3 -2
  123. esphome/components/lvgl/types.py +2 -1
  124. esphome/components/lvgl/widgets/__init__.py +23 -8
  125. esphome/components/lvgl/widgets/arc.py +5 -1
  126. esphome/components/lvgl/widgets/buttonmatrix.py +5 -1
  127. esphome/components/lvgl/widgets/checkbox.py +8 -3
  128. esphome/components/lvgl/widgets/meter.py +8 -1
  129. esphome/components/lvgl/widgets/msgbox.py +26 -15
  130. esphome/components/lvgl/widgets/page.py +51 -7
  131. esphome/components/lvgl/widgets/tileview.py +2 -8
  132. esphome/components/max31856/max31856.cpp +12 -1
  133. esphome/components/max31856/max31856.h +5 -2
  134. esphome/components/max31856/sensor.py +20 -0
  135. esphome/components/mcp9600/sensor.py +2 -2
  136. esphome/components/mdns/__init__.py +6 -6
  137. esphome/components/media_player/media_player.h +16 -0
  138. esphome/components/micro_wake_word/__init__.py +2 -25
  139. esphome/components/microphone/microphone.h +1 -1
  140. esphome/components/mics_4514/mics_4514.cpp +26 -36
  141. esphome/components/modbus_controller/__init__.py +6 -0
  142. esphome/components/modbus_controller/const.py +2 -0
  143. esphome/components/modbus_controller/modbus_controller.cpp +30 -27
  144. esphome/components/modbus_controller/modbus_controller.h +22 -4
  145. esphome/components/network/__init__.py +11 -8
  146. esphome/components/pipsolar/pipsolar.cpp +3 -0
  147. esphome/components/pipsolar/pipsolar.h +1 -0
  148. esphome/components/pipsolar/switch/__init__.py +2 -0
  149. esphome/components/prometheus/prometheus_handler.cpp +2 -0
  150. esphome/components/prometheus/prometheus_handler.h +3 -1
  151. esphome/components/rp2040/__init__.py +7 -8
  152. esphome/components/rpi_dpi_rgb/display.py +20 -17
  153. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +36 -6
  154. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h +4 -0
  155. esphome/components/socket/socket.cpp +2 -0
  156. esphome/components/socket/socket.h +2 -0
  157. esphome/components/speaker/speaker.h +1 -1
  158. esphome/components/st7701s/display.py +35 -37
  159. esphome/components/st7701s/st7701s.cpp +11 -6
  160. esphome/components/st7701s/st7701s.h +1 -0
  161. esphome/components/statsd/__init__.py +65 -0
  162. esphome/components/statsd/statsd.cpp +156 -0
  163. esphome/components/statsd/statsd.h +86 -0
  164. esphome/components/tuya/__init__.py +1 -0
  165. esphome/components/tuya/number/__init__.py +39 -2
  166. esphome/components/tuya/number/tuya_number.cpp +58 -2
  167. esphome/components/tuya/number/tuya_number.h +12 -3
  168. esphome/components/udp/__init__.py +158 -0
  169. esphome/components/udp/binary_sensor.py +27 -0
  170. esphome/components/udp/sensor.py +27 -0
  171. esphome/components/udp/udp_component.cpp +616 -0
  172. esphome/components/udp/udp_component.h +158 -0
  173. esphome/components/uponor_smatrix/uponor_smatrix.cpp +4 -6
  174. esphome/components/uponor_smatrix/uponor_smatrix.h +0 -1
  175. esphome/components/veml7700/sensor.py +2 -2
  176. esphome/components/voice_assistant/__init__.py +6 -0
  177. esphome/components/voice_assistant/voice_assistant.cpp +24 -2
  178. esphome/components/voice_assistant/voice_assistant.h +20 -0
  179. esphome/components/web_server/__init__.py +11 -11
  180. esphome/components/web_server/list_entities.cpp +2 -0
  181. esphome/components/web_server/list_entities.h +3 -1
  182. esphome/components/web_server/web_server.cpp +2 -1
  183. esphome/components/web_server/web_server.h +2 -0
  184. esphome/components/web_server_base/web_server_base.cpp +2 -0
  185. esphome/components/web_server_base/web_server_base.h +3 -1
  186. esphome/components/wifi/wifi_component_libretiny.cpp +15 -1
  187. esphome/components/wireguard/__init__.py +9 -6
  188. esphome/components/wireguard/wireguard.cpp +2 -1
  189. esphome/components/wireguard/wireguard.h +3 -1
  190. esphome/config_validation.py +8 -0
  191. esphome/const.py +8 -1
  192. esphome/core/bytebuffer.cpp +117 -84
  193. esphome/core/bytebuffer.h +69 -21
  194. esphome/core/config.py +0 -3
  195. esphome/core/defines.h +2 -0
  196. esphome/core/ring_buffer.cpp +13 -2
  197. esphome/core/ring_buffer.h +56 -0
  198. esphome/external_files.py +5 -3
  199. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/METADATA +1 -1
  200. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/RECORD +204 -169
  201. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/LICENSE +0 -0
  202. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/WHEEL +0 -0
  203. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/entry_points.txt +0 -0
  204. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/top_level.txt +0 -0
@@ -1,47 +1,39 @@
1
- import esphome.codegen as cg
2
- import esphome.config_validation as cv
3
1
  from esphome import pins
4
- from esphome.components import (
5
- spi,
6
- display,
2
+ import esphome.codegen as cg
3
+ from esphome.components import display, spi
4
+ from esphome.components.esp32 import const, only_on_variant
5
+ from esphome.components.rpi_dpi_rgb.display import (
6
+ CONF_PCLK_FREQUENCY,
7
+ CONF_PCLK_INVERTED,
7
8
  )
9
+ import esphome.config_validation as cv
8
10
  from esphome.const import (
9
- CONF_DC_PIN,
10
- CONF_HSYNC_PIN,
11
- CONF_RESET_PIN,
11
+ CONF_BLUE,
12
+ CONF_COLOR_ORDER,
12
13
  CONF_DATA_PINS,
13
- CONF_ID,
14
+ CONF_DC_PIN,
14
15
  CONF_DIMENSIONS,
15
- CONF_VSYNC_PIN,
16
- CONF_WIDTH,
16
+ CONF_GREEN,
17
17
  CONF_HEIGHT,
18
+ CONF_HSYNC_PIN,
19
+ CONF_ID,
20
+ CONF_IGNORE_STRAPPING_WARNING,
21
+ CONF_INVERT_COLORS,
18
22
  CONF_LAMBDA,
19
23
  CONF_MIRROR_X,
20
24
  CONF_MIRROR_Y,
21
- CONF_COLOR_ORDER,
22
- CONF_TRANSFORM,
25
+ CONF_NUMBER,
23
26
  CONF_OFFSET_HEIGHT,
24
27
  CONF_OFFSET_WIDTH,
25
- CONF_INVERT_COLORS,
26
28
  CONF_RED,
27
- CONF_GREEN,
28
- CONF_BLUE,
29
- CONF_NUMBER,
30
- CONF_IGNORE_STRAPPING_WARNING,
29
+ CONF_RESET_PIN,
30
+ CONF_TRANSFORM,
31
+ CONF_VSYNC_PIN,
32
+ CONF_WIDTH,
31
33
  )
34
+ from esphome.core import TimePeriod
32
35
 
33
- from esphome.components.esp32 import (
34
- only_on_variant,
35
- const,
36
- )
37
- from esphome.components.rpi_dpi_rgb.display import (
38
- CONF_PCLK_FREQUENCY,
39
- CONF_PCLK_INVERTED,
40
- )
41
- from .init_sequences import (
42
- ST7701S_INITS,
43
- cmd,
44
- )
36
+ from .init_sequences import ST7701S_INITS, cmd
45
37
 
46
38
  CONF_INIT_SEQUENCE = "init_sequence"
47
39
  CONF_DE_PIN = "de_pin"
@@ -59,6 +51,7 @@ DEPENDENCIES = ["spi", "esp32"]
59
51
  st7701s_ns = cg.esphome_ns.namespace("st7701s")
60
52
  ST7701S = st7701s_ns.class_("ST7701S", display.Display, cg.Component, spi.SPIDevice)
61
53
  ColorOrder = display.display_ns.enum("ColorMode")
54
+ ST7701S_DELAY_FLAG = 0xFF
62
55
 
63
56
  COLOR_ORDERS = {
64
57
  "RGB": ColorOrder.COLOR_ORDER_RGB,
@@ -93,18 +86,23 @@ def map_sequence(value):
93
86
  """
94
87
  An initialisation sequence can be selected from one of the pre-defined sequences in init_sequences.py,
95
88
  or can be a literal array of data bytes.
96
- The format is a repeated sequence of [CMD, LEN, <data>] where <data> is LEN bytes.
89
+ The format is a repeated sequence of [CMD, <data>] where <data> is s a sequence of bytes. The length is inferred
90
+ from the length of the sequence and should not be explicit.
91
+ A delay can be inserted by specifying "- delay N" where N is in ms
97
92
  """
93
+ if isinstance(value, str) and value.lower().startswith("delay "):
94
+ value = value.lower()[6:]
95
+ delay = cv.All(
96
+ cv.positive_time_period_milliseconds,
97
+ cv.Range(TimePeriod(milliseconds=1), TimePeriod(milliseconds=255)),
98
+ )(value)
99
+ return [delay, ST7701S_DELAY_FLAG]
98
100
  if not isinstance(value, list):
99
101
  value = cv.int_(value)
100
102
  value = cv.one_of(*ST7701S_INITS)(value)
101
103
  return ST7701S_INITS[value]
102
- # value = cv.ensure_list(cv.uint8_t)(value)
103
- data_length = len(value)
104
- if data_length == 0:
105
- raise cv.Invalid("Empty sequence")
106
- value = cmd(*value)
107
- return value
104
+ value = cv.Length(min=1, max=254)(value)
105
+ return cmd(*value)
108
106
 
109
107
 
110
108
  CONFIG_SCHEMA = cv.All(
@@ -138,11 +138,16 @@ void ST7701S::write_init_sequence_() {
138
138
  for (size_t i = 0; i != this->init_sequence_.size();) {
139
139
  uint8_t cmd = this->init_sequence_[i++];
140
140
  size_t len = this->init_sequence_[i++];
141
- this->write_sequence_(cmd, len, &this->init_sequence_[i]);
142
- i += len;
143
- esph_log_v(TAG, "Command %X, %d bytes", cmd, len);
144
- if (cmd == SW_RESET_CMD)
145
- delay(6);
141
+ if (len == ST7701S_DELAY_FLAG) {
142
+ ESP_LOGV(TAG, "Delay %dms", cmd);
143
+ delay(cmd);
144
+ } else {
145
+ this->write_sequence_(cmd, len, &this->init_sequence_[i]);
146
+ i += len;
147
+ ESP_LOGV(TAG, "Command %X, %d bytes", cmd, len);
148
+ if (cmd == SW_RESET_CMD)
149
+ delay(6);
150
+ }
146
151
  }
147
152
  // st7701 does not appear to support axis swapping
148
153
  this->write_sequence_(CMD2_BKSEL, sizeof(CMD2_BK0), CMD2_BK0);
@@ -153,7 +158,7 @@ void ST7701S::write_init_sequence_() {
153
158
  val |= 0x10;
154
159
  this->write_command_(MADCTL_CMD);
155
160
  this->write_data_(val);
156
- esph_log_d(TAG, "write MADCTL %X", val);
161
+ ESP_LOGD(TAG, "write MADCTL %X", val);
157
162
  this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF);
158
163
  this->set_timeout(120, [this] {
159
164
  this->write_command_(SLEEP_OUT);
@@ -25,6 +25,7 @@ const uint8_t INVERT_ON = 0x21;
25
25
  const uint8_t DISPLAY_ON = 0x29;
26
26
  const uint8_t CMD2_BKSEL = 0xFF;
27
27
  const uint8_t CMD2_BK0[5] = {0x77, 0x01, 0x00, 0x00, 0x10};
28
+ const uint8_t ST7701S_DELAY_FLAG = 0xFF;
28
29
 
29
30
  class ST7701S : public display::Display,
30
31
  public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
@@ -0,0 +1,65 @@
1
+ import esphome.codegen as cg
2
+ import esphome.config_validation as cv
3
+ from esphome.components import sensor, binary_sensor
4
+ from esphome.const import (
5
+ CONF_ID,
6
+ CONF_PORT,
7
+ CONF_NAME,
8
+ CONF_SENSORS,
9
+ CONF_BINARY_SENSORS,
10
+ )
11
+
12
+ AUTO_LOAD = ["socket"]
13
+ CODEOWNERS = ["@Links2004"]
14
+ DEPENDENCIES = ["network"]
15
+
16
+ CONF_HOST = "host"
17
+ CONF_PREFIX = "prefix"
18
+
19
+ statsd_component_ns = cg.esphome_ns.namespace("statsd")
20
+ StatsdComponent = statsd_component_ns.class_("StatsdComponent", cg.PollingComponent)
21
+
22
+ CONFIG_SENSORS_SCHEMA = cv.Schema(
23
+ {
24
+ cv.Required(CONF_ID): cv.use_id(sensor.Sensor),
25
+ cv.Required(CONF_NAME): cv.string_strict,
26
+ }
27
+ )
28
+
29
+ CONFIG_BINARY_SENSORS_SCHEMA = cv.Schema(
30
+ {
31
+ cv.Required(CONF_ID): cv.use_id(binary_sensor.BinarySensor),
32
+ cv.Required(CONF_NAME): cv.string_strict,
33
+ }
34
+ )
35
+
36
+ CONFIG_SCHEMA = cv.Schema(
37
+ {
38
+ cv.GenerateID(): cv.declare_id(StatsdComponent),
39
+ cv.Required(CONF_HOST): cv.string_strict,
40
+ cv.Optional(CONF_PORT, default=8125): cv.port,
41
+ cv.Optional(CONF_PREFIX, default=""): cv.string_strict,
42
+ cv.Optional(CONF_SENSORS): cv.ensure_list(CONFIG_SENSORS_SCHEMA),
43
+ cv.Optional(CONF_BINARY_SENSORS): cv.ensure_list(CONFIG_BINARY_SENSORS_SCHEMA),
44
+ }
45
+ ).extend(cv.polling_component_schema("10s"))
46
+
47
+
48
+ async def to_code(config):
49
+ var = cg.new_Pvariable(config[CONF_ID])
50
+ await cg.register_component(var, config)
51
+ cg.add(
52
+ var.configure(
53
+ config.get(CONF_HOST),
54
+ config.get(CONF_PORT),
55
+ config.get(CONF_PREFIX),
56
+ )
57
+ )
58
+
59
+ for sensor_cfg in config.get(CONF_SENSORS, []):
60
+ s = await cg.get_variable(sensor_cfg[CONF_ID])
61
+ cg.add(var.register_sensor(sensor_cfg[CONF_NAME], s))
62
+
63
+ for sensor_cfg in config.get(CONF_BINARY_SENSORS, []):
64
+ s = await cg.get_variable(sensor_cfg[CONF_ID])
65
+ cg.add(var.register_binary_sensor(sensor_cfg[CONF_NAME], s))
@@ -0,0 +1,156 @@
1
+ #include "esphome/core/log.h"
2
+
3
+ #include "statsd.h"
4
+
5
+ namespace esphome {
6
+ namespace statsd {
7
+
8
+ // send UDP packet if we reach 1Kb packed size
9
+ // this is needed since statsD does not support fragmented UDP packets
10
+ static const uint16_t SEND_THRESHOLD = 1024;
11
+
12
+ static const char *const TAG = "statsD";
13
+
14
+ void StatsdComponent::setup() {
15
+ #ifndef USE_ESP8266
16
+ this->sock_ = esphome::socket::socket(AF_INET, SOCK_DGRAM, 0);
17
+
18
+ struct sockaddr_in source;
19
+ source.sin_family = AF_INET;
20
+ source.sin_addr.s_addr = htonl(INADDR_ANY);
21
+ source.sin_port = htons(this->port_);
22
+ this->sock_->bind((struct sockaddr *) &source, sizeof(source));
23
+
24
+ this->destination_.sin_family = AF_INET;
25
+ this->destination_.sin_port = htons(this->port_);
26
+ this->destination_.sin_addr.s_addr = inet_addr(this->host_);
27
+ #endif
28
+ }
29
+
30
+ StatsdComponent::~StatsdComponent() {
31
+ #ifndef USE_ESP8266
32
+ if (!this->sock_) {
33
+ return;
34
+ }
35
+ this->sock_->close();
36
+ #endif
37
+ }
38
+
39
+ void StatsdComponent::dump_config() {
40
+ ESP_LOGCONFIG(TAG, "statsD:");
41
+ ESP_LOGCONFIG(TAG, " host: %s", this->host_);
42
+ ESP_LOGCONFIG(TAG, " port: %d", this->port_);
43
+ if (this->prefix_) {
44
+ ESP_LOGCONFIG(TAG, " prefix: %s", this->prefix_);
45
+ }
46
+
47
+ ESP_LOGCONFIG(TAG, " metrics:");
48
+ for (sensors_t s : this->sensors_) {
49
+ ESP_LOGCONFIG(TAG, " - name: %s", s.name);
50
+ ESP_LOGCONFIG(TAG, " type: %d", s.type);
51
+ }
52
+ }
53
+
54
+ float StatsdComponent::get_setup_priority() const { return esphome::setup_priority::AFTER_WIFI; }
55
+
56
+ #ifdef USE_SENSOR
57
+ void StatsdComponent::register_sensor(const char *name, esphome::sensor::Sensor *sensor) {
58
+ sensors_t s;
59
+ s.name = name;
60
+ s.sensor = sensor;
61
+ s.type = TYPE_SENSOR;
62
+ this->sensors_.push_back(s);
63
+ }
64
+ #endif
65
+
66
+ #ifdef USE_BINARY_SENSOR
67
+ void StatsdComponent::register_binary_sensor(const char *name, esphome::binary_sensor::BinarySensor *binary_sensor) {
68
+ sensors_t s;
69
+ s.name = name;
70
+ s.binary_sensor = binary_sensor;
71
+ s.type = TYPE_BINARY_SENSOR;
72
+ this->sensors_.push_back(s);
73
+ }
74
+ #endif
75
+
76
+ void StatsdComponent::update() {
77
+ std::string out;
78
+ out.reserve(SEND_THRESHOLD);
79
+
80
+ for (sensors_t s : this->sensors_) {
81
+ double val = 0;
82
+ switch (s.type) {
83
+ #ifdef USE_SENSOR
84
+ case TYPE_SENSOR:
85
+ if (!s.sensor->has_state()) {
86
+ continue;
87
+ }
88
+ val = s.sensor->state;
89
+ break;
90
+ #endif
91
+ #ifdef USE_BINARY_SENSOR
92
+ case TYPE_BINARY_SENSOR:
93
+ if (!s.binary_sensor->has_state()) {
94
+ continue;
95
+ }
96
+ // map bool to double
97
+ if (s.binary_sensor->state) {
98
+ val = 1;
99
+ }
100
+ break;
101
+ #endif
102
+ default:
103
+ ESP_LOGE(TAG, "type not known, name: %s type: %d", s.name, s.type);
104
+ continue;
105
+ }
106
+
107
+ // statsD gauge:
108
+ // https://github.com/statsd/statsd/blob/master/docs/metric_types.md
109
+ // This implies you can't explicitly set a gauge to a negative number without first setting it to zero.
110
+ if (val < 0) {
111
+ if (this->prefix_) {
112
+ out.append(str_sprintf("%s.", this->prefix_));
113
+ }
114
+ out.append(str_sprintf("%s:0|g\n", s.name));
115
+ }
116
+ if (this->prefix_) {
117
+ out.append(str_sprintf("%s.", this->prefix_));
118
+ }
119
+ out.append(str_sprintf("%s:%f|g\n", s.name, val));
120
+
121
+ if (out.length() > SEND_THRESHOLD) {
122
+ this->send_(&out);
123
+ out.clear();
124
+ }
125
+ }
126
+
127
+ this->send_(&out);
128
+ }
129
+
130
+ void StatsdComponent::send_(std::string *out) {
131
+ if (out->empty()) {
132
+ return;
133
+ }
134
+ #ifdef USE_ESP8266
135
+ IPAddress ip;
136
+ ip.fromString(this->host_);
137
+
138
+ this->sock_.beginPacket(ip, this->port_);
139
+ this->sock_.write((const uint8_t *) out->c_str(), out->length());
140
+ this->sock_.endPacket();
141
+
142
+ #else
143
+ if (!this->sock_) {
144
+ return;
145
+ }
146
+
147
+ int n_bytes = this->sock_->sendto(out->c_str(), out->length(), 0, reinterpret_cast<sockaddr *>(&this->destination_),
148
+ sizeof(this->destination_));
149
+ if (n_bytes != out->length()) {
150
+ ESP_LOGE(TAG, "Failed to send UDP packed (%d of %d)", n_bytes, out->length());
151
+ }
152
+ #endif
153
+ }
154
+
155
+ } // namespace statsd
156
+ } // namespace esphome
@@ -0,0 +1,86 @@
1
+ #pragma once
2
+
3
+ #include <vector>
4
+
5
+ #include "esphome/core/defines.h"
6
+ #include "esphome/core/component.h"
7
+ #include "esphome/components/socket/socket.h"
8
+ #include "esphome/components/network/ip_address.h"
9
+
10
+ #ifdef USE_SENSOR
11
+ #include "esphome/components/sensor/sensor.h"
12
+ #endif
13
+
14
+ #ifdef USE_BINARY_SENSOR
15
+ #include "esphome/components/binary_sensor/binary_sensor.h"
16
+ #endif
17
+
18
+ #ifdef USE_LOGGER
19
+ #include "esphome/components/logger/logger.h"
20
+ #endif
21
+
22
+ #ifdef USE_ESP8266
23
+ #include "WiFiUdp.h"
24
+ #include "IPAddress.h"
25
+ #endif
26
+
27
+ namespace esphome {
28
+ namespace statsd {
29
+
30
+ using sensor_type_t = enum { TYPE_SENSOR, TYPE_BINARY_SENSOR };
31
+
32
+ using sensors_t = struct {
33
+ const char *name;
34
+ sensor_type_t type;
35
+ union {
36
+ #ifdef USE_SENSOR
37
+ esphome::sensor::Sensor *sensor;
38
+ #endif
39
+ #ifdef USE_BINARY_SENSOR
40
+ esphome::binary_sensor::BinarySensor *binary_sensor;
41
+ #endif
42
+ };
43
+ };
44
+
45
+ class StatsdComponent : public PollingComponent {
46
+ public:
47
+ ~StatsdComponent();
48
+
49
+ void setup() override;
50
+ void dump_config() override;
51
+ void update() override;
52
+ float get_setup_priority() const override;
53
+
54
+ void configure(const char *host, uint16_t port, const char *prefix) {
55
+ this->host_ = host;
56
+ this->port_ = port;
57
+ this->prefix_ = prefix;
58
+ }
59
+
60
+ #ifdef USE_SENSOR
61
+ void register_sensor(const char *name, esphome::sensor::Sensor *sensor);
62
+ #endif
63
+
64
+ #ifdef USE_BINARY_SENSOR
65
+ void register_binary_sensor(const char *name, esphome::binary_sensor::BinarySensor *binary_sensor);
66
+ #endif
67
+
68
+ private:
69
+ const char *host_;
70
+ const char *prefix_;
71
+ uint16_t port_;
72
+
73
+ std::vector<sensors_t> sensors_;
74
+
75
+ #ifdef USE_ESP8266
76
+ WiFiUDP sock_;
77
+ #else
78
+ std::unique_ptr<esphome::socket::Socket> sock_;
79
+ struct sockaddr_in destination_;
80
+ #endif
81
+
82
+ void send_(std::string *out);
83
+ };
84
+
85
+ } // namespace statsd
86
+ } // namespace esphome
@@ -15,6 +15,7 @@ CONF_DATAPOINT_TYPE = "datapoint_type"
15
15
  CONF_STATUS_PIN = "status_pin"
16
16
 
17
17
  tuya_ns = cg.esphome_ns.namespace("tuya")
18
+ TuyaDatapointType = tuya_ns.enum("TuyaDatapointType", is_class=True)
18
19
  Tuya = tuya_ns.class_("Tuya", cg.Component, uart.UARTDevice)
19
20
 
20
21
  DPTYPE_ANY = "any"
@@ -8,18 +8,37 @@ from esphome.const import (
8
8
  CONF_MIN_VALUE,
9
9
  CONF_MULTIPLY,
10
10
  CONF_STEP,
11
+ CONF_INITIAL_VALUE,
12
+ CONF_RESTORE_VALUE,
11
13
  )
12
- from .. import tuya_ns, CONF_TUYA_ID, Tuya
14
+ from .. import tuya_ns, CONF_TUYA_ID, Tuya, TuyaDatapointType
13
15
 
14
16
  DEPENDENCIES = ["tuya"]
15
17
  CODEOWNERS = ["@frankiboy1"]
16
18
 
19
+ CONF_DATAPOINT_HIDDEN = "datapoint_hidden"
20
+ CONF_DATAPOINT_TYPE = "datapoint_type"
21
+
17
22
  TuyaNumber = tuya_ns.class_("TuyaNumber", number.Number, cg.Component)
18
23
 
24
+ DATAPOINT_TYPES = {
25
+ "int": TuyaDatapointType.INTEGER,
26
+ "uint": TuyaDatapointType.INTEGER,
27
+ "enum": TuyaDatapointType.ENUM,
28
+ }
29
+
19
30
 
20
31
  def validate_min_max(config):
21
- if config[CONF_MAX_VALUE] <= config[CONF_MIN_VALUE]:
32
+ max_value = config[CONF_MAX_VALUE]
33
+ min_value = config[CONF_MIN_VALUE]
34
+ if max_value <= min_value:
22
35
  raise cv.Invalid("max_value must be greater than min_value")
36
+ if hidden_config := config.get(CONF_DATAPOINT_HIDDEN):
37
+ if (initial_value := hidden_config.get(CONF_INITIAL_VALUE, None)) is not None:
38
+ if (initial_value > max_value) or (initial_value < min_value):
39
+ raise cv.Invalid(
40
+ f"{CONF_INITIAL_VALUE} must be a value between {CONF_MAX_VALUE} and {CONF_MIN_VALUE}"
41
+ )
23
42
  return config
24
43
 
25
44
 
@@ -33,6 +52,17 @@ CONFIG_SCHEMA = cv.All(
33
52
  cv.Required(CONF_MIN_VALUE): cv.float_,
34
53
  cv.Required(CONF_STEP): cv.positive_float,
35
54
  cv.Optional(CONF_MULTIPLY, default=1.0): cv.float_,
55
+ cv.Optional(CONF_DATAPOINT_HIDDEN): cv.All(
56
+ cv.Schema(
57
+ {
58
+ cv.Required(CONF_DATAPOINT_TYPE): cv.enum(
59
+ DATAPOINT_TYPES, lower=True
60
+ ),
61
+ cv.Optional(CONF_INITIAL_VALUE): cv.float_,
62
+ cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean,
63
+ }
64
+ )
65
+ ),
36
66
  }
37
67
  )
38
68
  .extend(cv.COMPONENT_SCHEMA),
@@ -56,3 +86,10 @@ async def to_code(config):
56
86
  cg.add(var.set_tuya_parent(parent))
57
87
 
58
88
  cg.add(var.set_number_id(config[CONF_NUMBER_DATAPOINT]))
89
+ if hidden_config := config.get(CONF_DATAPOINT_HIDDEN):
90
+ cg.add(var.set_datapoint_type(hidden_config[CONF_DATAPOINT_TYPE]))
91
+ if (
92
+ hidden_init_value := hidden_config.get(CONF_INITIAL_VALUE, None)
93
+ ) is not None:
94
+ cg.add(var.set_datapoint_initial_value(hidden_init_value))
95
+ cg.add(var.set_restore_value(hidden_config[CONF_RESTORE_VALUE]))
@@ -7,16 +7,58 @@ namespace tuya {
7
7
  static const char *const TAG = "tuya.number";
8
8
 
9
9
  void TuyaNumber::setup() {
10
+ if (this->restore_value_) {
11
+ this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
12
+ }
13
+
10
14
  this->parent_->register_listener(this->number_id_, [this](const TuyaDatapoint &datapoint) {
11
15
  if (datapoint.type == TuyaDatapointType::INTEGER) {
12
16
  ESP_LOGV(TAG, "MCU reported number %u is: %d", datapoint.id, datapoint.value_int);
13
- this->publish_state(datapoint.value_int / multiply_by_);
17
+ float value = datapoint.value_int / multiply_by_;
18
+ this->publish_state(value);
19
+ if (this->restore_value_)
20
+ this->pref_.save(&value);
14
21
  } else if (datapoint.type == TuyaDatapointType::ENUM) {
15
22
  ESP_LOGV(TAG, "MCU reported number %u is: %u", datapoint.id, datapoint.value_enum);
16
- this->publish_state(datapoint.value_enum);
23
+ float value = datapoint.value_enum;
24
+ this->publish_state(value);
25
+ if (this->restore_value_)
26
+ this->pref_.save(&value);
27
+ } else {
28
+ ESP_LOGW(TAG, "Reported type (%d) is not a number!", static_cast<int>(datapoint.type));
29
+ return;
30
+ }
31
+
32
+ if ((this->type_) && (this->type_ != datapoint.type)) {
33
+ ESP_LOGW(TAG, "Reported type (%d) different than previously set (%d)!", static_cast<int>(datapoint.type),
34
+ static_cast<int>(*this->type_));
17
35
  }
18
36
  this->type_ = datapoint.type;
19
37
  });
38
+
39
+ this->parent_->add_on_initialized_callback([this] {
40
+ if (this->type_) {
41
+ float value;
42
+ if (!this->restore_value_) {
43
+ if (this->initial_value_) {
44
+ value = *this->initial_value_;
45
+ } else {
46
+ return;
47
+ }
48
+ } else {
49
+ if (!this->pref_.load(&value)) {
50
+ if (this->initial_value_) {
51
+ value = *this->initial_value_;
52
+ } else {
53
+ value = this->traits.get_min_value();
54
+ ESP_LOGW(TAG, "Failed to restore and there is no initial value defined. Setting min_value (%f)", value);
55
+ }
56
+ }
57
+ }
58
+
59
+ this->control(value);
60
+ }
61
+ });
20
62
  }
21
63
 
22
64
  void TuyaNumber::control(float value) {
@@ -28,11 +70,25 @@ void TuyaNumber::control(float value) {
28
70
  this->parent_->set_enum_datapoint_value(this->number_id_, value);
29
71
  }
30
72
  this->publish_state(value);
73
+
74
+ if (this->restore_value_)
75
+ this->pref_.save(&value);
31
76
  }
32
77
 
33
78
  void TuyaNumber::dump_config() {
34
79
  LOG_NUMBER("", "Tuya Number", this);
35
80
  ESP_LOGCONFIG(TAG, " Number has datapoint ID %u", this->number_id_);
81
+ if (this->type_) {
82
+ ESP_LOGCONFIG(TAG, " Datapoint type is %d", static_cast<int>(*this->type_));
83
+ } else {
84
+ ESP_LOGCONFIG(TAG, " Datapoint type is unknown");
85
+ }
86
+
87
+ if (this->initial_value_) {
88
+ ESP_LOGCONFIG(TAG, " Initial Value: %f", *this->initial_value_);
89
+ }
90
+
91
+ ESP_LOGCONFIG(TAG, " Restore Value: %s", YESNO(this->restore_value_));
36
92
  }
37
93
 
38
94
  } // namespace tuya
@@ -1,8 +1,10 @@
1
1
  #pragma once
2
2
 
3
- #include "esphome/core/component.h"
4
- #include "esphome/components/tuya/tuya.h"
5
3
  #include "esphome/components/number/number.h"
4
+ #include "esphome/components/tuya/tuya.h"
5
+ #include "esphome/core/component.h"
6
+ #include "esphome/core/optional.h"
7
+ #include "esphome/core/preferences.h"
6
8
 
7
9
  namespace esphome {
8
10
  namespace tuya {
@@ -13,6 +15,9 @@ class TuyaNumber : public number::Number, public Component {
13
15
  void dump_config() override;
14
16
  void set_number_id(uint8_t number_id) { this->number_id_ = number_id; }
15
17
  void set_write_multiply(float factor) { multiply_by_ = factor; }
18
+ void set_datapoint_type(TuyaDatapointType type) { type_ = type; }
19
+ void set_datapoint_initial_value(float value) { this->initial_value_ = value; }
20
+ void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
16
21
 
17
22
  void set_tuya_parent(Tuya *parent) { this->parent_ = parent; }
18
23
 
@@ -22,7 +27,11 @@ class TuyaNumber : public number::Number, public Component {
22
27
  Tuya *parent_;
23
28
  uint8_t number_id_{0};
24
29
  float multiply_by_{1.0};
25
- TuyaDatapointType type_{};
30
+ optional<TuyaDatapointType> type_{};
31
+ optional<float> initial_value_{};
32
+ bool restore_value_{false};
33
+
34
+ ESPPreferenceObject pref_;
26
35
  };
27
36
 
28
37
  } // namespace tuya