Initial frame device
This commit is contained in:
682
frame.yaml
Normal file
682
frame.yaml
Normal file
@@ -0,0 +1,682 @@
|
||||
# =============================================================================
|
||||
# frame — beacon-driven sleep mode.
|
||||
#
|
||||
# WiFi is OFF except briefly during a refresh. Every 15 seconds the device
|
||||
# wakes, does a passive BLE scan, and only powers up WiFi if it
|
||||
# sees its own iBeacon (UUID = 10-byte prefix + this device's WiFi STA MAC).
|
||||
#
|
||||
# Beacon command codes (iBeacon Major+Minor):
|
||||
# 0x0000 / 0x0000 → refresh image now
|
||||
# 0x0001 / 0x0001 → stay awake (skip sleep, connect to HA, wait)
|
||||
#
|
||||
# Hardware override: press D2 to wake into stay-awake mode (for USB OTA, debug).
|
||||
#
|
||||
# Every 24h without HA contact the device still wakes, connects, pushes battery
|
||||
# state, and sleeps — without fetching the image.
|
||||
#
|
||||
# HA SETUP: create input_boolean.bigink_stay_awake_request as a Toggle helper.
|
||||
# HA-side automation flips it on → beacon broadcaster emits the stay-awake code
|
||||
# → bigink stays awake. Flip off → bigink returns to its sleep cycle.
|
||||
# =============================================================================
|
||||
|
||||
substitutions:
|
||||
# Compile-time constants. Edit + flash to change.
|
||||
wake_interval_s: "30" # seconds between BLE-scan wakes
|
||||
scan_duration_ms: "1200" # BLE scan window per wake
|
||||
debounce_s: "60" # min seconds between two refreshes
|
||||
heartbeat_interval_s: "86400" # 24h status-only HA contact
|
||||
|
||||
# 10-byte (20 hex chars) prefix of the iBeacon UUID this device responds to.
|
||||
# Full UUID = <prefix><6-byte WiFi STA MAC of this device>.
|
||||
ble_uuid_prefix: !secret ble_uuid_prefix
|
||||
|
||||
# Image source prefix. Full URL fetched per refresh is:
|
||||
# ${image_url_prefix}/<wifi_sta_mac_lowercase_no_separators>.png
|
||||
image_url_prefix: !secret image_url_prefix
|
||||
|
||||
# DEBUG: when "true", do_sleep does NOT enter deep sleep — it logs, delays
|
||||
# wake_interval_s, resets per-cycle state, and re-runs the scan loop. Keeps
|
||||
# UART<->USB alive so you can watch the device cycle in the serial console.
|
||||
# Set to "false" to use real deep sleep (production).
|
||||
debug_no_sleep: "false"
|
||||
|
||||
# DEBUG: when "true", do_ble_scan flashes GPIO21 with state-encoded blink
|
||||
# patterns each wake (entered → BLE active? → matched?). Useful because
|
||||
# USB-CDC drops on deep sleep so logger.log is invisible. Set to "false"
|
||||
# in production to keep the wake silent and save a few mA.
|
||||
debug_led_breadcrumbs: "false"
|
||||
|
||||
globals:
|
||||
# Persisted across deep sleep via flash NVS.
|
||||
- id: elapsed_since_ha_s
|
||||
type: uint32_t
|
||||
restore_value: yes
|
||||
initial_value: '0'
|
||||
- id: beacon_seen_count
|
||||
type: uint32_t
|
||||
restore_value: yes
|
||||
initial_value: '0'
|
||||
|
||||
# Transient (per-boot).
|
||||
- id: refresh_in_progress
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: image_loaded
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: beacon_matched
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: beacon_command
|
||||
type: uint32_t
|
||||
initial_value: '0' # (Major << 16) | Minor when matched
|
||||
- id: cold_boot
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: heartbeat_due
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: stay_awake_mode
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
|
||||
esphome:
|
||||
name: frame
|
||||
libraries:
|
||||
- SPI
|
||||
platformio_options:
|
||||
board_build.arduino.memory_type: qio_opi
|
||||
board_build.f_flash: 80000000L
|
||||
board_build.flash_mode: qio
|
||||
build_flags:
|
||||
- "-DBOARD_HAS_PSRAM"
|
||||
- "-mfix-esp32-psram-cache-issue"
|
||||
lib_ignore:
|
||||
- esp_insights
|
||||
- esp_rainmaker
|
||||
|
||||
on_boot:
|
||||
# WiFi is left disabled at boot (see wifi.enable_on_boot below). Each path
|
||||
# that needs the radio — do_refresh / do_heartbeat / do_stay_awake — flips
|
||||
# it on explicitly. Timer-wake / scan path never enables it.
|
||||
- priority: -100
|
||||
then:
|
||||
- lambda: |-
|
||||
// Approximate elapsed-since-last-HA-contact: each timer cycle adds
|
||||
// wake_interval_s. Refresh and heartbeat paths reset to 0.
|
||||
id(elapsed_since_ha_s) += ${wake_interval_s};
|
||||
id(heartbeat_due) = id(elapsed_since_ha_s) >= ${heartbeat_interval_s};
|
||||
|
||||
auto cause = esp_sleep_get_wakeup_cause();
|
||||
switch (cause) {
|
||||
case ESP_SLEEP_WAKEUP_UNDEFINED:
|
||||
ESP_LOGI("frame", "Cold boot — forcing full refresh");
|
||||
id(cold_boot) = true;
|
||||
break;
|
||||
case ESP_SLEEP_WAKEUP_EXT0:
|
||||
ESP_LOGI("frame", "D2 button wake — stay-awake");
|
||||
id(stay_awake_mode) = true;
|
||||
break;
|
||||
case ESP_SLEEP_WAKEUP_TIMER:
|
||||
ESP_LOGD("frame", "Timer wake (elapsed=%us, heartbeat_due=%d)",
|
||||
id(elapsed_since_ha_s), (int)id(heartbeat_due));
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW("frame", "Unknown wake cause %d — treating as cold boot", (int)cause);
|
||||
id(cold_boot) = true;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t mac[6];
|
||||
esphome::get_mac_address_raw(mac);
|
||||
ESP_LOGD("frame", "WiFi STA MAC %02X:%02X:%02X:%02X:%02X:%02X",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(cold_boot);'
|
||||
then:
|
||||
- script.execute: do_refresh
|
||||
else:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(stay_awake_mode);'
|
||||
then:
|
||||
- script.execute: do_stay_awake
|
||||
else:
|
||||
- script.execute: do_ble_scan
|
||||
|
||||
esp32:
|
||||
board: seeed_xiao_esp32s3
|
||||
variant: esp32s3
|
||||
framework:
|
||||
type: arduino
|
||||
|
||||
psram:
|
||||
mode: octal
|
||||
speed: 80MHz
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: "https://github.com/acegallagher/esphome-bigink"
|
||||
path: bigink_component
|
||||
|
||||
# D2 (GPIO3) is the hardware wake pin. Timer wakes come from sleep_duration
|
||||
# passed to deep_sleep.enter calls in scripts.
|
||||
deep_sleep:
|
||||
id: sleep_control
|
||||
wakeup_pin:
|
||||
number: GPIO3
|
||||
inverted: true
|
||||
mode:
|
||||
input: true
|
||||
pullup: true
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
api:
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
|
||||
wifi:
|
||||
# Never auto-connect at boot. The scripts (do_refresh / do_heartbeat /
|
||||
# do_stay_awake) call wifi.enable when they need the radio, and disable
|
||||
# it again on exit. Keeps the WiFi modem off during BLE-scan wakes.
|
||||
enable_on_boot: false
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
power_save_mode: NONE
|
||||
manual_ip:
|
||||
static_ip: !secret static_ip
|
||||
gateway: !secret gateway
|
||||
subnet: !secret subnet
|
||||
dns1: !secret dns1
|
||||
|
||||
http_request:
|
||||
useragent: esphome/device
|
||||
timeout: 30s
|
||||
|
||||
# Onboard user LED — lit while BLE is actively scanning (debug aid).
|
||||
# GPIO21 is the user LED on the XIAO ESP32-S3 family; active-low.
|
||||
output:
|
||||
- platform: gpio
|
||||
id: status_led
|
||||
pin:
|
||||
number: GPIO21
|
||||
inverted: true
|
||||
|
||||
# =============================================================================
|
||||
# Bluetooth — passive iBeacon scanner. Started on demand from do_ble_scan,
|
||||
# not continuously, to keep the radio off most of the time.
|
||||
#
|
||||
# An explicit esp32_ble block (with id) lets do_ble_scan wait for the
|
||||
# controller to reach ACTIVE before issuing start_scan — its init runs in
|
||||
# loop(), not setup(), so it isn't ready when called from on_boot.
|
||||
# =============================================================================
|
||||
esp32_ble:
|
||||
id: ble_id
|
||||
|
||||
esp32_ble_tracker:
|
||||
scan_parameters:
|
||||
interval: 50ms
|
||||
window: 50ms
|
||||
active: false
|
||||
continuous: false
|
||||
on_ble_manufacturer_data_advertise:
|
||||
- manufacturer_id: '004C' # Apple — iBeacons live in their mfr data block
|
||||
then:
|
||||
- lambda: |-
|
||||
// iBeacon: 02 15 <16-byte UUID> <2B major> <2B minor> <1B tx>
|
||||
if (x.size() < 23) return;
|
||||
if (x[0] != 0x02 || x[1] != 0x15) return;
|
||||
|
||||
uint16_t major = ((uint16_t) x[18] << 8) | x[19];
|
||||
uint16_t minor = ((uint16_t) x[20] << 8) | x[21];
|
||||
|
||||
// Log every iBeacon we see (debug visibility).
|
||||
char uuid_str[33];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
snprintf(uuid_str + 2 * i, 3, "%02X", x[2 + i]);
|
||||
}
|
||||
ESP_LOGI("frame", "iBeacon seen: uuid=%s major=0x%04X minor=0x%04X",
|
||||
uuid_str, major, minor);
|
||||
|
||||
// Compose the expected UUID once: prefix || own WiFi STA MAC.
|
||||
static uint8_t expected[16];
|
||||
static bool built = false;
|
||||
if (!built) {
|
||||
const char *hex = "${ble_uuid_prefix}";
|
||||
for (int i = 0; i < 10; i++) {
|
||||
unsigned int b = 0;
|
||||
sscanf(hex + 2 * i, "%2x", &b);
|
||||
expected[i] = (uint8_t) b;
|
||||
}
|
||||
uint8_t mac[6];
|
||||
esphome::get_mac_address_raw(mac);
|
||||
memcpy(expected + 10, mac, 6);
|
||||
built = true;
|
||||
}
|
||||
|
||||
if (memcmp(&x[2], expected, 16) != 0) {
|
||||
ESP_LOGD("frame", " → no match (not our UUID)");
|
||||
return;
|
||||
}
|
||||
|
||||
id(beacon_matched) = true;
|
||||
id(beacon_command) = ((uint32_t) major << 16) | minor;
|
||||
id(beacon_seen_count) += 1;
|
||||
ESP_LOGI("frame", " → MATCH (this device)");
|
||||
|
||||
# =============================================================================
|
||||
# Online Image — downloads PNG into PSRAM. The url here is a placeholder;
|
||||
# do_refresh rewrites it to ${image_url_prefix}/<mac>.png before each fetch.
|
||||
# =============================================================================
|
||||
online_image:
|
||||
- url: "${image_url_prefix}/placeholder.png"
|
||||
id: my_image
|
||||
format: PNG
|
||||
type: RGB
|
||||
resize: 1600x1200
|
||||
on_download_finished:
|
||||
- logger.log: "Image downloaded"
|
||||
- globals.set:
|
||||
id: image_loaded
|
||||
value: 'true'
|
||||
- logger.log: "Display rendered — pushing battery, then sleeping"
|
||||
- lambda: 'id(battery).sample();'
|
||||
- delay: 2s
|
||||
- globals.set:
|
||||
id: elapsed_since_ha_s
|
||||
value: '0'
|
||||
- globals.set:
|
||||
id: refresh_in_progress
|
||||
value: 'false'
|
||||
- wifi.disable:
|
||||
- delay: 200ms
|
||||
- lambda: |-
|
||||
if(!cached) {
|
||||
id(eink_display)->update();
|
||||
};
|
||||
- script.execute: do_sleep
|
||||
|
||||
on_error:
|
||||
- logger.log: "Image download failed — sleeping; next wake retries if beacon still on"
|
||||
- globals.set:
|
||||
id: refresh_in_progress
|
||||
value: 'false'
|
||||
- wifi.disable:
|
||||
- delay: 200ms
|
||||
- script.execute: do_sleep
|
||||
|
||||
# =============================================================================
|
||||
# Scripts — the state machine.
|
||||
# =============================================================================
|
||||
script:
|
||||
# GPIO21 status-LED breadcrumbs (no UART available during deep-sleep wakes).
|
||||
# Pattern per wake: 1 blink (entered) → 2 or 5 blinks (BLE active? yes/no)
|
||||
# → solid during scan → 3 blinks (matched) or 1 long blink (no match).
|
||||
- id: led_blink
|
||||
parameters:
|
||||
count: int
|
||||
then:
|
||||
- repeat:
|
||||
count: !lambda 'return count;'
|
||||
then:
|
||||
- output.turn_on: status_led
|
||||
- delay: 80ms
|
||||
- output.turn_off: status_led
|
||||
- delay: 120ms
|
||||
|
||||
- id: do_ble_scan
|
||||
then:
|
||||
- logger.log: "Starting BLE scan"
|
||||
# Breadcrumb A: 1 blink — do_ble_scan entered.
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return ${debug_led_breadcrumbs};'
|
||||
then:
|
||||
- script.execute:
|
||||
id: led_blink
|
||||
count: 1
|
||||
- script.wait: led_blink
|
||||
# Wait for the BLE controller to finish its async init. The component's
|
||||
# setup() only flags state=ENABLE; the actual esp_bt_controller_init /
|
||||
# bluedroid_init / GAP-callback dance runs in loop(), with a hard-coded
|
||||
# ~200ms internal delay before flipping to ACTIVE. start_scan silently
|
||||
# no-ops if invoked beforehand — which was the deep-sleep bug.
|
||||
- wait_until:
|
||||
condition:
|
||||
lambda: 'return id(ble_id).is_active();'
|
||||
timeout: 3s
|
||||
# Breadcrumb B: 2 blinks if BLE active, 5 blinks if not (= still broken).
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return ${debug_led_breadcrumbs};'
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(ble_id).is_active();'
|
||||
then:
|
||||
- script.execute:
|
||||
id: led_blink
|
||||
count: 2
|
||||
else:
|
||||
- script.execute:
|
||||
id: led_blink
|
||||
count: 5
|
||||
- script.wait: led_blink
|
||||
- delay: 150ms
|
||||
- output.turn_on: status_led
|
||||
- esp32_ble_tracker.start_scan:
|
||||
continuous: false
|
||||
- delay: ${scan_duration_ms}ms
|
||||
- esp32_ble_tracker.stop_scan:
|
||||
- output.turn_off: status_led
|
||||
- delay: 200ms
|
||||
- logger.log:
|
||||
format: "Scan done matched=%d cmd=0x%08X"
|
||||
args: ['(int)id(beacon_matched)', 'id(beacon_command)']
|
||||
# Breadcrumb C: 3 blinks if matched, 1 long blink if not.
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return ${debug_led_breadcrumbs};'
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(beacon_matched);'
|
||||
then:
|
||||
- script.execute:
|
||||
id: led_blink
|
||||
count: 3
|
||||
- script.wait: led_blink
|
||||
else:
|
||||
- output.turn_on: status_led
|
||||
- delay: 500ms
|
||||
- output.turn_off: status_led
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(beacon_matched);'
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(beacon_command) == 0x00000000;' # REFRESH
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(elapsed_since_ha_s) >= ${debounce_s};'
|
||||
then:
|
||||
- script.execute: do_refresh
|
||||
else:
|
||||
- logger.log: "REFRESH inside debounce window — skip"
|
||||
- script.execute: do_sleep
|
||||
else:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(beacon_command) == 0x00010001;' # STAY AWAKE
|
||||
then:
|
||||
- globals.set:
|
||||
id: stay_awake_mode
|
||||
value: 'true'
|
||||
- script.execute: do_stay_awake
|
||||
else:
|
||||
- logger.log: "Beacon match with reserved command — ignored"
|
||||
- script.execute: do_sleep
|
||||
else:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(heartbeat_due);'
|
||||
then:
|
||||
- script.execute: do_heartbeat
|
||||
else:
|
||||
- script.execute: do_sleep
|
||||
|
||||
- id: do_refresh
|
||||
then:
|
||||
- logger.log: "REFRESH — WiFi up"
|
||||
- globals.set:
|
||||
id: refresh_in_progress
|
||||
value: 'true'
|
||||
- wifi.enable:
|
||||
- wait_until:
|
||||
condition:
|
||||
wifi.connected:
|
||||
timeout: 10s
|
||||
- if:
|
||||
condition:
|
||||
wifi.connected:
|
||||
then:
|
||||
- online_image.set_url:
|
||||
id: my_image
|
||||
url: !lambda |-
|
||||
uint8_t mac[6];
|
||||
esphome::get_mac_address_raw(mac);
|
||||
char url[128];
|
||||
snprintf(url, sizeof(url),
|
||||
"%s/%02x%02x%02x%02x%02x%02x.png",
|
||||
"${image_url_prefix}",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
ESP_LOGI("frame", "WiFi connected — fetching %s", url);
|
||||
return std::string(url);
|
||||
- component.update: my_image
|
||||
# Flow continues from online_image on_download_finished / on_error.
|
||||
else:
|
||||
- logger.log: "WiFi connect timed out — sleeping, retry next wake"
|
||||
- globals.set:
|
||||
id: refresh_in_progress
|
||||
value: 'false'
|
||||
- wifi.disable:
|
||||
- script.execute: do_sleep
|
||||
|
||||
- id: do_heartbeat
|
||||
then:
|
||||
- logger.log: "HEARTBEAT — pushing battery to HA"
|
||||
- wifi.enable:
|
||||
- wait_until:
|
||||
condition:
|
||||
wifi.connected:
|
||||
timeout: 10s
|
||||
- if:
|
||||
condition:
|
||||
wifi.connected:
|
||||
then:
|
||||
- lambda: 'id(battery).sample();'
|
||||
- delay: 3s
|
||||
- globals.set:
|
||||
id: elapsed_since_ha_s
|
||||
value: '0'
|
||||
- logger.log: "Heartbeat done"
|
||||
else:
|
||||
- logger.log: "Heartbeat: WiFi failed"
|
||||
- wifi.disable:
|
||||
- delay: 200ms
|
||||
- script.execute: do_sleep
|
||||
|
||||
- id: do_stay_awake
|
||||
then:
|
||||
- logger.log: "STAY-AWAKE — WiFi up; the 30s poll will sleep when HA flips ha_stay_awake off"
|
||||
- globals.set:
|
||||
id: stay_awake_mode
|
||||
value: 'true'
|
||||
- wifi.enable:
|
||||
- wait_until:
|
||||
condition:
|
||||
wifi.connected:
|
||||
timeout: 15s
|
||||
- if:
|
||||
condition:
|
||||
wifi.connected:
|
||||
then:
|
||||
- delay: 5s
|
||||
- lambda: 'id(battery).sample();'
|
||||
- globals.set:
|
||||
id: elapsed_since_ha_s
|
||||
value: '0'
|
||||
- logger.log: "Stay-awake ready"
|
||||
else:
|
||||
- logger.log: "Stay-awake: WiFi failed — sleeping"
|
||||
- wifi.disable:
|
||||
- script.execute: do_sleep
|
||||
|
||||
- id: do_sleep
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return ${debug_no_sleep};'
|
||||
then:
|
||||
# DEBUG: simulate the deep-sleep + wake cycle without dropping UART.
|
||||
- logger.log:
|
||||
format: "[DEBUG] would deep_sleep for %ds (elapsed_since_ha=%us); simulating"
|
||||
args: ['(int)${wake_interval_s}', 'id(elapsed_since_ha_s)']
|
||||
- delay: ${wake_interval_s}s
|
||||
- logger.log: "[DEBUG] simulated wake — running timer-wake path"
|
||||
# Reset transient per-boot state, mimic on_boot's timer-wake branch.
|
||||
- globals.set:
|
||||
id: beacon_matched
|
||||
value: 'false'
|
||||
- globals.set:
|
||||
id: beacon_command
|
||||
value: '0'
|
||||
- globals.set:
|
||||
id: cold_boot
|
||||
value: 'false'
|
||||
- lambda: |-
|
||||
id(elapsed_since_ha_s) += ${wake_interval_s};
|
||||
id(heartbeat_due) = id(elapsed_since_ha_s) >= ${heartbeat_interval_s};
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(stay_awake_mode);'
|
||||
then:
|
||||
- logger.log: "[DEBUG] stay_awake_mode active — skipping scan loop"
|
||||
else:
|
||||
- script.execute: do_ble_scan
|
||||
else:
|
||||
- logger.log:
|
||||
format: "Sleeping for %ds (elapsed_since_ha=%us)"
|
||||
args: ['(int)${wake_interval_s}', 'id(elapsed_since_ha_s)']
|
||||
- deep_sleep.enter:
|
||||
id: sleep_control
|
||||
sleep_duration: !lambda 'return ${wake_interval_s} * 1000UL;'
|
||||
|
||||
# =============================================================================
|
||||
# Stay-awake poll — fires only while we're alive (i.e., in stay-awake mode).
|
||||
# When HA clears the flag, sleep.
|
||||
# =============================================================================
|
||||
interval:
|
||||
- interval: 30s
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- lambda: 'return id(stay_awake_mode);'
|
||||
- wifi.connected:
|
||||
- binary_sensor.is_off: ha_stay_awake
|
||||
then:
|
||||
- logger.log: "Stay-awake cleared — returning to sleep loop"
|
||||
- globals.set:
|
||||
id: stay_awake_mode
|
||||
value: 'false'
|
||||
- wifi.disable:
|
||||
- script.execute: do_sleep
|
||||
|
||||
# =============================================================================
|
||||
# Display
|
||||
# =============================================================================
|
||||
display:
|
||||
- platform: seeed_epaper_spectra6
|
||||
id: eink_display
|
||||
|
||||
clk_pin: 7
|
||||
mosi_pin: 9
|
||||
cs_master_pin: GPIO44
|
||||
cs_slave_pin: GPIO41
|
||||
dc_pin: GPIO10
|
||||
reset_pin: GPIO38
|
||||
power_pin: GPIO43
|
||||
busy_pin:
|
||||
number: GPIO4
|
||||
inverted: true
|
||||
mode:
|
||||
input: true
|
||||
pullup: true
|
||||
|
||||
flip_mode: false
|
||||
update_interval: never
|
||||
|
||||
lambda: |-
|
||||
if (id(image_loaded)) {
|
||||
it.image(0,0, id(my_image));
|
||||
} else {
|
||||
auto BLACK = Color(0, 0, 0);
|
||||
auto WHITE = Color(255, 255, 255);
|
||||
auto RED = Color(255, 0, 0);
|
||||
auto GRAY = Color(128, 128, 128);
|
||||
|
||||
it.fill(WHITE);
|
||||
|
||||
it.rectangle(20, 20, 1560, 1160, BLACK);
|
||||
it.rectangle(22, 22, 1556, 1156, BLACK);
|
||||
|
||||
it.filled_circle(800, 380, 15, BLACK);
|
||||
it.circle(800, 350, 80, BLACK);
|
||||
it.circle(800, 350, 130, BLACK);
|
||||
it.circle(800, 350, 180, BLACK);
|
||||
it.filled_rectangle(600, 380, 400, 150, WHITE);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
it.line(650+i, 220, 950+i, 450, RED);
|
||||
it.line(650+i, 450, 950+i, 220, RED);
|
||||
}
|
||||
|
||||
it.printf(800, 580, id(font_large), BLACK, TextAlign::CENTER, "No Connection");
|
||||
it.printf(800, 680, id(font_medium), GRAY, TextAlign::CENTER, "Waiting for beacon...");
|
||||
it.printf(800, 760, id(font_medium), GRAY, TextAlign::CENTER, "Press D2 to stay awake");
|
||||
}
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_large
|
||||
size: 80
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_medium
|
||||
size: 40
|
||||
|
||||
# =============================================================================
|
||||
# Battery Voltage Monitoring via battery_monitor driver, logs recent
|
||||
# samples and then averages them, looking up SOC via 3.7v
|
||||
# lithium voltage->SOC LUT
|
||||
#
|
||||
# EE02 has voltage divider on GPIO1, enable pin on GPIO6
|
||||
# =============================================================================
|
||||
battery_monitor:
|
||||
id: battery
|
||||
voltage_pin: GPIO1
|
||||
enable_pin: GPIO6
|
||||
samples_to_average: 10
|
||||
|
||||
sensor:
|
||||
- platform: battery_monitor
|
||||
battery_monitor_id: battery
|
||||
voltage:
|
||||
name: "Battery Voltage"
|
||||
percentage:
|
||||
name: "Battery Percentage"
|
||||
|
||||
- platform: template
|
||||
name: "Beacons Seen"
|
||||
accuracy_decimals: 0
|
||||
lambda: 'return id(beacon_seen_count);'
|
||||
update_interval: 60s
|
||||
|
||||
binary_sensor:
|
||||
- platform: homeassistant
|
||||
entity_id: input_boolean.bigink_stay_awake_request
|
||||
id: ha_stay_awake
|
||||
name: "Stay Awake Requested"
|
||||
|
||||
- platform: template
|
||||
name: "Stay Awake Mode"
|
||||
lambda: 'return id(stay_awake_mode);'
|
||||
Reference in New Issue
Block a user