esphome 2025.4.2__py3-none-any.whl → 2025.5.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 (444) hide show
  1. esphome/__main__.py +16 -14
  2. esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
  3. esphome/components/adc/__init__.py +51 -34
  4. esphome/components/airthings_wave_base/__init__.py +1 -1
  5. esphome/components/alarm_control_panel/__init__.py +37 -2
  6. esphome/components/am43/cover/__init__.py +4 -5
  7. esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
  8. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
  9. esphome/components/analog_threshold/binary_sensor.py +10 -8
  10. esphome/components/anova/climate.py +4 -5
  11. esphome/components/api/__init__.py +25 -8
  12. esphome/components/api/api_connection.cpp +416 -662
  13. esphome/components/api/api_connection.h +256 -57
  14. esphome/components/api/api_frame_helper.cpp +232 -177
  15. esphome/components/api/api_frame_helper.h +61 -8
  16. esphome/components/api/api_noise_context.h +13 -4
  17. esphome/components/api/api_pb2.cpp +1422 -1
  18. esphome/components/api/api_pb2.h +255 -1
  19. esphome/components/api/api_pb2_service.cpp +162 -49
  20. esphome/components/api/api_pb2_service.h +90 -51
  21. esphome/components/api/api_pb2_size.h +361 -0
  22. esphome/components/api/api_server.cpp +110 -34
  23. esphome/components/api/api_server.h +8 -0
  24. esphome/components/api/proto.h +86 -17
  25. esphome/components/as7341/as7341.h +1 -1
  26. esphome/components/at581x/at581x.h +4 -4
  27. esphome/components/atm90e32/__init__.py +1 -0
  28. esphome/components/atm90e32/atm90e32.cpp +576 -199
  29. esphome/components/atm90e32/atm90e32.h +128 -31
  30. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  31. esphome/components/atm90e32/button/__init__.py +62 -10
  32. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  33. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  34. esphome/components/atm90e32/number/__init__.py +130 -0
  35. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  36. esphome/components/atm90e32/sensor.py +21 -4
  37. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  38. esphome/components/audio/__init__.py +96 -49
  39. esphome/components/audio/audio.h +48 -0
  40. esphome/components/audio/audio_decoder.cpp +1 -1
  41. esphome/components/audio/audio_resampler.cpp +2 -0
  42. esphome/components/audio/audio_resampler.h +1 -0
  43. esphome/components/ballu/climate.py +2 -9
  44. esphome/components/bang_bang/climate.py +5 -6
  45. esphome/components/bedjet/bedjet_hub.cpp +1 -0
  46. esphome/components/bedjet/climate/__init__.py +3 -8
  47. esphome/components/bedjet/fan/__init__.py +2 -11
  48. esphome/components/binary/fan/__init__.py +13 -16
  49. esphome/components/binary_sensor/__init__.py +13 -10
  50. esphome/components/bl0906/constants.h +16 -16
  51. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  52. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  53. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
  54. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  55. esphome/components/button/__init__.py +11 -8
  56. esphome/components/canbus/canbus.cpp +3 -0
  57. esphome/components/canbus/canbus.h +16 -0
  58. esphome/components/ccs811/sensor.py +9 -6
  59. esphome/components/climate/__init__.py +35 -2
  60. esphome/components/climate/climate_mode.h +1 -1
  61. esphome/components/climate/climate_traits.h +63 -57
  62. esphome/components/climate_ir/__init__.py +57 -17
  63. esphome/components/climate_ir_lg/climate.py +2 -5
  64. esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
  65. esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
  66. esphome/components/color/__init__.py +2 -0
  67. esphome/components/const/__init__.py +5 -0
  68. esphome/components/coolix/climate.py +2 -9
  69. esphome/components/copy/cover/__init__.py +10 -9
  70. esphome/components/copy/fan/__init__.py +11 -9
  71. esphome/components/copy/lock/__init__.py +11 -9
  72. esphome/components/copy/text/__init__.py +9 -6
  73. esphome/components/cover/__init__.py +37 -2
  74. esphome/components/cse7766/cse7766.cpp +2 -1
  75. esphome/components/cst226/binary_sensor/__init__.py +28 -0
  76. esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
  77. esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
  78. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
  79. esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
  80. esphome/components/current_based/cover.py +37 -36
  81. esphome/components/current_based/current_based_cover.cpp +2 -1
  82. esphome/components/daikin/climate.py +2 -9
  83. esphome/components/daikin/daikin.cpp +15 -9
  84. esphome/components/daikin/daikin.h +5 -5
  85. esphome/components/daikin_arc/climate.py +2 -7
  86. esphome/components/daikin_brc/climate.py +3 -5
  87. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  88. esphome/components/dallas_temp/dallas_temp.h +0 -1
  89. esphome/components/daly_bms/daly_bms.cpp +2 -1
  90. esphome/components/debug/debug_component.cpp +6 -1
  91. esphome/components/debug/debug_component.h +8 -0
  92. esphome/components/debug/debug_esp32.cpp +109 -254
  93. esphome/components/debug/sensor.py +14 -0
  94. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  95. esphome/components/delonghi/climate.py +2 -9
  96. esphome/components/demo/__init__.py +18 -20
  97. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  98. esphome/components/dps310/sensor.py +6 -6
  99. esphome/components/ee895/sensor.py +9 -9
  100. esphome/components/emmeti/climate.py +2 -9
  101. esphome/components/endstop/cover.py +17 -16
  102. esphome/components/endstop/endstop_cover.cpp +2 -1
  103. esphome/components/ens160_base/__init__.py +12 -9
  104. esphome/components/esp32/__init__.py +60 -3
  105. esphome/components/esp32/core.cpp +11 -5
  106. esphome/components/esp32/gpio.cpp +86 -24
  107. esphome/components/esp32/gpio.py +15 -16
  108. esphome/components/esp32/gpio_esp32.py +1 -2
  109. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  110. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  111. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  112. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  113. esphome/components/esp32_ble/ble.cpp +1 -0
  114. esphome/components/esp32_ble/ble.h +5 -3
  115. esphome/components/esp32_ble/ble_advertising.cpp +2 -1
  116. esphome/components/esp32_ble/ble_advertising.h +1 -0
  117. esphome/components/esp32_ble_server/__init__.py +3 -0
  118. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  119. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  120. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  121. esphome/components/esp32_camera/__init__.py +1 -1
  122. esphome/components/esp32_camera/esp32_camera.cpp +2 -10
  123. esphome/components/esp32_camera/esp32_camera.h +1 -1
  124. esphome/components/esp32_can/esp32_can.cpp +1 -1
  125. esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
  126. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  127. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  128. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  129. esphome/components/esp32_touch/esp32_touch.cpp +1 -1
  130. esphome/components/esp8266/gpio.cpp +69 -8
  131. esphome/components/ethernet/ethernet_component.cpp +1 -1
  132. esphome/components/event/__init__.py +13 -10
  133. esphome/components/factory_reset/switch/__init__.py +7 -21
  134. esphome/components/fan/__init__.py +52 -5
  135. esphome/components/fastled_base/__init__.py +1 -4
  136. esphome/components/fastled_base/fastled_light.cpp +1 -1
  137. esphome/components/feedback/cover.py +38 -33
  138. esphome/components/feedback/feedback_cover.cpp +2 -1
  139. esphome/components/fujitsu_general/climate.py +2 -9
  140. esphome/components/gcja5/gcja5.cpp +2 -1
  141. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  142. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  143. esphome/components/gpio_expander/cached_gpio.h +22 -7
  144. esphome/components/gps/__init__.py +47 -17
  145. esphome/components/gps/gps.cpp +42 -23
  146. esphome/components/gps/gps.h +17 -13
  147. esphome/components/graph/__init__.py +1 -2
  148. esphome/components/gree/climate.py +4 -6
  149. esphome/components/gree/gree.cpp +16 -2
  150. esphome/components/gree/gree.h +2 -2
  151. esphome/components/growatt_solar/growatt_solar.cpp +2 -1
  152. esphome/components/haier/climate.py +37 -34
  153. esphome/components/hbridge/fan/__init__.py +19 -17
  154. esphome/components/he60r/cover.py +4 -5
  155. esphome/components/heatpumpir/climate.py +3 -6
  156. esphome/components/hitachi_ac344/climate.py +2 -9
  157. esphome/components/hitachi_ac424/climate.py +2 -9
  158. esphome/components/hm3301/hm3301.h +1 -1
  159. esphome/components/hte501/sensor.py +6 -6
  160. esphome/components/http_request/__init__.py +39 -6
  161. esphome/components/http_request/http_request.cpp +20 -0
  162. esphome/components/http_request/http_request.h +57 -15
  163. esphome/components/http_request/http_request_arduino.cpp +22 -6
  164. esphome/components/http_request/http_request_arduino.h +4 -3
  165. esphome/components/http_request/http_request_host.cpp +141 -0
  166. esphome/components/http_request/http_request_host.h +37 -0
  167. esphome/components/http_request/http_request_idf.cpp +35 -3
  168. esphome/components/http_request/http_request_idf.h +10 -3
  169. esphome/components/http_request/httplib.h +9691 -0
  170. esphome/components/http_request/update/__init__.py +11 -8
  171. esphome/components/hyt271/sensor.py +6 -6
  172. esphome/components/i2c/i2c.h +4 -0
  173. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  174. esphome/components/i2s_audio/__init__.py +131 -22
  175. esphome/components/i2s_audio/i2s_audio.h +44 -4
  176. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  177. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  178. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  179. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  180. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  181. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  182. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  183. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  184. esphome/components/image/__init__.py +37 -17
  185. esphome/components/image/image.cpp +25 -8
  186. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  187. esphome/components/key_collector/__init__.py +35 -0
  188. esphome/components/key_collector/key_collector.cpp +8 -0
  189. esphome/components/key_collector/key_collector.h +10 -0
  190. esphome/components/kuntze/kuntze.cpp +2 -1
  191. esphome/components/ld2410/ld2410.h +1 -1
  192. esphome/components/ld2450/ld2450.h +1 -1
  193. esphome/components/light/__init__.py +57 -0
  194. esphome/components/lock/__init__.py +51 -4
  195. esphome/components/lock/automation.h +2 -13
  196. esphome/components/logger/__init__.py +22 -0
  197. esphome/components/logger/logger.cpp +154 -103
  198. esphome/components/logger/logger.h +211 -36
  199. esphome/components/logger/task_log_buffer.cpp +138 -0
  200. esphome/components/logger/task_log_buffer.h +69 -0
  201. esphome/components/lvgl/__init__.py +13 -5
  202. esphome/components/lvgl/automation.py +50 -1
  203. esphome/components/lvgl/defines.py +0 -1
  204. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  205. esphome/components/lvgl/text/__init__.py +1 -2
  206. esphome/components/mapping/__init__.py +134 -0
  207. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
  208. esphome/components/max7219digit/max7219digit.cpp +28 -27
  209. esphome/components/mdns/__init__.py +11 -5
  210. esphome/components/mdns/mdns_component.cpp +11 -5
  211. esphome/components/mdns/mdns_component.h +3 -2
  212. esphome/components/mdns/mdns_esp32.cpp +4 -3
  213. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  214. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  215. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  216. esphome/components/media_player/__init__.py +33 -1
  217. esphome/components/mhz19/sensor.py +11 -7
  218. esphome/components/micro_wake_word/__init__.py +99 -31
  219. esphome/components/micro_wake_word/automation.h +54 -0
  220. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  221. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  222. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  223. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  224. esphome/components/micro_wake_word/streaming_model.h +85 -13
  225. esphome/components/microphone/__init__.py +139 -9
  226. esphome/components/microphone/automation.h +14 -2
  227. esphome/components/microphone/microphone.cpp +21 -0
  228. esphome/components/microphone/microphone.h +14 -5
  229. esphome/components/microphone/microphone_source.cpp +95 -0
  230. esphome/components/microphone/microphone_source.h +80 -0
  231. esphome/components/mics_4514/sensor.py +25 -14
  232. esphome/components/midea/climate.py +3 -4
  233. esphome/components/midea_ir/climate.py +3 -5
  234. esphome/components/mipi_spi/__init__.py +15 -0
  235. esphome/components/mipi_spi/display.py +474 -0
  236. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  237. esphome/components/mipi_spi/mipi_spi.h +171 -0
  238. esphome/components/mipi_spi/models/__init__.py +65 -0
  239. esphome/components/mipi_spi/models/amoled.py +72 -0
  240. esphome/components/mipi_spi/models/commands.py +82 -0
  241. esphome/components/mipi_spi/models/cyd.py +10 -0
  242. esphome/components/mipi_spi/models/ili.py +749 -0
  243. esphome/components/mipi_spi/models/jc.py +260 -0
  244. esphome/components/mipi_spi/models/lanbon.py +15 -0
  245. esphome/components/mipi_spi/models/lilygo.py +60 -0
  246. esphome/components/mipi_spi/models/waveshare.py +139 -0
  247. esphome/components/mitsubishi/climate.py +2 -5
  248. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  249. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  250. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  251. esphome/components/mlx90393/sensor.py +5 -0
  252. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  253. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  254. esphome/components/modbus/modbus.cpp +2 -1
  255. esphome/components/mqtt/__init__.py +1 -1
  256. esphome/components/mqtt/mqtt_client.cpp +6 -2
  257. esphome/components/mqtt/mqtt_const.h +4 -0
  258. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  259. esphome/components/mqtt/mqtt_fan.h +2 -0
  260. esphome/components/ms5611/sensor.py +6 -6
  261. esphome/components/ms8607/sensor.py +3 -3
  262. esphome/components/network/__init__.py +1 -1
  263. esphome/components/nextion/base_component.py +17 -16
  264. esphome/components/nextion/display.py +11 -2
  265. esphome/components/nextion/nextion.cpp +39 -1
  266. esphome/components/nextion/nextion.h +50 -0
  267. esphome/components/noblex/climate.py +2 -9
  268. esphome/components/number/__init__.py +12 -9
  269. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  270. esphome/components/one_wire/one_wire_bus.h +14 -8
  271. esphome/components/online_image/bmp_image.cpp +48 -11
  272. esphome/components/online_image/bmp_image.h +2 -0
  273. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  274. esphome/components/opentherm/number/__init__.py +11 -20
  275. esphome/components/opentherm/sensor/__init__.py +3 -3
  276. esphome/components/opentherm/switch/__init__.py +3 -5
  277. esphome/components/output/lock/__init__.py +11 -9
  278. esphome/components/packages/__init__.py +33 -31
  279. esphome/components/packet_transport/__init__.py +201 -0
  280. esphome/components/packet_transport/binary_sensor.py +19 -0
  281. esphome/components/packet_transport/packet_transport.cpp +534 -0
  282. esphome/components/packet_transport/packet_transport.h +154 -0
  283. esphome/components/packet_transport/sensor.py +19 -0
  284. esphome/components/pca9685/pca9685_output.cpp +2 -1
  285. esphome/components/pid/climate.py +2 -4
  286. esphome/components/pm2005/__init__.py +1 -0
  287. esphome/components/pm2005/pm2005.cpp +123 -0
  288. esphome/components/pm2005/pm2005.h +46 -0
  289. esphome/components/pm2005/sensor.py +86 -0
  290. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  291. esphome/components/pmsa003i/pmsa003i.h +25 -25
  292. esphome/components/pmsx003/pmsx003.cpp +195 -230
  293. esphome/components/pmsx003/pmsx003.h +51 -33
  294. esphome/components/pmsx003/sensor.py +21 -11
  295. esphome/components/pn7150/pn7150.h +2 -2
  296. esphome/components/pn7160/pn7160.h +2 -2
  297. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  298. esphome/components/prometheus/prometheus_handler.h +17 -0
  299. esphome/components/psram/__init__.py +7 -5
  300. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  301. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  302. esphome/components/pzem004t/pzem004t.cpp +2 -1
  303. esphome/components/qspi_dbi/__init__.py +0 -1
  304. esphome/components/qspi_dbi/display.py +2 -1
  305. esphome/components/qspi_dbi/models.py +1 -2
  306. esphome/components/remote_base/__init__.py +91 -0
  307. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  308. esphome/components/remote_base/beo4_protocol.h +43 -0
  309. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  310. esphome/components/remote_base/gobox_protocol.h +54 -0
  311. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  312. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  313. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  314. esphome/components/rf_bridge/rf_bridge.cpp +2 -1
  315. esphome/components/scd30/sensor.py +2 -3
  316. esphome/components/scd4x/sensor.py +4 -5
  317. esphome/components/sdp3x/sensor.py +2 -1
  318. esphome/components/sds011/sds011.cpp +2 -1
  319. esphome/components/select/__init__.py +19 -20
  320. esphome/components/sen5x/sen5x.cpp +55 -36
  321. esphome/components/sen5x/sensor.py +1 -1
  322. esphome/components/senseair/sensor.py +3 -3
  323. esphome/components/sensor/__init__.py +158 -14
  324. esphome/components/sensor/filter.cpp +23 -0
  325. esphome/components/sensor/filter.h +22 -0
  326. esphome/components/sgp30/sensor.py +14 -16
  327. esphome/components/sgp4x/sensor.py +1 -1
  328. esphome/components/sht4x/sht4x.cpp +43 -22
  329. esphome/components/sht4x/sht4x.h +1 -1
  330. esphome/components/shtcx/sensor.py +6 -6
  331. esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
  332. esphome/components/sml/text_sensor/__init__.py +4 -6
  333. esphome/components/sound_level/__init__.py +0 -0
  334. esphome/components/sound_level/sensor.py +97 -0
  335. esphome/components/sound_level/sound_level.cpp +194 -0
  336. esphome/components/sound_level/sound_level.h +73 -0
  337. esphome/components/speaker/media_player/__init__.py +4 -8
  338. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  339. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  340. esphome/components/speaker/speaker.h +4 -7
  341. esphome/components/speed/fan/__init__.py +17 -16
  342. esphome/components/spi/spi.h +11 -1
  343. esphome/components/sprinkler/__init__.py +18 -19
  344. esphome/components/sprinkler/sprinkler.cpp +6 -5
  345. esphome/components/switch/__init__.py +32 -42
  346. esphome/components/syslog/__init__.py +41 -0
  347. esphome/components/syslog/esphome_syslog.cpp +49 -0
  348. esphome/components/syslog/esphome_syslog.h +27 -0
  349. esphome/components/t6615/sensor.py +3 -3
  350. esphome/components/t6615/t6615.cpp +2 -1
  351. esphome/components/tca9555/tca9555.cpp +11 -6
  352. esphome/components/tcl112/climate.py +2 -9
  353. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  354. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  355. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  356. esphome/components/template/cover/__init__.py +27 -21
  357. esphome/components/template/fan/__init__.py +14 -12
  358. esphome/components/template/lock/__init__.py +20 -25
  359. esphome/components/template/lock/automation.h +18 -0
  360. esphome/components/template/text/__init__.py +4 -3
  361. esphome/components/template/valve/__init__.py +32 -21
  362. esphome/components/template/valve/automation.h +24 -0
  363. esphome/components/text/__init__.py +32 -1
  364. esphome/components/text_sensor/__init__.py +24 -29
  365. esphome/components/thermostat/climate.py +5 -5
  366. esphome/components/time_based/cover.py +17 -16
  367. esphome/components/time_based/time_based_cover.cpp +2 -1
  368. esphome/components/tm1638/switch/__init__.py +10 -7
  369. esphome/components/tormatic/cover.py +4 -5
  370. esphome/components/toshiba/climate.py +3 -5
  371. esphome/components/touchscreen/touchscreen.cpp +3 -1
  372. esphome/components/tuya/climate/__init__.py +5 -6
  373. esphome/components/tuya/cover/__init__.py +6 -11
  374. esphome/components/tuya/select/__init__.py +15 -5
  375. esphome/components/tuya/select/tuya_select.cpp +6 -1
  376. esphome/components/tuya/select/tuya_select.h +5 -1
  377. esphome/components/uart/packet_transport/__init__.py +20 -0
  378. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  379. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  380. esphome/components/uart/switch/uart_switch.cpp +2 -1
  381. esphome/components/udp/__init__.py +126 -128
  382. esphome/components/udp/automation.h +40 -0
  383. esphome/components/udp/binary_sensor.py +3 -25
  384. esphome/components/udp/packet_transport/__init__.py +29 -0
  385. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  386. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  387. esphome/components/udp/sensor.py +3 -25
  388. esphome/components/udp/udp_component.cpp +26 -470
  389. esphome/components/udp/udp_component.h +21 -128
  390. esphome/components/update/__init__.py +31 -1
  391. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  392. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
  393. esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
  394. esphome/components/uptime/text_sensor/__init__.py +47 -7
  395. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  396. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  397. esphome/components/valve/__init__.py +34 -3
  398. esphome/components/valve/automation.h +1 -19
  399. esphome/components/vl53l0x/sensor.py +11 -0
  400. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  401. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  402. esphome/components/voice_assistant/__init__.py +36 -10
  403. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  404. esphome/components/voice_assistant/voice_assistant.h +26 -25
  405. esphome/components/waveshare_epaper/display.py +6 -0
  406. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  407. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  408. esphome/components/weikai/weikai.cpp +0 -52
  409. esphome/components/whirlpool/climate.py +3 -5
  410. esphome/components/whynter/climate.py +3 -5
  411. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  412. esphome/components/yashima/climate.py +6 -6
  413. esphome/components/zhlt01/climate.py +2 -7
  414. esphome/config.py +13 -13
  415. esphome/config_validation.py +38 -58
  416. esphome/const.py +15 -1
  417. esphome/core/__init__.py +2 -0
  418. esphome/core/application.cpp +27 -10
  419. esphome/core/application.h +9 -1
  420. esphome/core/automation.h +4 -3
  421. esphome/core/component.cpp +28 -7
  422. esphome/core/component.h +10 -1
  423. esphome/core/defines.h +23 -17
  424. esphome/core/doxygen.h +13 -0
  425. esphome/core/macros.h +4 -0
  426. esphome/core/scheduler.cpp +7 -1
  427. esphome/cpp_generator.py +6 -2
  428. esphome/dashboard/web_server.py +3 -3
  429. esphome/helpers.py +39 -0
  430. esphome/loader.py +4 -0
  431. esphome/log.py +15 -19
  432. esphome/mqtt.py +23 -10
  433. esphome/platformio_api.py +1 -1
  434. esphome/schema_extractors.py +0 -1
  435. esphome/voluptuous_schema.py +3 -1
  436. esphome/vscode.py +15 -0
  437. esphome/wizard.py +47 -37
  438. esphome/zeroconf.py +7 -3
  439. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/METADATA +10 -11
  440. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/RECORD +444 -383
  441. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/WHEEL +1 -1
  442. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/entry_points.txt +0 -0
  443. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/licenses/LICENSE +0 -0
  444. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  #include "atm90e32.h"
