###### sthome-ut10 ###### external_components: - source: type: local path: components # Path relative to this YAML file components: [ ds3231, tlc59208f_ext, ads131m08 ] #, ac_voltage_sensor ] packages: - !include common/wifi.yaml - !include common/geyser.yaml - !include common/felicityinverter.yaml - !include common/modbus.yaml - !include common/gpio_assignments.yaml - !include common/canbus.yaml # - device: !include device.yaml substitutions: name: sthome-ut10 friendly_name: "sthome-ut10" MAX_SCHOOL_HOLIDAY_PERIODS: 12 BATTERY_INFO_TIMEOUT: 30 # 30 sec timeout GEYSER_HEAT_IND_MIN: 20 GEYSER_HEAT_IND_MAX: 70 ENERGY_RESET_PIN: GPIO0 CB1CS_PIN: GPIO3 CB2CS_PIN: GPIO10 RELAY1_PIN: GPIO4 RELAY2_PIN: GPIO5 RELAY3_PIN: GPIO6 RELAY4_PIN: GPIO7 HEAT_SENSOR_PIN: GPIO14 ADC_SYNC_RESET_PIN: GPIO21 ADC_CS_PIN: GPIO41 ADC_DRDY_PIN: GPIO42 SYSTEM_OK_LED_PIN: GPIO43 ECONOMY_MODE_LED_PIN: GPIO44 ECONOMY_MODE_SWITCH_PIN: GPIO45 LOW_BATTERY_SENSOR_PIN: GPIO46 LOW_BATTERY_LED_PIN: GPIO47 MAINS_FAIL_LED_PIN: GPIO48 LED_RESET_PIN: GPIO19 # temporary - to debug TLC59208F esphome: name: "${name}" friendly_name: "${friendly_name}" #libraries: # - "SPI" includes: - source # copies folder with files to relevant to be included in esphome compile - # angle brackets ensure file is included above globals in main.cpp. Make sure to use include GUARDS in the file to prevent double inclusion - - - - - - on_boot: - priority: 600 # This is where most sensors are set up (higher number means higher priority) then: - ds3231.read_time: # read the RTC time - lambda: |- id(light_economy_mode).turn_off(); id(light_mains_fail).turn_off().set_brightness(0).perform(); id(light_system_ok).turn_off().set_brightness(0).perform(); id(light_inverter_battery_low).turn_off(); - delay: 300ms globals: - id: time_synched type: bool restore_value: no initial_value: 'false' - id: can1_msgctr type: int restore_value: no - id: can2_msgctr type: int restore_value: no - id: g_cb_cache # the cache is used to only accept a frame after it has been received a specified number of times within a specified period. this hopefully will iron out spurious corrupted frames type: solar::cbf_cache restore_value: no - id: g_cb_request_queue type: std::queue< std::set > restore_value: no esp32: board: esp32-s3-devkitc-1 flash_size: 16MB cpu_frequency: 240MHz framework: type: esp-idf #arduino # # sdkconfig_options: # CONFIG_ESP32_S3_BOX_BOARD: "y" psram: mode: octal speed: 80MHz debug: update_interval: 15s # Enable logging logger: baud_rate: 115200 level: INFO initial_level: INFO logs: # i2c: DEBUG # i2c.idf: DEBUG # uart: INFO # light: INFO sensor: INFO # ds3231: DEBUG # tlc59208f: DEBUG # tlc59208f_ext: DEBUG # ads131m08: DEBUG # ads131m08_sensor: DEBUG # spi: DEBUG # spi_device: DEBUG # canbus: DEBUG # canbus_mcp2515: DEBUG # mcp2515: DEBUG # modbus: INFO # modbus_controller: INFO # modbus_controller.sensor: INFO # text_sensor: INFO # ac_voltage_sensor: INFO # uart_debug: INFO # If set to DEBUG, logs raw UART data in hex format, with direction indication (RX/TX) and timestamp. Can be very verbose, so use with caution. # uart.idf: INFO # logger: INFO # switch.gpio: INFO # ledc.output: INFO # gpio.binary_sensor: INFO # template.binary_sensor: INFO # restart: INFO # status: INFO # wifi_info: INFO # homeassistant.time: INFO # time: INFO # captive_portal: INFO # wifi: INFO # web_server: INFO # esphome.ota: INFO # safe_mode: INFO # web_server.ota: INFO # api: INFO # wifi_signal.sensor: INFO # mdns: INFO # Enable Home Assistant API api: encryption: key: "NApAUAUGkzfbNhOikFb2k+AIE8LMr7O8Ck15IaszrUU=" ota: - platform: esphome password: "d669ae14ff964bb0f195e820118af8e3" wifi: power_save_mode: none manual_ip: static_ip: 10.0.2.10 # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "${name} Fallback Hotspot" password: "YmuU9F5YgBZ1" captive_portal: one_wire: - platform: gpio pin: ${HEAT_SENSOR_PIN} id: geyser_temperature_sensors i2c: - id: bus_a sda: ${ESP32_S3_N16R8_DEVKITC_I2C1_SDA} scl: ${ESP32_S3_N16R8_DEVKITC_I2C1_SCL} scan: true frequency: 400kHz - id: bus_b sda: ${ESP32_S3_N16R8_DEVKITC_I2C2_SDA} scl: ${ESP32_S3_N16R8_DEVKITC_I2C2_SCL} scan: true frequency: 20kHz sda_pullup_enabled: true scl_pullup_enabled: true # Example configuration entry #web_server: # port: 80 # include_internal: true spi: - id: spi_bus0 clk_pin: ${ESP32_S3_N16R8_DEVKITC_SPI1_SCK} mosi_pin: ${ESP32_S3_N16R8_DEVKITC_SPI1_MOSI} miso_pin: ${ESP32_S3_N16R8_DEVKITC_SPI1_MISO} interface: any - id: spi_bus1 clk_pin: ${ESP32_S3_N16R8_DEVKITC_SPI2_SCK} mosi_pin: ${ESP32_S3_N16R8_DEVKITC_SPI2_MOSI} miso_pin: ${ESP32_S3_N16R8_DEVKITC_SPI2_MISO} interface: hardware ads131m08: id: highres_adc spi_id: spi_bus1 cs_pin: ${ADC_CS_PIN} drdy_pin: ${ADC_DRDY_PIN} sync_reset_pin: ${ADC_SYNC_RESET_PIN} reference_voltage: 1.25 data_rate: 10MHz clock_frequency: 8192kHz oversampling_ratio: 512 sensor: # # 30A clamp # - platform: ct_clamp # sensor: ads_ch2 # id: current2 # name: "ACL Current2" # update_interval: 5s # sample_duration: 200ms # state_class: measurement # device_class: current # filters: # # burden resistor is 62Ω in parallel with 33Ω = 21.54Ω # # multiplier should be 1860/21.54 = x86.35 # - multiply: 100 #86.35 # # 30A clamp # - platform: ct_clamp # sensor: ads_ch3 # id: current3 # name: "ACL Current3" # update_interval: 5s # sample_duration: 200ms # state_class: measurement # device_class: current # filters: # # burden resistor is 62Ω in parallel with 33Ω = 21.54Ω # # multiplier should be 1860/21.54 = x86.35 # - multiply: 100 #86.35 - platform: ads131m08 ads131m08_id: highres_adc channel: 0 accuracy_decimals: 6 state_class: measurement device_class: voltage name: "ads_ch0" gain: 1 input_select: normal offset_calibration: 0 gain_calibration: 0.98587182 - platform: ads131m08 ads131m08_id: highres_adc channel: 1 accuracy_decimals: 6 state_class: measurement device_class: voltage name: "ads_ch1" gain: 1 input_select: normal offset_calibration: 0 gain_calibration: 0.98642854 - platform: ads131m08 ads131m08_id: highres_adc channel: 2 accuracy_decimals: 6 state_class: measurement device_class: voltage id: ads_ch2 name: "ads_ch2" gain: 1 input_select: normal offset_calibration: 0 gain_calibration: 1.36153846 filters: - multiply: 102 read_rms: enable - platform: ads131m08 ads131m08_id: highres_adc channel: 3 accuracy_decimals: 6 state_class: measurement device_class: voltage id: ads_ch3 name: "ads_ch3" gain: 1 input_select: normal offset_calibration: 0 gain_calibration: 1.36153846 filters: - multiply: 102 read_rms: true - platform: ads131m08 ads131m08_id: highres_adc channel: 4 accuracy_decimals: 4 state_class: measurement device_class: voltage name: "ads_ch4" gain: 1 input_select: normal offset_calibration: 0 gain_calibration: 1.36153846 read_rms: yes - platform: ads131m08 ads131m08_id: highres_adc channel: 5 accuracy_decimals: 4 state_class: measurement device_class: voltage name: "ads_ch5" gain: 1 input_select: normal offset_calibration: 0 gain_calibration: 1 read_rms: disable - platform: ads131m08 ads131m08_id: highres_adc channel: 6 accuracy_decimals: 4 state_class: measurement device_class: voltage name: "ads_ch6" gain: 1 input_select: normal offset_calibration: 0 gain_calibration: 1 read_rms: yes - platform: ads131m08 ads131m08_id: highres_adc channel: 7 accuracy_decimals: 4 state_class: measurement device_class: voltage name: "ads_ch7" gain: 1 input_select: normal offset_calibration: 0 gain_calibration: 1 read_rms: yes - platform: ads131m08 ads131m08_id: highres_adc channel: 8 name: "Sample time" unit_of_measurement: "ms" - platform: ads131m08 ads131m08_id: highres_adc channel: 9 name: "Max samples" accuracy_decimals: 0 unit_of_measurement: "" - platform: ads131m08 ads131m08_id: highres_adc channel: 10 name: "CRC errors" accuracy_decimals: 0 unit_of_measurement: "" #sensor: # Report wifi signal strength every 5 min if changed - platform: wifi_signal name: WiFi Signal update_interval: 300s filters: - delta: 10% uart: - id: inv_uart1 tx_pin: ${ESP32_S3_N16R8_DEVKITC_UART1_TX} # Tx1 rx_pin: number: ${ESP32_S3_N16R8_DEVKITC_UART1_RX} # Rx1 inverted: false mode: input: true pullup: false # external pullup baud_rate: 2400 stop_bits: 1 parity: NONE debug: direction: BOTH dummy_receiver: false # after: # delimiter: "\r" sequence: - lambda: UARTDebug::log_hex(direction, bytes, ','); - id: inv_uart2 tx_pin: ${ESP32_S3_N16R8_DEVKITC_UART2_TX} # Tx2 rx_pin: number: ${ESP32_S3_N16R8_DEVKITC_UART2_RX} # Rx2 inverted: false mode: input: true pullup: false # external pullup baud_rate: 2400 stop_bits: 1 parity: NONE debug: direction: BOTH dummy_receiver: false # after: # delimiter: "\r" sequence: - lambda: UARTDebug::log_hex(direction, bytes, ' '); sun: id: sun_sensor latitude: !secret latitude longitude: !secret longitude canbus: - platform: mcp2515 cs_pin: ${CB1CS_PIN} # CB1CS spi_id: spi_bus0 id: canbus_sthome mode: NORMAL can_id: ${CB_CANBUS_ID10} bit_rate: 500KBPS on_frame: - can_id: 0 can_id_mask: 0 then: - lambda: |- using namespace solar; id(can1_msgctr)++; auto time_obj = id(time_source).now(); if(time_obj.is_valid()) { if(can_id >= 0x350 && can_id < 0x380) { auto cbitem = cbf_store_pylon(id(can1_msgctr), can_id, x, remote_transmission_request, time_obj.timestamp); //ESP_LOGI(cbitem.tag().c_str(), "%s", cbitem.to_string().c_str()); bool publish = id(g_cb_cache).additem(cbitem); if(publish) { ESP_LOGV(cbitem.tag().c_str(), "%s", cbitem.to_string().c_str()); } } else { auto cbitem = cbf_store_sthome(id(can1_msgctr), can_id, x, remote_transmission_request, time_obj.timestamp); ESP_LOGI(cbitem.tag().c_str(), "%s", cbitem.to_string().c_str()); } } - platform: mcp2515 cs_pin: ${CB2CS_PIN} # CB2CS spi_id: spi_bus0 id: canbus_solarbattery mode: NORMAL can_id: ${CB_CANBUS_ID10} bit_rate: 500KBPS on_frame: - can_id: 0 can_id_mask: 0 then: - lambda: |- using namespace solar; id(can2_msgctr)++; auto time_obj = id(time_source).now(); if(time_obj.is_valid()) { if(can_id >= 0x350 && can_id < 0x380) { auto cbitem = cbf_store_pylon(id(can2_msgctr), can_id, x, remote_transmission_request, time_obj.timestamp); //ESP_LOGI(cbitem.tag().c_str(), "%s", cbitem.to_string().c_str()); bool publish = id(g_cb_cache).additem(cbitem); if(publish) { ESP_LOGV(cbitem.tag().c_str(), "%s", cbitem.to_string().c_str()); } } else { auto cbitem = cbf_store_sthome(id(can2_msgctr), can_id, x, remote_transmission_request, time_obj.timestamp); ESP_LOGI(cbitem.tag().c_str(), "%s", cbitem.to_string().c_str()); } } time: - platform: ds3231 address: 0x68 i2c_id: bus_a # # repeated synchronization is not necessary unless the external RTC # # is much more accurate than the internal clock update_interval: never # # - platform: sntp # timezone: Africa/Johannesburg # servers: # - ntp1.meraka.csir.co.za # 146.64.24.58 # - ntp.as3741.net # 196.4.160.4 # - ntp1.inx.net.za # 196.10.52.57 - platform: homeassistant id: time_source on_time_sync: - ds3231.write_time: - lambda: |- id(time_synched) = true; - logger.log: "Synchronized system clock" on_time: # # do every second - seconds: '*' minutes: '*' then: - lambda: |- auto time_obj = id(time_source).now(); if(time_obj.is_valid()) { id(set_status_indicators).execute(); } switch: # in economy mode, geyser is only switched on when it can be powered by solar only, i.e. without using mains - platform: gpio pin: number: ${ECONOMY_MODE_SWITCH_PIN} inverted: true mode: input: true pullup: false # external pullup # filters: # - delayed_off: 100ms id: economy_mode # mode_select_switch name: "Economy Mode" # "Mode Select" icon: "mdi:beach" - platform: restart name: "${name} Restart" id: "restart_switch" - platform: gpio id: reset_energy_counters pin: number: ${ENERGY_RESET_PIN} inverted: true mode: input: true pullup: true name: "Reset Energy Counters" disabled_by_default: True restore_mode: RESTORE_DEFAULT_OFF on_turn_on: then: - lambda: |- ESP_LOGI("reset_energy_counters", "Time source is %s", id(time_source).now().is_valid() ? "valid" : "invalid"); - platform: gpio pin: number: ${RELAY1_PIN} inverted: false mode: output id: geyser_relay name: "Geyser Relay" icon: "mdi:water-thermometer" restore_mode: ALWAYS_OFF - platform: gpio pin: number: ${RELAY2_PIN} inverted: true mode: output id: pool_relay name: "Pool Relay" icon: "mdi:pool" restore_mode: ALWAYS_OFF tlc59208f: address: 0x20 id: tlc59208f_1 i2c_id: bus_b #tlc59208f_ext: # address: 0x20 # id: tlc59208f_1 # i2c_id: bus_b # reset_pin: # number: ${RELAY4_PIN} #${LED_RESET_PIN} # there was no more spare GPIO. if reset functionality is needed, we can consider using an IO expander pin for it. # mode: # output: true # pullup: true output: - platform: ledc pin: number: ${LOW_BATTERY_LED_PIN} # LED_LOW_BAT inverted: false #true id: led_inverter_battery_low - platform: ledc pin: number: ${ECONOMY_MODE_LED_PIN} inverted: false #true id: led_economy_mode - platform: ledc pin: number: ${MAINS_FAIL_LED_PIN} inverted: false #true id: led_mains_fail - platform: ledc pin: number: ${SYSTEM_OK_LED_PIN} inverted: false #true id: led_system_ok - platform: tlc59208f channel: 0 tlc59208f_id: 'tlc59208f_1' id: led0 - platform: tlc59208f channel: 1 tlc59208f_id: 'tlc59208f_1' id: led1 - platform: tlc59208f channel: 2 tlc59208f_id: 'tlc59208f_1' id: led2 - platform: tlc59208f channel: 3 tlc59208f_id: 'tlc59208f_1' id: led3 - platform: tlc59208f channel: 4 tlc59208f_id: 'tlc59208f_1' id: led4 - platform: tlc59208f channel: 5 tlc59208f_id: 'tlc59208f_1' id: led5 - platform: tlc59208f channel: 6 tlc59208f_id: 'tlc59208f_1' id: led6 - platform: tlc59208f channel: 7 tlc59208f_id: 'tlc59208f_1' id: led7 light: - platform: monochromatic output: led_economy_mode name: "LED Economy Mode" id: light_economy_mode default_transition_length: 20ms - platform: monochromatic output: led_mains_fail name: "LED Mains Fail" id: light_mains_fail default_transition_length: 20ms - platform: monochromatic output: led_system_ok name: "LED System OK" id: light_system_ok default_transition_length: 20ms - platform: monochromatic output: led0 name: "LED Geyser Temperature 0" id: led_geyser_temp0 default_transition_length: 20ms on_turn_on: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED BLUE on"); on_turn_off: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED BLUE off"); - platform: monochromatic output: led1 name: "LED Geyser Temperature 1" id: led_geyser_temp1 default_transition_length: 20ms on_turn_on: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED GREEN on"); on_turn_off: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED GREEN off"); - platform: monochromatic output: led2 name: "LED Geyser Temperature 2" id: led_geyser_temp2 default_transition_length: 20ms on_turn_on: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED YELLOW on"); on_turn_off: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED YELLOW off"); - platform: monochromatic output: led3 name: "LED Geyser Temperature 3" id: led_geyser_temp3 default_transition_length: 20ms on_turn_on: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED YELLOW2 on"); on_turn_off: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED YELLOW2 off"); - platform: monochromatic output: led4 name: "LED Geyser Temperature 4" id: led_geyser_temp4 default_transition_length: 20ms on_turn_on: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED ORANGE on"); on_turn_off: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED ORANGE off"); - platform: monochromatic output: led5 name: "LED Geyser Temperature 5" id: led_geyser_temp5 default_transition_length: 20ms on_turn_on: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED ORANGE2 on"); on_turn_off: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED ORANGE2 off"); - platform: monochromatic output: led6 name: "LED Geyser Temperature 6" id: led_geyser_temp6 default_transition_length: 20ms on_turn_on: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED RED on"); on_turn_off: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED RED off"); - platform: monochromatic output: led7 name: "LED Geyser Temperature 7" id: led_geyser_temp7 default_transition_length: 20ms on_turn_on: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED RED2 on"); on_turn_off: - lambda: |- ESP_LOGV("geyser", "Geyser Temperature LED RED2 off"); - platform: monochromatic output: led_inverter_battery_low name: "LED Inverter Battery Low" id: light_inverter_battery_low default_transition_length: 20ms on_turn_on: - lambda: |- ESP_LOGI("battery", "Battery Low"); # on_turn_off: # - lambda: |- # ESP_LOGI("battery", "Battery OK"); binary_sensor: - platform: status # Status platform provides a connectivity sensor name: "Status" device_class: connectivity - platform: gpio pin: number: ${LOW_BATTERY_SENSOR_PIN} # LOW_BAT inverted: false mode: input: true pullup: true filters: - delayed_off: 50ms id: inverter_battery_charge_state name: "Inverter Battery Charge" device_class: battery - platform: template id: mains_supply name: "Mains Supply" lambda: |- return 200; device_class: power text_sensor: - platform: debug device: name: "Device Info" reset_reason: name: "Reset Reason" # Expose WiFi information as sensors - platform: wifi_info ip_address: name: IP mac_address: name: Mac Address script: - id: set_status_indicators then: - lambda: |- if(id(economy_mode).state) { id(led_economy_mode).turn_on(); } else { id(led_economy_mode).turn_off(); } if(id(inverter_battery_charge_state).state) { id(led_inverter_battery_low).turn_on(); } else { id(led_inverter_battery_low).turn_off(); } if(id(mains_supply).state) { id(light_system_ok).turn_on().set_brightness(1).perform(); id(light_mains_fail).turn_off().set_brightness(0).perform(); } else { id(light_system_ok).turn_off().set_brightness(0).perform(); id(light_mains_fail).turn_on().set_brightness(1).perform(); }