2020-09-04 13:02:20 -05:00
// base weather display class
2022-12-14 11:20:25 -06:00
import STATUS , { calcStatusClass , statusClasses } from './status.mjs' ;
2022-11-22 16:29:10 -06:00
import { DateTime } from '../vendor/auto/luxon.mjs' ;
2022-12-06 16:14:56 -06:00
import {
2022-12-21 15:17:50 -06:00
msg , displayNavMessage , isPlaying , updateStatus , timeZone ,
2022-12-06 16:14:56 -06:00
} from './navigation.mjs' ;
2025-11-10 12:54:54 -06:00
import { parseQueryString } from './utils/setting.mjs' ;
2024-07-29 23:12:47 -05:00
import settings from './settings.mjs' ;
2025-05-29 08:30:01 -05:00
import { elemForEach } from './utils/elem.mjs' ;
2025-06-24 22:56:08 -04:00
import { debugFlag } from './utils/debug.mjs' ;
2020-09-04 13:02:20 -05:00
class WeatherDisplay {
2022-11-21 21:50:22 -06:00
constructor ( navId , elemId , name , defaultEnabled ) {
2022-12-06 16:14:56 -06:00
// navId is used in messaging and sort order
2020-09-04 13:02:20 -05:00
this . navId = navId ;
this . elemId = undefined ;
this . data = undefined ;
this . loadingStatus = STATUS . loading ;
2020-10-29 16:44:28 -05:00
this . name = name ? ? elemId ;
2020-10-20 20:04:51 -05:00
this . getDataCallbacks = [ ] ;
2022-12-12 13:53:33 -06:00
this . stillWaitingCallbacks = [ ] ;
2022-12-06 16:14:56 -06:00
this . defaultEnabled = defaultEnabled ;
this . okToDrawCurrentConditions = true ;
this . okToDrawCurrentDateTime = true ;
2022-12-14 16:28:33 -06:00
this . showOnProgress = true ;
2025-04-02 11:10:58 -05:00
this . autoRefreshHandle = null ;
2020-09-04 13:02:20 -05:00
// default navigation timing
this . timing = {
totalScreens : 1 ,
2025-06-24 22:56:08 -04:00
baseDelay : 9000 , // 9 seconds
2020-09-04 13:02:20 -05:00
delay : 1 , // 1*1second = 1 second total display time
} ;
this . navBaseCount = 0 ;
2020-09-09 14:29:03 -05:00
this . screenIndex = - 1 ; // special starting condition
2020-09-04 13:02:20 -05:00
2022-11-21 21:50:22 -06:00
// store elemId once
this . storeElemId ( elemId ) ;
2020-09-18 14:59:58 -05:00
2022-12-14 13:08:49 -06:00
if ( this . isEnabled ) {
2020-09-18 11:24:45 -05:00
this . setStatus ( STATUS . loading ) ;
} else {
this . setStatus ( STATUS . disabled ) ;
}
2020-09-18 16:31:50 -05:00
this . startNavCount ( ) ;
2022-07-29 16:12:42 -05:00
// get any templates
2022-12-07 10:53:18 -06:00
document . addEventListener ( 'DOMContentLoaded' , ( ) => {
this . loadTemplates ( ) ;
} ) ;
2020-09-04 13:02:20 -05:00
}
2022-12-06 16:14:56 -06:00
generateCheckbox ( defaultEnabled = true ) {
// no checkbox if progress
if ( this . elemId === 'progress' ) return false ;
2024-04-12 00:03:21 -05:00
// get url provided state
const urlValue = parseQueryString ( ) ? . [ ` ${ this . elemId } -checkbox ` ] ;
let urlState ;
if ( urlValue !== undefined ) {
urlState = urlValue === 'true' ;
}
// get the saved status of the checkbox, but defer to a value set in the url
let savedStatus = urlState ? ? window . localStorage . getItem ( ` display-enabled: ${ this . elemId } ` ) ;
2020-09-18 11:24:45 -05:00
if ( savedStatus === null ) savedStatus = defaultEnabled ;
2023-01-06 14:39:39 -06:00
this . isEnabled = ! ! ( ( savedStatus === 'true' || savedStatus === true ) ) ;
2020-09-18 11:24:45 -05:00
2020-09-18 14:59:58 -05:00
// refresh (or initially store the state of the checkbox)
2022-12-14 13:08:49 -06:00
window . localStorage . setItem ( ` display-enabled: ${ this . elemId } ` , this . isEnabled ) ;
2020-09-18 14:59:58 -05:00
2020-09-18 11:24:45 -05:00
// create a checkbox in the selected displays area
2022-12-14 11:20:25 -06:00
const label = document . createElement ( 'label' ) ;
label . for = ` ${ this . elemId } -checkbox ` ;
label . id = ` ${ this . elemId } -label ` ;
const checkbox = document . createElement ( 'input' ) ;
checkbox . type = 'checkbox' ;
checkbox . value = true ;
checkbox . id = ` ${ this . elemId } -checkbox ` ;
checkbox . name = ` ${ this . elemId } -checkbox ` ;
2022-12-14 13:08:49 -06:00
checkbox . checked = this . isEnabled ;
2022-12-14 11:20:25 -06:00
checkbox . addEventListener ( 'change' , ( e ) => this . checkboxChange ( e ) ) ;
const span = document . createElement ( 'span' ) ;
span . innerHTML = this . name ;
2022-12-14 16:28:33 -06:00
const alert = document . createElement ( 'span' ) ;
alert . innerHTML = '!!!' ;
alert . classList . add ( 'alert' ) ;
2022-12-14 11:20:25 -06:00
2022-12-14 16:28:33 -06:00
label . append ( checkbox , span , alert ) ;
2022-12-14 11:20:25 -06:00
this . checkbox = label ;
return label ;
2020-09-18 11:24:45 -05:00
}
checkboxChange ( e ) {
2020-09-18 14:59:58 -05:00
// update the state
2022-12-14 13:08:49 -06:00
this . isEnabled = e . target . checked ;
2020-09-18 14:59:58 -05:00
// store the value for the next load
2022-12-14 13:08:49 -06:00
window . localStorage . setItem ( ` display-enabled: ${ this . elemId } ` , this . isEnabled ) ;
2020-09-18 14:59:58 -05:00
// calling get data will update the status and actually get the data if we're set to enabled
this . getData ( ) ;
2020-09-18 11:24:45 -05:00
}
2020-09-04 13:02:20 -05:00
// set data status and send update to navigation module
setStatus ( value ) {
this . status = value ;
2022-12-06 16:14:56 -06:00
updateStatus ( {
2020-09-04 13:02:20 -05:00
id : this . navId ,
status : this . status ,
} ) ;
2022-12-14 11:20:25 -06:00
// update coloring of checkbox at bottom of page
if ( ! this . checkbox ) return ;
this . checkbox . classList . remove ( ... statusClasses ) ;
this . checkbox . classList . add ( calcStatusClass ( value ) ) ;
2020-09-04 13:02:20 -05:00
}
get status ( ) {
return this . loadingStatus ;
}
set status ( state ) {
this . loadingStatus = state ;
}
2022-11-21 21:50:22 -06:00
storeElemId ( elemId ) {
2020-09-04 13:02:20 -05:00
// only create it once
if ( this . elemId ) return ;
this . elemId = elemId ;
}
// get necessary data for this display
2025-04-02 11:10:58 -05:00
getData ( weatherParameters , refresh ) {
2025-04-02 22:34:59 -05:00
// refresh doesn't delete existing data, and is reused if the silent refresh fails
2025-04-02 11:10:58 -05:00
if ( ! refresh ) {
this . data = undefined ;
2025-05-02 23:22:00 -05:00
// clear any refresh timers
this . clearAutoReload ( ) ;
2025-04-02 11:10:58 -05:00
}
2020-09-18 11:24:45 -05:00
2020-09-25 09:55:29 -05:00
// store weatherParameters locally in case we need them later
if ( weatherParameters ) this . weatherParameters = weatherParameters ;
// set status
2022-12-14 13:08:49 -06:00
if ( this . isEnabled ) {
2020-09-18 11:24:45 -05:00
this . setStatus ( STATUS . loading ) ;
} else {
this . setStatus ( STATUS . disabled ) ;
return false ;
}
2020-09-04 15:46:31 -05:00
2025-05-02 23:22:00 -05:00
// set up auto reload if necessary
this . setAutoReload ( ) ;
2025-04-02 11:10:58 -05:00
2020-09-09 14:29:03 -05:00
// recalculate navigation timing (in case it was modified in the constructor)
this . calcNavTiming ( ) ;
2020-09-18 11:24:45 -05:00
return true ;
2020-09-04 13:02:20 -05:00
}
2020-10-20 20:04:51 -05:00
// return any data requested before it was available
getDataCallback ( ) {
// call each callback
2020-10-29 16:44:28 -05:00
this . getDataCallbacks . forEach ( ( fxn ) => fxn ( this . data ) ) ;
2020-10-20 20:04:51 -05:00
// clear the callbacks
this . getDataCallbacks = [ ] ;
}
2020-09-04 13:02:20 -05:00
drawCanvas ( ) {
2020-09-18 14:59:58 -05:00
// clean up the first-run flag in screen index
2020-10-29 16:44:28 -05:00
if ( this . screenIndex < 0 ) this . screenIndex = 0 ;
2023-06-27 16:06:45 -04:00
if ( this . okToDrawCurrentDateTime ) this . drawCurrentDateTime ( ) ;
2025-06-02 14:48:53 -05:00
if ( this . okToDrawCurrentConditions ) postMessage ( { type : 'current-weather-scroll' , method : 'start' } ) ;
2025-09-24 22:27:31 -05:00
if ( this . okToDrawCurrentConditions === false ) postMessage ( { type : 'current-weather-scroll' , method : 'hide' } ) ;
2020-09-04 13:02:20 -05:00
}
finishDraw ( ) {
2022-12-06 16:14:56 -06:00
// draw date and time
if ( this . okToDrawCurrentDateTime ) {
this . drawCurrentDateTime ( ) ;
2020-09-04 13:02:20 -05:00
// auto clock refresh
if ( ! this . dateTimeInterval ) {
2023-06-27 16:06:45 -04:00
// only draw if canvas is active to conserve battery
2024-05-19 22:39:15 -05:00
this . dateTimeInterval = setInterval ( ( ) => this . active && this . drawCurrentDateTime ( ) , 100 ) ;
2020-09-04 13:02:20 -05:00
}
}
}
2022-07-29 16:12:42 -05:00
drawCurrentDateTime ( ) {
2020-09-04 13:02:20 -05:00
// Get the current date and time.
2022-12-21 15:17:50 -06:00
const now = DateTime . local ( ) . setZone ( timeZone ( ) ) ;
2020-09-04 13:02:20 -05:00
2020-10-29 16:44:28 -05:00
// time = "11:35:08 PM";
const time = now . toLocaleString ( DateTime . TIME _WITH _SECONDS ) . padStart ( 11 , ' ' ) ;
2022-12-21 15:17:50 -06:00
const date = now . toFormat ( ' ccc LLL ' ) + now . day . toString ( ) . padStart ( 2 , ' ' ) ;
const dateElem = this . elem . querySelector ( '.date-time.date' ) ;
const timeElem = this . elem . querySelector ( '.date-time.time' ) ;
2020-09-04 13:02:20 -05:00
2022-12-21 15:17:50 -06:00
if ( timeElem && this . lastTime !== time ) {
timeElem . innerHTML = time . toUpperCase ( ) ;
2020-09-04 13:02:20 -05:00
}
2022-07-29 16:12:42 -05:00
this . lastTime = time ;
2020-09-04 13:02:20 -05:00
2022-12-21 15:17:50 -06:00
if ( dateElem && this . lastDate !== date ) {
dateElem . innerHTML = date . toUpperCase ( ) ;
2020-09-04 13:02:20 -05:00
}
2022-07-29 16:12:42 -05:00
this . lastDate = date ;
2020-09-04 13:02:20 -05:00
}
// show/hide the canvas and start/stop the navigation timer
showCanvas ( navCmd ) {
2020-09-18 16:31:50 -05:00
// reset timing if enabled
2020-09-04 13:02:20 -05:00
// if a nav command is present call it to set the screen index
2022-12-06 16:14:56 -06:00
if ( navCmd === msg . command . firstFrame ) this . navNext ( navCmd ) ;
if ( navCmd === msg . command . lastFrame ) this . navPrev ( navCmd ) ;
2020-09-04 13:02:20 -05:00
2022-07-29 16:12:42 -05:00
this . startNavCount ( ) ;
2022-11-21 21:50:22 -06:00
this . elem . classList . add ( 'show' ) ;
2023-12-19 23:43:37 -06:00
document . querySelector ( '#divTwc' ) . classList . add ( this . elemId ) ;
2020-09-04 13:02:20 -05:00
}
2020-10-29 16:44:28 -05:00
2020-09-04 13:02:20 -05:00
hideCanvas ( ) {
2020-09-23 11:03:58 -05:00
this . resetNavBaseCount ( ) ;
2022-11-21 21:50:22 -06:00
this . elem . classList . remove ( 'show' ) ;
2023-12-19 23:43:37 -06:00
// used to change backgrounds for widescreen
document . querySelector ( '#divTwc' ) . classList . remove ( this . elemId ) ;
2020-09-04 13:02:20 -05:00
}
2022-12-14 13:08:49 -06:00
get active ( ) {
2022-08-04 11:07:35 -05:00
return this . elem . offsetHeight !== 0 ;
2020-09-04 13:02:20 -05:00
}
2022-12-14 13:08:49 -06:00
get enabled ( ) {
return this . isEnabled ;
2020-09-17 16:34:38 -05:00
}
2020-09-04 13:02:20 -05:00
// navigation timings
// totalScreens = total number of screens that are available
// baseDelay = ms to delay before re-evaluating screenIndex
// delay: three options
// integer = each screen will display for this number of baseDelays
// [integer, integer, ...] = screenIndex 0 displays for integer[0]*baseDelay, etc.
// [{time, si}, ...] = time as above, si is specific screen index to display during this interval
// if the array forms are used totalScreens is overwritten by the size of the array
navBaseTime ( ) {
// see if play is active and screen is active
2022-12-14 13:08:49 -06:00
if ( ! isPlaying ( ) || ! this . active ) return ;
2020-09-04 13:02:20 -05:00
// increment the base count
2020-10-29 16:44:28 -05:00
this . navBaseCount += 1 ;
2020-09-04 13:02:20 -05:00
2025-06-24 22:56:08 -04:00
if ( debugFlag ( 'weatherdisplay' ) ) {
const now = Date . now ( ) ;
if ( ! this . timingDebug ) {
this . timingDebug = { startTime : now , lastTransition : now , baseCountLog : [ ] } ;
if ( this . navBaseCount !== 1 ) {
console . log ( ` ⏱️ [ ${ this . constructor . name } ] Starting at baseCount ${ this . navBaseCount } ` ) ;
}
}
const elapsed = now - this . timingDebug . lastTransition ;
this . timingDebug . baseCountLog . push ( {
baseCount : this . navBaseCount ,
timestamp : now ,
elapsedMs : elapsed ,
screenIndex : this . screenIndex ,
} ) ;
}
2020-09-04 15:46:31 -05:00
// call base count change if available for this function
if ( this . baseCountChange ) this . baseCountChange ( this . navBaseCount ) ;
2020-09-04 13:02:20 -05:00
2020-09-09 14:29:03 -05:00
// handle base count/screen index changes
this . updateScreenFromBaseCount ( ) ;
}
2020-09-17 16:34:38 -05:00
async updateScreenFromBaseCount ( ) {
2020-09-09 14:29:03 -05:00
// get the next screen index
2020-10-29 16:44:28 -05:00
const nextScreenIndex = this . screenIndexFromBaseCount ( ) ;
2020-09-09 14:29:03 -05:00
// special cases for first and last frame
// must compare with false as nextScreenIndex could be 0 which is valid
if ( nextScreenIndex === false ) {
2022-12-06 16:14:56 -06:00
this . sendNavDisplayMessage ( msg . response . next ) ;
2020-09-04 13:02:20 -05:00
return ;
}
2020-09-04 15:46:31 -05:00
2020-09-09 14:29:03 -05:00
// test for no change and exit early
if ( nextScreenIndex === this . screenIndex ) return ;
2025-06-24 22:56:08 -04:00
if ( debugFlag ( 'weatherdisplay' ) && this . timingDebug ) {
const now = Date . now ( ) ;
const elapsed = now - this . timingDebug . lastTransition ;
this . timingDebug . lastTransition = now ;
console . log ( ` ⏱️ [ ${ this . constructor . name } ] Screen Transition: ${ this . screenIndex } → ${ nextScreenIndex === - 1 ? 0 : nextScreenIndex } , baseCount= ${ this . navBaseCount } , duration= ${ elapsed } ms ` ) ;
if ( this . screenIndex !== - 1 && this . timing && this . timing . delay !== undefined ) { // Skip expected duration calculation for the first transition (screenIndex -1 → 0)
let expectedMs ;
if ( Array . isArray ( this . timing . delay ) ) { // Array-based timing (different delay per screen/period)
// Find the timing index for the screen we just LEFT (the one that just finished displaying)
// For transition "X → Y", we want the timing for screen X (which is this.screenIndex before it gets updated)
const timingIndex = this . screenIndex ;
if ( timingIndex >= 0 && timingIndex < this . timing . delay . length ) { // Handle both simple number delays and object delays with time property (radar)
const delayValue = typeof this . timing . delay [ timingIndex ] === 'object' ? this . timing . delay [ timingIndex ] . time : this . timing . delay [ timingIndex ] ;
expectedMs = this . timing . baseDelay * delayValue * ( settings ? . speed ? . value || 1 ) ;
}
} else if ( typeof this . timing . delay === 'number' ) { // Simple number-based timing (same delay for all screens)
expectedMs = this . timing . baseDelay * this . timing . delay * ( settings ? . speed ? . value || 1 ) ;
}
if ( expectedMs !== undefined ) {
console . log ( ` ⏱️ [ ${ this . constructor . name } ] Expected duration: ${ expectedMs } ms, Actual: ${ elapsed } ms, Diff: ${ elapsed - expectedMs } ms ` ) ;
}
}
}
2020-09-17 16:34:38 -05:00
// test for -1 (no screen displayed yet)
2023-01-06 14:39:39 -06:00
this . screenIndex = nextScreenIndex === - 1 ? 0 : nextScreenIndex ;
2020-09-09 14:29:03 -05:00
// call the appropriate screen index change method
2023-01-06 14:39:39 -06:00
if ( this . screenIndexChange ) {
2020-09-09 14:29:03 -05:00
this . screenIndexChange ( this . screenIndex ) ;
2023-01-06 14:39:39 -06:00
} else {
await this . drawCanvas ( ) ;
2020-09-09 14:29:03 -05:00
}
2022-12-14 16:28:33 -06:00
this . showCanvas ( ) ;
2020-09-09 14:29:03 -05:00
}
// take the three timing formats shown above and break them into arrays for consistent usage in navigation functions
// this.timing.fullDelay = [end of screen index 0 in base counts, end of screen index 1...]
// this.timing.screenIndexes = [screen index to use during this.timing.fullDelay[0], screen index to use during this.timing.fullDelay[1], ...]
calcNavTiming ( ) {
2020-09-23 11:03:58 -05:00
if ( this . timing === false ) return ;
2020-09-09 14:29:03 -05:00
// update total screens
if ( Array . isArray ( this . timing . delay ) ) this . timing . totalScreens = this . timing . delay . length ;
// if the delay is provided as a single value, expand it to a series of the same value
let intermediateDelay = [ ] ;
if ( typeof this . timing . delay === 'number' ) {
2020-10-29 16:44:28 -05:00
for ( let i = 0 ; i < this . timing . totalScreens ; i += 1 ) intermediateDelay . push ( this . timing . delay ) ;
2020-09-09 14:29:03 -05:00
} else {
// map just the delays to the intermediate block
2020-10-29 16:44:28 -05:00
intermediateDelay = this . timing . delay . map ( ( delay ) => {
2020-09-09 14:29:03 -05:00
if ( typeof delay === 'object' ) return delay . time ;
return delay ;
} ) ;
}
// calculate the cumulative end point of each delay
let sum = 0 ;
2020-10-29 16:44:28 -05:00
this . timing . fullDelay = intermediateDelay . map ( ( val ) => {
2020-09-09 14:29:03 -05:00
const calc = sum + val ;
sum += val ;
return calc ;
} ) ;
// generate a list of screen either sequentially if not provided in an object or from the object
if ( Array . isArray ( this . timing . delay ) && typeof this . timing . delay [ 0 ] === 'object' ) {
// extract screen indexes from objects
2020-10-29 16:44:28 -05:00
this . timing . screenIndexes = this . timing . delay . map ( ( delay ) => delay . si ) ;
2020-09-09 14:29:03 -05:00
} else {
// generate sequential screen indexes
this . timing . screenIndexes = [ ] ;
2020-10-29 16:44:28 -05:00
for ( let i = 0 ; i < this . timing . totalScreens ; i += 1 ) this . timing . screenIndexes . push ( i ) ;
2020-09-04 15:46:31 -05:00
}
2020-09-04 13:02:20 -05:00
}
// navigate to next screen
navNext ( command ) {
// check for special 'first frame' command
2022-12-06 16:14:56 -06:00
if ( command === msg . command . firstFrame ) {
2020-09-04 13:02:20 -05:00
this . resetNavBaseCount ( ) ;
2020-09-04 15:46:31 -05:00
} else {
2020-09-09 14:29:03 -05:00
// set the base count to the next available frame
2020-10-29 16:44:28 -05:00
const newBaseCount = this . timing . fullDelay . find ( ( delay ) => delay > this . navBaseCount ) ;
2020-09-09 14:29:03 -05:00
this . navBaseCount = newBaseCount ;
2020-09-04 15:46:31 -05:00
}
2020-09-09 14:29:03 -05:00
this . updateScreenFromBaseCount ( ) ;
2020-09-04 13:02:20 -05:00
}
// navigate to previous screen
navPrev ( command ) {
// check for special 'last frame' command
2022-12-06 16:14:56 -06:00
if ( command === msg . command . lastFrame ) {
2020-10-29 16:44:28 -05:00
this . navBaseCount = this . timing . fullDelay [ this . timing . totalScreens - 1 ] - 1 ;
2020-09-04 15:46:31 -05:00
} else {
2020-09-09 14:29:03 -05:00
// find the highest fullDelay that is less than the current base count
const newBaseCount = this . timing . fullDelay . reduce ( ( acc , delay ) => {
if ( delay < this . navBaseCount ) return delay ;
return acc ;
2020-10-29 16:44:28 -05:00
} , 0 ) ;
2020-09-09 14:29:03 -05:00
// if the new base count is zero then we're already at the first screen
if ( newBaseCount === 0 && this . navBaseCount === 0 ) {
2022-12-06 16:14:56 -06:00
this . sendNavDisplayMessage ( msg . response . previous ) ;
2020-09-09 14:29:03 -05:00
return ;
}
this . navBaseCount = newBaseCount ;
2020-09-04 15:46:31 -05:00
}
2020-09-09 14:29:03 -05:00
this . updateScreenFromBaseCount ( ) ;
2020-09-04 15:46:31 -05:00
}
2020-09-09 14:29:03 -05:00
// get the screen index for the current base count, returns false if past end of timing array (go to next screen, stop timing)
screenIndexFromBaseCount ( ) {
2020-09-25 13:25:12 -05:00
// test for timing enabled
if ( ! this . timing ) return 0 ;
2022-12-14 16:28:33 -06:00
if ( this . timing . totalScreens === 0 ) return false ;
2020-09-09 14:29:03 -05:00
// find the first timing in the timing array that is greater than the base count
2020-09-25 13:25:12 -05:00
if ( this . timing && ! this . timing . fullDelay ) this . calcNavTiming ( ) ;
2020-10-29 16:44:28 -05:00
const timingIndex = this . timing . fullDelay . findIndex ( ( delay ) => delay > this . navBaseCount ) ;
2020-09-09 14:29:03 -05:00
if ( timingIndex === - 1 ) return false ;
return this . timing . screenIndexes [ timingIndex ] ;
2020-09-04 13:02:20 -05:00
}
// start and stop base counter
2020-09-18 16:31:50 -05:00
startNavCount ( ) {
2025-06-24 22:56:08 -04:00
if ( ! this . navInterval ) {
if ( debugFlag ( 'weatherdisplay' ) ) {
console . log ( ` ⏱️ [ ${ this . constructor . name } ] Starting navigation: ` , {
baseDelay : this . timing . baseDelay ,
intervalMs : this . timing . baseDelay * ( settings ? . speed ? . value || 1 ) ,
totalScreens : this . timing . totalScreens ,
delayArray : this . timing . delay ,
fullDelayArray : this . timing . fullDelay ,
screenIndexes : this . timing . screenIndexes ,
} ) ;
}
this . navInterval = setInterval ( ( ) => this . navBaseTime ( ) , this . timing . baseDelay * settings . speed . value ) ;
}
2020-09-04 13:02:20 -05:00
}
2020-09-23 11:03:58 -05:00
2020-09-04 13:02:20 -05:00
resetNavBaseCount ( ) {
2025-06-24 22:56:08 -04:00
if ( debugFlag ( 'weatherdisplay' ) && this . timingDebug && this . timingDebug . baseCountLog . length > 1 ) {
const totalDuration = this . timingDebug . baseCountLog [ this . timingDebug . baseCountLog . length - 1 ] . timestamp - this . timingDebug . baseCountLog [ 0 ] . timestamp ;
const avgInterval = totalDuration / ( this . timingDebug . baseCountLog . length - 1 ) ;
console . log ( ` ⏱️ [ ${ this . constructor . name } ] Total duration: ${ totalDuration } ms, Avg base interval: ${ avgInterval . toFixed ( 1 ) } ms, Base count range: ${ this . timingDebug . baseCountLog [ 0 ] . baseCount } - ${ this . timingDebug . baseCountLog [ this . timingDebug . baseCountLog . length - 1 ] . baseCount } ` ) ;
this . timingDebug = null ;
}
2020-09-04 13:02:20 -05:00
this . navBaseCount = 0 ;
2020-09-09 14:29:03 -05:00
this . screenIndex = - 1 ;
2020-09-23 11:03:58 -05:00
// reset the timing so we don't short-change the first screen
if ( this . navInterval ) {
clearInterval ( this . navInterval ) ;
this . navInterval = undefined ;
}
2020-09-04 13:02:20 -05:00
}
sendNavDisplayMessage ( message ) {
2022-12-06 16:14:56 -06:00
displayNavMessage ( {
2020-09-04 13:02:20 -05:00
id : this . navId ,
type : message ,
} ) ;
}
2022-07-29 16:12:42 -05:00
loadTemplates ( ) {
this . templates = { } ;
2023-01-06 14:39:39 -06:00
this . elem = document . querySelector ( ` # ${ this . elemId } -html ` ) ;
2022-08-05 12:05:14 -05:00
if ( ! this . elem ) return ;
2025-05-29 08:30:01 -05:00
elemForEach ( ` # ${ this . elemId } -html .template ` , ( template ) => {
2022-08-05 12:05:14 -05:00
const className = template . classList [ 0 ] ;
const node = template . cloneNode ( true ) ;
node . classList . remove ( 'template' ) ;
this . templates [ className ] = node ;
template . remove ( ) ;
} ) ;
2022-07-29 16:12:42 -05:00
}
2022-08-02 21:39:27 -05:00
fillTemplate ( name , fillValues ) {
// get the template
const templateNode = this . templates [ name ] ;
if ( ! templateNode ) return false ;
// clone it
const template = templateNode . cloneNode ( true ) ;
Object . entries ( fillValues ) . forEach ( ( [ key , value ] ) => {
// get the specified element
const elem = template . querySelector ( ` . ${ key } ` ) ;
if ( ! elem ) return ;
// fill based on type provided
if ( typeof value === 'string' || typeof value === 'number' ) {
// string and number fill the first found selector
elem . innerHTML = value ;
} else if ( value ? . type === 'img' ) {
// fill the image source
elem . querySelector ( 'img' ) . src = value . src ;
2025-05-23 23:13:50 -05:00
} else if ( value ? . type === 'canvas' ) {
elem . append ( value . canvas ) ;
2022-08-02 21:39:27 -05:00
}
} ) ;
return template ;
}
2022-12-12 13:53:33 -06:00
// still waiting for data (retries triggered)
stillWaiting ( ) {
2022-12-14 13:08:49 -06:00
if ( this . isEnabled ) this . setStatus ( STATUS . retrying ) ;
2022-12-12 13:53:33 -06:00
// handle still waiting callbacks
this . stillWaitingCallbacks . forEach ( ( callback ) => callback ( ) ) ;
this . stillWaitingCallbacks = [ ] ;
}
2025-05-02 23:22:00 -05:00
clearAutoReload ( ) {
clearInterval ( this . autoRefreshHandle ) ;
this . autoRefreshHandle = null ;
}
setAutoReload ( ) {
2025-06-02 15:57:58 -05:00
// refresh time can be forced by the user (for hazards)
const refreshTime = this . refreshTime ? ? settings . refreshTime . value ;
2025-06-24 22:56:08 -04:00
this . autoRefreshHandle = this . autoRefreshHandle ? ? setInterval ( ( ) => this . getData ( this . weatherParameters , true ) , refreshTime ) ;
2025-05-02 23:22:00 -05:00
}
2020-10-29 16:44:28 -05:00
}
2022-11-22 16:29:10 -06:00
export default WeatherDisplay ;