2
- #include "atm90e32_reg.h"
3
- #include "esphome/core/log.h"
4
2
  #include <cinttypes>
3
+ #include <cmath>
4
+ #include "esphome/core/log.h"
5
5
 
6
6
  namespace esphome {
7
7
  namespace atm90e32 {
@@ -11,115 +11,84 @@ void ATM90E32Component::loop() {
11
11
  if (this->get_publish_interval_flag_()) {
12
12
  this->set_publish_interval_flag_(false);
13
13
  for (uint8_t phase = 0; phase < 3; phase++) {
14
- if (this->phase_[phase].voltage_sensor_ != nullptr) {
14
+ if (this->phase_[phase].voltage_sensor_ != nullptr)
15
15
  this->phase_[phase].voltage_ = this->get_phase_voltage_(phase);
16
- }
17
- }
18
- for (uint8_t phase = 0; phase < 3; phase++) {
19
- if (this->phase_[phase].current_sensor_ != nullptr) {
16
+
17
+ if (this->phase_[phase].current_sensor_ != nullptr)
20
18
  this->phase_[phase].current_ = this->get_phase_current_(phase);
21
- }
22
- }
23
- for (uint8_t phase = 0; phase < 3; phase++) {
24
- if (this->phase_[phase].power_sensor_ != nullptr) {
19
+
20
+ if (this->phase_[phase].power_sensor_ != nullptr)
25
21
  this->phase_[phase].active_power_ = this->get_phase_active_power_(phase);
26
- }
27
- }
28
- for (uint8_t phase = 0; phase < 3; phase++) {
29
- if (this->phase_[phase].power_factor_sensor_ != nullptr) {
22
+
23
+ if (this->phase_[phase].power_factor_sensor_ != nullptr)
30
24
  this->phase_[phase].power_factor_ = this->get_phase_power_factor_(phase);
31
- }
32
- }
33
- for (uint8_t phase = 0; phase < 3; phase++) {
34
- if (this->phase_[phase].reactive_power_sensor_ != nullptr) {
25
+
26
+ if (this->phase_[phase].reactive_power_sensor_ != nullptr)
35
27
  this->phase_[phase].reactive_power_ = this->get_phase_reactive_power_(phase);
36
- }
37
- }
38
- for (uint8_t phase = 0; phase < 3; phase++) {
39
- if (this->phase_[phase].forward_active_energy_sensor_ != nullptr) {
28
+
29
+ if (this->phase_[phase].apparent_power_sensor_ != nullptr)
30
+ this->phase_[phase].apparent_power_ = this->get_phase_apparent_power_(phase);
31
+
32
+ if (this->phase_[phase].forward_active_energy_sensor_ != nullptr)
40
33
  this->phase_[phase].forward_active_energy_ = this->get_phase_forward_active_energy_(phase);
41
- }
42
- }
43
- for (uint8_t phase = 0; phase < 3; phase++) {
44
- if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr) {
34
+
35
+ if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr)
45
36
  this->phase_[phase].reverse_active_energy_ = this->get_phase_reverse_active_energy_(phase);
46
- }
47
- }
48
- for (uint8_t phase = 0; phase < 3; phase++) {
49
- if (this->phase_[phase].phase_angle_sensor_ != nullptr) {
37
+
38
+ if (this->phase_[phase].phase_angle_sensor_ != nullptr)
50
39
  this->phase_[phase].phase_angle_ = this->get_phase_angle_(phase);
51
- }
52
- }
53
- for (uint8_t phase = 0; phase < 3; phase++) {
54
- if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr) {
40
+
41
+ if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr)
55
42
  this->phase_[phase].harmonic_active_power_ = this->get_phase_harmonic_active_power_(phase);
56
- }
57
- }
58
- for (uint8_t phase = 0; phase < 3; phase++) {
59
- if (this->phase_[phase].peak_current_sensor_ != nullptr) {
43
+
44
+ if (this->phase_[phase].peak_current_sensor_ != nullptr)
60
45
  this->phase_[phase].peak_current_ = this->get_phase_peak_current_(phase);
61
- }
62
- }
63
- // After the local store in collected we can publish them trusting they are withing +-1 haardware sampling
64
- for (uint8_t phase = 0; phase < 3; phase++) {
65
- if (this->phase_[phase].voltage_sensor_ != nullptr) {
46
+
47
+ // After the local store is collected we can publish them trusting they are within +-1 hardware sampling
48
+ if (this->phase_[phase].voltage_sensor_ != nullptr)
66
49
  this->phase_[phase].voltage_sensor_->publish_state(this->get_local_phase_voltage_(phase));
67
- }
68
- }
69
- for (uint8_t phase = 0; phase < 3; phase++) {
70
- if (this->phase_[phase].current_sensor_ != nullptr) {
50
+
51
+ if (this->phase_[phase].current_sensor_ != nullptr)
71
52
  this->phase_[phase].current_sensor_->publish_state(this->get_local_phase_current_(phase));
72
- }
73
- }
74
- for (uint8_t phase = 0; phase < 3; phase++) {
75
- if (this->phase_[phase].power_sensor_ != nullptr) {
53
+
54
+ if (this->phase_[phase].power_sensor_ != nullptr)
76
55
  this->phase_[phase].power_sensor_->publish_state(this->get_local_phase_active_power_(phase));
77
- }
78
- }
79
- for (uint8_t phase = 0; phase < 3; phase++) {
80
- if (this->phase_[phase].power_factor_sensor_ != nullptr) {
56
+
57
+ if (this->phase_[phase].power_factor_sensor_ != nullptr)
81
58
  this->phase_[phase].power_factor_sensor_->publish_state(this->get_local_phase_power_factor_(phase));
82
- }
83
- }
84
- for (uint8_t phase = 0; phase < 3; phase++) {
85
- if (this->phase_[phase].reactive_power_sensor_ != nullptr) {
59
+
60
+ if (this->phase_[phase].reactive_power_sensor_ != nullptr)
86
61
  this->phase_[phase].reactive_power_sensor_->publish_state(this->get_local_phase_reactive_power_(phase));
87
- }
88
- }
89
- for (uint8_t phase = 0; phase < 3; phase++) {
62
+
63
+ if (this->phase_[phase].apparent_power_sensor_ != nullptr)
64
+ this->phase_[phase].apparent_power_sensor_->publish_state(this->get_local_phase_apparent_power_(phase));
65
+
90
66
  if (this->phase_[phase].forward_active_energy_sensor_ != nullptr) {
91
67
  this->phase_[phase].forward_active_energy_sensor_->publish_state(
92
68
  this->get_local_phase_forward_active_energy_(phase));
93
69
  }
94
- }
95
- for (uint8_t phase = 0; phase < 3; phase++) {
70
+
96
71
  if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr) {
97
72
  this->phase_[phase].reverse_active_energy_sensor_->publish_state(
98
73
  this->get_local_phase_reverse_active_energy_(phase));
99
74
  }
