configs/sthome-ut10.yaml

830 lines
21 KiB
YAML

###### 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
- <source/solar/cb_frame.h> # angle brackets ensure file is included above globals in main.cpp. Make sure to use include GUARDS in the file to prevent double inclusion
- <source/solar/cbf_store.h>
- <source/solar/cbf_pylon.h>
- <source/solar/cbf_store_pylon.h>
- <source/solar/cbf_sthome.h>
- <source/solar/cbf_store_sthome.h>
- <source/solar/cbf_cache.h>
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<uint32_t> >
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();
}