Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.DS_Store
.esphome/
build/
build-artifacts/
secrets.yaml
20*-local-command-*.txt
__pycache__/
*.pyc
*.elf
*.map
*.o
*.a

# Do not publish vendor firmware dumps or derived disassembly.
*stock*.bin
*dump*.bin
dreo_app.bin
dreo_chunks/
dreo_analysis/
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ into OTA mode (hold the oscillation button for 5 seconds until the countdown end
and visit http://192.168.0.1 and upload the file. I have tested this allows me to install a functioning ESPhome
although I haven't tested the fan functionality - although that should be fine

There is also a Dreo 712S ESP32-C3 example in
[example-dreo-712s-esp32c3.yaml](example-dreo-712s-esp32c3.yaml), with model
notes in [docs/dreo-712s/readme.md](docs/dreo-712s/readme.md). Unlike the HTF018S
config, the 712S keeps the stock ESP32-C3 module and uses the stock-compatible
[dreo_partitions.csv](docs/dreo-712s/dreo_partitions.csv), so no hardware swap is needed. It
drives the local GPIO7 RGB strip, and maps the model-specific fan, main CCT
light, button-event debounce, timers, and diagnostic/probe entities.

## Tested models

| Model | Type | Module | Config | Status |
Expand All @@ -25,6 +33,7 @@ although I haven't tested the fan functionality - although that should be fine
| DR-HTF018S | Tower | MBL01 (original) | [example-dreo-htf018s-mbl01.yaml](example-dreo-htf018s-mbl01.yaml) | Tested ESPhome installs okay, not tested fan functionality. Reports welcome. |
| DR-HTF018S | Tower | ESP32C3 | [example-dreo-htf018s-esp32c3.yaml](example-dreo-htf018s-esp32c3.yaml) | Tested, works perfectly. |
| DR-HTF024S | Tower | MBL01 (original) | [example-dreo-htf024s-mbl01.yaml](example-dreo-htf024s-mbl01.yaml) | Untested but [may work](https://github.com/davidc/dreo-protocol/issues/1). |
| 712S | Ceiling | ESP32-C3 (stock) | [example-dreo-712s-esp32c3.yaml](example-dreo-712s-esp32c3.yaml) | Tested on one unit, works. Datapoint map differs from the HTF models - see [docs/dreo-712s/readme.md](docs/dreo-712s/readme.md). |

Please report back successes or failures. Please be careful and always ensure you have a way to restore the original firmware
if needed (e.g. UART), I accept no responsibility for bricked devices!
49 changes: 49 additions & 0 deletions components/dreo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,24 @@
CODEOWNERS = ["@davidc"]

CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS = "ignore_mcu_update_on_datapoints"
CONF_LOCAL_DATAPOINTS = "local_datapoints"

CONF_ON_DATAPOINT_UPDATE = "on_datapoint_update"
CONF_ON_BUTTON_EVENT = "on_button_event"
CONF_DATAPOINT_TYPE = "datapoint_type"
CONF_STARTUP_HEARTBEAT_INTERVAL = "startup_heartbeat_interval"
CONF_MAX_INIT_RETRIES = "max_init_retries"
CONF_QUERY_VERSION_ON_SYNC = "query_version_on_sync"
CONF_WIFI_STATUS_ON_SYNC = "wifi_status_on_sync"
CONF_PERIODIC_WIFI_STATUS = "periodic_wifi_status"
CONF_BUTTON_EVENT_REPEAT_GUARD = "button_event_repeat_guard"

dreo_ns = cg.esphome_ns.namespace("dreo")
DreoDatapointType = dreo_ns.enum("DreoDatapointType", is_class=True)
Dreo = dreo_ns.class_("Dreo", cg.Component, uart.UARTDevice)
DreoButtonEventTrigger = dreo_ns.class_(
"DreoButtonEventTrigger", automation.Trigger.template(cg.uint8)
)

DPTYPE_ANY = "any"
DPTYPE_BOOL = "bool"
Expand Down Expand Up @@ -67,9 +78,20 @@ def assign_declare_id(value):
cv.Schema(
{
cv.GenerateID(): cv.declare_id(Dreo),
cv.Optional(
CONF_STARTUP_HEARTBEAT_INTERVAL, default="15s"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_MAX_INIT_RETRIES, default=5): cv.uint8_t,
cv.Optional(CONF_QUERY_VERSION_ON_SYNC, default=False): cv.boolean,
cv.Optional(CONF_WIFI_STATUS_ON_SYNC, default=False): cv.boolean,
cv.Optional(CONF_PERIODIC_WIFI_STATUS, default=False): cv.boolean,
cv.Optional(
CONF_BUTTON_EVENT_REPEAT_GUARD, default="0ms"
): cv.time_period,
cv.Optional(CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS): cv.ensure_list(
cv.uint8_t
),
cv.Optional(CONF_LOCAL_DATAPOINTS): cv.ensure_list(cv.uint8_t),
cv.Optional(CONF_ON_DATAPOINT_UPDATE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
Expand All @@ -82,6 +104,13 @@ def assign_declare_id(value):
},
extra_validators=assign_declare_id,
),
cv.Optional(CONF_ON_BUTTON_EVENT): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
DreoButtonEventTrigger
),
}
),
}
)
.extend(cv.COMPONENT_SCHEMA)
Expand All @@ -93,13 +122,33 @@ async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
cg.add(
var.set_startup_heartbeat_interval(
config[CONF_STARTUP_HEARTBEAT_INTERVAL].total_milliseconds
)
)
cg.add(var.set_max_init_retries(config[CONF_MAX_INIT_RETRIES]))
cg.add(var.set_query_version_on_sync(config[CONF_QUERY_VERSION_ON_SYNC]))
cg.add(var.set_wifi_status_on_sync(config[CONF_WIFI_STATUS_ON_SYNC]))
cg.add(var.set_periodic_wifi_status(config[CONF_PERIODIC_WIFI_STATUS]))
cg.add(
var.set_button_event_repeat_guard(
config[CONF_BUTTON_EVENT_REPEAT_GUARD].total_milliseconds
)
)
if CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS in config:
for dp in config[CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS]:
cg.add(var.add_ignore_mcu_update_on_datapoints(dp))
if CONF_LOCAL_DATAPOINTS in config:
for dp in config[CONF_LOCAL_DATAPOINTS]:
cg.add(var.add_local_datapoint(dp))
for conf in config.get(CONF_ON_DATAPOINT_UPDATE, []):
trigger = cg.new_Pvariable(
conf[CONF_TRIGGER_ID], var, conf[CONF_SENSOR_DATAPOINT]
)
await automation.build_automation(
trigger, [(DATAPOINT_TYPES[conf[CONF_DATAPOINT_TYPE]], "x")], conf
)
for conf in config.get(CONF_ON_BUTTON_EVENT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [(cg.uint8, "x")], conf)
7 changes: 6 additions & 1 deletion components/dreo/automation.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class DreoEnumDatapointUpdateTrigger final : public Trigger<uint8_t> {
explicit DreoEnumDatapointUpdateTrigger(Dreo *parent, uint8_t sensor_id);
};

class DreoButtonEventTrigger final : public Trigger<uint8_t> {
public:
explicit DreoButtonEventTrigger(Dreo *parent) {
parent->register_button_listener([this](const DreoButtonEvent &event) { this->trigger(event.code); });
}
};

} // namespace esphome::dreo

Loading