100
- }
101
- for (uint8_t phase = 0; phase < 3; phase++) {
102
- if (this->phase_[phase].phase_angle_sensor_ != nullptr) {
75
+
76
+ if (this->phase_[phase].phase_angle_sensor_ != nullptr)
103
77
  this->phase_[phase].phase_angle_sensor_->publish_state(this->get_local_phase_angle_(phase));
104
- }
105
- }
106
- for (uint8_t phase = 0; phase < 3; phase++) {
78
+
107
79
  if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr) {
108
80
  this->phase_[phase].harmonic_active_power_sensor_->publish_state(
109
81
  this->get_local_phase_harmonic_active_power_(phase));
110
82
  }
111
- }
112
- for (uint8_t phase = 0; phase < 3; phase++) {
113
- if (this->phase_[phase].peak_current_sensor_ != nullptr) {
83
+
84
+ if (this->phase_[phase].peak_current_sensor_ != nullptr)
114
85
  this->phase_[phase].peak_current_sensor_->publish_state(this->get_local_phase_peak_current_(phase));
115
- }
116
86
  }
117
- if (this->freq_sensor_ != nullptr) {
87
+ if (this->freq_sensor_ != nullptr)
118
88
  this->freq_sensor_->publish_state(this->get_frequency_());
119
- }
120
- if (this->chip_temperature_sensor_ != nullptr) {
89
+
90
+ if (this->chip_temperature_sensor_ != nullptr)
121
91
  this->chip_temperature_sensor_->publish_state(this->get_chip_temperature_());
122
- }
123
92
  }
