Skip to content

Instantly share code, notes, and snippets.

@HarvsG
Last active December 28, 2025 18:21
Show Gist options
  • Select an option

  • Save HarvsG/5471bc770d84a2214ff2c0f0a473816f to your computer and use it in GitHub Desktop.

Select an option

Save HarvsG/5471bc770d84a2214ff2c0f0a473816f to your computer and use it in GitHub Desktop.
A ESPHome configuration for a deluminator as used by dumbledoor. It should turn out the nearest zigbee light! https://discord.com/channels/429907082951524364/1454852824650354729
#include "esp_zigbee_core.h"
#include "esp_log.h"
#include <string>
#include <atomic>
static const char *TAG = "DELUMINATOR_ZIGBEE";
static const uint8_t COORDINATOR_IEEE[8] = {0xad, 0x4a, 0x07, 0xff, 0xff, 0x2e, 0x21, 0x00};
static volatile uint8_t g_best_ieee[8] = {0};
static volatile int16_t g_max_lqi = -1;
static std::atomic<bool> g_found{false};
class ZigbeeScanner {
public:
static void lqi_cb(const esp_zb_zdo_mgmt_lqi_rsp_t *rsp, void *user_ctx) {
if (!rsp || rsp->status != 0) return;
ESP_LOGI(TAG, "Table Page: %d/%d", rsp->start_index, rsp->neighbor_table_entries);
esp_zb_zdo_neighbor_table_list_record_t *record = rsp->neighbor_table_list;
for (int i = 0; i < rsp->neighbor_table_list_count; i++) {
if (record->network_addr == 0x0000 || memcmp(record->extended_addr, COORDINATOR_IEEE, 8) == 0) {
record++; continue;
}
char ieee_str[24];
snprintf(ieee_str, sizeof(ieee_str), "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
record->extended_addr[7], record->extended_addr[6], record->extended_addr[5],
record->extended_addr[4], record->extended_addr[3], record->extended_addr[2],
record->extended_addr[1], record->extended_addr[0]);
ESP_LOGI(TAG, " -> Bulb: %s | LQI: %d", ieee_str, record->lqi);
// We only care about the highest LQI seen across ALL pages
if (record->lqi > g_max_lqi) {
g_max_lqi = record->lqi;
memcpy((void*)g_best_ieee, record->extended_addr, 8);
g_found = true;
ESP_LOGW(TAG, " *** CURRENT CLOSEST: %s (LQI: %d) ***", ieee_str, record->lqi);
}
record++;
}
uint8_t processed = rsp->start_index + rsp->neighbor_table_list_count;
if (processed < rsp->neighbor_table_entries) {
request_page(processed);
}
}
static void request_page(uint8_t index) {
esp_zb_zdo_mgmt_lqi_req_param_t req;
req.start_index = index;
req.dst_addr = esp_zb_get_short_address();
if (esp_zb_lock_acquire(pdMS_TO_TICKS(100))) {
esp_zb_zdo_mgmt_lqi_req(&req, lqi_cb, NULL);
esp_zb_lock_release();
}
}
static void trigger_fresh_scan() {
ESP_LOGI(TAG, "--- REFRESHING MESH (Active Proximity Mode) ---");
g_max_lqi = -1;
g_found = false;
// "Stir the Mesh": Send a broadcast to an address that doesn't exist.
// This forces Routers (bulbs) to check the packet, updating the LQI.
esp_zb_zdo_ieee_addr_req_param_t ping;
ping.addr_of_interest = 0xFFFE; // Nobody has this short address
ping.request_type = 0x00;
ping.start_index = 0;
if (esp_zb_lock_acquire(pdMS_TO_TICKS(500))) {
// Using ieee_addr_req as a broadcast ping
esp_zb_zdo_ieee_addr_req(&ping, nullptr, nullptr);
esp_zb_lock_release();
}
// Give the radio 200ms to receive ACKs and update the Neighbor Table
vTaskDelay(pdMS_TO_TICKS(200));
request_page(0);
}
static std::string get_strongest_ieee() {
if (!g_found.load()) return "";
char buf[24];
snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
g_best_ieee[7], g_best_ieee[6], g_best_ieee[5], g_best_ieee[4],
g_best_ieee[3], g_best_ieee[2], g_best_ieee[1], g_best_ieee[0]);
return std::string(buf);
}
};
esphome:
name: "deluminator"
friendly_name: "Deluminator"
includes:
- deluminate.h
on_boot:
priority: -100 # Run very late to ensure radio is stable
then:
- logger.log: "✨ Deluminator is stable and ready!"
esp32:
board: esp32-c6-devkitc-1
framework:
type: esp-idf
# Forces ESPHome to use our custom memory layout
partitions: partitions_zigbee.csv
# Helps resolve the 0x10000 overlap by explicitly
# defining the variant behavior for C6
variant: esp32c6
logger:
level: DEBUG
hardware_uart: USB_SERIAL_JTAG
external_components:
- source: github://luar123/zigbee_esphome
components: [zigbee]
api:
reboot_timeout: 1min # Increased from 0s to prevent aggressive rebooting
encryption:
key: "<YOUR KEY>"
ota:
- platform: esphome
password: "<YOUR KEY>"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Waveshare C6-Zero Onboard NeoPixel (WS2812)
light:
- platform: esp32_rmt_led_strip
id: wand_led
name: "Wand RGB"
pin: GPIO8
num_leds: 1
rgb_order: RGB # on some devices this is GRB
chipset: WS2812
zigbee:
id: zb_hub
router: true # if we use end devices then it is lazy about updating signal strength
script:
- id: deluminate_sequence
mode: restart
then:
- light.turn_on:
id: wand_led
transition_length: 0s
brightness: 100%
red: 0%
green: 10%
blue: 100%
- lambda: |-
ESP_LOGI("main", "Starting Deluminator scan...");
ZigbeeScanner::trigger_fresh_scan();
- delay: 1500ms # there may be room to optimise this delay for snappier performance
- lambda: |-
std::string target = ZigbeeScanner::get_strongest_ieee();
auto call = id(wand_led).turn_on();
call.set_transition_length(200); // Fixed: uses integer ms
if (!target.empty()) {
ESP_LOGI("main", "Target found! Sending IEEE %s to Home Assistant", target.c_str());
id(send_toggle_to_ha).execute(target);
// Turn LED Green to indicate success
call.set_rgb(0, 1, 0);
} else {
ESP_LOGW("main", "No Zigbee targets found in range.");
// Turn LED Red to indicate failure
call.set_rgb(1, 0, 0);
}
call.perform();
- delay: 2s
- light.turn_off: wand_led
- id: send_toggle_to_ha
parameters:
target_ieee: string
then:
- homeassistant.service:
service: zha.issue_zigbee_cluster_command
data:
ieee: !lambda "return target_ieee;"
endpoint_id: "1"
cluster_id: "6"
command: "2"
command_type: "server"
data_template:
params: "{}" # otherwise an error is returned by ZHA
button: # Button exposed to HA for easier testing
- platform: template
name: "Remote Delumination"
icon: "mdi:auto-fix"
on_press:
- script.execute: deluminate_sequence
binary_sensor:
- platform: gpio
pin:
number: GPIO9 # Onboard BOOT button for Waveshare Zero
inverted: true
mode: INPUT_PULLUP
name: "Wand Button"
on_press:
- script.execute: deluminate_sequence
# Name Type SubType Offset Size Flags
nvs data nvs 0x9000 0x4000
otadata data ota 0xd000 0x2000
phy_init data phy 0xf000 0x1000
factory app factory 0x10000 0x250000
zb_storage data fat 16K
zb_fct data fat 1K
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment