{"id":25412444,"url":"https://github.com/topi314/esphome-dashboard","last_synced_at":"2025-04-14T17:42:47.338Z","repository":{"id":276398613,"uuid":"929179162","full_name":"topi314/esphome-dashboard","owner":"topi314","description":"EInk esphome powered dashboard","archived":false,"fork":false,"pushed_at":"2025-03-16T19:48:11.000Z","size":4392,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-14T17:42:29.079Z","etag":null,"topics":["dashboard","eink","einkdisplay","esphome","esphome-device","waveshare","waveshare-eink"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/topi314.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}},"created_at":"2025-02-08T00:33:28.000Z","updated_at":"2025-03-25T08:35:31.000Z","dependencies_parsed_at":"2025-02-15T02:25:50.812Z","dependency_job_id":null,"html_url":"https://github.com/topi314/esphome-dashboard","commit_stats":null,"previous_names":["topi314/esphome-dashboard"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/topi314%2Fesphome-dashboard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/topi314%2Fesphome-dashboard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/topi314%2Fesphome-dashboard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/topi314%2Fesphome-dashboard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/topi314","download_url":"https://codeload.github.com/topi314/esphome-dashboard/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248929914,"owners_count":21184914,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["dashboard","eink","einkdisplay","esphome","esphome-device","waveshare","waveshare-eink"],"created_at":"2025-02-16T11:20:14.710Z","updated_at":"2025-04-14T17:42:47.325Z","avatar_url":"https://github.com/topi314.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Go Report](https://goreportcard.com/badge/github.com/topi314/esphome-dashboard)](https://goreportcard.com/report/github.com/topi314/esphome-dashboard)\n[![Go Version](https://img.shields.io/github/go-mod/go-version/topi314/esphome-dashboard)](https://golang.org/doc/devel/release.html)\n[![License](https://img.shields.io/github/license/topi314/esphome-dashboard)](LICENSE)\n[![Version](https://img.shields.io/github/v/tag/topi314/esphome-dashboard?label=release)](https://github.com/topi314/esphome-dashboard/releases/latest)\n[![Docker](https://github.com/topi314/esphome-dashboard/actions/workflows/build.yml/badge.svg)](https://github.com/topi314/esphome-dashboard/actions/workflows/build.yml)\n[![Discord](https://discordapp.com/api/guilds/608506410803658753/embed.png?style=shield)](https://discord.gg/sD3ABd5)\n\n# ESPHome Dashboard\n\n\u003e [!IMPORTANT]\n\u003e If you are using an ESP32 without PSRAM this does not seem to work with esphome 2025.2.0+. I recommend using an older version of esphome until I find a solution.\n\nA simple Go service to render a custom HTML/CSS dashboard as a PNG image from [Home Assistant](https://www.home-assistant.io/) entities.\n\nThis service is intended to be used with [ESPHome](https://esphome.io/) devices that have a display and can show a PNG image.\n\n\u003cimg src=\".github/images/device_1.jpg\" width=\"200\" alt=\"Dashboard in picture frame from IKEA from the front\"\u003e \u003cimg src=\".github/images/device_2.jpg\" width=\"200\" alt=\"Dashboard in picture frame from IKEA from the side\"\u003e\n\n\u003cimg src=\".github/images/device_3.jpg\" width=\"200\" alt=\"Dashboard in picture frame from IKEA from the top\"\u003e \u003cimg src=\".github/images/device_4.jpg\" width=\"200\" alt=\"Dashboard in picture frame from IKEA from the back\"\u003e\n\n## Table of Contents\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to expand\u003c/summary\u003e\n\n- [Features](#features)\n- [Installation](#installation)\n    - [Docker Compose](#docker-compose)\n    - [ESPHome](#esphome)\n- [Configuration](#configuration)\n    - [Dashboard Configuration](#dashboard-configuration)\n    - [ESPHome Configuration](#esphome-configuration)\n- [Templates](#templates)\n    - [Base Template](#base-template)\n    - [Page Template](#page-template)\n    - [Template Variables](#template-variables)\n    - [Template Functions](#template-functions)\n- [API](#api)\n    - [Get Control](#get-control)\n    - [Get Page](#get-page)\n    - [Get Version](#get-version)\n- [License](#license)\n- [Contributing](#contributing)\n- [Contact](#contact)\n\n\u003c/details\u003e\n\n## Features\n\n- Customizable dashboard with HTML/CSS/JS \u0026 [Go template](https://pkg.go.dev/html/template)\n- Fetch data from Home Assistant entities, actions \u0026 calendars\n- Render the dashboard as a PNG image or HTML/CSS/JS\n- Cycle through multiple pages of the dashboard (via interval or touch sensitive buttons)\n- Use the [Home Assistant REST API](https://developers.home-assistant.io/docs/api/rest) to fetch data\n- ESPHome device configuration for ESP32 with WaveShare 7.5inch e-Paper HAT (v2) display \u0026 touch sensitive buttons\n\n## Installation\n\nYou can either run the binary directly on your machine or use the provided Docker image.\n\n### Docker Compose\n\nHere is an example [`compose.yml`](compose.yml) file to get you started:\n\n```yaml\nservices:\n  esphome-dashboard:\n    image: ghcr.io/topi314/esphome-dashboard:latest\n    container_name: esphome-dashboard\n    restart: unless-stopped\n    volumes:\n      # Mount your configuration file\n      - ./config.toml:/var/lib/esphome-dashboard/config.toml\n      # Mount your dashboard configuration \u0026 templates\n      - ./dashboards/:/var/lib/esphome-dashboard/dashboards/\n    ports:\n      # Expose the service on port 8080\n      - \"8080:8080\"\n```\n\n### ESPHome\n\n## Configuration\n\nThe configuration is done via a TOML file. You can find an example configuration in the [example directory](example.config.toml).\n\nTo get a Home Assistant API token, follow the instructions [here](https://developers.home-assistant.io/docs/auth_api/#long-lived-access-token).\n\n```toml\n# Enable hot reloading of the built-in templates (only useful for development)\ndev = false\n# The address the server should listen on\nlisten_addr = \"0.0.0.0\"\n# The port the server should listen on\nlisten_port = 8080\n# The directory where your dashboards are stored\ndashboard_dir = \"/var/lib/esphome-dashboard/dashboards/\"\n\n[log]\n# The log level (debug, info, warn, error)\nlevel = \"info\"\n# The log format (text, json, log-fmt)\nformat = \"text\"\n# Whether to add the source location to the log output\nadd_source = true\n# Whether to add the log level to the log output (only useful for text format)\nno_color = false\n\n# The Home Assistant configuration (optional)\n[home_assistant]\n# The hostname/IP of your Home Assistant instance\nhost = \"localhost\"\n# The port of your Home Assistant instance\nport = 80\n# Whether to use HTTPS or HTTP to connect to Home Assistant\nsecure = false\n# The Home Assistant API token\ntoken = \"\"\n```\n\n### Dashboard Configuration\n\nThe dashboard configuration is also done via a TOML file. You can find an example configuration [here](dashboards/default/config.toml).\n\n```toml\n# The width in pixels of the dashboard (should be the same as the display width)\nwidth = 800\n# The height in pixels of the dashboard (should be the same as the display height)\nheight = 480\n# The base template to use for the dashboard\n# The path is relative to the dashboard directory\nbase = 'base.gohtml'\n# The pages which can be injected into the base template\n# The paths are relative to the dashboard directory\npages = [\n    'pages/forecast.gohtml',\n    'pages/mealplan.gohtml',\n    'pages/calendar.gohtml',\n    'pages/timeline.gohtml',\n    'pages/pokemon-go-timeline.gohtml',\n]\n\n# The Home Assistant configuration (optional)\n[home_assistant]\n# The entities to fetch from Home Assistant\nentities = [\n    { name = 'Forecast', id = 'weather.forecast_home' },\n]\n# The calendars to fetch from Home Assistant (optional)\n# name: The name of the calendar (used in the template)\n# ids: The IDs of the calendars entities which should be fetched and merged\n# days: The number of days to fetch events for (please note it we always start fetching from the week start (Monday))\n# max_events: The maximum number of events to fetch from all calendars combined (optional)\n# skip_past_events: Whether to skip past events (optional)\ncalendars = [\n    { name = 'Mealie', ids = ['calendar.mealie_dinner', 'calendar.mealie_lunch'], days = 7 },\n    { name = 'Calendar', ids = ['calendar.konzerte', 'calendar.urlaub', 'calendar.feiertage'], days = 28 },\n    { name = 'Timeline', ids = ['calendar.konzerte', 'calendar.urlaub', 'calendar.feiertage'], days = 28, max_events = 10, skip_past_events = true },\n    { name = 'PokemonGo', ids = ['calendar.pokemon_go_local_events'], days = 28, max_events = 10, skip_past_events = true },\n]\n# The services to call from Home Assistant (optional)\n# name: The name of the service (used in the template)\n# domain: The domain of the service\n# service: The service to call\n# return_response: Whether the service returns a response (optional)\n# data: The data to send to the service (optional)\nservices = [\n    { name = 'WeatherForecasts', domain = 'weather', service = 'get_forecasts', return_response = true, data = { entity_id = 'weather.forecast_home', type = 'daily' } },\n]\n```\n\n### ESPHome Configuration\n\nHere is an example ESPHome configuration for an `ESP32` with a `WaveShare 7.5inch e-Paper HAT (v2)` display \u0026 touch sensitive buttons.\n\nMake sure to copy [`config/common/dashboard-base.yaml`](config/common/dashboard-base.yaml) to your ESPHome configuration directory (e.g. `esphome/common/dashboard-base.yaml`).\n\n```yaml\npackages:\n  dashboard: !include\n    file: common/dashboard-base.yaml\n    vars:\n      touch_threshold: 1000 # Change this to a higher value if the touch buttons are too sensitive\n      display_rotation: 0° # Change this to 90°, 180° or 270° if your display is rotated\n      base_url: 'http://192.168.178.68:1234' # Change this to the IP of your dashboard server\n      dashboard_name: 'default' # Change this to the name of your dashboard\n\nesphome:\n  name: dashboard\n  friendly_name: Dashboard\n  name_add_mac_suffix: false\n\nwifi:\n  ssid: !secret wifi_ssid\n  password: !secret wifi_password\n\n```\n\n## Templates\n\nThe dashboard is rendered using Go templates. You can find an example dashboard in the [example directory](dashboards/default).\n\nTo get started with go templates I recommend reading the [`text/template`](https://pkg.go.dev/text/template) and the [`html/template`](https://pkg.go.dev/html/template) documentation.\n\n### Base Template\n\nThe base template is a Go template which can be used to render the dashboard. You can find an example base template [here](dashboards/default/base.gohtml).\nThis template then includes the other pages which can be injected into the base template.\n\nYou can include the current Page with the following code:\n\n```html\n{{ template \"page\" . }}\n```\n\nEach page is additionally a Go template with the filename as the template name.\n\nYou can include the other pages with the following code:\n\n```html\n{{ template \"forecast.gohtml\" . }}\n```\n\nYou can also include frontmatter in the templates to pass additional data to the template which is available in the `Vars` variable.\nFor pages they are available via `{{ .Page.Vars }}` and `{{ $page index .Pages 0 }} {{ $page.Vars }}` for the base template.\n\n```html\n---\ntitle: \"Weather\"\n---\n\u003cspan\u003e{{ .Vars.title }}\u003c/span\u003e\n```\n\nYou can include assets via the `assets` directory in the dashboard directory. The assets are then available via the `/dashboards/{dashboard}/assets/` route.\nYou can use a relative path to the assets directory in the template. For example, to include a CSS file you can use the following code:\n\n```html\n\u003clink rel=\"stylesheet\" href=\"../assets/style.css\"\u003e\n```\n\nHere is the example base template:\n\n```html\n\u003cstyle\u003e\n    @font-face {\n        font-family: 'Gotham Round';\n        font-style: normal;\n        src: url(\"../assets/gothamrnd.otf\") format(\"opentype\");\n    }\n\n    * {\n        box-sizing: border-box;\n    }\n\n    html, body {\n        height: 100%;\n        width: 100%;\n        margin: 0;\n        padding: 0;\n        font-family: 'Gotham Round', system-ui;\n    }\n\n    body {\n        display: flex;\n        flex-direction: column;\n    }\n\n    h1 {\n        margin: 0;\n        font-size: 40px;\n        text-transform: uppercase;\n        font-weight: bolder;\n    }\n\n    .page {\n        width: 100%;\n        flex-grow: 1;\n        display: flex;\n        flex-direction: column;\n        align-items: center;\n        overflow: hidden;\n    }\n\n    .container {\n        height: 100%;\n        width: 100%;\n        padding: 0 10px;\n    }\n\u003c/style\u003e\n\u003cbody id=\"root\"\u003e\n\u003cdiv class=\"page\"\u003e\n    {{ template \"page\" . }}\n\u003c/div\u003e\n{{ template \"pages\" . }}\n{{ template \"gen-time\" }}\n\u003c/body\u003e\n```\n\n#### Page Template\n\nThe page template gets injected into the base template and can be used to render the content you want to display. You can find an example page\ntemplate [here](dashboards/default/pages/forecast.gohtml).\n\nStyles and scripts are shared between the base and page templates.\n\nThis template calls the built-in [`forecast`](templates/forecast.gohtml) template which displays the weather forecast using\nthe [Home Assistant Weather Forecast](https://www.home-assistant.io/integrations/weather/#action-weatherget_forecasts) action.\n\nIt expects the following data to be passed to the template:\n\n- `Now`: The current weather forecast entity from Home Assistant\n- `Forecast`: The weather forecast service response from Home Assistant\n\n```html\n\u003ch1\u003eWeather\u003c/h1\u003e\n\u003cdiv class=\"container\"\u003e\n    {{ $now := index .HomeAssistant.Entities.Forecast }}\n    {{ $forecast := index .HomeAssistant.Services.WeatherForecasts.ServiceResponse \"weather.forecast_home\" }}\n    {{ template \"forecast\" dict \"Now\" $now \"Forecast\" $forecast }}\n\u003c/div\u003e\n\n```\n\n#### Template Variables\n\nThe following variables are available in the dashboard templates:\n\n- `PageIndex`: The current page index\n- `Page`: The current page (this is a method)\n    - `Index`: The index of the page\n    - `Vars`: The frontmatter of the page\n- `PageCount`: The total number of pages\n- `Pages`: The list of pages\n    - `Index`: The index of the page\n    - `Vars`: The frontmatter of the page\n- `Vars`: The frontmatter of the base template\n- `HomeAssistant`: The Home Assistant entities, calendars \u0026 services\n    - `Entities`: The entities to fetch from Home Assistant\n        - `\u003cName\u003e`: The entity name defined in the configuration\n            - `EntityID`: The entity ID\n            - `LastChanged`: The last changed timestamp (this is a [`time.Time`](https://pkg.go.dev/time#Time) struct)\n            - `LastUpdated`: The last updated timestamp (this is a [`time.Time`](https://pkg.go.dev/time#Time) struct)\n            - `State`: The state of the entity\n            - `Attributes`: The attributes of the entity (this is a `map[string]any`)\n    - `Calendars`: The calendars to fetch from Home Assistant\n        - `\u003cName\u003e`: The calendar name defined in the configuration (this is a `map[string][]CalendarDay` where [\n          `CalendarDay`](https://pkg.go.dev/github.com/topi314/esphome-dashboard/dashboard#CalendarDay) is a struct)\n            - `Time`: The day timestamp (this is a [`time.Time`](https://pkg.go.dev/time#Time) struct)\n            - `IsPast`: Whether the day is in the past\n            - `IsToday`: Whether the day is today\n            - `Events`: The events of the day as a list\n                - `Start`: The event start timestamp (this is a [`Date`](https://pkg.go.dev/github.com/topi314/esphome-dashboard/dashboard/homeassistant#Date) struct)\n                - `End`: The event end timestamp (this is a [`Date`](https://pkg.go.dev/github.com/topi314/esphome-dashboard/dashboard/homeassistant#Date) struct)\n                - `Summary`: The event summary\n                - `Description`: The event description\n                - `Location`: The event location\n    - `Services`: The services to call from Home Assistant\n        - `\u003cName\u003e`: The service name defined in the configuration (this is a `map[string]Response` where [\n          `Response`](https://pkg.go.dev/github.com/topi314/esphome-dashboard/dashboard/homeassistant#Response) is a struct)\n            - `ChangedStates`: The changed states of the service as a list\n                - `EntityID`: The entity ID\n                - `LastChanged`: The last changed timestamp (this is a [`time.Time`](https://pkg.go.dev/time#Time) struct)\n                - `LastUpdated`: The last updated timestamp (this is a [`time.Time`](https://pkg.go.dev/time#Time) struct)\n                - `State`: The state of the entity\n                - `Attributes`: The attributes of the entity (this is a `map[string]any`)\n            - `ServiceResponse`: The service response (this is a `map[string]any`)\n\n#### Template Functions\n\nThe following functions are available in the dashboard templates in addition to the [built-in functions](https://pkg.go.dev/text/template#hdr-Functions):\n\n- `seq`: Generates a sequence of integers from 0 to n-1\n    - `n`: The number of integers to generate\n- `reverse`: Reverses a list\n    - `list`: The list to reverse\n- `parseTime`: Parses a RFC3339 time string into a [`time.Time]`(https://pkg.go.dev/time#Time) struct\n    - `s`: The time string to parse\n- `formatTimeToDay`: Formats a time into a day string (e.g. `Mon 02 Jan`)\n    - `t`: The time to format\n- `formatTimeToRelDay`: Formats a time into a relative day string (e.g. `Today`, `Tomorrow`, `Yesterday`, `Mon 02 Jan`)\n    - `t`: The time to format\n- `convertNewLinesToBR`: Converts new lines to `\u003cbr\u003e` tags\n    - `s`: The string to convert\n- `safeHTML`: Marks a string as safe HTML so it is not escaped\n    - `s`: The string to mark as safe\n- `safeCSS`: Marks a string as safe CSS so it is not escaped\n    - `s`: The string to mark as safe\n- `safeJS`: Marks a string as safe JS so it is not escaped\n    - `s`: The string to mark as safe\n- `safeHTMLAttr`: Marks a string as safe HTML attribute so it is not escaped\n    - `s`: The string to mark as safe\n- `safeURL`: Marks a string as safe URL so it is not escaped\n    - `s`: The string to mark as safe\n- `safeJS`: Marks a string as safe JS so it is not escaped\n    - `s`: The string to mark as safe\n- `safeJSStr`: Marks a string as safe JS string so it is not escaped\n    - `s`: The string to mark as safe\n- `safeSrcset`: Marks a string as safe srcset so it is not escaped\n    - `s`: The string to mark as safe\n- `dict`: Creates a `map[string]any` from a list of key-value pairs\n    - `kvs`: The list of key-value pairs (e.g. `dict \"key1\" value1 \"key2\" value2`)\n\n## API\n\nThe API is a simple REST API which can be used to find the next page based on an action or get a specific page as a PNG image or HTML file.\n\n### Get Control\n\nThis endpoint is used to get the next page index based on an action.\n\n```http request\nGET /dashboards/{dashboard}/control\n```\n\nQuery Parameters:\n\n| Name   | Description                                                                            |\n|--------|----------------------------------------------------------------------------------------|\n| action | The action to perform (`refresh`, `next_page`, `last_page`, `prev_page`, `first_page`) |\n\nResponse:\n\n404 Not Found\n\n200 OK:\n\n* Content-Type: text/plain\n\n```\n1\n```\n\nthe next page index\n\n### Get Page\n\n```http\nGET /dashboards/{dashboard}/pages/{page}\n```\n\nQuery Parameters:\n\n| Name   | Default | Description                                                 |\n|--------|---------|-------------------------------------------------------------|\n| format | `html`  | The format of the response (`html`, `png`, `jpeg` or `bmp`) |\n\nResponse:\n\n404 Not Found\n\n200 OK:\n\n* Content-Type: image/png\n\n* Content-Type: text/html; charset=utf-8\n\n### Get Version\n\n```http\nGET /version\n```\n\nResponse:\n\n* 200 OK:\n\n```\nDashboard v0.0.0-20250214131520-b8b38f8257cf+dirty (Go go1.24.0)\n```\n\n## License\n\nESPHome Dashboard is licensed under the [Apache License 2.0](LICENSE).\n\n## Contributing\n\nContributions are always welcome! Just open a pull request or discussion and I will take a look at it.\n\n## Contact\n\n- [Discord](https://discord.gg/sD3ABd5)\n- [Email](mailto:hi@topi.wtf)\n- [Matrix](https://matrix.to/#/@topi:topi.wtf)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftopi314%2Fesphome-dashboard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftopi314%2Fesphome-dashboard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftopi314%2Fesphome-dashboard/lists"}