124
93
  }
125
94
 
@@ -130,82 +99,30 @@ void ATM90E32Component::update() {
130
99
  }
131
100
  this->set_publish_interval_flag_(true);
132
101
  this->status_clear_warning();
133
- }
134
102
 
135
- void ATM90E32Component::restore_calibrations_() {
136
- if (enable_offset_calibration_) {
137
- this->pref_.load(&this->offset_phase_);
138
- }
139
- };
140
-
141
- void ATM90E32Component::run_offset_calibrations() {
142
- // Run the calibrations and
143
- // Setup voltage and current calibration offsets for PHASE A
144
- this->offset_phase_[PHASEA].voltage_offset_ = calibrate_voltage_offset_phase(PHASEA);
145
- this->phase_[PHASEA].voltage_offset_ = this->offset_phase_[PHASEA].voltage_offset_;
146
- this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_); // C Voltage offset
147
- this->offset_phase_[PHASEA].current_offset_ = calibrate_current_offset_phase(PHASEA);
148
- this->phase_[PHASEA].current_offset_ = this->offset_phase_[PHASEA].current_offset_;
149
- this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_); // C Current offset
150
- // Setup voltage and current calibration offsets for PHASE B
151
- this->offset_phase_[PHASEB].voltage_offset_ = calibrate_voltage_offset_phase(PHASEB);
152
- this->phase_[PHASEB].voltage_offset_ = this->offset_phase_[PHASEB].voltage_offset_;
153
- this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_); // C Voltage offset
154
- this->offset_phase_[PHASEB].current_offset_ = calibrate_current_offset_phase(PHASEB);
155
- this->phase_[PHASEB].current_offset_ = this->offset_phase_[PHASEB].current_offset_;
156
- this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_); // C Current offset
157
- // Setup voltage and current calibration offsets for PHASE C
158
- this->offset_phase_[PHASEC].voltage_offset_ = calibrate_voltage_offset_phase(PHASEC);
159
- this->phase_[PHASEC].voltage_offset_ = this->offset_phase_[PHASEC].voltage_offset_;
160
- this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_); // C Voltage offset
161
- this->offset_phase_[PHASEC].current_offset_ = calibrate_current_offset_phase(PHASEC);
162
- this->phase_[PHASEC].current_offset_ = this->offset_phase_[PHASEC].current_offset_;
163
- this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_); // C Current offset
164
- this->pref_.save(&this->offset_phase_);
165
- ESP_LOGI(TAG, "PhaseA Vo=%5d PhaseB Vo=%5d PhaseC Vo=%5d", this->offset_phase_[PHASEA].voltage_offset_,
166
- this->offset_phase_[PHASEB].voltage_offset_, this->offset_phase_[PHASEC].voltage_offset_);
167
- ESP_LOGI(TAG, "PhaseA Io=%5d PhaseB Io=%5d PhaseC Io=%5d", this->offset_phase_[PHASEA].current_offset_,
168
- this->offset_phase_[PHASEB].current_offset_, this->offset_phase_[PHASEC].current_offset_);
169
- }
170
-
171
- void ATM90E32Component::clear_offset_calibrations() {
172
- // Clear the calibrations and
173
- this->offset_phase_[PHASEA].voltage_offset_ = 0;
174
- this->phase_[PHASEA].voltage_offset_ = this->offset_phase_[PHASEA].voltage_offset_;
175
- this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_); // C Voltage offset
176
- this->offset_phase_[PHASEA].current_offset_ = 0;
177
- this->phase_[PHASEA].current_offset_ = this->offset_phase_[PHASEA].current_offset_;
178
- this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_); // C Current offset
179
- this->offset_phase_[PHASEB].voltage_offset_ = 0;
180
- this->phase_[PHASEB].voltage_offset_ = this->offset_phase_[PHASEB].voltage_offset_;
181
- this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_); // C Voltage offset
182
- this->offset_phase_[PHASEB].current_offset_ = 0;
183
- this->phase_[PHASEB].current_offset_ = this->offset_phase_[PHASEB].current_offset_;
184
- this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_); // C Current offset
185
- this->offset_phase_[PHASEC].voltage_offset_ = 0;
186
- this->phase_[PHASEC].voltage_offset_ = this->offset_phase_[PHASEC].voltage_offset_;
187
- this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_); // C Voltage offset
188
- this->offset_phase_[PHASEC].current_offset_ = 0;
189
- this->phase_[PHASEC].current_offset_ = this->offset_phase_[PHASEC].current_offset_;
190
- this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_); // C Current offset
191
- this->pref_.save(&this->offset_phase_);
192
- ESP_LOGI(TAG, "PhaseA Vo=%5d PhaseB Vo=%5d PhaseC Vo=%5d", this->offset_phase_[PHASEA].voltage_offset_,
193
- this->offset_phase_[PHASEB].voltage_offset_, this->offset_phase_[PHASEC].voltage_offset_);
194
- ESP_LOGI(TAG, "PhaseA Io=%5d PhaseB Io=%5d PhaseC Io=%5d", this->offset_phase_[PHASEA].current_offset_,
195
- this->offset_phase_[PHASEB].current_offset_, this->offset_phase_[PHASEC].current_offset_);
103
+ #ifdef USE_TEXT_SENSOR
104
+ this->check_phase_status();
105
+ this->check_over_current();
106
+ this->check_freq_status();
107
+ #endif
196
108
  }
197
109
 
198
110
  void ATM90E32Component::setup() {
199
111
  ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component...");
200
112
  this->spi_setup();
201
- if (this->enable_offset_calibration_) {
202
- uint32_t hash = fnv1_hash(App.get_friendly_name());
203
- this->pref_ = global_preferences->make_preference<Calibration[3]>(hash, true);
204
- this->restore_calibrations_();
205
- }
113
+
206
114
  uint16_t mmode0 = 0x87; // 3P4W 50Hz
115
+ uint16_t high_thresh = 0;
116
+ uint16_t low_thresh = 0;
117
+
207
118
  if (line_freq_ == 60) {
208
119
  mmode0 |= 1 << 12; // sets 12th bit to 1, 60Hz
120
+ // for freq threshold registers
121
+ high_thresh = 6300; // 63.00 Hz
122
+ low_thresh = 5700; // 57.00 Hz
123
+ } else {
124
+ high_thresh = 5300; // 53.00 Hz
125
+ low_thresh = 4700; // 47.00 Hz
209
126
  }
210
127
 
211
128
  if (current_phases_ == 2) {
@@ -216,34 +133,84 @@ void ATM90E32Component::setup() {
216
133
  this->write16_(ATM90E32_REGISTER_SOFTRESET, 0x789A); // Perform soft reset
217
134
  delay(6); // Wait for the minimum 5ms + 1ms
218
135
  this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA); // enable register config access
219
- if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != 0x55AA) {
136
+ if (!this->validate_spi_read_(0x55AA, "setup()")) {
220
137
  ESP_LOGW(TAG, "Could not initialize ATM90E32 IC, check SPI settings");
221
138
  this->mark_failed();
222
139
  return;
223
140
  }
224
141
 
225
142
  this->write16_(ATM90E32_REGISTER_METEREN, 0x0001); // Enable Metering
226
- this->write16_(ATM90E32_REGISTER_SAGPEAKDETCFG, 0xFF3F); // Peak Detector time ms (15:8), Sag Period ms (7:0)
143
+ this->write16_(ATM90E32_REGISTER_SAGPEAKDETCFG, 0xFF3F); // Peak Detector time (15:8) 255ms, Sag Period (7:0) 63ms
227
144
  this->write16_(ATM90E32_REGISTER_PLCONSTH, 0x0861); // PL Constant MSB (default) = 140625000
228
145
  this->write16_(ATM90E32_REGISTER_PLCONSTL, 0xC468); // PL Constant LSB (default)
229
- this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0xD654); // ZX2, ZX1, ZX0 pin config
146
+ this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0xD654); // Zero crossing (ZX2, ZX1, ZX0) pin config
230
147
  this->write16_(ATM90E32_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program)
231
148
  this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_); // PGA Gain Configuration for Current Channels
149
+ this->write16_(ATM90E32_REGISTER_FREQHITH, high_thresh); // Frequency high threshold
150
+ this->write16_(ATM90E32_REGISTER_FREQLOTH, low_thresh); // Frequency low threshold
232
151
  this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x1D4C); // All Active Startup Power Threshold - 0.02A/0.00032 = 7500
233
152
  this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
234
153
  this->write16_(ATM90E32_REGISTER_SSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
235
154
  this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
236
155
  this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE); // Each phase Reactive Phase Threshold - 10%
