hass.tibber_prices/developer/timer-architecture.html
github-actions[bot] e9aea64a2e deploy: 6898c126e3
2025-12-06 01:42:39 +00:00

278 lines
No EOL
82 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" dir="ltr" class="docs-wrapper plugin-docs plugin-id-default docs-version-current docs-doc-page docs-doc-id-timer-architecture" data-has-hydrated="false">
<head>
<meta charset="UTF-8">
<meta name="generator" content="Docusaurus v3.9.2">
<title data-rh="true">Timer Architecture | Tibber Prices - Developer Guide</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://jpawlowski.github.io/hass.tibber_prices/developer/img/social-card.png"><meta data-rh="true" name="twitter:image" content="https://jpawlowski.github.io/hass.tibber_prices/developer/img/social-card.png"><meta data-rh="true" property="og:url" content="https://jpawlowski.github.io/hass.tibber_prices/developer/timer-architecture"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Timer Architecture | Tibber Prices - Developer Guide"><meta data-rh="true" name="description" content="This document explains the timer/scheduler system in the Tibber Prices integration - what runs when, why, and how they coordinate."><meta data-rh="true" property="og:description" content="This document explains the timer/scheduler system in the Tibber Prices integration - what runs when, why, and how they coordinate."><link data-rh="true" rel="icon" href="/hass.tibber_prices/developer/img/logo.svg"><link data-rh="true" rel="canonical" href="https://jpawlowski.github.io/hass.tibber_prices/developer/timer-architecture"><link data-rh="true" rel="alternate" href="https://jpawlowski.github.io/hass.tibber_prices/developer/timer-architecture" hreflang="en"><link data-rh="true" rel="alternate" href="https://jpawlowski.github.io/hass.tibber_prices/developer/timer-architecture" hreflang="x-default"><script data-rh="true" type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Timer Architecture","item":"https://jpawlowski.github.io/hass.tibber_prices/developer/timer-architecture"}]}</script><link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Space+Grotesk:wght@500;600;700&amp;display=swap"><link rel="stylesheet" href="/hass.tibber_prices/developer/assets/css/styles.be4f3d68.css">
<script src="/hass.tibber_prices/developer/assets/js/runtime~main.09b7b31a.js" defer="defer"></script>
<script src="/hass.tibber_prices/developer/assets/js/main.5eb4cf38.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
<svg style="display: none;"><defs>
<symbol id="theme-svg-external-link" viewBox="0 0 24 24"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></symbol>
</defs></svg>
<script>!function(){var t=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();document.documentElement.setAttribute("data-theme",t||(window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light")),document.documentElement.setAttribute("data-theme-choice",t||"system")}(),function(){try{const c=new URLSearchParams(window.location.search).entries();for(var[t,e]of c)if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id="__docusaurus"><link rel="preload" as="image" href="/hass.tibber_prices/developer/img/logo.svg"><div role="region" aria-label="Skip to main content"><a class="skipToContent_fXgn" href="#__docusaurus_skipToContent_fallback">Skip to main content</a></div><nav aria-label="Main" class="theme-layout-navbar navbar navbar--fixed-top"><div class="navbar__inner"><div class="theme-layout-navbar-left navbar__items"><button aria-label="Toggle navigation bar" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/hass.tibber_prices/developer/"><div class="navbar__logo"><img src="/hass.tibber_prices/developer/img/logo.svg" alt="Tibber Prices Integration Logo" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="/hass.tibber_prices/developer/img/logo.svg" alt="Tibber Prices Integration Logo" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><b class="navbar__title text--truncate">Tibber Prices HA</b></a><a class="navbar__item navbar__link" href="/hass.tibber_prices/developer/intro">Developer Guide</a><a href="https://jpawlowski.github.io/hass.tibber_prices/user/" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">User Docs<svg width="13.5" height="13.5" aria-label="(opens in new tab)" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></div><div class="theme-layout-navbar-right navbar__items navbar__items--right"><a class="navbar__item navbar__link" href="/hass.tibber_prices/developer/timer-architecture">Next 🚧</a><a href="https://github.com/jpawlowski/hass.tibber_prices" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">GitHub<svg width="13.5" height="13.5" aria-label="(opens in new tab)" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a><div class="toggle_vylO colorModeToggle_DEke"><button class="clean-btn toggleButton_gllP toggleButtonDisabled_aARS" type="button" disabled="" title="system mode" aria-label="Switch between dark and light mode (currently system mode)"><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP lightToggleIcon_pyhR"><path fill="currentColor" d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP darkToggleIcon_wfgR"><path fill="currentColor" d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP systemToggleIcon_QzmC"><path fill="currentColor" d="m12 21c4.971 0 9-4.029 9-9s-4.029-9-9-9-9 4.029-9 9 4.029 9 9 9zm4.95-13.95c1.313 1.313 2.05 3.093 2.05 4.95s-0.738 3.637-2.05 4.95c-1.313 1.313-3.093 2.05-4.95 2.05v-14c1.857 0 3.637 0.737 4.95 2.05z"></path></svg></button></div><div class="navbarSearchContainer_Bca1"><div class="navbar__search"><span aria-label="expand searchbar" role="button" class="search-icon" tabindex="0"></span><input id="search_input_react" type="search" placeholder="Loading..." aria-label="Search" class="navbar__search-input search-bar" disabled=""></div></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div id="__docusaurus_skipToContent_fallback" class="theme-layout-main main-wrapper mainWrapper_z2l0"><div class="docsWrapper_hBAB"><button aria-label="Scroll back to top" class="clean-btn theme-back-to-top-button backToTopButton_sjWU" type="button"></button><div class="docRoot_UBD9"><aside class="theme-doc-sidebar-container docSidebarContainer_YfHR"><div class="sidebarViewport_aRkj"><div class="sidebar_njMd"><nav aria-label="Docs sidebar" class="menu thin-scrollbar menu_SIkG"><ul class="theme-doc-sidebar-menu menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/hass.tibber_prices/developer/intro"><span title="Developer Documentation" class="linkLabel_WmDU">Developer Documentation</span></a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="categoryLink_byQd menu__link menu__link--sublist menu__link--sublist-caret menu__link--active" role="button" aria-expanded="true" href="/hass.tibber_prices/developer/architecture"><span title="🏗️ Architecture" class="categoryLinkLabel_W154">🏗️ Architecture</span></a></div><ul class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/architecture"><span title="Architecture" class="linkLabel_WmDU">Architecture</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link menu__link--active" aria-current="page" tabindex="0" href="/hass.tibber_prices/developer/timer-architecture"><span title="Timer Architecture" class="linkLabel_WmDU">Timer Architecture</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/caching-strategy"><span title="Caching Strategy" class="linkLabel_WmDU">Caching Strategy</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/api-reference"><span title="API Reference" class="linkLabel_WmDU">API Reference</span></a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="categoryLink_byQd menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="true" href="/hass.tibber_prices/developer/setup"><span title="💻 Development" class="categoryLinkLabel_W154">💻 Development</span></a></div><ul class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/setup"><span title="Development Setup" class="linkLabel_WmDU">Development Setup</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/coding-guidelines"><span title="Coding Guidelines" class="linkLabel_WmDU">Coding Guidelines</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/critical-patterns"><span title="Critical Behavior Patterns - Testing Guide" class="linkLabel_WmDU">Critical Behavior Patterns - Testing Guide</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/debugging"><span title="Debugging Guide" class="linkLabel_WmDU">Debugging Guide</span></a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="categoryLink_byQd menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="true" href="/hass.tibber_prices/developer/period-calculation-theory"><span title="📐 Advanced Topics" class="categoryLinkLabel_W154">📐 Advanced Topics</span></a></div><ul class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/period-calculation-theory"><span title="Period Calculation Theory" class="linkLabel_WmDU">Period Calculation Theory</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/refactoring-guide"><span title="Refactoring Guide" class="linkLabel_WmDU">Refactoring Guide</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/performance"><span title="Performance Optimization" class="linkLabel_WmDU">Performance Optimization</span></a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="categoryLink_byQd menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="true" href="/hass.tibber_prices/developer/contributing"><span title="📝 Contributing" class="categoryLinkLabel_W154">📝 Contributing</span></a></div><ul class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/contributing"><span title="Contributing Guide" class="linkLabel_WmDU">Contributing Guide</span></a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="categoryLink_byQd menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="true" href="/hass.tibber_prices/developer/release-management"><span title="🚀 Release" class="categoryLinkLabel_W154">🚀 Release</span></a></div><ul class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/release-management"><span title="Release Notes Generation" class="linkLabel_WmDU">Release Notes Generation</span></a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/hass.tibber_prices/developer/testing"><span title="Testing" class="linkLabel_WmDU">Testing</span></a></li></ul></li></ul></nav><button type="button" title="Collapse sidebar" aria-label="Collapse sidebar" class="button button--secondary button--outline collapseSidebarButton_PEFL"><svg width="20" height="20" aria-hidden="true" class="collapseSidebarButtonIcon_kv0_"><g fill="#7a7a7a"><path d="M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"></path><path d="M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"></path></g></svg></button></div></div></aside><main class="docMainContainer_TBSr"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_VOVn"><div class="theme-doc-version-banner alert alert--warning margin-bottom--md" role="alert"><div>This is unreleased documentation for <!-- -->Tibber Prices - Developer Guide<!-- --> <b>Next 🚧</b> version.</div><div class="margin-top--md">For up-to-date documentation, see the <b><a href="/hass.tibber_prices/developer/timer-architecture">latest version</a></b> (<!-- -->Next 🚧<!-- -->).</div></div><div class="docItemContainer_Djhp"><article><nav class="theme-doc-breadcrumbs breadcrumbsContainer_Z_bl" aria-label="Breadcrumbs"><ul class="breadcrumbs"><li class="breadcrumbs__item"><a aria-label="Home page" class="breadcrumbs__link" href="/hass.tibber_prices/developer/"><svg viewBox="0 0 24 24" class="breadcrumbHomeIcon_YNFT"><path d="M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z" fill="currentColor"></path></svg></a></li><li class="breadcrumbs__item"><span class="breadcrumbs__link">🏗️ Architecture</span></li><li class="breadcrumbs__item breadcrumbs__item--active"><span class="breadcrumbs__link">Timer Architecture</span></li></ul></nav><span class="theme-doc-version-badge badge badge--secondary">Version: Next 🚧</span><div class="tocCollapsible_ETCw theme-doc-toc-mobile tocMobile_ITEo"><button type="button" class="clean-btn tocCollapsibleButton_TO0P">On this page</button></div><div class="theme-doc-markdown markdown"><header><h1>Timer Architecture</h1></header>
<p>This document explains the timer/scheduler system in the Tibber Prices integration - what runs when, why, and how they coordinate.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="overview">Overview<a href="#overview" class="hash-link" aria-label="Direct link to Overview" title="Direct link to Overview" translate="no"></a></h2>
<p>The integration uses <strong>three independent timer mechanisms</strong> for different purposes:</p>
<table><thead><tr><th>Timer</th><th>Type</th><th>Interval</th><th>Purpose</th><th>Trigger Method</th></tr></thead><tbody><tr><td><strong>Timer #1</strong></td><td>HA built-in</td><td>15 minutes</td><td>API data updates</td><td><code>DataUpdateCoordinator</code></td></tr><tr><td><strong>Timer #2</strong></td><td>Custom</td><td>:00, :15, :30, :45</td><td>Entity state refresh</td><td><code>async_track_utc_time_change()</code></td></tr><tr><td><strong>Timer #3</strong></td><td>Custom</td><td>Every minute</td><td>Countdown/progress</td><td><code>async_track_utc_time_change()</code></td></tr></tbody></table>
<p><strong>Key principle:</strong> Timer #1 (HA) controls <strong>data fetching</strong>, Timer #2 controls <strong>entity updates</strong>, Timer #3 controls <strong>timing displays</strong>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="timer-1-dataupdatecoordinator-ha-built-in">Timer #1: DataUpdateCoordinator (HA Built-in)<a href="#timer-1-dataupdatecoordinator-ha-built-in" class="hash-link" aria-label="Direct link to Timer #1: DataUpdateCoordinator (HA Built-in)" title="Direct link to Timer #1: DataUpdateCoordinator (HA Built-in)" translate="no"></a></h2>
<p><strong>File:</strong> <code>coordinator/core.py</code><code>TibberPricesDataUpdateCoordinator</code></p>
<p><strong>Type:</strong> Home Assistant&#x27;s built-in <code>DataUpdateCoordinator</code> with <code>UPDATE_INTERVAL = 15 minutes</code></p>
<p><strong>What it is:</strong></p>
<ul>
<li class="">HA provides this timer system automatically when you inherit from <code>DataUpdateCoordinator</code></li>
<li class="">Triggers <code>_async_update_data()</code> method every 15 minutes</li>
<li class=""><strong>Not</strong> synchronized to clock boundaries (each installation has different start time)</li>
</ul>
<p><strong>Purpose:</strong> Check if fresh API data is needed, fetch if necessary</p>
<p><strong>What it does:</strong></p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">_async_update_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TibberPricesData</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Step 1: Check midnight turnover FIRST (prevents race with Timer #2)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_check_midnight_turnover_needed</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dt_util</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">now</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_perform_midnight_data_rotation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dt_util</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">now</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Notify ALL entities after midnight turnover</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data </span><span class="token comment" style="color:#999988;font-style:italic"># Early return</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Step 2: Check if we need tomorrow data (after 13:00)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_should_update_price_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;tomorrow_check&quot;</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_fetch_and_update_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Fetch from API</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Step 3: Use cached data (fast path - most common)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><br></span></code></pre></div></div>
<p><strong>Load Distribution:</strong></p>
<ul>
<li class="">Each HA installation starts Timer #1 at different times → natural distribution</li>
<li class="">Tomorrow data check adds 0-30s random delay → prevents &quot;thundering herd&quot; on Tibber API</li>
<li class="">Result: API load spread over ~30 minutes instead of all at once</li>
</ul>
<p><strong>Midnight Coordination:</strong></p>
<ul>
<li class="">Atomic check: <code>_check_midnight_turnover_needed(now)</code> compares dates only (no side effects)</li>
<li class="">If midnight turnover needed → performs it and returns early</li>
<li class="">Timer #2 will see turnover already done and skip gracefully</li>
</ul>
<p><strong>Why we use HA&#x27;s timer:</strong></p>
<ul>
<li class="">Automatic restart after HA restart</li>
<li class="">Built-in retry logic for temporary failures</li>
<li class="">Standard HA integration pattern</li>
<li class="">Handles backpressure (won&#x27;t queue up if previous update still running)</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="timer-2-quarter-hour-refresh-custom">Timer #2: Quarter-Hour Refresh (Custom)<a href="#timer-2-quarter-hour-refresh-custom" class="hash-link" aria-label="Direct link to Timer #2: Quarter-Hour Refresh (Custom)" title="Direct link to Timer #2: Quarter-Hour Refresh (Custom)" translate="no"></a></h2>
<p><strong>File:</strong> <code>coordinator/listeners.py</code><code>ListenerManager.schedule_quarter_hour_refresh()</code></p>
<p><strong>Type:</strong> Custom timer using <code>async_track_utc_time_change(minute=[0, 15, 30, 45], second=0)</code></p>
<p><strong>Purpose:</strong> Update time-sensitive entity states at interval boundaries <strong>without waiting for API poll</strong></p>
<p><strong>Problem it solves:</strong></p>
<ul>
<li class="">Timer #1 runs every 15 minutes but NOT synchronized to clock (:03, :18, :33, :48)</li>
<li class="">Current price changes at :00, :15, :30, :45 → entities would show stale data for up to 15 minutes</li>
<li class="">Example: 14:00 new price, but Timer #1 ran at 13:58 → next update at 14:13 → users see old price until 14:13</li>
</ul>
<p><strong>What it does:</strong></p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">_handle_quarter_hour_refresh</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> now</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> datetime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Step 1: Check midnight turnover (coordinates with Timer #1)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_check_midnight_turnover_needed</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">now</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Timer #1 might have already done this → atomic check handles it</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_perform_midnight_data_rotation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">now</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Notify ALL entities after midnight turnover</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Step 2: Normal quarter-hour refresh (most common path)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Only notify time-sensitive entities (current_interval_price, etc.)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_listener_manager</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">async_update_time_sensitive_listeners</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p><strong>Smart Boundary Tolerance:</strong></p>
<ul>
<li class="">Uses <code>round_to_nearest_quarter_hour()</code> with ±2 second tolerance</li>
<li class="">HA may schedule timer at 14:59:58 → rounds to 15:00:00 (shows new interval)</li>
<li class="">HA restart at 14:59:30 → stays at 14:45:00 (shows current interval)</li>
<li class="">See <a class="" href="/hass.tibber_prices/developer/architecture#3-quarter-hour-precision">Architecture</a> for details</li>
</ul>
<p><strong>Absolute Time Scheduling:</strong></p>
<ul>
<li class=""><code>async_track_utc_time_change()</code> plans for <strong>all future boundaries</strong> (15:00, 15:15, 15:30, ...)</li>
<li class="">NOT relative delays (&quot;in 15 minutes&quot;)</li>
<li class="">If triggered at 14:59:58 → next trigger is 15:15:00, NOT 15:00:00 (prevents double updates)</li>
</ul>
<p><strong>Which entities listen:</strong></p>
<ul>
<li class="">All sensors that depend on &quot;current interval&quot; (e.g., <code>current_interval_price</code>, <code>next_interval_price</code>)</li>
<li class="">Binary sensors that check &quot;is now in period?&quot; (e.g., <code>best_price_period_active</code>)</li>
<li class="">~50-60 entities out of 120+ total</li>
</ul>
<p><strong>Why custom timer:</strong></p>
<ul>
<li class="">HA&#x27;s built-in coordinator doesn&#x27;t support exact boundary timing</li>
<li class="">We need <strong>absolute time</strong> triggers, not periodic intervals</li>
<li class="">Allows fast entity updates without expensive data transformation</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="timer-3-minute-refresh-custom">Timer #3: Minute Refresh (Custom)<a href="#timer-3-minute-refresh-custom" class="hash-link" aria-label="Direct link to Timer #3: Minute Refresh (Custom)" title="Direct link to Timer #3: Minute Refresh (Custom)" translate="no"></a></h2>
<p><strong>File:</strong> <code>coordinator/listeners.py</code><code>ListenerManager.schedule_minute_refresh()</code></p>
<p><strong>Type:</strong> Custom timer using <code>async_track_utc_time_change(second=0)</code> (every minute)</p>
<p><strong>Purpose:</strong> Update countdown and progress sensors for smooth UX</p>
<p><strong>What it does:</strong></p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">_handle_minute_refresh</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> now</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> datetime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Only notify minute-update entities</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># No data fetching, no transformation, no midnight handling</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_listener_manager</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">async_update_minute_listeners</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p><strong>Which entities listen:</strong></p>
<ul>
<li class=""><code>best_price_remaining_minutes</code> - Countdown timer</li>
<li class=""><code>peak_price_remaining_minutes</code> - Countdown timer</li>
<li class=""><code>best_price_progress</code> - Progress bar (0-100%)</li>
<li class=""><code>peak_price_progress</code> - Progress bar (0-100%)</li>
<li class="">~10 entities total</li>
</ul>
<p><strong>Why custom timer:</strong></p>
<ul>
<li class="">Users want smooth countdowns (not jumping 15 minutes at a time)</li>
<li class="">Progress bars need minute-by-minute updates</li>
<li class="">Very lightweight (no data processing, just state recalculation)</li>
</ul>
<p><strong>Why NOT every second:</strong></p>
<ul>
<li class="">Minute precision sufficient for countdown UX</li>
<li class="">Reduces CPU load (60× fewer updates than seconds)</li>
<li class="">Home Assistant best practice (avoid sub-minute updates)</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="listener-pattern-pythonha-terminology">Listener Pattern (Python/HA Terminology)<a href="#listener-pattern-pythonha-terminology" class="hash-link" aria-label="Direct link to Listener Pattern (Python/HA Terminology)" title="Direct link to Listener Pattern (Python/HA Terminology)" translate="no"></a></h2>
<p><strong>Your question:</strong> &quot;Sind Timer für dich eigentlich &#x27;Listener&#x27;?&quot;</p>
<p><strong>Answer:</strong> In Home Assistant terminology:</p>
<ul>
<li class=""><strong>Timer</strong> = The mechanism that triggers at specific times (<code>async_track_utc_time_change</code>)</li>
<li class=""><strong>Listener</strong> = A callback function that gets called when timer triggers</li>
<li class=""><strong>Observer Pattern</strong> = Entities register callbacks, coordinator notifies them</li>
</ul>
<p><strong>How it works:</strong></p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Entity registers a listener callback</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">TibberPricesSensor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">CoordinatorEntity</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">async_added_to_hass</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Register this entity&#x27;s update callback</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_remove_listener </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">coordinator</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">async_add_time_sensitive_listener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_handle_coordinator_update</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Coordinator maintains list of listeners</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">ListenerManager</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_time_sensitive_listeners </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># List of callbacks</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">async_add_time_sensitive_listener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> callback</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_time_sensitive_listeners</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">append</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">callback</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">async_update_time_sensitive_listeners</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Timer triggered → notify all listeners</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> callback </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_time_sensitive_listeners</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Entity updates itself</span><br></span></code></pre></div></div>
<p><strong>Why this pattern:</strong></p>
<ul>
<li class="">Decouples timer logic from entity logic</li>
<li class="">One timer can notify many entities efficiently</li>
<li class="">Entities can unregister when removed (cleanup)</li>
<li class="">Standard HA pattern for coordinator-based integrations</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="timer-coordination-scenarios">Timer Coordination Scenarios<a href="#timer-coordination-scenarios" class="hash-link" aria-label="Direct link to Timer Coordination Scenarios" title="Direct link to Timer Coordination Scenarios" translate="no"></a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="scenario-1-normal-operation-no-midnight">Scenario 1: Normal Operation (No Midnight)<a href="#scenario-1-normal-operation-no-midnight" class="hash-link" aria-label="Direct link to Scenario 1: Normal Operation (No Midnight)" title="Direct link to Scenario 1: Normal Operation (No Midnight)" translate="no"></a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">14:00:00 → Timer #2 triggers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Update time-sensitive entities (current price changed)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → 60 entities updated (~5ms)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">14:03:12 → Timer #1 triggers (HA&#x27;s 15-min cycle)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Check if tomorrow data needed (no, still cached)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Return cached data (fast path, ~2ms)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">14:15:00 → Timer #2 triggers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Update time-sensitive entities</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → 60 entities updated (~5ms)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">14:16:00 → Timer #3 triggers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Update countdown/progress entities</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → 10 entities updated (~1ms)</span><br></span></code></pre></div></div>
<p><strong>Key observation:</strong> Timer #1 and Timer #2 run <strong>independently</strong>, no conflicts.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="scenario-2-midnight-turnover">Scenario 2: Midnight Turnover<a href="#scenario-2-midnight-turnover" class="hash-link" aria-label="Direct link to Scenario 2: Midnight Turnover" title="Direct link to Scenario 2: Midnight Turnover" translate="no"></a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">23:45:12 → Timer #1 triggers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Check midnight: current_date=2025-11-17, last_check=2025-11-17</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → No turnover needed</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Return cached data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">00:00:00 → Timer #2 triggers FIRST (synchronized to midnight)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Check midnight: current_date=2025-11-18, last_check=2025-11-17</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Turnover needed! Perform rotation, save cache</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → _last_midnight_check = 2025-11-18</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Notify ALL entities</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">00:03:12 → Timer #1 triggers (its regular cycle)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Check midnight: current_date=2025-11-18, last_check=2025-11-18</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Turnover already done → skip</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Return existing data (fast path)</span><br></span></code></pre></div></div>
<p><strong>Key observation:</strong> Atomic date comparison prevents double-turnover, whoever runs first wins.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="scenario-3-tomorrow-data-check-after-1300">Scenario 3: Tomorrow Data Check (After 13:00)<a href="#scenario-3-tomorrow-data-check-after-1300" class="hash-link" aria-label="Direct link to Scenario 3: Tomorrow Data Check (After 13:00)" title="Direct link to Scenario 3: Tomorrow Data Check (After 13:00)" translate="no"></a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">13:00:00 → Timer #2 triggers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Normal quarter-hour refresh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Update time-sensitive entities</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">13:03:12 → Timer #1 triggers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Check tomorrow data: missing or invalid</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Fetch from Tibber API (~300ms)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Transform data (~200ms)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Calculate periods (~100ms)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Notify ALL entities (new data available)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">13:15:00 → Timer #2 triggers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Normal quarter-hour refresh (uses newly fetched data)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> → Update time-sensitive entities</span><br></span></code></pre></div></div>
<p><strong>Key observation:</strong> Timer #1 does expensive work (API + transform), Timer #2 does cheap work (entity notify).</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-we-keep-has-timer-timer-1">Why We Keep HA&#x27;s Timer (Timer #1)<a href="#why-we-keep-has-timer-timer-1" class="hash-link" aria-label="Direct link to Why We Keep HA&#x27;s Timer (Timer #1)" title="Direct link to Why We Keep HA&#x27;s Timer (Timer #1)" translate="no"></a></h2>
<p><strong>Your question:</strong> &quot;warum wir den HA timer trotzdem weiter benutzen, da er ja für uns unkontrollierte aktualisierte änderungen triggert&quot;</p>
<p><strong>Answer:</strong> You&#x27;re correct that it&#x27;s not synchronized, but that&#x27;s actually <strong>intentional</strong>:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="reason-1-load-distribution-on-tibber-api">Reason 1: Load Distribution on Tibber API<a href="#reason-1-load-distribution-on-tibber-api" class="hash-link" aria-label="Direct link to Reason 1: Load Distribution on Tibber API" title="Direct link to Reason 1: Load Distribution on Tibber API" translate="no"></a></h3>
<p>If all installations used synchronized timers:</p>
<ul>
<li class="">❌ Everyone fetches at 13:00:00 → Tibber API overload</li>
<li class="">❌ Everyone fetches at 14:00:00 → Tibber API overload</li>
<li class="">&quot;Thundering herd&quot; problem</li>
</ul>
<p>With HA&#x27;s unsynchronized timer:</p>
<ul>
<li class="">✅ Installation A: 13:03:12, 13:18:12, 13:33:12, ...</li>
<li class="">✅ Installation B: 13:07:45, 13:22:45, 13:37:45, ...</li>
<li class="">✅ Installation C: 13:11:28, 13:26:28, 13:41:28, ...</li>
<li class="">✅ Natural distribution over ~30 minutes</li>
<li class="">✅ Plus: Random 0-30s delay on tomorrow checks</li>
</ul>
<p><strong>Result:</strong> API load spread evenly, no spikes.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="reason-2-what-timer-1-actually-checks">Reason 2: What Timer #1 Actually Checks<a href="#reason-2-what-timer-1-actually-checks" class="hash-link" aria-label="Direct link to Reason 2: What Timer #1 Actually Checks" title="Direct link to Reason 2: What Timer #1 Actually Checks" translate="no"></a></h3>
<p>Timer #1 does NOT blindly update. It checks:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">_should_update_price_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Check 1: Do we have tomorrow data? (only relevant after ~13:00)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> tomorrow_missing </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> tomorrow_invalid</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;tomorrow_check&quot;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Fetch needed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Check 2: Is cache still valid?</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> cache_valid</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;cached&quot;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># No fetch needed (most common!)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Check 3: Has enough time passed?</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> time_since_last_update </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> threshold</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;cached&quot;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Too soon, skip fetch</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;update_needed&quot;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Rare case</span><br></span></code></pre></div></div>
<p><strong>Most Timer #1 cycles:</strong> Fast path (~2ms), no API call, just returns cached data.</p>
<p><strong>API fetch only when:</strong></p>
<ul>
<li class="">Tomorrow data missing/invalid (after 13:00)</li>
<li class="">Cache expired (midnight turnover)</li>
<li class="">Explicit user refresh</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="reason-3-ha-integration-best-practices">Reason 3: HA Integration Best Practices<a href="#reason-3-ha-integration-best-practices" class="hash-link" aria-label="Direct link to Reason 3: HA Integration Best Practices" title="Direct link to Reason 3: HA Integration Best Practices" translate="no"></a></h3>
<ul>
<li class="">✅ Standard HA pattern: <code>DataUpdateCoordinator</code> is recommended by HA docs</li>
<li class="">✅ Automatic retry logic for temporary API failures</li>
<li class="">✅ Backpressure handling (won&#x27;t queue updates if previous still running)</li>
<li class="">✅ Developer tools integration (users can manually trigger refresh)</li>
<li class="">✅ Diagnostics integration (shows last update time, success/failure)</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-we-do-synchronize">What We DO Synchronize<a href="#what-we-do-synchronize" class="hash-link" aria-label="Direct link to What We DO Synchronize" title="Direct link to What We DO Synchronize" translate="no"></a></h3>
<ul>
<li class=""><strong>Timer #2:</strong> Entity state updates at exact boundaries (user-visible)</li>
<li class=""><strong>Timer #3:</strong> Countdown/progress at exact minutes (user-visible)</li>
<li class=""><strong>Timer #1:</strong> API fetch timing (invisible to user, distribution wanted)</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="performance-characteristics">Performance Characteristics<a href="#performance-characteristics" class="hash-link" aria-label="Direct link to Performance Characteristics" title="Direct link to Performance Characteristics" translate="no"></a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="timer-1-dataupdatecoordinator">Timer #1 (DataUpdateCoordinator)<a href="#timer-1-dataupdatecoordinator" class="hash-link" aria-label="Direct link to Timer #1 (DataUpdateCoordinator)" title="Direct link to Timer #1 (DataUpdateCoordinator)" translate="no"></a></h3>
<ul>
<li class=""><strong>Triggers:</strong> Every 15 minutes (unsynchronized)</li>
<li class=""><strong>Fast path:</strong> ~2ms (cache check, return existing data)</li>
<li class=""><strong>Slow path:</strong> ~600ms (API fetch + transform + calculate)</li>
<li class=""><strong>Frequency:</strong> ~96 times/day</li>
<li class=""><strong>API calls:</strong> ~1-2 times/day (cached otherwise)</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="timer-2-quarter-hour-refresh">Timer #2 (Quarter-Hour Refresh)<a href="#timer-2-quarter-hour-refresh" class="hash-link" aria-label="Direct link to Timer #2 (Quarter-Hour Refresh)" title="Direct link to Timer #2 (Quarter-Hour Refresh)" translate="no"></a></h3>
<ul>
<li class=""><strong>Triggers:</strong> 96 times/day (exact boundaries)</li>
<li class=""><strong>Processing:</strong> ~5ms (notify 60 entities)</li>
<li class=""><strong>No API calls:</strong> Uses cached/transformed data</li>
<li class=""><strong>No transformation:</strong> Just entity state updates</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="timer-3-minute-refresh">Timer #3 (Minute Refresh)<a href="#timer-3-minute-refresh" class="hash-link" aria-label="Direct link to Timer #3 (Minute Refresh)" title="Direct link to Timer #3 (Minute Refresh)" translate="no"></a></h3>
<ul>
<li class=""><strong>Triggers:</strong> 1440 times/day (every minute)</li>
<li class=""><strong>Processing:</strong> ~1ms (notify 10 entities)</li>
<li class=""><strong>No API calls:</strong> No data processing at all</li>
<li class=""><strong>Lightweight:</strong> Just countdown math</li>
</ul>
<p><strong>Total CPU budget:</strong> ~15 seconds/day for all timers combined.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="debugging-timer-issues">Debugging Timer Issues<a href="#debugging-timer-issues" class="hash-link" aria-label="Direct link to Debugging Timer Issues" title="Direct link to Debugging Timer Issues" translate="no"></a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="check-timer-1-ha-coordinator">Check Timer #1 (HA Coordinator)<a href="#check-timer-1-ha-coordinator" class="hash-link" aria-label="Direct link to Check Timer #1 (HA Coordinator)" title="Direct link to Check Timer #1 (HA Coordinator)" translate="no"></a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Enable debug logging</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">_LOGGER</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">setLevel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">logging</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DEBUG</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Watch for these log messages:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">&quot;Fetching data from API (reason: tomorrow_check)&quot;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># API call</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">&quot;Using cached data (no update needed)&quot;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Fast path</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">&quot;Midnight turnover detected (Timer #1)&quot;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Turnover</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="check-timer-2-quarter-hour">Check Timer #2 (Quarter-Hour)<a href="#check-timer-2-quarter-hour" class="hash-link" aria-label="Direct link to Check Timer #2 (Quarter-Hour)" title="Direct link to Check Timer #2 (Quarter-Hour)" translate="no"></a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Watch coordinator logs:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">&quot;Updated 60 time-sensitive entities at quarter-hour boundary&quot;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Normal</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">&quot;Midnight turnover detected (Timer #2)&quot;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Turnover</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="check-timer-3-minute">Check Timer #3 (Minute)<a href="#check-timer-3-minute" class="hash-link" aria-label="Direct link to Check Timer #3 (Minute)" title="Direct link to Check Timer #3 (Minute)" translate="no"></a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Watch coordinator logs:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">&quot;Updated 10 minute-update entities&quot;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Every minute</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="common-issues">Common Issues<a href="#common-issues" class="hash-link" aria-label="Direct link to Common Issues" title="Direct link to Common Issues" translate="no"></a></h3>
<ol>
<li class="">
<p><strong>Timer #2 not triggering:</strong></p>
<ul>
<li class="">Check: <code>schedule_quarter_hour_refresh()</code> called in <code>__init__</code>?</li>
<li class="">Check: <code>_quarter_hour_timer_cancel</code> properly stored?</li>
</ul>
</li>
<li class="">
<p><strong>Double updates at midnight:</strong></p>
<ul>
<li class="">Should NOT happen (atomic coordination)</li>
<li class="">Check: Both timers use same date comparison logic?</li>
</ul>
</li>
<li class="">
<p><strong>API overload:</strong></p>
<ul>
<li class="">Check: Random delay working? (0-30s jitter on tomorrow check)</li>
<li class="">Check: Cache validation logic correct?</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="related-documentation">Related Documentation<a href="#related-documentation" class="hash-link" aria-label="Direct link to Related Documentation" title="Direct link to Related Documentation" translate="no"></a></h2>
<ul>
<li class=""><strong><a class="" href="/hass.tibber_prices/developer/architecture">Architecture</a></strong> - Overall system design, data flow</li>
<li class=""><strong><a class="" href="/hass.tibber_prices/developer/caching-strategy">Caching Strategy</a></strong> - Cache lifetimes, invalidation, midnight turnover</li>
<li class=""><strong><a href="https://github.com/jpawlowski/hass.tibber_prices/blob/v0.20.0/AGENTS.md" target="_blank" rel="noopener noreferrer" class="">AGENTS.md</a></strong> - Complete reference for AI development</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary">Summary<a href="#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no"></a></h2>
<p><strong>Three independent timers:</strong></p>
<ol>
<li class=""><strong>Timer #1</strong> (HA built-in, 15 min, unsynchronized) → Data fetching (when needed)</li>
<li class=""><strong>Timer #2</strong> (Custom, :00/:15/:30/:45) → Entity state updates (always)</li>
<li class=""><strong>Timer #3</strong> (Custom, every minute) → Countdown/progress (always)</li>
</ol>
<p><strong>Key insights:</strong></p>
<ul>
<li class="">Timer #1 unsynchronized = good (load distribution on API)</li>
<li class="">Timer #2 synchronized = good (user sees correct data immediately)</li>
<li class="">Timer #3 synchronized = good (smooth countdown UX)</li>
<li class="">All three coordinate gracefully (atomic midnight checks, no conflicts)</li>
</ul>
<p><strong>&quot;Listener&quot; terminology:</strong></p>
<ul>
<li class="">Timer = mechanism that triggers</li>
<li class="">Listener = callback that gets called</li>
<li class="">Observer pattern = entities register, coordinator notifies</li>
</ul></div><footer class="theme-doc-footer docusaurus-mt-lg"><div class="row margin-top--sm theme-doc-footer-edit-meta-row"><div class="col noPrint_WFHX"><a href="https://github.com/jpawlowski/hass.tibber_prices/tree/main/docs/developer/docs/timer-architecture.md" target="_blank" rel="noopener noreferrer" class="theme-edit-this-page"><svg fill="currentColor" height="20" width="20" viewBox="0 0 40 40" class="iconEdit_Z9Sw" aria-hidden="true"><g><path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"></path></g></svg>Edit this page</a></div><div class="col lastUpdated_JAkA"><span class="theme-last-updated">Last updated<!-- --> on <b><time datetime="2025-12-06T01:37:06.000Z" itemprop="dateModified">Dec 6, 2025</time></b></span></div></div></footer></article><nav class="docusaurus-mt-lg pagination-nav" aria-label="Docs pages"><a class="pagination-nav__link pagination-nav__link--prev" href="/hass.tibber_prices/developer/architecture"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">Architecture</div></a><a class="pagination-nav__link pagination-nav__link--next" href="/hass.tibber_prices/developer/caching-strategy"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">Caching Strategy</div></a></nav></div></div><div class="col col--3"><div class="tableOfContents_bqdL thin-scrollbar theme-doc-toc-desktop"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#overview" class="table-of-contents__link toc-highlight">Overview</a></li><li><a href="#timer-1-dataupdatecoordinator-ha-built-in" class="table-of-contents__link toc-highlight">Timer #1: DataUpdateCoordinator (HA Built-in)</a></li><li><a href="#timer-2-quarter-hour-refresh-custom" class="table-of-contents__link toc-highlight">Timer #2: Quarter-Hour Refresh (Custom)</a></li><li><a href="#timer-3-minute-refresh-custom" class="table-of-contents__link toc-highlight">Timer #3: Minute Refresh (Custom)</a></li><li><a href="#listener-pattern-pythonha-terminology" class="table-of-contents__link toc-highlight">Listener Pattern (Python/HA Terminology)</a></li><li><a href="#timer-coordination-scenarios" class="table-of-contents__link toc-highlight">Timer Coordination Scenarios</a><ul><li><a href="#scenario-1-normal-operation-no-midnight" class="table-of-contents__link toc-highlight">Scenario 1: Normal Operation (No Midnight)</a></li><li><a href="#scenario-2-midnight-turnover" class="table-of-contents__link toc-highlight">Scenario 2: Midnight Turnover</a></li><li><a href="#scenario-3-tomorrow-data-check-after-1300" class="table-of-contents__link toc-highlight">Scenario 3: Tomorrow Data Check (After 13:00)</a></li></ul></li><li><a href="#why-we-keep-has-timer-timer-1" class="table-of-contents__link toc-highlight">Why We Keep HA&#39;s Timer (Timer #1)</a><ul><li><a href="#reason-1-load-distribution-on-tibber-api" class="table-of-contents__link toc-highlight">Reason 1: Load Distribution on Tibber API</a></li><li><a href="#reason-2-what-timer-1-actually-checks" class="table-of-contents__link toc-highlight">Reason 2: What Timer #1 Actually Checks</a></li><li><a href="#reason-3-ha-integration-best-practices" class="table-of-contents__link toc-highlight">Reason 3: HA Integration Best Practices</a></li><li><a href="#what-we-do-synchronize" class="table-of-contents__link toc-highlight">What We DO Synchronize</a></li></ul></li><li><a href="#performance-characteristics" class="table-of-contents__link toc-highlight">Performance Characteristics</a><ul><li><a href="#timer-1-dataupdatecoordinator" class="table-of-contents__link toc-highlight">Timer #1 (DataUpdateCoordinator)</a></li><li><a href="#timer-2-quarter-hour-refresh" class="table-of-contents__link toc-highlight">Timer #2 (Quarter-Hour Refresh)</a></li><li><a href="#timer-3-minute-refresh" class="table-of-contents__link toc-highlight">Timer #3 (Minute Refresh)</a></li></ul></li><li><a href="#debugging-timer-issues" class="table-of-contents__link toc-highlight">Debugging Timer Issues</a><ul><li><a href="#check-timer-1-ha-coordinator" class="table-of-contents__link toc-highlight">Check Timer #1 (HA Coordinator)</a></li><li><a href="#check-timer-2-quarter-hour" class="table-of-contents__link toc-highlight">Check Timer #2 (Quarter-Hour)</a></li><li><a href="#check-timer-3-minute" class="table-of-contents__link toc-highlight">Check Timer #3 (Minute)</a></li><li><a href="#common-issues" class="table-of-contents__link toc-highlight">Common Issues</a></li></ul></li><li><a href="#related-documentation" class="table-of-contents__link toc-highlight">Related Documentation</a></li><li><a href="#summary" class="table-of-contents__link toc-highlight">Summary</a></li></ul></div></div></div></div></main></div></div></div><footer class="theme-layout-footer footer footer--dark"><div class="container container-fluid"><div class="row footer__links"><div class="theme-layout-footer-column col footer__col"><div class="footer__title">Documentation</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://jpawlowski.github.io/hass.tibber_prices/user/" target="_blank" rel="noopener noreferrer" class="footer__link-item">User Guide<svg width="13.5" height="13.5" aria-label="(opens in new tab)" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a class="footer__link-item" href="/hass.tibber_prices/developer/intro">Developer Guide</a></li></ul></div><div class="theme-layout-footer-column col footer__col"><div class="footer__title">Community</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://github.com/jpawlowski/hass.tibber_prices/issues" target="_blank" rel="noopener noreferrer" class="footer__link-item">GitHub Issues<svg width="13.5" height="13.5" aria-label="(opens in new tab)" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://community.home-assistant.io/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Home Assistant Community<svg width="13.5" height="13.5" aria-label="(opens in new tab)" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li></ul></div><div class="theme-layout-footer-column col footer__col"><div class="footer__title">More</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://github.com/jpawlowski/hass.tibber_prices" target="_blank" rel="noopener noreferrer" class="footer__link-item">GitHub<svg width="13.5" height="13.5" aria-label="(opens in new tab)" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://github.com/jpawlowski/hass.tibber_prices/releases" target="_blank" rel="noopener noreferrer" class="footer__link-item">Release Notes<svg width="13.5" height="13.5" aria-label="(opens in new tab)" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li></ul></div></div><div class="footer__bottom text--center"><div class="footer__copyright">Not affiliated with Tibber AS. Community-maintained custom integration. Built with Docusaurus.</div></div></div></footer></div>
</body>
</html>