esphome 2025.9.2__py3-none-any.whl → 2025.10.0b1__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 (344) hide show
  1. esphome/__main__.py +87 -31
  2. esphome/address_cache.py +142 -0
  3. esphome/automation.py +130 -32
  4. esphome/build_gen/platformio.py +1 -3
  5. esphome/codegen.py +1 -0
  6. esphome/components/animation/animation.cpp +2 -2
  7. esphome/components/api/__init__.py +167 -3
  8. esphome/components/api/api_connection.cpp +84 -41
  9. esphome/components/api/api_connection.h +22 -16
  10. esphome/components/api/api_frame_helper.cpp +33 -19
  11. esphome/components/api/api_frame_helper.h +19 -4
  12. esphome/components/api/api_frame_helper_noise.cpp +41 -53
  13. esphome/components/api/api_frame_helper_noise.h +1 -1
  14. esphome/components/api/api_frame_helper_plaintext.cpp +22 -31
  15. esphome/components/api/api_frame_helper_plaintext.h +1 -1
  16. esphome/components/api/api_pb2.cpp +189 -15
  17. esphome/components/api/api_pb2.h +132 -20
  18. esphome/components/api/api_pb2_dump.cpp +97 -9
  19. esphome/components/api/api_pb2_service.cpp +118 -160
  20. esphome/components/api/api_pb2_service.h +31 -3
  21. esphome/components/api/api_server.cpp +78 -11
  22. esphome/components/api/api_server.h +32 -4
  23. esphome/components/api/custom_api_device.h +8 -8
  24. esphome/components/api/homeassistant_service.h +123 -6
  25. esphome/components/api/proto.h +6 -2
  26. esphome/components/api/user_services.h +2 -2
  27. esphome/components/as7341/sensor.py +1 -1
  28. esphome/components/audio/__init__.py +1 -1
  29. esphome/components/audio/audio.cpp +1 -1
  30. esphome/components/audio/audio_decoder.cpp +9 -9
  31. esphome/components/bl0906/bl0906.cpp +2 -2
  32. esphome/components/bl0942/bl0942.cpp +2 -2
  33. esphome/components/ble_client/__init__.py +1 -1
  34. esphome/components/bluetooth_proxy/__init__.py +4 -30
  35. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +11 -4
  36. esphome/components/bluetooth_proxy/bluetooth_connection.h +2 -2
  37. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -2
  38. esphome/components/camera_encoder/__init__.py +2 -4
  39. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +4 -2
  40. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +3 -1
  41. esphome/components/canbus/canbus.cpp +7 -5
  42. esphome/components/canbus/canbus.h +4 -4
  43. esphome/components/captive_portal/__init__.py +18 -1
  44. esphome/components/captive_portal/captive_portal.cpp +40 -46
  45. esphome/components/captive_portal/captive_portal.h +20 -22
  46. esphome/components/captive_portal/dns_server_esp32_idf.cpp +205 -0
  47. esphome/components/captive_portal/dns_server_esp32_idf.h +27 -0
  48. esphome/components/ccs811/ccs811.cpp +1 -1
  49. esphome/components/climate/climate.cpp +10 -7
  50. esphome/components/cm1106/cm1106.cpp +1 -1
  51. esphome/components/copy/lock/copy_lock.cpp +1 -1
  52. esphome/components/cover/cover.cpp +1 -0
  53. esphome/components/daikin_arc/daikin_arc.cpp +19 -12
  54. esphome/components/deep_sleep/__init__.py +9 -2
  55. esphome/components/deep_sleep/deep_sleep_component.h +11 -9
  56. esphome/components/deep_sleep/deep_sleep_esp32.cpp +51 -27
  57. esphome/components/ektf2232/touchscreen/__init__.py +8 -5
  58. esphome/components/ektf2232/touchscreen/ektf2232.cpp +4 -4
  59. esphome/components/ektf2232/touchscreen/ektf2232.h +2 -2
  60. esphome/components/epaper_spi/__init__.py +1 -0
  61. esphome/components/epaper_spi/display.py +80 -0
  62. esphome/components/epaper_spi/epaper_spi.cpp +227 -0
  63. esphome/components/epaper_spi/epaper_spi.h +93 -0
  64. esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.cpp +42 -0
  65. esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.h +45 -0
  66. esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp +135 -0
  67. esphome/components/epaper_spi/epaper_spi_spectra_e6.h +23 -0
  68. esphome/components/es7210/es7210.cpp +3 -3
  69. esphome/components/esp32/__init__.py +254 -339
  70. esphome/components/esp32/boards.py +81 -0
  71. esphome/components/esp32/preferences.cpp +23 -17
  72. esphome/components/esp32_ble/__init__.py +159 -44
  73. esphome/components/esp32_ble/ble.cpp +47 -3
  74. esphome/components/esp32_ble/ble.h +18 -0
  75. esphome/components/esp32_ble/ble_advertising.cpp +7 -3
  76. esphome/components/esp32_ble/ble_advertising.h +4 -0
  77. esphome/components/esp32_ble/ble_uuid.cpp +16 -42
  78. esphome/components/esp32_ble_beacon/__init__.py +3 -4
  79. esphome/components/esp32_ble_client/ble_client_base.cpp +14 -12
  80. esphome/components/esp32_ble_server/__init__.py +28 -14
  81. esphome/components/esp32_ble_server/ble_characteristic.cpp +67 -57
  82. esphome/components/esp32_ble_server/ble_characteristic.h +27 -16
  83. esphome/components/esp32_ble_server/ble_descriptor.cpp +4 -3
  84. esphome/components/esp32_ble_server/ble_descriptor.h +13 -9
  85. esphome/components/esp32_ble_server/ble_server.cpp +59 -24
  86. esphome/components/esp32_ble_server/ble_server.h +38 -20
  87. esphome/components/esp32_ble_server/ble_server_automations.cpp +49 -33
  88. esphome/components/esp32_ble_server/ble_server_automations.h +39 -24
  89. esphome/components/esp32_ble_tracker/__init__.py +25 -80
  90. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +2 -4
  91. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +0 -3
  92. esphome/components/esp32_camera/__init__.py +1 -3
  93. esphome/components/esp32_can/esp32_can.cpp +22 -4
  94. esphome/components/esp32_can/esp32_can.h +3 -0
  95. esphome/components/esp32_hosted/__init__.py +2 -1
  96. esphome/components/esp32_improv/esp32_improv_component.cpp +102 -44
  97. esphome/components/esp32_improv/esp32_improv_component.h +6 -1
  98. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  99. esphome/components/esp8266/__init__.py +3 -3
  100. esphome/components/esphome/ota/__init__.py +21 -2
  101. esphome/components/esphome/ota/ota_esphome.cpp +455 -145
  102. esphome/components/esphome/ota/ota_esphome.h +49 -2
  103. esphome/components/ethernet/__init__.py +39 -22
  104. esphome/components/ethernet/ethernet_component.cpp +28 -5
  105. esphome/components/ethernet/ethernet_component.h +5 -1
  106. esphome/components/external_components/__init__.py +8 -6
  107. esphome/components/fingerprint_grow/fingerprint_grow.cpp +1 -1
  108. esphome/components/fingerprint_grow/fingerprint_grow.h +2 -1
  109. esphome/components/font/__init__.py +5 -5
  110. esphome/components/graph/graph.cpp +1 -1
  111. esphome/components/graphical_display_menu/graphical_display_menu.cpp +3 -2
  112. esphome/components/haier/hon_climate.cpp +2 -2
  113. esphome/components/haier/hon_climate.h +1 -1
  114. esphome/components/hdc1080/hdc1080.cpp +42 -34
  115. esphome/components/hdc1080/hdc1080.h +1 -3
  116. esphome/components/homeassistant/number/homeassistant_number.cpp +2 -2
  117. esphome/components/homeassistant/switch/homeassistant_switch.cpp +2 -2
  118. esphome/components/http_request/__init__.py +3 -3
  119. esphome/components/htu21d/htu21d.cpp +13 -18
  120. esphome/components/htu21d/htu21d.h +1 -1
  121. esphome/components/i2s_audio/__init__.py +1 -2
  122. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  123. esphome/components/ili9xxx/ili9xxx_display.cpp +2 -2
  124. esphome/components/improv_serial/improv_serial_component.cpp +12 -15
  125. esphome/components/improv_serial/improv_serial_component.h +6 -8
  126. esphome/components/json/json_util.cpp +35 -43
  127. esphome/components/json/json_util.h +57 -0
  128. esphome/components/kamstrup_kmp/kamstrup_kmp.cpp +2 -2
  129. esphome/components/key_collector/key_collector.h +4 -4
  130. esphome/components/libretiny/__init__.py +6 -6
  131. esphome/components/libretiny/preferences.cpp +23 -16
  132. esphome/components/light/light_call.cpp +98 -120
  133. esphome/components/light/light_call.h +17 -7
  134. esphome/components/lm75b/__init__.py +0 -0
  135. esphome/components/lm75b/lm75b.cpp +39 -0
  136. esphome/components/lm75b/lm75b.h +19 -0
  137. esphome/components/lm75b/sensor.py +34 -0
  138. esphome/components/lock/lock.h +12 -6
  139. esphome/components/logger/__init__.py +15 -27
  140. esphome/components/logger/logger.cpp +10 -20
  141. esphome/components/logger/logger.h +105 -62
  142. esphome/components/logger/logger_esp32.cpp +0 -48
  143. esphome/components/logger/logger_zephyr.cpp +2 -3
  144. esphome/components/logger/select/logger_level_select.cpp +6 -7
  145. esphome/components/logger/select/logger_level_select.h +7 -0
  146. esphome/components/ltr501/ltr501.cpp +7 -6
  147. esphome/components/ltr_als_ps/ltr_als_ps.cpp +7 -6
  148. esphome/components/matrix_keypad/matrix_keypad.h +4 -4
  149. esphome/components/max7219digit/max7219digit.cpp +1 -1
  150. esphome/components/mcp2515/mcp2515.cpp +31 -3
  151. esphome/components/mcp2515/mcp2515_defs.h +3 -1
  152. esphome/components/md5/md5.cpp +0 -26
  153. esphome/components/md5/md5.h +10 -20
  154. esphome/components/mdns/__init__.py +19 -6
  155. esphome/components/mdns/mdns_component.cpp +27 -59
  156. esphome/components/mdns/mdns_component.h +23 -10
  157. esphome/components/mdns/mdns_esp32.cpp +7 -7
  158. esphome/components/mdns/mdns_esp8266.cpp +6 -6
  159. esphome/components/mdns/mdns_libretiny.cpp +3 -3
  160. esphome/components/mdns/mdns_rp2040.cpp +3 -3
  161. esphome/components/mipi/__init__.py +1 -5
  162. esphome/components/mipi_spi/display.py +24 -8
  163. esphome/components/mipi_spi/mipi_spi.h +3 -3
  164. esphome/components/mixer/speaker/mixer_speaker.cpp +3 -3
  165. esphome/components/mmc5603/mmc5603.cpp +3 -3
  166. esphome/components/modbus/modbus.cpp +27 -13
  167. esphome/components/modbus/modbus.h +5 -3
  168. esphome/components/modbus/modbus_definitions.h +86 -0
  169. esphome/components/modbus_controller/__init__.py +29 -1
  170. esphome/components/modbus_controller/const.py +4 -0
  171. esphome/components/modbus_controller/modbus_controller.cpp +38 -13
  172. esphome/components/modbus_controller/modbus_controller.h +18 -29
  173. esphome/components/mpr121/mpr121.cpp +41 -42
  174. esphome/components/mpr121/mpr121.h +0 -1
  175. esphome/components/nau7802/nau7802.cpp +2 -2
  176. esphome/components/network/__init__.py +7 -3
  177. esphome/components/nextion/display.py +4 -4
  178. esphome/components/nextion/nextion.cpp +8 -8
  179. esphome/components/number/__init__.py +2 -0
  180. esphome/components/number/number_call.cpp +23 -12
  181. esphome/components/number/number_call.h +5 -0
  182. esphome/components/online_image/bmp_image.cpp +2 -1
  183. esphome/components/online_image/jpeg_image.cpp +4 -2
  184. esphome/components/openthread/openthread.cpp +6 -7
  185. esphome/components/openthread/openthread.h +0 -1
  186. esphome/components/ota/ota_backend.h +1 -0
  187. esphome/components/packages/__init__.py +10 -8
  188. esphome/components/packet_transport/packet_transport.cpp +2 -0
  189. esphome/components/pid/pid_controller.cpp +1 -1
  190. esphome/components/prometheus/prometheus_handler.cpp +239 -239
  191. esphome/components/psram/__init__.py +30 -28
  192. esphome/components/qmc5883l/qmc5883l.cpp +15 -0
  193. esphome/components/qmc5883l/qmc5883l.h +3 -0
  194. esphome/components/qmc5883l/sensor.py +31 -12
  195. esphome/components/remote_base/gobox_protocol.cpp +3 -3
  196. esphome/components/remote_receiver/__init__.py +14 -2
  197. esphome/components/remote_receiver/{remote_receiver_esp8266.cpp → remote_receiver.cpp} +2 -2
  198. esphome/components/remote_receiver/remote_receiver.h +4 -0
  199. esphome/components/remote_receiver/remote_receiver_esp32.cpp +18 -1
  200. esphome/components/remote_transmitter/__init__.py +2 -2
  201. esphome/components/remote_transmitter/remote_transmitter.cpp +103 -0
  202. esphome/components/rp2040/__init__.py +11 -11
  203. esphome/components/rtttl/rtttl.cpp +2 -2
  204. esphome/components/scd30/sensor.py +1 -1
  205. esphome/components/script/__init__.py +1 -1
  206. esphome/components/script/script.h +7 -7
  207. esphome/components/select/select.cpp +5 -4
  208. esphome/components/select/select_call.cpp +1 -1
  209. esphome/components/sensirion_common/i2c_sensirion.cpp +2 -1
  210. esphome/components/sensor/__init__.py +2 -0
  211. esphome/components/sha256/__init__.py +22 -0
  212. esphome/components/sha256/sha256.cpp +116 -0
  213. esphome/components/sha256/sha256.h +60 -0
  214. esphome/components/sim800l/sim800l.cpp +8 -4
  215. esphome/components/socket/lwip_raw_tcp_impl.cpp +34 -6
  216. esphome/components/sonoff_d1/sonoff_d1.cpp +1 -1
  217. esphome/components/spi/__init__.py +0 -3
  218. esphome/components/split_buffer/__init__.py +5 -0
  219. esphome/components/split_buffer/split_buffer.cpp +133 -0
  220. esphome/components/split_buffer/split_buffer.h +40 -0
  221. esphome/components/sps30/sps30.cpp +14 -10
  222. esphome/components/sps30/sps30.h +2 -0
  223. esphome/components/st7567_i2c/st7567_i2c.cpp +3 -1
  224. esphome/components/st7789v/st7789v.cpp +3 -2
  225. esphome/components/statsd/statsd.cpp +1 -1
  226. esphome/components/substitutions/__init__.py +3 -1
  227. esphome/components/substitutions/jinja.py +13 -3
  228. esphome/components/sx126x/__init__.py +16 -0
  229. esphome/components/sx126x/sx126x.cpp +15 -1
  230. esphome/components/sx126x/sx126x.h +9 -1
  231. esphome/components/sx126x/sx126x_reg.h +2 -0
  232. esphome/components/text_sensor/text_sensor.cpp +16 -0
  233. esphome/components/text_sensor/text_sensor.h +3 -10
  234. esphome/components/tormatic/tormatic_cover.cpp +1 -1
  235. esphome/components/tuya/select/tuya_select.cpp +1 -1
  236. esphome/components/tuya/tuya.cpp +29 -4
  237. esphome/components/uart/__init__.py +36 -26
  238. esphome/components/uart/uart.h +6 -0
  239. esphome/components/uart/uart_component.cpp +8 -0
  240. esphome/components/uart/uart_component.h +28 -0
  241. esphome/components/uart/uart_component_esp_idf.cpp +64 -10
  242. esphome/components/uart/uart_component_esp_idf.h +5 -2
  243. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +1 -1
  244. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +1 -1
  245. esphome/components/uponor_smatrix/uponor_smatrix.cpp +3 -3
  246. esphome/components/usb_host/__init__.py +2 -1
  247. esphome/components/usb_host/usb_host.h +82 -13
  248. esphome/components/usb_host/usb_host_client.cpp +180 -24
  249. esphome/components/usb_host/usb_host_component.cpp +1 -1
  250. esphome/components/usb_uart/__init__.py +0 -1
  251. esphome/components/usb_uart/ch34x.cpp +4 -4
  252. esphome/components/usb_uart/cp210x.cpp +3 -3
  253. esphome/components/usb_uart/usb_uart.cpp +88 -32
  254. esphome/components/usb_uart/usb_uart.h +30 -6
  255. esphome/components/valve/valve.cpp +1 -0
  256. esphome/components/veml7700/veml7700.cpp +7 -6
  257. esphome/components/version/version_text_sensor.cpp +2 -1
  258. esphome/components/voice_assistant/voice_assistant.cpp +3 -3
  259. esphome/components/waveshare_epaper/waveshare_epaper.cpp +4 -4
  260. esphome/components/web_server/list_entities.cpp +3 -4
  261. esphome/components/web_server/list_entities.h +8 -10
  262. esphome/components/web_server/ota/__init__.py +1 -1
  263. esphome/components/web_server/ota/ota_web_server.cpp +9 -3
  264. esphome/components/web_server/web_server.cpp +509 -404
  265. esphome/components/web_server/web_server.h +5 -6
  266. esphome/components/web_server/web_server_v1.cpp +21 -19
  267. esphome/components/web_server_base/__init__.py +5 -2
  268. esphome/components/web_server_base/web_server_base.h +27 -7
  269. esphome/components/web_server_idf/__init__.py +1 -1
  270. esphome/components/web_server_idf/multipart.cpp +2 -2
  271. esphome/components/web_server_idf/multipart.h +2 -2
  272. esphome/components/web_server_idf/utils.cpp +2 -2
  273. esphome/components/web_server_idf/utils.h +2 -2
  274. esphome/components/web_server_idf/web_server_idf.cpp +118 -26
  275. esphome/components/web_server_idf/web_server_idf.h +12 -10
  276. esphome/components/wifi/__init__.py +13 -11
  277. esphome/components/wifi/wifi_component.cpp +73 -56
  278. esphome/components/wifi/wifi_component.h +4 -4
  279. esphome/components/wifi/wifi_component_esp8266.cpp +1 -1
  280. esphome/components/wifi/wifi_component_esp_idf.cpp +24 -4
  281. esphome/components/wireguard/__init__.py +1 -1
  282. esphome/components/wts01/__init__.py +0 -0
  283. esphome/components/wts01/sensor.py +41 -0
  284. esphome/components/wts01/wts01.cpp +91 -0
  285. esphome/components/wts01/wts01.h +27 -0
  286. esphome/components/zephyr/__init__.py +5 -5
  287. esphome/components/zwave_proxy/__init__.py +43 -0
  288. esphome/components/zwave_proxy/zwave_proxy.cpp +346 -0
  289. esphome/components/zwave_proxy/zwave_proxy.h +93 -0
  290. esphome/config.py +79 -24
  291. esphome/config_validation.py +13 -15
  292. esphome/const.py +9 -2
  293. esphome/core/__init__.py +31 -22
  294. esphome/core/component.cpp +28 -18
  295. esphome/core/component_iterator.h +2 -1
  296. esphome/core/config.py +15 -15
  297. esphome/core/defines.h +19 -0
  298. esphome/core/hash_base.h +56 -0
  299. esphome/core/helpers.cpp +19 -3
  300. esphome/core/helpers.h +26 -0
  301. esphome/core/scheduler.cpp +5 -21
  302. esphome/core/scheduler.h +19 -8
  303. esphome/core/string_ref.h +1 -1
  304. esphome/core/time.cpp +5 -5
  305. esphome/cpp_generator.py +4 -29
  306. esphome/dashboard/const.py +21 -4
  307. esphome/dashboard/core.py +10 -8
  308. esphome/dashboard/dns.py +15 -0
  309. esphome/dashboard/entries.py +15 -21
  310. esphome/dashboard/models.py +76 -0
  311. esphome/dashboard/settings.py +7 -7
  312. esphome/dashboard/status/mdns.py +46 -2
  313. esphome/dashboard/web_server.py +367 -93
  314. esphome/espota2.py +111 -31
  315. esphome/external_files.py +6 -7
  316. esphome/git.py +8 -0
  317. esphome/helpers.py +124 -77
  318. esphome/loader.py +8 -9
  319. esphome/platformio_api.py +25 -18
  320. esphome/storage_json.py +26 -21
  321. esphome/types.py +30 -2
  322. esphome/util.py +32 -16
  323. esphome/vscode.py +8 -8
  324. esphome/wizard.py +10 -10
  325. esphome/writer.py +50 -15
  326. esphome/yaml_util.py +37 -31
  327. esphome/zeroconf.py +12 -3
  328. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/METADATA +11 -11
  329. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/RECORD +333 -313
  330. esphome/components/event_emitter/__init__.py +0 -5
  331. esphome/components/event_emitter/event_emitter.cpp +0 -14
  332. esphome/components/event_emitter/event_emitter.h +0 -63
  333. esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -125
  334. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +0 -107
  335. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +0 -110
  336. esphome/components/uart/uart_component_esp32_arduino.cpp +0 -214
  337. esphome/components/uart/uart_component_esp32_arduino.h +0 -60
  338. esphome/components/wifi/wifi_component_esp32_arduino.cpp +0 -860
  339. esphome/core/string_ref.cpp +0 -12
  340. esphome/dashboard/util/file.py +0 -63
  341. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/WHEEL +0 -0
  342. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/entry_points.txt +0 -0
  343. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/licenses/LICENSE +0 -0
  344. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/top_level.txt +0 -0