237
- // Setup voltage and current gain for PHASE A
238
- this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[PHASEA].voltage_gain_); // A Voltage rms gain
239
- this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[PHASEA].ct_gain_); // A line current gain
240
- // Setup voltage and current gain for PHASE B
241
- this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[PHASEB].voltage_gain_); // B Voltage rms gain
242
- this->write16_(ATM90E32_REGISTER_IGAINB, this->phase_[PHASEB].ct_gain_); // B line current gain
243
- // Setup voltage and current gain for PHASE C
244
- this->write16_(ATM90E32_REGISTER_UGAINC, this->phase_[PHASEC].voltage_gain_); // C Voltage rms gain
245
- this->write16_(ATM90E32_REGISTER_IGAINC, this->phase_[PHASEC].ct_gain_); // C line current gain
246
- this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000); // end configuration
156
+
157
+ if (this->enable_offset_calibration_) {
158
+ // Initialize flash storage for offset calibrations
159
+ uint32_t o_hash = fnv1_hash(std::string("_offset_calibration_") + this->cs_->dump_summary());
160
+ this->offset_pref_ = global_preferences->make_preference<OffsetCalibration[3]>(o_hash, true);
161
+ this->restore_offset_calibrations_();
162
+
163
+ // Initialize flash storage for power offset calibrations
164
+ uint32_t po_hash = fnv1_hash(std::string("_power_offset_calibration_") + this->cs_->dump_summary());
165
+ this->power_offset_pref_ = global_preferences->make_preference<PowerOffsetCalibration[3]>(po_hash, true);
166
+ this->restore_power_offset_calibrations_();
167
+ } else {
168
+ ESP_LOGI(TAG, "[CALIBRATION] Power & Voltage/Current offset calibration is disabled. Using config file values.");
169
+ for (uint8_t phase = 0; phase < 3; ++phase) {
170
+ this->write16_(this->voltage_offset_registers[phase],
171
+ static_cast<uint16_t>(this->offset_phase_[phase].voltage_offset_));
172
+ this->write16_(this->current_offset_registers[phase],
173
+ static_cast<uint16_t>(this->offset_phase_[phase].current_offset_));
174
+ this->write16_(this->power_offset_registers[phase],
175
+ static_cast<uint16_t>(this->power_offset_phase_[phase].active_power_offset));
176
+ this->write16_(this->reactive_power_offset_registers[phase],
177
+ static_cast<uint16_t>(this->power_offset_phase_[phase].reactive_power_offset));
178
+ }
179
+ }
180
+
181
+ if (this->enable_gain_calibration_) {
182
+ // Initialize flash storage for gain calibration
183
+ uint32_t g_hash = fnv1_hash(std::string("_gain_calibration_") + this->cs_->dump_summary());
184
+ this->gain_calibration_pref_ = global_preferences->make_preference<GainCalibration[3]>(g_hash, true);
185
+ this->restore_gain_calibrations_();
186
+
187
+ if (this->using_saved_calibrations_) {
188
+ ESP_LOGI(TAG, "[CALIBRATION] Successfully restored gain calibration from memory.");
189
+ } else {
190
+ for (uint8_t phase = 0; phase < 3; ++phase) {
191
+ this->write16_(voltage_gain_registers[phase], this->phase_[phase].voltage_gain_);
192
+ this->write16_(current_gain_registers[phase], this->phase_[phase].ct_gain_);
193
+ }
194
+ }
195
+ } else {
196
+ ESP_LOGI(TAG, "[CALIBRATION] Gain calibration is disabled. Using config file values.");
197
+
198
+ for (uint8_t phase = 0; phase < 3; ++phase) {
199
+ this->write16_(voltage_gain_registers[phase], this->phase_[phase].voltage_gain_);
200
+ this->write16_(current_gain_registers[phase], this->phase_[phase].ct_gain_);
201
+ }
202
+ }
203
+
204
+ // Sag threshold (78%)
205
+ uint16_t sagth = calculate_voltage_threshold(line_freq_, this->phase_[0].voltage_gain_, 0.78f);
206
+ // Overvoltage threshold (122%)
207
+ uint16_t ovth = calculate_voltage_threshold(line_freq_, this->phase_[0].voltage_gain_, 1.22f);
208
+
209
+ // Write to registers
210
+ this->write16_(ATM90E32_REGISTER_SAGTH, sagth);
211
+ this->write16_(ATM90E32_REGISTER_OVTH, ovth);
212
+
213
+ this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000); // end configuration
247
214
  }
248
215
 
