Last year, I built a smart mailbox that tells me when I have mail. At some point this year, it stopped working. Specifically, the one MQTT topic that gives a simple binary answer to the question ‘Got mail?’ seems to have stopped publishing. Everything else seems to work just fine. I’ve even checked MQTT Explorer and the topic just doesn’t seem to be written, despite the logs from the ESP32 saying it’s publishing it.
I’m pulling my hair out trying to see what’s wrong. Sorry to dump the whole script here, but if anyone has some insight or suggestions how to proceed, please let me know.
substitutions:
devicename: "smart-mailbox"
sub_ota: "1"
sub_sun: "2"
upd_prox1: "1"
upd_prox2: "2"
upd_adc: "4"
esphome:
name: $devicename
on_boot:
- priority: -300
then:
- script.execute: check_stuff
esp32:
board: esp32dev
framework:
type: esp-idf
wifi:
ssid: !secret wifi_ext_ssid
password: !secret wifi_password
power_save_mode: LIGHT
fast_connect: true
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Smart Mailbox Fallback Hotspot"
password: !secret ota_smart_mailbox
# captive_portal:
# Enable logging
logger:
level: DEBUG
# baud_rate: 0
# level: NONE
# Enable API
# api:
# Enable MQTT
mqtt:
id: mqtt_cli
broker: !secret mqtt_broker
username: !secret mqtt_username
password: !secret mqtt_password
birth_message:
will_message:
ota:
- platform: esphome
password: !secret ota_smart_mailbox
on_end:
then:
- lambda: |-
id(ota_mode) = 0;
id(got_mail) = 0;
- mqtt.publish:
topic: "$devicename/ota"
payload: "OFF"
retain: true
- lambda: |-
id(ota_ready).publish_state(false);
id(got_mail).publish_state(false);
globals:
- id: updates
type: int
restore_value: no
initial_value: '0'
- id: subscriptions
type: int
restore_value: no
initial_value: '0'
- id: ota_mode
type: int
restore_value: no
initial_value: '0'
- id: sun_status
type: std::string
restore_value: no
initial_value: '"unknown"'
- id: naptime
type: int
restore_value: no
initial_value: '300'
deep_sleep:
id: sleepy
i2c:
- id: bus_a
sda: 21
scl: 22
- id: bus_b
sda: 25
scl: 26
external_components:
source: github://dixonte/esphome-vcnl4010
components: [vcnl4010_i2c_sensor]
script:
- id: check_stuff
then:
# Announce device is online
- mqtt.publish:
topic: "$devicename/status"
payload: "online"
retain: true
# Get status from Home Assistant
- lambda: |-
id(ota_ready).publish_state(false);
id(mqtt_cli).subscribe("$devicename/ota", [=](const std::string &topic, const std::string &payload) {
ESP_LOGD("mqtt", "Received OTA status: %s", payload.c_str());
auto previous_ota = id(ota_mode);
id(ota_mode) = (payload.compare("ON") == 0);
id(subscriptions) |= $sub_ota;
if (!id(ota_mode) && previous_ota) {
ESP_LOGD("mailbox", "OTA mode aborted, going into immediate microsleep...");
id(ota_ready).publish_state(false);
id(sleepy).set_sleep_duration(1000);
id(sleepy).begin_sleep(true);
}
});
id(mqtt_cli).subscribe("sun/status", [=](const std::string &topic, const std::string &payload) {
ESP_LOGD("mqtt", "Received sun status: %s", payload.c_str());
id(sun_status) = payload;
id(subscriptions) |= $sub_sun;
});
# Wait for status from Home Assistant
- wait_until:
lambda: |-
return (id(subscriptions) == ($sub_ota | $sub_sun));
# How long should we sleep after updating?
- lambda: |-
if (id(sun_status) == "above_horizon") {
id(naptime) = 60; //600;
} else if (id(sun_status) == "below_horizon") {
id(naptime) = 3600;
} else {
id(naptime) = 60;
}
# Update sensors
- lambda: |-
ESP_LOGD("mailbox", "Updating sensors");
id(sensor1).update();
id(sensor2).update();
id(lastUpdate).update();
id(bat_voltage_raw).set_update_interval(1300);
id(bat_voltage_raw).start_poller();
# Wait for updates
- wait_until:
lambda: |-
return (id(updates) == ($upd_prox1 | $upd_prox2 | $upd_adc));
# Do we have mail?
- lambda: |-
//bool gotMail;
if ((id(prox1).state > 30000) || (id(prox2).state > 30000)) {
//gotMail = true;
ESP_LOGD("mailbox", "Publishing got mail state: You've got mail");
id(got_mail).publish_state(true);
} else {
//gotMail = false;
ESP_LOGD("mailbox", "Publishing got mail state: No mail is good mail");
id(got_mail).publish_state(false);
}
//ESP_LOGD("mailbox", "Publishing got mail state: %d", gotMail);
//id(got_mail).publish_state(gotMail);
- delay: 1s
- logger.log:
format: "Sleeping for %d seconds | OTA: %d"
args: [ 'id(naptime)', 'id(ota_mode)' ]
# Shut down indefinitely if battery low
- if:
condition:
lambda: 'return (id(bat_voltage).state < 3.1);'
then:
- mqtt.publish:
topic: "$devicename/status"
payload: "offline"
retain: true
- deep_sleep.enter:
id: sleepy
sleep_duration: 3650d
# Deep sleep to save battery, if not OTA mode
- if:
condition:
lambda: 'return id(ota_mode) == 0;'
then:
- deep_sleep.enter:
id: sleepy
sleep_duration: !lambda |-
return id(naptime) * 1000;
else:
- lambda: |-
ESP_LOGD("mqtt", "OTA ready.");
id(ota_ready).publish_state(true);
button:
- platform: template
name: "Manual update"
on_press:
- logger.log: Checking mail on demand
- component.update: sensor1
- component.update: sensor2
sensor:
- platform: vcnl4010_i2c_sensor
id: sensor1
i2c_id: bus_a
update_interval: never
proximity:
name: Proximity sensor 1
id: prox1
on_value:
lambda: |-
id(updates) |= $upd_prox1;
# ambient:
# name: Light sensor 1
- platform: vcnl4010_i2c_sensor
id: sensor2
i2c_id: bus_b
update_interval: never
proximity:
name: Proximity sensor 2
id: prox2
on_value:
lambda: |-
id(updates) |= $upd_prox2;
# ambient:
# name: Light sensor 2
- platform: adc
pin: 33
id: "bat_voltage_raw"
name: "Battery voltage (raw)"
accuracy_decimals: 3
update_interval: never
attenuation: auto
# filters:
# - sliding_window_moving_average:
# window_size: 30
# send_every: 5
# send_first_at: 1
filters:
- median:
window_size: 10
send_every: 10
send_first_at: 10
on_value:
lambda: |-
id(bat_voltage_raw).stop_poller();
id(bat_voltage).update();
id(bat_percentage).update();
id(updates) |= $upd_adc;
- platform: template
id: bat_voltage
name: "Battery voltage"
unit_of_measurement: 'V'
icon: "mdi:battery"
accuracy_decimals: 3
update_interval: never
lambda: |-
return ((360.0f * id(bat_voltage_raw).state) + (1000.0f * id(bat_voltage_raw).state)) / 1000.0f;
- platform: template
id: bat_percentage
name: "Battery level"
unit_of_measurement: '%'
icon: "mdi:battery-check"
update_interval: never
lambda: |-
return id(bat_voltage).state;
filters:
- calibrate_linear:
- 3.1 -> 0
- 4.2 -> 100
- platform: template
id: lastUpdate
name: "Last update"
icon: "mdi:timer-sand-complete"
state_class: "measurement"
accuracy_decimals: 1
update_interval: never
lambda: |-
return millis();
binary_sensor:
- platform: template
id: got_mail
name: Got mail?
icon: mdi:mailbox
- platform: template
id: ota_ready
name: OTA ready?
icon: mdi:download
Further testing suggests that mqtt.publish isn’t working for me, either. The $devicename/status is not being written to MQTT.