mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 05:13:40 +00:00
278 lines
No EOL
82 KiB
HTML
278 lines
No EOL
82 KiB
HTML
<!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&family=Space+Grotesk:wght@500;600;700&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'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">></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">"tomorrow_check"</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 "thundering herd" 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'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'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">></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 ("in 15 minutes")</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 "current interval" (e.g., <code>current_interval_price</code>, <code>next_interval_price</code>)</li>
|
||
<li class="">Binary sensors that check "is now in period?" (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's built-in coordinator doesn'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">></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> "Sind Timer für dich eigentlich 'Listener'?"</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'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'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'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's Timer (Timer #1)" title="Direct link to Why We Keep HA's Timer (Timer #1)" translate="no"></a></h2>
|
||
<p><strong>Your question:</strong> "warum wir den HA timer trotzdem weiter benutzen, da er ja für uns unkontrollierte aktualisierte änderungen triggert"</p>
|
||
<p><strong>Answer:</strong> You're correct that it's not synchronized, but that'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="">❌ "Thundering herd" problem</li>
|
||
</ul>
|
||
<p>With HA'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">></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">"tomorrow_check"</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">"cached"</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"><</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">"cached"</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">"update_needed"</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'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">"Fetching data from API (reason: tomorrow_check)"</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">"Using cached data (no update needed)"</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">"Midnight turnover detected (Timer #1)"</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">"Updated 60 time-sensitive entities at quarter-hour boundary"</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">"Midnight turnover detected (Timer #2)"</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">"Updated 10 minute-update entities"</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>"Listener" 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'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> |