@@ -572,7 +572,7 @@ void MixerSpeaker::audio_mixer_task(void *params) {
572
572
  }
573
573
  } else {
574
574
  // Determine how many frames to mix
575
- for (int i = 0; i < transfer_buffers_with_data.size(); ++i) {
575
+ for (size_t i = 0; i < transfer_buffers_with_data.size(); ++i) {
576
576
  const uint32_t frames_available_in_buffer =
577
577
  speakers_with_data[i]->get_audio_stream_info().bytes_to_frames(transfer_buffers_with_data[i]->available());
578
578
  frames_to_mix = std::min(frames_to_mix, frames_available_in_buffer);
@@ -581,7 +581,7 @@ void MixerSpeaker::audio_mixer_task(void *params) {
581
581
  audio::AudioStreamInfo primary_stream_info = speakers_with_data[0]->get_audio_stream_info();
582
582
 
583
583
  // Mix two streams together
584
- for (int i = 1; i < transfer_buffers_with_data.size(); ++i) {
584
+ for (size_t i = 1; i < transfer_buffers_with_data.size(); ++i) {
585
585
  mix_audio_samples(primary_buffer, primary_stream_info,
586
586
  reinterpret_cast<int16_t *>(transfer_buffers_with_data[i]->get_buffer_start()),
587
587
  speakers_with_data[i]->get_audio_stream_info(),
@@ -596,7 +596,7 @@ void MixerSpeaker::audio_mixer_task(void *params) {
596
596
  }
597
597
 
598
598
  // Update source transfer buffer lengths and add new audio durations to the source speaker pending playbacks
599
- for (int i = 0; i < transfer_buffers_with_data.size(); ++i) {
599
+ for (size_t i = 0; i < transfer_buffers_with_data.size(); ++i) {
600
600
  transfer_buffers_with_data[i]->decrease_buffer_length(
601
601
  speakers_with_data[i]->get_audio_stream_info().frames_to_bytes(frames_to_mix));
602
602
  speakers_with_data[i]->pending_playback_frames_ += frames_to_mix;
@@ -128,21 +128,21 @@ void MMC5603Component::update() {
128
128
  raw_x |= buffer[1] << 4;
129
129
  raw_x |= buffer[2] << 0;
130
130
 
131
- const float x = 0.0625 * (raw_x - 524288);
131
+ const float x = 0.00625 * (raw_x - 524288);
132
132
 
133
133
  int32_t raw_y = 0;
134
134
  raw_y |= buffer[3] << 12;
135
135
  raw_y |= buffer[4] << 4;
136
136
  raw_y |= buffer[5] << 0;
137
137
 
138
- const float y = 0.0625 * (raw_y - 524288);
138
+ const float y = 0.00625 * (raw_y - 524288);
139
139
 
140
140
  int32_t raw_z = 0;
141
141
  raw_z |= buffer[6] << 12;
142
142
  raw_z |= buffer[7] << 4;
143
143
  raw_z |= buffer[8] << 0;
144
144
 
145
- const float z = 0.0625 * (raw_z - 524288);
145
+ const float z = 0.00625 * (raw_z - 524288);
146
146
 
147
147
  const float heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
148
148
  ESP_LOGD(TAG, "Got x=%0.02fµT y=%0.02fµT z=%0.02fµT heading=%0.01f°", x, y, z, heading);
@@ -66,7 +66,10 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) {
66
66
  uint8_t data_offset = 3;
67
67
 
68
68
  // Per https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf Ch 5 User-Defined function codes
69
- if (((function_code >= 65) && (function_code <= 72)) || ((function_code >= 100) && (function_code <= 110))) {
69
+ if (((function_code >= FUNCTION_CODE_USER_DEFINED_SPACE_1_INIT) &&
70
+ (function_code <= FUNCTION_CODE_USER_DEFINED_SPACE_1_END)) ||
71
+ ((function_code >= FUNCTION_CODE_USER_DEFINED_SPACE_2_INIT) &&
72
+ (function_code <= FUNCTION_CODE_USER_DEFINED_SPACE_2_END))) {
70
73
  // Handle user-defined function, since we don't know how big this ought to be,
71
74
  // ideally we should delegate the entire length detection to whatever handler is
72
75
  // installed, but wait, there is the CRC, and if we get a hit there is a good
@@ -91,10 +94,14 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) {
91
94
  } else {
92
95
  // data starts at 2 and length is 4 for read registers commands
93
96
  if (this->role == ModbusRole::SERVER) {
94
- if (function_code == 0x1 || function_code == 0x3 || function_code == 0x4 || function_code == 0x6) {
97
+ if (function_code == ModbusFunctionCode::READ_COILS ||
98
+ function_code == ModbusFunctionCode::READ_DISCRETE_INPUTS ||
99
+ function_code == ModbusFunctionCode::READ_HOLDING_REGISTERS ||
100
+ function_code == ModbusFunctionCode::READ_INPUT_REGISTERS ||
101
+ function_code == ModbusFunctionCode::WRITE_SINGLE_REGISTER) {
95
102
  data_offset = 2;
96
103
  data_len = 4;
97
- } else if (function_code == 0x10) {
104
+ } else if (function_code == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) {
98
105
  if (at < 6) {
99
106
  return true;
100
107
  }
@@ -104,7 +111,10 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) {
104
111
  }
105
112
  } else {
106
113
  // the response for write command mirrors the requests and data starts at offset 2 instead of 3 for read commands
107
- if (function_code == 0x5 || function_code == 0x06 || function_code == 0xF || function_code == 0x10) {
114
+ if (function_code == ModbusFunctionCode::WRITE_SINGLE_COIL ||
115
+ function_code == ModbusFunctionCode::WRITE_SINGLE_REGISTER ||
116
+ function_code == ModbusFunctionCode::WRITE_MULTIPLE_COILS ||
117
+ function_code == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) {
108
118
  data_offset = 2;
109
119
  data_len = 4;
110
120
  }
@@ -112,7 +122,7 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) {
112
122
 
113
123
  // Error ( msb indicates error )
114
124
  // response format: Byte[0] = device address, Byte[1] function code | 0x80 , Byte[2] exception code, Byte[3-4] crc
115
- if ((function_code & 0x80) == 0x80) {
125
+ if ((function_code & FUNCTION_CODE_EXCEPTION_MASK) == FUNCTION_CODE_EXCEPTION_MASK) {
116
126
  data_offset = 2;
117
127
  data_len = 1;
118
128
  }
@@ -143,10 +153,10 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) {
143
153
  if (device->address_ == address) {
144
154
  found = true;
145
155
  // Is it an error response?
146
- if ((function_code & 0x80) == 0x80) {
156
+ if ((function_code & FUNCTION_CODE_EXCEPTION_MASK) == FUNCTION_CODE_EXCEPTION_MASK) {
147
157
  ESP_LOGD(TAG, "Modbus error function code: 0x%X exception: %d", function_code, raw[2]);
148
158
  if (waiting_for_response != 0) {
149
- device->on_modbus_error(function_code & 0x7F, raw[2]);
159
+ device->on_modbus_error(function_code & FUNCTION_CODE_MASK, raw[2]);
150
160
  } else {
151
161
  // Ignore modbus exception not related to a pending command
152
162
  ESP_LOGD(TAG, "Ignoring Modbus error - not expecting a response");
@@ -154,12 +164,14 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) {
154
164
  continue;
155
165
  }
156
166
  if (this->role == ModbusRole::SERVER) {
157
- if (function_code == 0x3 || function_code == 0x4) {
167
+ if (function_code == ModbusFunctionCode::READ_HOLDING_REGISTERS ||
168
+ function_code == ModbusFunctionCode::READ_INPUT_REGISTERS) {
158
169
  device->on_modbus_read_registers(function_code, uint16_t(data[1]) | (uint16_t(data[0]) << 8),
159
170
  uint16_t(data[3]) | (uint16_t(data[2]) << 8));
160
171
  continue;
161
172
  }
162
- if (function_code == 0x6 || function_code == 0x10) {
173
+ if (function_code == ModbusFunctionCode::WRITE_SINGLE_REGISTER ||
174
+ function_code == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) {
163
175
  device->on_modbus_write_registers(function_code, data);
164
176
  continue;
165
177
  }
@@ -199,7 +211,7 @@ void Modbus::send(uint8_t address, uint8_t function_code, uint16_t start_address
199
211
 
200
212
  // Only check max number of registers for standard function codes
201
213
  // Some devices use non standard codes like 0x43
202
- if (number_of_entities > MAX_VALUES && function_code <= 0x10) {
214
+ if (number_of_entities > MAX_VALUES && function_code <= ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) {
203
215
  ESP_LOGE(TAG, "send too many values %d max=%zu", number_of_entities, MAX_VALUES);
204
216
  return;
205
217
  }
@@ -210,15 +222,17 @@ void Modbus::send(uint8_t address, uint8_t function_code, uint16_t start_address
210
222
  if (this->role == ModbusRole::CLIENT) {
211
223
  data.push_back(start_address >> 8);
212
224
  data.push_back(start_address >> 0);
213
- if (function_code != 0x5 && function_code != 0x6) {
225
+ if (function_code != ModbusFunctionCode::WRITE_SINGLE_COIL &&
226
+ function_code != ModbusFunctionCode::WRITE_SINGLE_REGISTER) {
214
227
  data.push_back(number_of_entities >> 8);
215
228
  data.push_back(number_of_entities >> 0);
216
229
  }
217
230
  }
218
231
 
219
232
  if (payload != nullptr) {
220
- if (this->role == ModbusRole::SERVER || function_code == 0xF || function_code == 0x10) { // Write multiple
221
- data.push_back(payload_len); // Byte count is required for write
233
+ if (this->role == ModbusRole::SERVER || function_code == ModbusFunctionCode::WRITE_MULTIPLE_COILS ||
234
+ function_code == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) { // Write multiple
235
+ data.push_back(payload_len); // Byte count is required for write
222
236
  } else {
223
237
  payload_len = 2; // Write single register or coil
224
238
  }
@@ -3,6 +3,8 @@
3
3
  #include "esphome/core/component.h"
4
4
  #include "esphome/components/uart/uart.h"
5
5
 
6
+ #include "esphome/components/modbus/modbus_definitions.h"
7
+
6
8
  #include <vector>
7
9
 
8
10
  namespace esphome {
@@ -65,12 +67,12 @@ class ModbusDevice {
65
67
  this->parent_->send(this->address_, function, start_address, number_of_entities, payload_len, payload);
66
68
  }
67
69
  void send_raw(const std::vector<uint8_t> &payload) { this->parent_->send_raw(payload); }
68
- void send_error(uint8_t function_code, uint8_t exception_code) {
70
+ void send_error(uint8_t function_code, ModbusExceptionCode exception_code) {
69
71
  std::vector<uint8_t> error_response;
70
72
  error_response.reserve(3);
71
73
  error_response.push_back(this->address_);
72
- error_response.push_back(function_code | 0x80);
73
- error_response.push_back(exception_code);
74
+ error_response.push_back(function_code | FUNCTION_CODE_EXCEPTION_MASK);
75
+ error_response.push_back(static_cast<uint8_t>(exception_code));
74
76
  this->send_raw(error_response);
75
77
  }
76
78
  // If more than one device is connected block sending a new command before a response is received
@@ -0,0 +1,86 @@
1
+ #pragma once
2
+
3
+ #include "esphome/core/component.h"
4
+
5
+ namespace esphome {
6
+ namespace modbus {
7
+
8
+ /// Modbus definitions from specs:
9
+ /// https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf
10
+ // 5 Function Code Categories
11
+ const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_1_INIT = 65; // 0x41
12
+ const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_1_END = 72; // 0x48
13
+
14
+ const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_2_INIT = 100; // 0x64
15
+ const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_2_END = 110; // 0x6E
16
+
17
+ enum class ModbusFunctionCode : uint8_t {
18
+ CUSTOM = 0x00,
19
+ READ_COILS = 0x01,
20
+ READ_DISCRETE_INPUTS = 0x02,
21
+ READ_HOLDING_REGISTERS = 0x03,
22
+ READ_INPUT_REGISTERS = 0x04,
23
+ WRITE_SINGLE_COIL = 0x05,
24
+ WRITE_SINGLE_REGISTER = 0x06,
25
+ READ_EXCEPTION_STATUS = 0x07, // not implemented
26
+ DIAGNOSTICS = 0x08, // not implemented
27
+ GET_COMM_EVENT_COUNTER = 0x0B, // not implemented
28
+ GET_COMM_EVENT_LOG = 0x0C, // not implemented
29
+ WRITE_MULTIPLE_COILS = 0x0F,
30
+ WRITE_MULTIPLE_REGISTERS = 0x10,
31
+ REPORT_SERVER_ID = 0x11, // not implemented
32
+ READ_FILE_RECORD = 0x14, // not implemented
33
+ WRITE_FILE_RECORD = 0x15, // not implemented
34
+ MASK_WRITE_REGISTER = 0x16, // not implemented
35
+ READ_WRITE_MULTIPLE_REGISTERS = 0x17, // not implemented
36
+ READ_FIFO_QUEUE = 0x18, // not implemented
37
+ };
38
+
39
+ /*Allow comparison operators between ModbusFunctionCode and uint8_t*/
40
+ inline bool operator==(ModbusFunctionCode lhs, uint8_t rhs) { return static_cast<uint8_t>(lhs) == rhs; }
41
+ inline bool operator==(uint8_t lhs, ModbusFunctionCode rhs) { return lhs == static_cast<uint8_t>(rhs); }
42
+ inline bool operator!=(ModbusFunctionCode lhs, uint8_t rhs) { return !(static_cast<uint8_t>(lhs) == rhs); }
43
+ inline bool operator!=(uint8_t lhs, ModbusFunctionCode rhs) { return !(lhs == static_cast<uint8_t>(rhs)); }
44
+ inline bool operator<(ModbusFunctionCode lhs, uint8_t rhs) { return static_cast<uint8_t>(lhs) < rhs; }
45
+ inline bool operator<(uint8_t lhs, ModbusFunctionCode rhs) { return lhs < static_cast<uint8_t>(rhs); }
46
+ inline bool operator<=(ModbusFunctionCode lhs, uint8_t rhs) { return static_cast<uint8_t>(lhs) <= rhs; }
47
+ inline bool operator<=(uint8_t lhs, ModbusFunctionCode rhs) { return lhs <= static_cast<uint8_t>(rhs); }
48
+ inline bool operator>(ModbusFunctionCode lhs, uint8_t rhs) { return static_cast<uint8_t>(lhs) > rhs; }
49
+ inline bool operator>(uint8_t lhs, ModbusFunctionCode rhs) { return lhs > static_cast<uint8_t>(rhs); }
50
+ inline bool operator>=(ModbusFunctionCode lhs, uint8_t rhs) { return static_cast<uint8_t>(lhs) >= rhs; }
51
+ inline bool operator>=(uint8_t lhs, ModbusFunctionCode rhs) { return lhs >= static_cast<uint8_t>(rhs); }
52
+
53
+ // 4.3 MODBUS Data model
54
+ enum class ModbusRegisterType : uint8_t {
55
+ CUSTOM = 0x00,
56
+ COIL = 0x01,
57
+ DISCRETE_INPUT = 0x02,
58
+ HOLDING = 0x03,
59
+ READ = 0x04,
60
+ };
61
+
62
+ // 7 MODBUS Exception Responses:
63
+ const uint8_t FUNCTION_CODE_MASK = 0x7F;
64
+ const uint8_t FUNCTION_CODE_EXCEPTION_MASK = 0x80;
65
+
66
+ enum class ModbusExceptionCode : uint8_t {
67
+ ILLEGAL_FUNCTION = 0x01,
68
+ ILLEGAL_DATA_ADDRESS = 0x02,
69
+ ILLEGAL_DATA_VALUE = 0x03,
70
+ SERVICE_DEVICE_FAILURE = 0x04,
71
+ ACKNOWLEDGE = 0x05,
72
+ SERVER_DEVICE_BUSY = 0x06,
73
+ MEMORY_PARITY_ERROR = 0x08,
74
+ GATEWAY_PATH_UNAVAILABLE = 0x0A,
75
+ GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 0x0B,
76
+ };
77
+
78
+ // 6.12 16 (0x10) Write Multiple registers:
79
+ const uint8_t MAX_NUM_OF_REGISTERS_TO_WRITE = 123; // 0x7B
80
+
81
+ // 6.3 03 (0x03) Read Holding Registers
82
+ // 6.4 04 (0x04) Read Input Registers
83
+ const uint8_t MAX_NUM_OF_REGISTERS_TO_READ = 125; // 0x7D
84
+ /// End of Modbus definitions
85
+ } // namespace modbus
86
+ } // namespace esphome
@@ -20,6 +20,7 @@ from .const import (
20
20
  CONF_BYTE_OFFSET,
21
21
  CONF_COMMAND_THROTTLE,
22
22
  CONF_CUSTOM_COMMAND,
23
+ CONF_ENABLED,
23
24
  CONF_FORCE_NEW_RANGE,
24
25
  CONF_MAX_CMD_RETRIES,
25
26
  CONF_MODBUS_CONTROLLER_ID,
@@ -28,8 +29,11 @@ from .const import (
28
29
  CONF_ON_OFFLINE,
29
30
  CONF_ON_ONLINE,
30
31
  CONF_REGISTER_COUNT,
32
+ CONF_REGISTER_LAST_ADDRESS,
31
33
  CONF_REGISTER_TYPE,
34
+ CONF_REGISTER_VALUE,
32
35
  CONF_RESPONSE_SIZE,
36
+ CONF_SERVER_COURTESY_RESPONSE,
33
37
  CONF_SKIP_UPDATES,
34
38
  CONF_VALUE_TYPE,
35
39
  )
@@ -49,6 +53,7 @@ ModbusController = modbus_controller_ns.class_(
49
53
  )
50
54
 
51
55
  SensorItem = modbus_controller_ns.struct("SensorItem")
56
+ ServerCourtesyResponse = modbus_controller_ns.struct("ServerCourtesyResponse")
52
57
  ServerRegister = modbus_controller_ns.struct("ServerRegister")
53
58
 
54
59
  ModbusFunctionCode_ns = modbus_controller_ns.namespace("ModbusFunctionCode")
@@ -143,6 +148,14 @@ ModbusOfflineTrigger = modbus_controller_ns.class_(
143
148
 
144
149
  _LOGGER = logging.getLogger(__name__)
145
150
 
151
+ SERVER_COURTESY_RESPONSE_SCHEMA = cv.Schema(
152
+ {
153
+ cv.Optional(CONF_ENABLED, default=False): cv.boolean,
154
+ cv.Optional(CONF_REGISTER_LAST_ADDRESS, default=0xFFFF): cv.hex_uint16_t,
155
+ cv.Optional(CONF_REGISTER_VALUE, default=0): cv.hex_uint16_t,
156
+ }
157
+ )
158
+
146
159
  ModbusServerRegisterSchema = cv.Schema(
147
160
  {
148
161
  cv.GenerateID(): cv.declare_id(ServerRegister),
@@ -162,6 +175,7 @@ CONFIG_SCHEMA = cv.All(
162
175
  cv.Optional(
163
176
  CONF_COMMAND_THROTTLE, default="0ms"
164
177
  ): cv.positive_time_period_milliseconds,
178
+ cv.Optional(CONF_SERVER_COURTESY_RESPONSE): SERVER_COURTESY_RESPONSE_SCHEMA,
165
179
  cv.Optional(CONF_MAX_CMD_RETRIES, default=4): cv.positive_int,
166
180
  cv.Optional(CONF_OFFLINE_SKIP_UPDATES, default=0): cv.positive_int,
167
181
  cv.Optional(
@@ -232,7 +246,7 @@ def validate_modbus_register(config):
232
246
 
233
247
 
234
248
  def _final_validate(config):
235
- if CONF_SERVER_REGISTERS in config:
249
+ if CONF_SERVER_COURTESY_RESPONSE in config or CONF_SERVER_REGISTERS in config:
236
250
  return modbus.final_validate_modbus_device("modbus_controller", role="server")(
237
251
  config
238
252
  )
@@ -299,6 +313,20 @@ async def to_code(config):
299
313
  var = cg.new_Pvariable(config[CONF_ID])
300
314
  cg.add(var.set_allow_duplicate_commands(config[CONF_ALLOW_DUPLICATE_COMMANDS]))
301
315
  cg.add(var.set_command_throttle(config[CONF_COMMAND_THROTTLE]))
316
+ if server_courtesy_response := config.get(CONF_SERVER_COURTESY_RESPONSE):
317
+ cg.add(
318
+ var.set_server_courtesy_response(
319
+ cg.StructInitializer(
320
+ ServerCourtesyResponse,
321
+ ("enabled", server_courtesy_response[CONF_ENABLED]),
322
+ (
323
+ "register_last_address",
324
+ server_courtesy_response[CONF_REGISTER_LAST_ADDRESS],
325
+ ),
326
+ ("register_value", server_courtesy_response[CONF_REGISTER_VALUE]),
327
+ )
328
+ )
329
+ )
302
330
  cg.add(var.set_max_cmd_retries(config[CONF_MAX_CMD_RETRIES]))
303
331
  cg.add(var.set_offline_skip_updates(config[CONF_OFFLINE_SKIP_UPDATES]))
304
332
  if CONF_SERVER_REGISTERS in config:
@@ -2,6 +2,7 @@ CONF_ALLOW_DUPLICATE_COMMANDS = "allow_duplicate_commands"
2
2
  CONF_BITMASK = "bitmask"
3
3
  CONF_BYTE_OFFSET = "byte_offset"
4
4
  CONF_COMMAND_THROTTLE = "command_throttle"
5
+ CONF_ENABLED = "enabled"
5
6
  CONF_OFFLINE_SKIP_UPDATES = "offline_skip_updates"
6
7
  CONF_CUSTOM_COMMAND = "custom_command"
7
8
  CONF_FORCE_NEW_RANGE = "force_new_range"
@@ -13,8 +14,11 @@ CONF_ON_ONLINE = "on_online"
13
14
  CONF_ON_OFFLINE = "on_offline"
14
15
  CONF_RAW_ENCODE = "raw_encode"
15
16
  CONF_REGISTER_COUNT = "register_count"
17
+ CONF_REGISTER_LAST_ADDRESS = "register_last_address"
16
18
  CONF_REGISTER_TYPE = "register_type"
19
+ CONF_REGISTER_VALUE = "register_value"
17
20
  CONF_RESPONSE_SIZE = "response_size"
21
+ CONF_SERVER_COURTESY_RESPONSE = "server_courtesy_response"
18
22
  CONF_SKIP_UPDATES = "skip_updates"
19
23
  CONF_USE_WRITE_MULTIPLE = "use_write_multiple"
20
24
  CONF_VALUE_TYPE = "value_type"
@@ -112,6 +112,12 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t
112
112
  "0x%X.",
113
113
  this->address_, function_code, start_address, number_of_registers);
114
114
 
115
+ if (number_of_registers == 0 || number_of_registers > modbus::MAX_NUM_OF_REGISTERS_TO_READ) {
116
+ ESP_LOGW(TAG, "Invalid number of registers %d. Sending exception response.", number_of_registers);
117
+ this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_ADDRESS);
118
+ return;
119
+ }
120
+
115
121
  std::vector<uint16_t> sixteen_bit_response;
116
122
  for (uint16_t current_address = start_address; current_address < start_address + number_of_registers;) {
117
123
  bool found = false;
@@ -136,9 +142,21 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t
136
142
  }
137
143
 
138
144
  if (!found) {
139
- ESP_LOGW(TAG, "Could not match any register to address %02X. Sending exception response.", current_address);
140
- send_error(function_code, 0x02);
141
- return;
145
+ if (this->server_courtesy_response_.enabled &&
146
+ (current_address <= this->server_courtesy_response_.register_last_address)) {
147
+ ESP_LOGD(TAG,
148
+ "Could not match any register to address 0x%02X, but default allowed. "
149
+ "Returning default value: %d.",
150
+ current_address, this->server_courtesy_response_.register_value);
151
+ sixteen_bit_response.push_back(this->server_courtesy_response_.register_value);
152
+ current_address += 1; // Just increment by 1, as the default response is a single register
153
+ } else {
154
+ ESP_LOGW(TAG,
155
+ "Could not match any register to address 0x%02X and default not allowed. Sending exception response.",
156
+ current_address);
157
+ this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_ADDRESS);
158
+ return;
159
+ }
142
160
  }
143
161
  }
144
162
 
@@ -156,27 +174,27 @@ void ModbusController::on_modbus_write_registers(uint8_t function_code, const st
156
174
  uint16_t number_of_registers;
157
175
  uint16_t payload_offset;
158
176
 
159
- if (function_code == 0x10) {
177
+ if (function_code == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) {
160
178
  number_of_registers = uint16_t(data[3]) | (uint16_t(data[2]) << 8);
161
- if (number_of_registers == 0 || number_of_registers > 0x7B) {
179
+ if (number_of_registers == 0 || number_of_registers > modbus::MAX_NUM_OF_REGISTERS_TO_WRITE) {
162
180
  ESP_LOGW(TAG, "Invalid number of registers %d. Sending exception response.", number_of_registers);
163
- send_error(function_code, 3);
181
+ this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE);
164
182
  return;
165
183
  }
166
184
  uint16_t payload_size = data[4];
167
185
  if (payload_size != number_of_registers * 2) {
168
186
  ESP_LOGW(TAG, "Payload size of %d bytes is not 2 times the number of registers (%d). Sending exception response.",
169
187
  payload_size, number_of_registers);
170
- send_error(function_code, 3);
188
+ this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE);
171
189
  return;
172
190
  }
173
191
  payload_offset = 5;
174
- } else if (function_code == 0x06) {
192
+ } else if (function_code == ModbusFunctionCode::WRITE_SINGLE_REGISTER) {
175
193
  number_of_registers = 1;
176
194
  payload_offset = 2;
177
195
  } else {
178
196
  ESP_LOGW(TAG, "Invalid function code 0x%X. Sending exception response.", function_code);
179
- send_error(function_code, 1);
197
+ this->send_error(function_code, ModbusExceptionCode::ILLEGAL_FUNCTION);
180
198
  return;
181
199
  }
182
200
 
@@ -211,7 +229,7 @@ void ModbusController::on_modbus_write_registers(uint8_t function_code, const st
211
229
  if (!for_each_register([](ServerRegister *server_register, uint16_t offset) -> bool {
212
230
  return server_register->write_lambda != nullptr;
213
231
  })) {
214
- send_error(function_code, 1);
232
+ this->send_error(function_code, ModbusExceptionCode::ILLEGAL_FUNCTION);
215
233
  return;
216
234
  }
217
235
 
@@ -220,7 +238,7 @@ void ModbusController::on_modbus_write_registers(uint8_t function_code, const st
220
238
  int64_t number = payload_to_number(data, server_register->value_type, offset, 0xFFFFFFFF);
221
239
  return server_register->write_lambda(number);
222
240
  })) {
223
- send_error(function_code, 4);
241
+ this->send_error(function_code, ModbusExceptionCode::SERVICE_DEVICE_FAILURE);
224
242
  return;
225
243
  }
226
244
 
@@ -431,8 +449,15 @@ void ModbusController::dump_config() {
431
449
  "ModbusController:\n"
432
450
  " Address: 0x%02X\n"
433
451
  " Max Command Retries: %d\n"
434
- " Offline Skip Updates: %d",
435
- this->address_, this->max_cmd_retries_, this->offline_skip_updates_);
452
+ " Offline Skip Updates: %d\n"
453
+ " Server Courtesy Response:\n"
454
+ " Enabled: %s\n"
455
+ " Register Last Address: 0x%02X\n"
456
+ " Register Value: %d",
457
+ this->address_, this->max_cmd_retries_, this->offline_skip_updates_,
458
+ this->server_courtesy_response_.enabled ? "true" : "false",
459
+ this->server_courtesy_response_.register_last_address, this->server_courtesy_response_.register_value);
460
+
436
461
  #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
437
462
  ESP_LOGCONFIG(TAG, "sensormap");
438
463
  for (auto &it : this->sensorset_) {
@@ -16,35 +16,9 @@ namespace modbus_controller {
16
16
 
17
17
  class ModbusController;
18
18
 
19
- enum class ModbusFunctionCode {
20
- CUSTOM = 0x00,
21
- READ_COILS = 0x01,
22
- READ_DISCRETE_INPUTS = 0x02,
23
- READ_HOLDING_REGISTERS = 0x03,
24
- READ_INPUT_REGISTERS = 0x04,
25
- WRITE_SINGLE_COIL = 0x05,
26
- WRITE_SINGLE_REGISTER = 0x06,
27
- READ_EXCEPTION_STATUS = 0x07, // not implemented
28
- DIAGNOSTICS = 0x08, // not implemented
29
- GET_COMM_EVENT_COUNTER = 0x0B, // not implemented
30
- GET_COMM_EVENT_LOG = 0x0C, // not implemented
31
- WRITE_MULTIPLE_COILS = 0x0F,
32
- WRITE_MULTIPLE_REGISTERS = 0x10,
33
- REPORT_SERVER_ID = 0x11, // not implemented
34
- READ_FILE_RECORD = 0x14, // not implemented
35
- WRITE_FILE_RECORD = 0x15, // not implemented
36
- MASK_WRITE_REGISTER = 0x16, // not implemented
37
- READ_WRITE_MULTIPLE_REGISTERS = 0x17, // not implemented
38
- READ_FIFO_QUEUE = 0x18, // not implemented
39
- };
40
-
41
- enum class ModbusRegisterType : uint8_t {
42
- CUSTOM = 0x0,
43
- COIL = 0x01,
44
- DISCRETE_INPUT = 0x02,
45
- HOLDING = 0x03,
46
- READ = 0x04,
47
- };
19
+ using modbus::ModbusFunctionCode;
20
+ using modbus::ModbusRegisterType;
21
+ using modbus::ModbusExceptionCode;
48
22
 
49
23
  enum class SensorValueType : uint8_t {
50
24
  RAW = 0x00, // variable length
@@ -256,6 +230,12 @@ class SensorItem {
256
230
  bool force_new_range{false};
257
231
  };
258
232
 
233
+ struct ServerCourtesyResponse {
234
+ bool enabled{false};
235
+ uint16_t register_last_address{0xFFFF};
236
+ uint16_t register_value{0};
237
+ };
238
+
259
239
  class ServerRegister {
260
240
  using ReadLambda = std::function<int64_t()>;
261
241
  using WriteLambda = std::function<bool(int64_t value)>;
@@ -530,6 +510,12 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice {
530
510
  void set_max_cmd_retries(uint8_t max_cmd_retries) { this->max_cmd_retries_ = max_cmd_retries; }
531
511
  /// get how many times a command will be (re)sent if no response is received
532
512
  uint8_t get_max_cmd_retries() { return this->max_cmd_retries_; }
513
+ /// Called by esphome generated code to set the server courtesy response object
514
+ void set_server_courtesy_response(const ServerCourtesyResponse &server_courtesy_response) {
515
+ this->server_courtesy_response_ = server_courtesy_response;
516
+ }
517
+ /// Get the server courtesy response object
518
+ ServerCourtesyResponse get_server_courtesy_response() const { return this->server_courtesy_response_; }
533
519
 
534
520
  protected:
535
521
  /// parse sensormap_ and create range of sequential addresses
@@ -572,6 +558,9 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice {
572
558
  CallbackManager<void(int, int)> online_callback_{};
573
559
  /// Server offline callback
574
560
  CallbackManager<void(int, int)> offline_callback_{};
561
+ /// Server courtesy response
562
+ ServerCourtesyResponse server_courtesy_response_{
563
+ .enabled = false, .register_last_address = 0xFFFF, .register_value = 0};
575
564
  };
576
565
 
577
566
  /** Convert vector<uint8_t> response payload to float.
@@ -11,47 +11,49 @@ namespace mpr121 {
11
11
  static const char *const TAG = "mpr121";
12
12
 
13
13
  void MPR121Component::setup() {
14
+ this->disable_loop();
14
15
  // soft reset device
15
16
  this->write_byte(MPR121_SOFTRESET, 0x63);
16
- delay(100); // NOLINT
17
- if (!this->write_byte(MPR121_ECR, 0x0)) {
18
- this->error_code_ = COMMUNICATION_FAILED;
19
- this->mark_failed();
20
- return;
21
- }
22
-
23
- // set touch sensitivity for all 12 channels
24
- for (auto *channel : this->channels_) {
25
- channel->setup();
26
- }
27
- this->write_byte(MPR121_MHDR, 0x01);
28
- this->write_byte(MPR121_NHDR, 0x01);
29
- this->write_byte(MPR121_NCLR, 0x0E);
30
- this->write_byte(MPR121_FDLR, 0x00);
31
-
32
- this->write_byte(MPR121_MHDF, 0x01);
33
- this->write_byte(MPR121_NHDF, 0x05);
34
- this->write_byte(MPR121_NCLF, 0x01);
35
- this->write_byte(MPR121_FDLF, 0x00);
36
-
37
- this->write_byte(MPR121_NHDT, 0x00);
38
- this->write_byte(MPR121_NCLT, 0x00);
39
- this->write_byte(MPR121_FDLT, 0x00);
40
-
41
- this->write_byte(MPR121_DEBOUNCE, 0);
42
- // default, 16uA charge current
43
- this->write_byte(MPR121_CONFIG1, 0x10);
44
- // 0.5uS encoding, 1ms period
45
- this->write_byte(MPR121_CONFIG2, 0x20);
46
-
47
- // Write the Electrode Configuration Register
48
- // * Highest 2 bits is "Calibration Lock", which we set to a value corresponding to 5 bits.
49
- // * The 2 bits below is "Proximity Enable" and are left at 0.
50
- // * The 4 least significant bits control how many electrodes are enabled. Electrodes are enabled
51
- // as a range, starting at 0 up to the highest channel index used.
52
- this->write_byte(MPR121_ECR, 0x80 | (this->max_touch_channel_ + 1));
53
-
54
- this->flush_gpio_();
17
+ this->set_timeout(100, [this]() {
18
+ if (!this->write_byte(MPR121_ECR, 0x0)) {
19
+ this->error_code_ = COMMUNICATION_FAILED;
20
+ this->mark_failed();
21
+ return;
22
+ }
23
+ // set touch sensitivity for all 12 channels
24
+ for (auto *channel : this->channels_) {
25
+ channel->setup();
26
+ }
27
+ this->write_byte(MPR121_MHDR, 0x01);
28
+ this->write_byte(MPR121_NHDR, 0x01);
29
+ this->write_byte(MPR121_NCLR, 0x0E);
30
+ this->write_byte(MPR121_FDLR, 0x00);
31
+
32
+ this->write_byte(MPR121_MHDF, 0x01);
33
+ this->write_byte(MPR121_NHDF, 0x05);
34
+ this->write_byte(MPR121_NCLF, 0x01);
35
+ this->write_byte(MPR121_FDLF, 0x00);
36
+
37
+ this->write_byte(MPR121_NHDT, 0x00);
38
+ this->write_byte(MPR121_NCLT, 0x00);
39
+ this->write_byte(MPR121_FDLT, 0x00);
40
+
41
+ this->write_byte(MPR121_DEBOUNCE, 0);
42
+ // default, 16uA charge current
43
+ this->write_byte(MPR121_CONFIG1, 0x10);
44
+ // 0.5uS encoding, 1ms period
45
+ this->write_byte(MPR121_CONFIG2, 0x20);
46
+
47
+ // Write the Electrode Configuration Register
48
+ // * Highest 2 bits is "Calibration Lock", which we set to a value corresponding to 5 bits.
49
+ // * The 2 bits below is "Proximity Enable" and are left at 0.
50
+ // * The 4 least significant bits control how many electrodes are enabled. Electrodes are enabled
51
+ // as a range, starting at 0 up to the highest channel index used.
52
+ this->write_byte(MPR121_ECR, 0x80 | (this->max_touch_channel_ + 1));
53
+
54
+ this->flush_gpio_();
55
+ this->enable_loop();
56
+ });
55
57
  }
56
58
 
57
59
  void MPR121Component::set_touch_debounce(uint8_t debounce) {
@@ -73,9 +75,6 @@ void MPR121Component::dump_config() {
73
75
  case COMMUNICATION_FAILED:
74
76
  ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
75
77
  break;
76
- case WRONG_CHIP_STATE:
77
- ESP_LOGE(TAG, "MPR121 has wrong default value for CONFIG2?");
78
- break;
79
78
  case NONE:
80
79
  default:
81
80
  break;