{"id":50789866,"url":"https://github.com/robertklep/esphome-eventbus","last_synced_at":"2026-06-12T10:03:23.192Z","repository":{"id":359762528,"uuid":"1247422939","full_name":"robertklep/esphome-eventbus","owner":"robertklep","description":"An event bus component for ESPHome","archived":false,"fork":false,"pushed_at":"2026-05-23T10:00:02.000Z","size":12,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-23T11:34:40.825Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robertklep.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-23T09:45:19.000Z","updated_at":"2026-05-23T11:14:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/robertklep/esphome-eventbus","commit_stats":null,"previous_names":["robertklep/esphome-eventbus"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/robertklep/esphome-eventbus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertklep%2Fesphome-eventbus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertklep%2Fesphome-eventbus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertklep%2Fesphome-eventbus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertklep%2Fesphome-eventbus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robertklep","download_url":"https://codeload.github.com/robertklep/esphome-eventbus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertklep%2Fesphome-eventbus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34238715,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-12T10:03:22.669Z","updated_at":"2026-06-12T10:03:23.180Z","avatar_url":"https://github.com/robertklep.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ESPHome Event Bus\n\nAn event bus for ESPHome.\n\nAllows emitting and handling events and is most useful for larger ESPHome projects that use [packages](https://esphome.io/components/packages/), where you want to limit the amount of dependencies between packages (for example, anywhere where package A directly references components/scripts/etc in package B).\n\nSee below for a rationale and more explanation on why this might be useful.\n\n## Setup\n\nAdd the following to your project's YAML:\n\n```yaml\nexternal_components:\n  - source: github://robertklep/esphome-eventbus@1.0.0\n    components: [ eventbus ]\n    refresh: never\n\neventbus:\n  id: ev\n```\n\nYou only have to assign an `id` if you want to emit events programmatically (see below). There's only one event bus instance active in a project.\n\n## Emitting events\n\nThere are various ways of emitting events, starting with a regular ESPHome action:\n\n```yaml\neventbus.emit:\n  event: my_event\n  data:\n    name: Alice\n    age: 23\n    admin: 'false'\n```\n\nSo you emit an event, `my_event` in this case, with some (optional) data.\n\nYou can use lambdas for individual fields:\n```yaml\neventbus.emit:\n  event: my_event\n  data:\n    name: Alice\n    age: 23\n    admin: !lambda 'return false;'\n```\n\nOr a single lambda for all the fields:\n```yaml\neventbus.emit:\n  event: my_event\n  data: !lambda |-\n    data[\"name\"] = \"Alice\";\n    data[\"age\"] = 23;\n    data[\"admin\"] = false;\n```\n(see below for more explanation on what `data` is, and how it works)\n\nOr programmatically:\n```c++\nid(ev).emit(\"my_event\",\n  \"name\",  \"Alice\",\n  \"age\",   23,\n  \"admin\", false\n);\n```\n\nNote how `ev` refers to the `id` in the `eventbus` component setup. There's also a global variable that you can always use:\n```c++\nesphome::eventbus::global_eventbus-\u003eemit(\"my_event\", ...);\n```\n\nAs said, data is optional:\n```yaml\neventbus.emit: my_event\n```\n```c++\nid(ev).emit(\"my_event\");\n```\n\n## Side note about `data`\n\nThe `data` variable that is available to event handler triggers, and also to action lambdas, is a fairly flexible container that holds key/value pairs. Keys are always strings, values can be most C++ types (integers, floats, booleans, string, `std` containers, etc).\n\nIt is loosely modeled after the [`JsonVariant` class from the ArduinoJSON library](https://arduinojson.org/v7/api/jsonvariant/).\n\n### Extracting data by implicit or explicit casting\n\nGenerally speaking, it's up to you to keep track of the type of value you associate with a key: if you _emit_ a string value, you need to explicitly _extract_ a string value when handling the data.\n\nFor example, the last emit example above emits three key/value pairs:\n* `name`, which is a string;\n* `age`, which is an integer;\n* `admin`, which is a boolean.\n\nTo extract their respective values inside a handler, you need to explicitly cast them to their respective type:\n```c++\nstd::string name = data[\"name\"];\nunsigned int age = data[\"age\"];\nbool admin       = data[\"admin\"];\n```\n\nOr, alternatively, use `.as\u003ctypename T\u003e\u003c()`:\n```c++\nauto name  = data[\"name\"].as\u003cstd::string\u003e();\nauto age   = data[\"age\"].as\u003cunsigned int\u003e();\nauto admin = data[\"admin\"].as\u003cbool\u003e();\n```\n\n### Default/fallback values\n\nCommon C++ types will return a reasonable default value if you try to access a key that doesn't exist.\n\n| Type                                         | Default value       |\n| -------------------------------------------- | ------------------- |\n| Integral types (`short`, `int`, `long`, etc) | `0`                 |\n| Floating point types (`float`, `double`)     | `0`                 |\n| `std:string`                                 | `\"\"` (empty string) |\n| `const char*`                                | `nullptr`           |\n| `bool`                                       | `false`             |\n\nAssign the default for an `unsigned int` (0) to `value`:\n```c++\nunsigned int value = data[\"missing\"];\n```\n\nYou can also specify a fallback value that should be used if a key doesn't exist, using the `|` operator:\n```c++\nunsigned int value = data[\"missing\"] | 123;\n```\n\n### Type conversion\n\nSome types can be converted from/to each other:\n\n* strings to numbers (both integral and floating point types):\n    ```c++\n    // assigning\n    data[\"num\"] = \"123\";\n\n    // extracting\n    unsigned int num = data[\"num\"];\n    ```\n\n* numbers (both integral and floating point types) to strings:\n    ```c++\n    // assigning\n    data[\"num\"] = 123;\n\n    // extracting\n    std::string num = data[\"num\"];\n    ```\n\n* strings to booleans:\n    ```c++\n    // assigning\n    data[\"b\"] = \"False\";\n\n    // extracting\n    bool b = data[\"b\"];\n    ```\n\n    Supported are strings that _start_ with `true` or `false` (case-independent) or strings containing numerical values (where `\"0\"` converts to `false`, and all other numerical values to `true`).\n\n* `std::string` to `const char*`:\n\n    ```c++\n    // assigning\n    data[\"str\"] = std::string(\"string\");\n\n    // extracting\n    const char* str = data[\"str\"];\n    ```\n\n### Existence checking\n\nYou can check if a particular key exists using an explicit boolean cast:\n```c++\nif (data[\"somekey\"]) {\n  ...\n}\n```\n\nOr using a method call:\n```c++\nif (data[\"somekey\"].exists()) {\n  ...\n}\n```\n\nPossible pitfall: if `data[\"somekey\"]` is a boolean, and you want to use its value in a conditional statement, you need to use a cast first:\n```c++\nif (data[\"somekey\"].as\u003cbool\u003e()) {\n  ...\n}\n```\n\n### Type checking\n\nCheck if a key holds a value of a particular type:\n```c++\nif (data[\"name\"].is\u003cstd::string\u003e()) {\n  ...\n}\n```\n\n## Receiving and handling events\n\nTo receive and handle events, use the `on_event` trigger:\n```yaml\neventbus:\n  on_event:\n    - event: my_event\n      then:\n        ...\n```\n\nIt's also possible to trigger on multiple events:\n```yaml\neventbus:\n  on_event:\n    - event:\n        - my_event\n        - my_other_event\n      then:\n        ...\n```\n\nActions receive two arguments:\n* `event`, a `std::string` that contains the event name (`my_event`, `my_other_event`, ...);\n* `data`, the aforementioned key/value container.\n\n```yaml\neventbus:\n  on_event:\n    - event:\n        - my_event\n        - my_other_event\n      then:\n        lambda: |-\n          ESP_LOGD(\"\", \"Received event '%s'\", event.c_str();\n\n          if (event == \"my_event\") {\n            const char* name = data[\"name\"];\n            ESP_LOGE(\"\", \"- name = %s\", name);\n          } else if (event == \"my_other_event\") {\n            unsigned int age = data[\"age\"];\n            ESP_LOGE(\"\", \"- age = %u\", age);\n          }\n```\n\n```yaml\neventbus:\n  on_event:\n    - event:\n        - my_event\n        - my_other_event\n      then:\n        if:\n          condition:\n            lambda: 'return event == \"my_event\";'\n          then:\n            ...\n```\n\n## Rationale\n\nIn large ESPHome projects that are split up across different packages, you might run into issues where different parts of the project may need to respond to certain events/input/action/etc.\n\nFor instance, you may have an LVGL-based UI with a button that, when pressed, should publish a message over MQTT and turn on an LED.\n\nAn obvious way of implementing this could look like this:\n```yaml\n# light_package.yaml\nlight:\n  - platform: binary\n    id: my_led\n    ...\n\n# mqtt_package.yaml\nmqtt:\n  ...\n\n# lvgl_package.yaml\nlvgl:\n  ...\n  button:\n    ...\n    on_press:\n      - light.turn_on: my_led\n      - mqtt.publish: ...\n```\n\nWhich works fine, but causes a lot of dependencies between different parts of the project. What if you need to make MQTT-support in your project optional? What if you also want to support LED-strips instead of just a single LED?\n\nThis is where the event bus comes in. Instead of hard-coding references to other components, you only emit an event when the button is pressed:\n```yaml\nbutton:\n  ...\n  on_press:\n    eventbus.emit: button_is_pressed\n```\n\nThen, wherever you need to respond to button presses, you add an event listeners to the package (you can, of course, create event listeners anywhere you want, but I find that keeping related components and event listeners together helps with maintaining projects):\n```yaml\n# light_package.yaml\nlight:\n  - platform: binary\n    id: my_led\n\neventbus:\n  on_event:\n    event: button_is_pressed\n    then:\n      light.turn_on: my_led\n\n# mqtt_package.yaml\nmqtt:\n  ...\n\neventbus:\n  on_event:\n    event: button_is_pressed\n    then:\n      mqtt.publish: ...\n```\n\nThen, when you want to build your project without MQTT support, you simply don't load the `mqtt_package.yaml` file and nothing will break.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertklep%2Fesphome-eventbus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobertklep%2Fesphome-eventbus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertklep%2Fesphome-eventbus/lists"}