249
216
  void ATM90E32Component::dump_config() {
@@ -257,6 +224,7 @@ void ATM90E32Component::dump_config() {
257
224
  LOG_SENSOR(" ", "Current A", this->phase_[PHASEA].current_sensor_);
258
225
  LOG_SENSOR(" ", "Power A", this->phase_[PHASEA].power_sensor_);
259
226
  LOG_SENSOR(" ", "Reactive Power A", this->phase_[PHASEA].reactive_power_sensor_);
227
+ LOG_SENSOR(" ", "Apparent Power A", this->phase_[PHASEA].apparent_power_sensor_);
260
228
  LOG_SENSOR(" ", "PF A", this->phase_[PHASEA].power_factor_sensor_);
261
229
  LOG_SENSOR(" ", "Active Forward Energy A", this->phase_[PHASEA].forward_active_energy_sensor_);
262
230
  LOG_SENSOR(" ", "Active Reverse Energy A", this->phase_[PHASEA].reverse_active_energy_sensor_);
@@ -267,22 +235,24 @@ void ATM90E32Component::dump_config() {
267
235
  LOG_SENSOR(" ", "Current B", this->phase_[PHASEB].current_sensor_);
268
236
  LOG_SENSOR(" ", "Power B", this->phase_[PHASEB].power_sensor_);
269
237
  LOG_SENSOR(" ", "Reactive Power B", this->phase_[PHASEB].reactive_power_sensor_);
238
+ LOG_SENSOR(" ", "Apparent Power B", this->phase_[PHASEB].apparent_power_sensor_);
270
239
  LOG_SENSOR(" ", "PF B", this->phase_[PHASEB].power_factor_sensor_);
271
240
  LOG_SENSOR(" ", "Active Forward Energy B", this->phase_[PHASEB].forward_active_energy_sensor_);
272
241
  LOG_SENSOR(" ", "Active Reverse Energy B", this->phase_[PHASEB].reverse_active_energy_sensor_);
273
- LOG_SENSOR(" ", "Harmonic Power A", this->phase_[PHASEB].harmonic_active_power_sensor_);
274
- LOG_SENSOR(" ", "Phase Angle A", this->phase_[PHASEB].phase_angle_sensor_);
275
- LOG_SENSOR(" ", "Peak Current A", this->phase_[PHASEB].peak_current_sensor_);
242
+ LOG_SENSOR(" ", "Harmonic Power B", this->phase_[PHASEB].harmonic_active_power_sensor_);
243
+ LOG_SENSOR(" ", "Phase Angle B", this->phase_[PHASEB].phase_angle_sensor_);
244
+ LOG_SENSOR(" ", "Peak Current B", this->phase_[PHASEB].peak_current_sensor_);
276
245
  LOG_SENSOR(" ", "Voltage C", this->phase_[PHASEC].voltage_sensor_);
277
246
  LOG_SENSOR(" ", "Current C", this->phase_[PHASEC].current_sensor_);
278
247
  LOG_SENSOR(" ", "Power C", this->phase_[PHASEC].power_sensor_);
279
248
  LOG_SENSOR(" ", "Reactive Power C", this->phase_[PHASEC].reactive_power_sensor_);
249
+ LOG_SENSOR(" ", "Apparent Power C", this->phase_[PHASEC].apparent_power_sensor_);
280
250
  LOG_SENSOR(" ", "PF C", this->phase_[PHASEC].power_factor_sensor_);
281
251
  LOG_SENSOR(" ", "Active Forward Energy C", this->phase_[PHASEC].forward_active_energy_sensor_);
282
252
  LOG_SENSOR(" ", "Active Reverse Energy C", this->phase_[PHASEC].reverse_active_energy_sensor_);
283
- LOG_SENSOR(" ", "Harmonic Power A", this->phase_[PHASEC].harmonic_active_power_sensor_);
284
- LOG_SENSOR(" ", "Phase Angle A", this->phase_[PHASEC].phase_angle_sensor_);
285
- LOG_SENSOR(" ", "Peak Current A", this->phase_[PHASEC].peak_current_sensor_);
253
+ LOG_SENSOR(" ", "Harmonic Power C", this->phase_[PHASEC].harmonic_active_power_sensor_);
254
+ LOG_SENSOR(" ", "Phase Angle C", this->phase_[PHASEC].phase_angle_sensor_);
255
+ LOG_SENSOR(" ", "Peak Current C", this->phase_[PHASEC].peak_current_sensor_);
286
256
  LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
287
257
  LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_);
288
258
  }
@@ -298,7 +268,7 @@ uint16_t ATM90E32Component::read16_(uint16_t a_register) {
298
268
  uint8_t data[2];
299
269
  uint16_t output;
300
270
  this->enable();
301
- delay_microseconds_safe(10);
271
+ delay_microseconds_safe(1); // min delay between CS low and first SCK is 200ns - 1ms is plenty
302
272
  this->write_byte(addrh);
303
273
  this->write_byte(addrl);
304
274
  this->read_array(data, 2);
@@ -328,8 +298,7 @@ void ATM90E32Component::write16_(uint16_t a_register, uint16_t val) {
328
298
  this->write_byte16(a_register);
329
299
  this->write_byte16(val);
330
300
  this->disable();
331
- if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != val)
332
- ESP_LOGW(TAG, "SPI write error 0x%04X val 0x%04X", a_register, val);
301
+ this->validate_spi_read_(val, "write16()");
333
302
  }
334
303
 
335
304
  float ATM90E32Component::get_local_phase_voltage_(uint8_t phase) { return this->phase_[phase].voltage_; }
@@ -340,6 +309,8 @@ float ATM90E32Component::get_local_phase_active_power_(uint8_t phase) { return t
340
309
 
341
310
  float ATM90E32Component::get_local_phase_reactive_power_(uint8_t phase) { return this->phase_[phase].reactive_power_; }
342
311
 
312
+ float ATM90E32Component::get_local_phase_apparent_power_(uint8_t phase) { return this->phase_[phase].apparent_power_; }
313
+
343
314
  float ATM90E32Component::get_local_phase_power_factor_(uint8_t phase) { return this->phase_[phase].power_factor_; }
344
315
 
345
316
  float ATM90E32Component::get_local_phase_forward_active_energy_(uint8_t phase) {
@@ -360,8 +331,7 @@ float ATM90E32Component::get_local_phase_peak_current_(uint8_t phase) { return t
360
331
 
361
332
  float ATM90E32Component::get_phase_voltage_(uint8_t phase) {
362
333
  const uint16_t voltage = this->read16_(ATM90E32_REGISTER_URMS + phase);
363
- if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != voltage)
364
- ESP_LOGW(TAG, "SPI URMS voltage register read error.");
334
+ this->validate_spi_read_(voltage, "get_phase_voltage()");
365
335
  return (float) voltage / 100;
366
336
  }
367
337
 
@@ -371,8 +341,7 @@ float ATM90E32Component::get_phase_voltage_avg_(uint8_t phase) {
371
341
  uint16_t voltage = 0;
372
342
  for (uint8_t i = 0; i < reads; i++) {
373
343
  voltage = this->read16_(ATM90E32_REGISTER_URMS + phase);
374
- if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != voltage)
375
- ESP_LOGW(TAG, "SPI URMS voltage register read error.");
344
+ this->validate_spi_read_(voltage, "get_phase_voltage_avg_()");
376
345
  accumulation += voltage;
377
346
  }
378
347
  voltage = accumulation / reads;
@@ -386,8 +355,7 @@ float ATM90E32Component::get_phase_current_avg_(uint8_t phase) {
386
355
  uint16_t current = 0;
387
356
  for (uint8_t i = 0; i < reads; i++) {
388
357
  current = this->read16_(ATM90E32_REGISTER_IRMS + phase);
389
- if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != current)
390
- ESP_LOGW(TAG, "SPI IRMS current register read error.");
358
+ this->validate_spi_read_(current, "get_phase_current_avg_()");
391
359
  accumulation += current;
392
360
  }
393
361
  current = accumulation / reads;
@@ -397,8 +365,7 @@ float ATM90E32Component::get_phase_current_avg_(uint8_t phase) {
397
365
 
398
366
  float ATM90E32Component::get_phase_current_(uint8_t phase) {
399
367
  const uint16_t current = this->read16_(ATM90E32_REGISTER_IRMS + phase);
400
- if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != current)
401
- ESP_LOGW(TAG, "SPI IRMS current register read error.");
368
+ this->validate_spi_read_(current, "get_phase_current_()");
402
369
  return (float) current / 1000;
403
370
  }
404
371
 
@@ -412,11 +379,15 @@ float ATM90E32Component::get_phase_reactive_power_(uint8_t phase) {
412
379
  return val * 0.00032f;
413
380
  }
414
381
 
382
+ float ATM90E32Component::get_phase_apparent_power_(uint8_t phase) {
383
+ const int val = this->read32_(ATM90E32_REGISTER_SMEAN + phase, ATM90E32_REGISTER_SMEANLSB + phase);
384
+ return val * 0.00032f;
385
+ }
386
+
415
387
  float ATM90E32Component::get_phase_power_factor_(uint8_t phase) {
416
- const int16_t powerfactor = this->read16_(ATM90E32_REGISTER_PFMEAN + phase);
417
- if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != powerfactor)
418
- ESP_LOGW(TAG, "SPI power factor read error.");
419
- return (float) powerfactor / 1000;
388
+ uint16_t powerfactor = this->read16_(ATM90E32_REGISTER_PFMEAN + phase); // unsigned to compare to lastspidata
389
+ this->validate_spi_read_(powerfactor, "get_phase_power_factor_()");
390
+ return (float) ((int16_t) powerfactor) / 1000; // make it signed again
420
391
  }
421
392
 
422
393
  float ATM90E32Component::get_phase_forward_active_energy_(uint8_t phase) {
@@ -426,17 +397,19 @@ float ATM90E32Component::get_phase_forward_active_energy_(uint8_t phase) {
426
397
  } else {
427
398
  this->phase_[phase].cumulative_forward_active_energy_ = val;
428
399
  }
429
- return ((float) this->phase_[phase].cumulative_forward_active_energy_ * 10 / 3200);
400
+ // 0.01CF resolution = 0.003125 Wh per count
401
+ return ((float) this->phase_[phase].cumulative_forward_active_energy_ * (10.0f / 3200.0f));
430
402
  }
431
403
 
432
404
  float ATM90E32Component::get_phase_reverse_active_energy_(uint8_t phase) {
433
- const uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGY);
405
+ const uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGY + phase);
434
406
  if (UINT32_MAX - this->phase_[phase].cumulative_reverse_active_energy_ > val) {
435
407
  this->phase_[phase].cumulative_reverse_active_energy_ += val;
436
408
  } else {
437
409
  this->phase_[phase].cumulative_reverse_active_energy_ = val;
438
410
  }
439
- return ((float) this->phase_[phase].cumulative_reverse_active_energy_ * 10 / 3200);
411
+ // 0.01CF resolution = 0.003125 Wh per count
412
+ return ((float) this->phase_[phase].cumulative_reverse_active_energy_ * (10.0f / 3200.0f));
440
413
  }
441
414
 
