From 30043f755abfd2ebe9b7f7f72e9e925152f48be2 Mon Sep 17 00:00:00 2001 From: Lani Aung Date: Thu, 12 Jun 2025 19:37:52 -0700 Subject: [PATCH] Now I need to style it --- src/styles.less | 12 ++- src/weather.ts | 200 ++++++++++++++++++++++++------------------------ www/styles.css | 11 ++- 3 files changed, 122 insertions(+), 101 deletions(-) diff --git a/src/styles.less b/src/styles.less index c60e6b1..ec59e34 100644 --- a/src/styles.less +++ b/src/styles.less @@ -8,14 +8,24 @@ body { #weather-panel { border-radius: 50px; box-shadow: #999a 0 0 25px; + color: #fff; font-family: Verdana, sans-serif; + font-weight: bold; height: 500px; left: calc(50% - 250px); padding: 40px; position: absolute; - text-shadow: #fff 0 0 5px, #fff 0 0 5px, #fff 0 0 5px, #fff 0 0 5px; + text-shadow: #000 1px 1px, + #000 -1px 1px, + #000 -1px -1px, + #000 1px -1px; top: calc(50% - 250px); width: 500px; z-index: 10; + + @supports (-webkit-text-stroke: 1px #000) { + -webkit-text-stroke: 1px #000; + -webkit-text-fill-color: #fff; + } } } \ No newline at end of file diff --git a/src/weather.ts b/src/weather.ts index b735a6c..3b0ef19 100644 --- a/src/weather.ts +++ b/src/weather.ts @@ -1,4 +1,5 @@ -import { fetchWeatherApi } from 'openmeteo' +import { fetchWeatherApi } from "openmeteo" +import { VariablesWithTime } from "@openmeteo/sdk/variables-with-time" export default class Weather { private readonly apiUrl = "https://api.open-meteo.com/v1/forecast" @@ -15,113 +16,124 @@ export default class Weather { this.options = new WeatherOptions(latitude ?? 47.61002138071677, longitude ?? -122.17906310779568) const me = this - setInterval(() => me.fetchWeather().then(() => me.setDisplay()), 60 * 1000) + setInterval(() => me.fetchWeather().then(() => me.setDisplay()), 60000) me.fetchWeather().then(() => me.setDisplay()) } async fetchWeather(): Promise { const me = this const parameters: any = { ...me.options } - let getData = false + if (!me.doFetchData(parameters)) return + const responses = await fetchWeatherApi(me.apiUrl, parameters) + for (const response of responses) { + const current = response.current() + const hourly = response.hourly() + const daily = response.daily() + + if (current != null) me.setCurrentData(current) + if (hourly != null) me.setHourlyData(hourly) + if (daily != null) me.setDailyData(daily) + } + } + + private doFetchData(parameters: any): boolean { + const me = this if (!me.lastFetched) { parameters["daily"] = me.dailyVariables parameters["hourly"] = me.hourlyVariables parameters["current"] = me.currentVariables me.lastFetched = new LastFetched() - getData = true + return true } else { - // Reasonable-ish timers? - if (me.lastFetched.minutesSinceCurrent() > 15) { - parameters["current"] = me.currentVariables - me.lastFetched.current = new Date() - getData = true + // Weather data is updated :00, :15, :30, :45 so wait at least a minute + if (me.lastFetched.current.getMinutes() % 15 < 5) { + const now = new Date() + // Make sure data is over 5 minutes old at least + if ((now.getTime() - me.lastFetched.current.getTime()) > 300000) { + parameters["current"] = me.currentVariables + me.lastFetched.current = new Date() + return true + } } + // Reasonable-ish timers? if (me.lastFetched.hoursSinceHourly() > 3) { parameters["hourly"] = me.hourlyVariables me.lastFetched.hourly = new Date() - getData = true + return true } if (me.lastFetched.hoursSinceDaily() > 8) { parameters["daily"] = me.dailyVariables me.lastFetched.daily = new Date() - getData = true + return true } } - if (!getData) return + return false + } - const responses = await fetchWeatherApi(me.apiUrl, parameters) - for (const response of responses) { - const utcOffsetSeconds = response.utcOffsetSeconds() - const current = response.current() - const hourly = response.hourly() - const daily = response.daily() + private setCurrentData(current: VariablesWithTime) { + this.current = { + time: new Date(Number(current.time()) * 1000), + temperature: current.variables(0)!.value(), + humidity: current.variables(1)!.value(), + feelsLike: current.variables(2)!.value(), + precipitation: current.variables(3)!.value(), + windSpeed: current.variables(4)!.value(), + windGusts: current.variables(5)!.value(), + windDirection: current.variables(6)!.value(), + cloudCoverage: current.variables(7)!.value() + } + } - if (current != null) { - me.current = { - time: new Date(Number(current.time()) * 1000), - temperature: current.variables(0)!.value(), - humidity: current.variables(1)!.value(), - feelsLike: current.variables(2)!.value(), - precipitation: current.variables(3)!.value(), - windSpeed: current.variables(4)!.value(), - windGusts: current.variables(5)!.value(), - windDirection: current.variables(6)!.value(), - cloudCoverage: current.variables(7)!.value() - } - } + private setHourlyData(hourly: VariablesWithTime) { + const length = Number(hourly.timeEnd() - hourly.time()) / hourly.interval() + const temperatures = hourly.variables(0)!.valuesArray() + const precipitationTotals = hourly.variables(1)!.valuesArray() + const precipitationProbabilities = hourly.variables(2)!.valuesArray() + this.hourly.length = 0 - if (hourly != null) { - const length = Number(hourly.timeEnd() - hourly.time()) / hourly.interval() - const temperatures = hourly.variables(0)!.valuesArray() - const precipitationTotals = hourly.variables(1)!.valuesArray() - const precipitationProbabilities = hourly.variables(2)!.valuesArray() - me.hourly.length = 0 + for (let i = 0; i < length; i++) { + this.hourly.push({ + time: new Date((Number(hourly.time()) + i * hourly.interval()) * 1000), + temperature: temperatures[i], + precipitation: precipitationTotals[i], + precipitationProbability: precipitationProbabilities[i] + }) + } + } - for (let i = 0; i < length; i++) { - me.hourly.push({ - time: new Date((Number(hourly.time()) + i * hourly.interval() + utcOffsetSeconds) * 1000), - temperature: temperatures[i], - precipitation: precipitationTotals[i], - precipitationProbability: precipitationProbabilities[i] - }) - } - } + private setDailyData(daily: VariablesWithTime) { + const length = Number(daily.timeEnd() - daily.time()) / daily.interval() + const maxTemperatures = daily.variables(0)!.valuesArray() + const minTemperatures = daily.variables(1)!.valuesArray() + const sunrise = daily.variables(2) + const sunset = daily.variables(3) + const maxUVs = daily.variables(4)!.valuesArray() + const precipitationTotals = daily.variables(5)!.valuesArray() + const precipitationHours = daily.variables(6)!.valuesArray() + const maxWindSpeeds = daily.variables(7)!.valuesArray() + const maxWindGusts = daily.variables(8)!.valuesArray() + const prominentWindDirections = daily.variables(9)!.valuesArray() - if (daily != null) { - const length = Number(daily.timeEnd() - daily.time()) / daily.interval() - const maxTemperatures = daily.variables(0)!.valuesArray() - const minTemperatures = daily.variables(1)!.valuesArray() - const sunrise = daily.variables(2) - const sunset = daily.variables(3) - const maxUVs = daily.variables(4)!.valuesArray() - const precipitationTotals = daily.variables(5)!.valuesArray() - const precipitationHours = daily.variables(6)!.valuesArray() - const maxWindSpeeds = daily.variables(7)!.valuesArray() - const maxWindGusts = daily.variables(8)!.valuesArray() - const prominentWindDirections = daily.variables(9)!.valuesArray() + this.daily.length = 0 - me.daily.length = 0 - - for (let i = 0; i < length; i++) { - me.daily.push({ - time: new Date((Number(daily.time()) + i * daily.interval()) * 1000), - maxTemperature: maxTemperatures[i], - minTemperature: minTemperatures[i], - sunrise: new Date((Number(sunrise.valuesInt64(i))) * 1000), - sunset: new Date((Number(sunset.valuesInt64(i))) * 1000), - maxUV: maxUVs[i], - totalPrecipitation: precipitationTotals[i], - hoursOfPrecipitation: precipitationHours[i], - maxWindSpeed: maxWindSpeeds[i], - maxWindGusts: maxWindGusts[i], - prominentWindDirection: prominentWindDirections[i] - }) - } - } + for (let i = 0; i < length; i++) { + this.daily.push({ + time: new Date((Number(daily.time()) + i * daily.interval()) * 1000), + maxTemperature: maxTemperatures[i], + minTemperature: minTemperatures[i], + sunrise: new Date((Number(sunrise.valuesInt64(i))) * 1000), + sunset: new Date((Number(sunset.valuesInt64(i))) * 1000), + maxUV: maxUVs[i], + totalPrecipitation: precipitationTotals[i], + hoursOfPrecipitation: precipitationHours[i], + maxWindSpeed: maxWindSpeeds[i], + maxWindGusts: maxWindGusts[i], + prominentWindDirection: prominentWindDirections[i] + }) } } @@ -134,31 +146,25 @@ export default class Weather { } while (panel.firstChild) panel.removeChild(panel.firstChild) + this.setDisplayCurrent(panel) + } + private setDisplayCurrent(panel: HTMLElement) { const currentSection = document.createElement("section") currentSection.id = "current-section" panel.appendChild(currentSection) - this.setDisplayCurrent(currentSection) } - private setDisplayCurrent(section: HTMLElement) { - const header = document.createElement("header") - header.textContent = "Current" - - section.appendChild(header) - - const list = document.createElement("dl") - for (let key of Object.keys(this.current)) { - const term = document.createElement("dt") - term.textContent = key - - const value = document.createElement("dd") - value.textContent = (this.current as any)[key] - - list.append(term, value) - } - - section.appendChild(list) + private InCardinals(direction: number): string { + if (direction > 337.5 || direction <= 22.5) return "N" + if (direction > 22.5 || direction <= 67.5) return "NE" + if (direction > 67.5 || direction <= 112.5) return "E" + if (direction > 112.5 || direction <= 157.5) return "SE" + if (direction > 157.5 || direction <= 202.5) return "S" + if (direction > 202.5 || direction <= 249.5) return "SW" + if (direction > 249.5 || direction <= 294.5) return "W" + if (direction > 294.5 || direction <= 337.5) return "NW" + return null } } @@ -215,10 +221,6 @@ class LastFetched { hourly: Date = new Date() daily: Date = new Date() - minutesSinceCurrent(): number { - return (new Date().getTime() - this.current.getTime()) / 60000 - } - hoursSinceHourly(): number { return (new Date().getTime() - this.hourly.getTime()) / 3600000 } diff --git a/www/styles.css b/www/styles.css index 4d7bcf0..bddedb8 100644 --- a/www/styles.css +++ b/www/styles.css @@ -9,13 +9,22 @@ html #weather-panel, body #weather-panel { border-radius: 50px; box-shadow: #999a 0 0 25px; + color: #fff; font-family: Verdana, sans-serif; + font-weight: bold; height: 500px; left: calc(50% - 250px); padding: 40px; position: absolute; - text-shadow: #fff 0 0 5px, #fff 0 0 5px, #fff 0 0 5px, #fff 0 0 5px; + text-shadow: #000 1px 1px, #000 -1px 1px, #000 -1px -1px, #000 1px -1px; top: calc(50% - 250px); width: 500px; z-index: 10; } +@supports (-webkit-text-stroke: 1px #000) { + html #weather-panel, + body #weather-panel { + -webkit-text-stroke: 1px #000; + -webkit-text-fill-color: #fff; + } +}