442
415
  float ATM90E32Component::get_phase_harmonic_active_power_(uint8_t phase) {
@@ -446,15 +419,15 @@ float ATM90E32Component::get_phase_harmonic_active_power_(uint8_t phase) {
446
419
 
447
420
  float ATM90E32Component::get_phase_angle_(uint8_t phase) {
448
421
  uint16_t val = this->read16_(ATM90E32_REGISTER_PANGLE + phase) / 10.0;
449
- return (float) (val > 180) ? val - 360.0 : val;
422
+ return (val > 180) ? (float) (val - 360.0f) : (float) val;
450
423
  }
451
424
 
452
425
  float ATM90E32Component::get_phase_peak_current_(uint8_t phase) {
453
426
  int16_t val = (float) this->read16_(ATM90E32_REGISTER_IPEAK + phase);
454
427
  if (!this->peak_current_signed_)
455
- val = abs(val);
428
+ val = std::abs(val);
456
429
  // phase register * phase current gain value / 1000 * 2^13
457
- return (float) (val * this->phase_[phase].ct_gain_ / 8192000.0);
430
+ return (val * this->phase_[phase].ct_gain_ / 8192000.0);
458
431
  }
459
432
 
460
433
  float ATM90E32Component::get_frequency_() {
@@ -467,29 +440,433 @@ float ATM90E32Component::get_chip_temperature_() {
467
440
  return (float) ctemp;
468
441
  }
469
442
 
470
- uint16_t ATM90E32Component::calibrate_voltage_offset_phase(uint8_t phase) {
443
+ void ATM90E32Component::run_gain_calibrations() {
444
+ if (!this->enable_gain_calibration_) {
445
+ ESP_LOGW(TAG, "[CALIBRATION] Gain calibration is disabled! Enable it first with enable_gain_calibration: true");
446
+ return;
447
+ }
448
+
449
+ float ref_voltages[3] = {
450
+ this->get_reference_voltage(0),
451
+ this->get_reference_voltage(1),
452
+ this->get_reference_voltage(2),
453
+ };
454
+ float ref_currents[3] = {this->get_reference_current(0), this->get_reference_current(1),
455
+ this->get_reference_current(2)};
456
+
457
+ ESP_LOGI(TAG, "[CALIBRATION] ");
458
+ ESP_LOGI(TAG, "[CALIBRATION] ========================= Gain Calibration =========================");
459
+ ESP_LOGI(TAG, "[CALIBRATION] ---------------------------------------------------------------------");
460
+ ESP_LOGI(TAG,
461
+ "[CALIBRATION] | Phase | V_meas (V) | I_meas (A) | V_ref | I_ref | V_gain (old→new) | I_gain (old→new) |");
462
+ ESP_LOGI(TAG, "[CALIBRATION] ---------------------------------------------------------------------");
463
+
464
+ for (uint8_t phase = 0; phase < 3; phase++) {
465
+ float measured_voltage = this->get_phase_voltage_avg_(phase);
466
+ float measured_current = this->get_phase_current_avg_(phase);
467
+
468
+ float ref_voltage = ref_voltages[phase];
469
+ float ref_current = ref_currents[phase];
470
+
471
+ uint16_t current_voltage_gain = this->read16_(voltage_gain_registers[phase]);
472
+ uint16_t current_current_gain = this->read16_(current_gain_registers[phase]);
473
+
474
+ bool did_voltage = false;
475
+ bool did_current = false;
476
+
477
+ // Voltage calibration
478
+ if (ref_voltage <= 0.0f) {
479
+ ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Skipping voltage calibration: reference voltage is 0.",
480
+ phase_labels[phase]);
481
+ } else if (measured_voltage == 0.0f) {
482
+ ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Skipping voltage calibration: measured voltage is 0.",
483
+ phase_labels[phase]);
484
+ } else {
485
+ uint32_t new_voltage_gain = static_cast<uint16_t>((ref_voltage / measured_voltage) * current_voltage_gain);
486
+ if (new_voltage_gain == 0) {
487
+ ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Voltage gain would be 0. Check reference and measured voltage.",
488
+ phase_labels[phase]);
489
+ } else {
490
+ if (new_voltage_gain >= 65535) {
491
+ ESP_LOGW(
492
+ TAG,
493
+ "[CALIBRATION] Phase %s - Voltage gain exceeds 65535. You may need a higher output voltage transformer.",
494
+ phase_labels[phase]);
495
+ new_voltage_gain = 65535;
496
+ }
497
+ this->gain_phase_[phase].voltage_gain = static_cast<uint16_t>(new_voltage_gain);
498
+ did_voltage = true;
499
+ }
500
+ }
501
+
502
+ // Current calibration
503
+ if (ref_current == 0.0f) {
504
+ ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Skipping current calibration: reference current is 0.",
505
+ phase_labels[phase]);
506
+ } else if (measured_current == 0.0f) {
507
+ ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Skipping current calibration: measured current is 0.",
508
+ phase_labels[phase]);
509
+ } else {
510
+ uint32_t new_current_gain = static_cast<uint16_t>((ref_current / measured_current) * current_current_gain);
511
+ if (new_current_gain == 0) {
512
+ ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Current gain would be 0. Check reference and measured current.",
513
+ phase_labels[phase]);
514
+ } else {
515
+ if (new_current_gain >= 65535) {
516
+ ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Current gain exceeds 65535. You may need to turn up pga gain.",
517
+ phase_labels[phase]);
518
+ new_current_gain = 65535;
519
+ }
520
+ this->gain_phase_[phase].current_gain = static_cast<uint16_t>(new_current_gain);
521
+ did_current = true;
522
+ }
523
+ }
524
+
525
+ // Final row output
526
+ ESP_LOGI(TAG, "[CALIBRATION] | %c | %9.2f | %9.4f | %5.2f | %6.4f | %5u → %-5u | %5u → %-5u |",
527
+ 'A' + phase, measured_voltage, measured_current, ref_voltage, ref_current, current_voltage_gain,
528
+ did_voltage ? this->gain_phase_[phase].voltage_gain : current_voltage_gain, current_current_gain,
529
+ did_current ? this->gain_phase_[phase].current_gain : current_current_gain);
530
+ }
531
+
532
+ ESP_LOGI(TAG, "[CALIBRATION] =====================================================================\n");
533
+
534
+ this->save_gain_calibration_to_memory_();
535
+ this->write_gains_to_registers_();
536
+ this->verify_gain_writes_();
537
+ }
538
+
539
+ void ATM90E32Component::save_gain_calibration_to_memory_() {
540
+ bool success = this->gain_calibration_pref_.save(&this->gain_phase_);
541
+ if (success) {
542
+ this->using_saved_calibrations_ = true;
543
+ ESP_LOGI(TAG, "[CALIBRATION] Gain calibration saved to memory.");
544
+ } else {
545
+ this->using_saved_calibrations_ = false;
546
+ ESP_LOGE(TAG, "[CALIBRATION] Failed to save gain calibration to memory!");
547
+ }
548
+ }
549
+
550
+ void ATM90E32Component::run_offset_calibrations() {
551
+ if (!this->enable_offset_calibration_) {
552
+ ESP_LOGW(TAG, "[CALIBRATION] Offset calibration is disabled! Enable it first with enable_offset_calibration: true");
553
+ return;
554
+ }
555
+
556
+ for (uint8_t phase = 0; phase < 3; phase++) {
557
+ int16_t voltage_offset = calibrate_offset(phase, true);
558
+ int16_t current_offset = calibrate_offset(phase, false);
559
+
560
+ this->write_offsets_to_registers_(phase, voltage_offset, current_offset);
561
+
562
+ ESP_LOGI(TAG, "[CALIBRATION] Phase %c - offset_voltage: %d, offset_current: %d", 'A' + phase, voltage_offset,
563
+ current_offset);
564
+ }
565
+
566
+ this->offset_pref_.save(&this->offset_phase_); // Save to flash
567
+ }
568
+
569
+ void ATM90E32Component::run_power_offset_calibrations() {
570
+ if (!this->enable_offset_calibration_) {
571
+ ESP_LOGW(
572
+ TAG,
573
+ "[CALIBRATION] Offset power calibration is disabled! Enable it first with enable_offset_calibration: true");
574
+ return;
575
+ }
576
+
577
+ for (uint8_t phase = 0; phase < 3; ++phase) {
578
+ int16_t active_offset = calibrate_power_offset(phase, false);
579
+ int16_t reactive_offset = calibrate_power_offset(phase, true);
580
+
581
+ this->write_power_offsets_to_registers_(phase, active_offset, reactive_offset);
582
+
583
+ ESP_LOGI(TAG, "[CALIBRATION] Phase %c - offset_active_power: %d, offset_reactive_power: %d", 'A' + phase,
584
+ active_offset, reactive_offset);
585
+ }
586
+
587
+ this->power_offset_pref_.save(&this->power_offset_phase_); // Save to flash
588
+ }
589
+
590
+ void ATM90E32Component::write_gains_to_registers_() {
591
+ this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA);
592
+
593
+ for (int phase = 0; phase < 3; phase++) {
594
+ this->write16_(voltage_gain_registers[phase], this->gain_phase_[phase].voltage_gain);
595
+ this->write16_(current_gain_registers[phase], this->gain_phase_[phase].current_gain);
596
+ }
597
+
598
+ this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000);
599
+ }
600
+
601
+ void ATM90E32Component::write_offsets_to_registers_(uint8_t phase, int16_t voltage_offset, int16_t current_offset) {
602
+ // Save to runtime
603
+ this->offset_phase_[phase].voltage_offset_ = voltage_offset;
604
+ this->phase_[phase].voltage_offset_ = voltage_offset;
605
+
606
+ // Save to flash-storable struct
607
+ this->offset_phase_[phase].current_offset_ = current_offset;
608
+ this->phase_[phase].current_offset_ = current_offset;
609
+
610
+ // Write to registers
611
+ this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA);
612
+ this->write16_(voltage_offset_registers[phase], static_cast<uint16_t>(voltage_offset));
613
+ this->write16_(current_offset_registers[phase], static_cast<uint16_t>(current_offset));
614
+ this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000);
615
+ }
616
+
617
+ void ATM90E32Component::write_power_offsets_to_registers_(uint8_t phase, int16_t p_offset, int16_t q_offset) {
618
+ // Save to runtime
619
+ this->phase_[phase].active_power_offset_ = p_offset;
620
+ this->phase_[phase].reactive_power_offset_ = q_offset;
621
+
622
+ // Save to flash-storable struct
623
+ this->power_offset_phase_[phase].active_power_offset = p_offset;
624
+ this->power_offset_phase_[phase].reactive_power_offset = q_offset;
625
+
626
+ // Write to registers
627
+ this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA);
628
+ this->write16_(this->power_offset_registers[phase], static_cast<uint16_t>(p_offset));
629
+ this->write16_(this->reactive_power_offset_registers[phase], static_cast<uint16_t>(q_offset));
630
+ this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000);
631
+ }
632
+
633
+ void ATM90E32Component::restore_gain_calibrations_() {
634
+ if (this->gain_calibration_pref_.load(&this->gain_phase_)) {
635
+ ESP_LOGI(TAG, "[CALIBRATION] Restoring saved gain calibrations to registers:");
636
+
637
+ for (uint8_t phase = 0; phase < 3; phase++) {
638
+ uint16_t v_gain = this->gain_phase_[phase].voltage_gain;
639
+ uint16_t i_gain = this->gain_phase_[phase].current_gain;
640
+ ESP_LOGI(TAG, "[CALIBRATION] Phase %c - Voltage Gain: %u, Current Gain: %u", 'A' + phase, v_gain, i_gain);
641
+ }
642
+
643
+ this->write_gains_to_registers_();
644
+
645
+ if (this->verify_gain_writes_()) {
646
+ this->using_saved_calibrations_ = true;
647
+ ESP_LOGI(TAG, "[CALIBRATION] Gain calibration loaded and verified successfully.");
648
+ } else {
649
+ this->using_saved_calibrations_ = false;
650
+ ESP_LOGE(TAG, "[CALIBRATION] Gain verification failed! Calibration may not be applied correctly.");
651
+ }
652
+ } else {
653
+ this->using_saved_calibrations_ = false;
654
+ ESP_LOGW(TAG, "[CALIBRATION] No stored gain calibrations found. Using config file values.");
655
+ }
656
+ }
657
+
658
+ void ATM90E32Component::restore_offset_calibrations_() {
659
+ if (this->offset_pref_.load(&this->offset_phase_)) {
660
+ ESP_LOGI(TAG, "[CALIBRATION] Successfully restored offset calibration from memory.");
661
+
662
+ for (uint8_t phase = 0; phase < 3; phase++) {
663
+ auto &offset = this->offset_phase_[phase];
664
+ write_offsets_to_registers_(phase, offset.voltage_offset_, offset.current_offset_);
665
+ ESP_LOGI(TAG, "[CALIBRATION] Phase %c - offset_voltage:: %d, offset_current: %d", 'A' + phase,
666
+ offset.voltage_offset_, offset.current_offset_);
667
+ }
668
+ } else {
669
+ ESP_LOGW(TAG, "[CALIBRATION] No stored offset calibrations found. Using default values.");
670
+ }
671
+ }
672
+
673
+ void ATM90E32Component::restore_power_offset_calibrations_() {
674
+ if (this->power_offset_pref_.load(&this->power_offset_phase_)) {
675
+ ESP_LOGI(TAG, "[CALIBRATION] Successfully restored power offset calibration from memory.");
676
+
677
+ for (uint8_t phase = 0; phase < 3; ++phase) {
678
+ auto &offset = this->power_offset_phase_[phase];
679
+ write_power_offsets_to_registers_(phase, offset.active_power_offset, offset.reactive_power_offset);
680
+ ESP_LOGI(TAG, "[CALIBRATION] Phase %c - offset_active_power: %d, offset_reactive_power: %d", 'A' + phase,
681
+ offset.active_power_offset, offset.reactive_power_offset);
682
+ }
683
+ } else {
684
+ ESP_LOGW(TAG, "[CALIBRATION] No stored power offsets found. Using default values.");
685
+ }
686
+ }
687
+
688
+ void ATM90E32Component::clear_gain_calibrations() {
689
+ ESP_LOGI(TAG, "[CALIBRATION] Clearing stored gain calibrations and restoring config-defined values...");
690
+
691
+ for (int phase = 0; phase < 3; phase++) {
692
+ gain_phase_[phase].voltage_gain = this->phase_[phase].voltage_gain_;
693
+ gain_phase_[phase].current_gain = this->phase_[phase].ct_gain_;
694
+ }
695
+
696
+ bool success = this->gain_calibration_pref_.save(&this->gain_phase_);
697
+ this->using_saved_calibrations_ = false;
698
+
699
+ if (success) {
700
+ ESP_LOGI(TAG, "[CALIBRATION] Gain calibrations cleared. Config values restored:");
701
+ for (int phase = 0; phase < 3; phase++) {
702
+ ESP_LOGI(TAG, "[CALIBRATION] Phase %c - Voltage Gain: %u, Current Gain: %u", 'A' + phase,
703
+ gain_phase_[phase].voltage_gain, gain_phase_[phase].current_gain);
704
+ }
705
+ } else {
706
+ ESP_LOGE(TAG, "[CALIBRATION] Failed to clear gain calibrations!");
707
+ }
708
+
709
+ this->write_gains_to_registers_(); // Apply them to the chip immediately
710
+ }
711
+
712
+ void ATM90E32Component::clear_offset_calibrations() {
713
+ for (uint8_t phase = 0; phase < 3; phase++) {
714
+ this->write_offsets_to_registers_(phase, 0, 0);
715
+ }
716
+
717
+ this->offset_pref_.save(&this->offset_phase_); // Save cleared values to flash memory
718
+
719
+ ESP_LOGI(TAG, "[CALIBRATION] Offsets cleared.");
720
+ }
721
+
722
+ void ATM90E32Component::clear_power_offset_calibrations() {
723
+ for (uint8_t phase = 0; phase < 3; phase++) {
724
+ this->write_power_offsets_to_registers_(phase, 0, 0);
725
+ }
726
+
727
+ this->power_offset_pref_.save(&this->power_offset_phase_);
728
+
729
+ ESP_LOGI(TAG, "[CALIBRATION] Power offsets cleared.");
730
+ }
731
+
732
+ int16_t ATM90E32Component::calibrate_offset(uint8_t phase, bool voltage) {
471
733
  const uint8_t num_reads = 5;
472
734
  uint64_t total_value = 0;
473
- for (int i = 0; i < num_reads; ++i) {
474
- const uint32_t measurement_value = read32_(ATM90E32_REGISTER_URMS + phase, ATM90E32_REGISTER_URMSLSB + phase);
475
- total_value += measurement_value;
735
+
736
+ for (uint8_t i = 0; i < num_reads; ++i) {
737
+ uint32_t reading = voltage ? this->read32_(ATM90E32_REGISTER_URMS + phase, ATM90E32_REGISTER_URMSLSB + phase)
738
+ : this->read32_(ATM90E32_REGISTER_IRMS + phase, ATM90E32_REGISTER_IRMSLSB + phase);
739
+ total_value += reading;
476
740
  }
741
+
477
742
  const uint32_t average_value = total_value / num_reads;
478
- const uint32_t shifted_value = average_value >> 7;
479
- const uint32_t voltage_offset = ~shifted_value + 1;
480
- return voltage_offset & 0xFFFF; // Take the lower 16 bits
743
+ const uint32_t shifted = average_value >> 7;
744
+ const uint32_t offset = ~shifted + 1;
745
+ return static_cast<int16_t>(offset); // Takes lower 16 bits
481
746
  }
482
747
 
483
- uint16_t ATM90E32Component::calibrate_current_offset_phase(uint8_t phase) {
748
+ int16_t ATM90E32Component::calibrate_power_offset(uint8_t phase, bool reactive) {
484
749
  const uint8_t num_reads = 5;
485
750
  uint64_t total_value = 0;
486
- for (int i = 0; i < num_reads; ++i) {
487
- const uint32_t measurement_value = read32_(ATM90E32_REGISTER_IRMS + phase, ATM90E32_REGISTER_IRMSLSB + phase);
488
- total_value += measurement_value;
751
+
752
+ for (uint8_t i = 0; i < num_reads; ++i) {
753
+ uint32_t reading = reactive ? this->read32_(ATM90E32_REGISTER_QMEAN + phase, ATM90E32_REGISTER_QMEANLSB + phase)
754
+ : this->read32_(ATM90E32_REGISTER_PMEAN + phase, ATM90E32_REGISTER_PMEANLSB + phase);
755
+ total_value += reading;
489
756
  }
757
+
490
758
  const uint32_t average_value = total_value / num_reads;
491
- const uint32_t current_offset = ~average_value + 1;
492
- return current_offset & 0xFFFF; // Take the lower 16 bits
759
+ const uint32_t power_offset = ~average_value + 1;
760
+ return static_cast<int16_t>(power_offset); // Takes the lower 16 bits
761
+ }
762
+
763
+ bool ATM90E32Component::verify_gain_writes_() {
764
+ bool success = true;
765
+ for (uint8_t phase = 0; phase < 3; phase++) {
766
+ uint16_t read_voltage = this->read16_(voltage_gain_registers[phase]);
767
+ uint16_t read_current = this->read16_(current_gain_registers[phase]);
768
+
769
+ if (read_voltage != this->gain_phase_[phase].voltage_gain ||
770
+ read_current != this->gain_phase_[phase].current_gain) {
771
+ ESP_LOGE(TAG, "[CALIBRATION] Mismatch detected for Phase %s!", phase_labels[phase]);
772
+ success = false;
773
+ }
774
+ }
775
+ return success; // Return true if all writes were successful, false otherwise
776
+ }
777
+
778
+ #ifdef USE_TEXT_SENSOR
779
+ void ATM90E32Component::check_phase_status() {
780
+ uint16_t state0 = this->read16_(ATM90E32_REGISTER_EMMSTATE0);
781
+ uint16_t state1 = this->read16_(ATM90E32_REGISTER_EMMSTATE1);
782
+
783
+ for (int phase = 0; phase < 3; phase++) {
784
+ std::string status;
785
+
786
+ if (state0 & over_voltage_flags[phase])
787
+ status += "Over Voltage; ";
788
+ if (state1 & voltage_sag_flags[phase])
789
+ status += "Voltage Sag; ";
790
+ if (state1 & phase_loss_flags[phase])
791
+ status += "Phase Loss; ";
792
+
793
+ auto *sensor = this->phase_status_text_sensor_[phase];
794
+ const char *phase_name = sensor ? sensor->get_name().c_str() : "Unknown Phase";
795
+ if (!status.empty()) {
796
+ status.pop_back(); // remove space
797
+ status.pop_back(); // remove semicolon
798
+ ESP_LOGW(TAG, "%s: %s", phase_name, status.c_str());
799
+ if (sensor != nullptr)
800
+ sensor->publish_state(status);
801
+ } else {
802
+ if (sensor != nullptr)
803
+ sensor->publish_state("Okay");
804
+ }
805
+ }
806
+ }
807
+
808
+ void ATM90E32Component::check_freq_status() {
809
+ uint16_t state1 = this->read16_(ATM90E32_REGISTER_EMMSTATE1);
810
+
811
+ std::string freq_status;
812
+
813
+ if (state1 & ATM90E32_STATUS_S1_FREQHIST) {
814
+ freq_status = "HIGH";
815
+ } else if (state1 & ATM90E32_STATUS_S1_FREQLOST) {
816
+ freq_status = "LOW";
817
+ } else {
818
+ freq_status = "Normal";
819
+ }
820
+ ESP_LOGW(TAG, "Frequency status: %s", freq_status.c_str());
821
+
822
+ if (this->freq_status_text_sensor_ != nullptr) {
823
+ this->freq_status_text_sensor_->publish_state(freq_status);
824
+ }
825
+ }
826
+
827
+ void ATM90E32Component::check_over_current() {
828
+ constexpr float max_current_threshold = 65.53f;
829
+
830
+ for (uint8_t phase = 0; phase < 3; phase++) {
831
+ float current_val =
832
+ this->phase_[phase].current_sensor_ != nullptr ? this->phase_[phase].current_sensor_->state : 0.0f;
833
+
834
+ if (current_val > max_current_threshold) {
835
+ ESP_LOGW(TAG, "Over current detected on Phase %c: %.2f A", 'A' + phase, current_val);
836
+ ESP_LOGW(TAG, "You may need to half your gain_ct: value & multiply the current and power values by 2");
837
+ if (this->phase_status_text_sensor_[phase] != nullptr) {
838
+ this->phase_status_text_sensor_[phase]->publish_state("Over Current; ");
839
+ }
840
+ }
841
+ }
842
+ }
843
+ #endif
844
+
845
+ uint16_t ATM90E32Component::calculate_voltage_threshold(int line_freq, uint16_t ugain, float multiplier) {
846
+ // this assumes that 60Hz electrical systems use 120V mains,
847
+ // which is usually, but not always the case
848
+ float nominal_voltage = (line_freq == 60) ? 120.0f : 220.0f;
849
+ float target_voltage = nominal_voltage * multiplier;
850
+
851
+ float peak_01v = target_voltage * 100.0f * std::sqrt(2.0f); // convert RMS → peak, scale to 0.01V
852
+ float divider = (2.0f * ugain) / 32768.0f;
853
+
854
+ float threshold = peak_01v / divider;
855
+
856
+ return static_cast<uint16_t>(threshold);
857
+ }
858
+
859
+ bool ATM90E32Component::validate_spi_read_(uint16_t expected, const char *context) {
860
+ uint16_t last = this->read16_(ATM90E32_REGISTER_LASTSPIDATA);
861
+ if (last != expected) {
862
+ if (context != nullptr) {
863
+ ESP_LOGW(TAG, "[%s] SPI read mismatch: expected 0x%04X, got 0x%04X", context, expected, last);
864
+ } else {
865
+ ESP_LOGW(TAG, "SPI read mismatch: expected 0x%04X, got 0x%04X", expected, last);
866
+ }
867
+ return false;
868
+ }
869
+ return true;
493
870
  }
494
871
 
495
872
  } // namespace atm90e32