diff --git a/docs/wk3-paired-validation.md b/docs/wk3-paired-validation.md new file mode 100644 index 00000000..52c6d022 --- /dev/null +++ b/docs/wk3-paired-validation.md @@ -0,0 +1,61 @@ +# 27.3 JSON / FlatBuffer 成对验证(2026-11-13 基线) + +2026-11-13,macOS 27.3 (26C5035r)。杭州、天津、西雅图及西雅图复测共四组, +每组同 URL / 地点 / 请求参数,仅改变 Accept;启动差小于 2 ms。 +抓取时旁路改写,恢复后继续使用 WK3 Preview。使用当时 dev 的三个产品 codec +解码,并独立读取原始 vtable 的时间整数与分钟向量长度。 + +## 后续进展(2026-11-14) + +下表记录 v2.0.5 局部 schema 的基线结果。后续 +[apple.WeatherKit #1](https://github.com/NSRingo/apple.WeatherKit/pull/1) 已定义 15 个根产品, +并根据原生访问器把分钟摘要 condition 补为 UInt8 向量; +[WeatherKit #111](https://github.com/NSRingo/WeatherKit/pull/111) 已接入三类天气替换。 +这些基线缺项不再代表新 codec 的现状。成对测试同时支持局部 schema 和完整 schema: +有 condition 访问器时必须逐值对齐,不再期待 deprecated 缺项。 + +## 结果 + +| 样本 | 完整字段行数 | 已对齐 | 当前产品 codec 缺失 | 摘要 condition 缺失 | 返回值/版本差异 | +|---|---:|---:|---:|---:|---:| +| 杭州 | 2121 | 48 | 2073 | 0 | 0 | +| 天津 | 2057 | 48 | 2009 | 0 | 0 | +| 西雅图 | 2436 | 420 | 2014 | 1 | 1 | +| 西雅图复测 | 2448 | 422 | 2014 | 1 | 11 | +| 合计 | 9062 | 938 | 8110 | 2 | 12 | + +“已对齐”包括逐值相同、float32 表示精度、UUID 字节/字符串等价和明确的缺省值。 +空气质量污染物按名称对齐;原始数组顺序相反。时间戳保持整数精确比较。 + +| 字段(样本) | JSON 值 | FlatBuffer 解码值 | 是否一致 | 原因 / 原始字节复核 | +|---|---|---|---|---| +| AQ index(杭州) | 50 | 50 | 是 | short 标量一致 | +| AQ CO amount(杭州) | 0.7 | 0.699999988079071 | 是 | 同一 float32;单位枚举相同 | +| alert severity(西雅图) | MINOR | MINOR | 是 | 枚举一致 | +| alert importance(西雅图) | LOW | LOW | 是 | 枚举一致 | +| alert responses(西雅图) | MONITOR | MONITOR | 是 | 枚举一致 | +| nextHour minutes(西雅图) | 81 条 | 81 条 | 是 | 所有已声明分钟字段逐项比对 | +| nextHour.summary[0].condition[0] | CLEAR | 未输出 | 否 | 当前 schema 将摘要槽 2 标记 deprecated | +| AQ metadata.expireTime(西雅图) | 1794571344 | 1794571285 | 否 | 原始槽 1 的 uint32 确为 1794571285 | +| AQ metadata.expireTime(复测) | 1794571287 | 1794571351 | 否 | 原始槽 1 的 uint32 确为 1794571351 | +| nextHour.metadata.reportedTime(复测) | 1794569160 | 1794569280 | 否 | 原始时间值相差 120 秒 | +| nextHour.forecastEnd(复测) | 1794574560 | 1794574680 | 否 | 原始时间值也相差 120 秒 | +| nextHour.minutes(复测) | 82 条 | 84 条 | 否 | 原始向量长度 84;多出两条、共八个字段 | +| currentWeather / daily / hourly 等 | 有值 | 未输出 | 否 | dev 没有这些产品的 WK3 codec;没有套用 WK2 或占位 ubyte | + +完整 9062 行由 `node scripts/compare-wk3-pair.mjs output/wk3-paired` 生成, +列为字段名、JSON 值、FlatBuffer 解码值、是否一致及原因,并带样本名。 +四组成对脱敏响应和 SHA-256 已存入 `tests/fixtures/wk3-paired/`,可重复生成表格。 + +## 基线问题与处理状态 + +- cloudCover 在彩云 provider 边界修正为百分比,覆盖小时与日间/夜间预报; + 0.5 → 50、0.8 → 80,回归另覆盖 0、0.005、1。 +- 三个已实现产品中,样本内的枚举和单位标签逐项对齐;发现的时间差直接存在于原始 + 整数中。复测的 reportedTime 和 forecastEnd 同时相差 120 秒,记录为不同产品版本, + 不归因于枚举或单位换算。 +- 分钟摘要 condition 已在协议包 PR 补为 UInt8 向量,依据原生向量访问器的单字节寻址, + 并用独立 flatc 写出的多元素向量做回归。 +- 当前/逐日/逐小时等其余产品已在协议包 PR 定义并完成成对样本解码和往返; + 本报告的 8110 项保留为原始基线记录,完整 codec 验证见协议包 PR。 +- 当前分钟雨样本的雨强为零;检验非零雨强数值换算,需要追加正在降雨地区的原始成对样本。 diff --git a/docs/wk3-weather-json.md b/docs/wk3-weather-json.md new file mode 100644 index 00000000..ca52109e --- /dev/null +++ b/docs/wk3-weather-json.md @@ -0,0 +1,75 @@ +# WK3 主天气 JSON 适配(开发入口) + +基于 `dev` 的 WK3 二进制支持,补充当前天气、逐日、逐小时天气替换。 +不将 WK3 二进制交给 WK2 codec;不新增供应商、缓存、空气质量算法或平台构建分支。 + +## 请求与处理边界 + +- 仅 HTTPS `weatherkit.apple.com/api/v3/weather/` 的 GET,且 User-Agent 为 + `WeatherKit_Weather_iOS_` / `WeatherKit_Weather_macOS_` 前缀。 +- 仅在天气来源选择彩云或和风、国家在既有 `Weather.Replace` 范围内,且 URL + `dataSets` 与用户设置的交集包含当前、逐日或逐小时天气时协商 `Accept: application/json`。 +- 小组件、后台客户端、WK2、仅请求空气质量或分钟雨等请求继续原路径;发布入口不变。 +- JSON 响应调用现有六种产品注入函数,仍受 URL 与设置交集限制。未请求的产品 + 不发起供应商请求。单产品异常、非有限数字或“暂不可用”结果保留 Apple 产品。 +- JSON 协商改变了整个响应的表示,因此同包中未替换的已知 Apple 产品也需兼容 + 主 App JSON reader:天气枚举向量转标量、降水统计保留分类量并补标量、预警 UUID + 字节对象转 UUID 字符串。未知根产品和未知嵌套对象不作转换。 +- 返回 JSON 后删除旧 `bodyBytes`、正文长度、编码及缓存验证头。响应最多 4 MiB, + 遍历最多 64 层 / 100000 个节点,单数组最多 8192 项。 + +表示适配依据 [PR #107 的固定版本与原生观察](https://github.com/hhhoratioxu/WeatherKit/blob/355b5f5dad9d01cb13167949f8334a489b301811/docs/loon-v3-compatibility.md)。 +派生代码保留作者及 Apache-2.0 声明;未引入该 PR 的缓存、备用来源、地理判定和算法重构。 + +## 与协议包和二进制接入的分工 + +三项改动组成同一条请求/响应链: + +| 层次 | PR | 职责 | +| --- | --- | --- | +| 协议模型 | [apple.WeatherKit #1](https://github.com/NSRingo/apple.WeatherKit/pull/1) | 提供 WK3 Schema/Codec,不决定客户端路由 | +| JSON 协商和处理 | [WeatherKit #110](https://github.com/NSRingo/WeatherKit/pull/110) | 符合设置的主 App 请求协商 JSON,JSON 响应复用注入函数 | +| FlatBuffer 响应处理 | [WeatherKit #111](https://github.com/NSRingo/WeatherKit/pull/111) | 对实际返回的 FlatBuffer 调用 Codec,保留其他产品的原始数据 | + +两条响应路径按 Content-Type 分流。JSON 分支返回后不会进入二进制分支,也不会为同一 +产品重复请求供应商。回归在正常 Request → Response 调用中检查 Accept=application/json、 +二进制 decode/encode 均未调用、每种供应商产品只调用一次。 + +#110 可独立合并;#1 发布后,#111 更新协议依赖并合并。建议继续以本 PR 的 JSON 协商 +作为主 App 默认路径,由维护者确认长期策略。#111 中主 App 的二进制实测是专用模块下的 +codec 兼容性验收,不改变正常路由。macOS 小组件已在 #111 完成三次二进制网络响应与显示核对,本轮保留 FlatBuffer 路径, +暂不扩展小组件 JSON 白名单。 + +## 验证 + +```sh +npm ci +npm run build +npm run build:dev +npm run build:dev-args +node --test tests/*.test.mjs +``` + +`weatherKit3JSON.test.mjs` 覆盖设置边界、三种天气替换、混合数据集、失败回退、 +未知字段、字节正文和主 App 表示转换。`platformBundleReplay.test.mjs` 从实际构建的 +request/response bundle 发起模拟供应商调用,检查 Surge `$done` 形状和天气结果。 +测试夹具是合成数据,不代表本机原生解码或真实天气来源已经验收。 + +## macOS 验收(2026-11-13) + +macOS 27.3(26C5035r)、Surge 6.12.0(12820),本地加载同一构建产物: + +- 主 App 的 v3 请求实际协商 `application/json`;抓取的改写响应中,当前、逐日和 + 逐小时产品均保留彩云来源(逐日 11 项、逐小时 274 项),空气标准为 `EU.EAQI`。 +- 苏州、无锡预览显示当前天气、逐小时、逐日、分钟雨与 EAQI;无锡逐日详情中的 + 当前温度、最高/最低温与主页面一致,小时图表可正常打开。 +- 真机发现的 EAQI 污染物 `index=-1` 哨兵、降水分类枚举、`restOfDayForecast` + 和 `forecastPeriodic.forecasts[].periods[]` 均已补回归。修正后的三个有界日志窗口 + 合计 30 次 WeatherData 更新成功,无 `Unable to decode` 或 CloudKit 回退记录。 +- 未将原始位置、授权头或完整抓包加入仓库。未实测 iPhone、watchOS 或其他代理客户端; + 这不是长时间稳定性/耗电结论。后台小组件保留既有 WK3 二进制路径。 + +本机 GitHub 登录缺少 Packages 读取权限,因此协议依赖由公开 `v2.0.5` 标签 +(`NSRingo/apple.WeatherKit@c87c1dcd851ea9293141bcfb32fde1b2cca564b9`)源码构建; +未改依赖声明或锁文件,也未声称与发布的 npm tarball 字节一致。 +Siri 路由例外属于独立配置修复,不混入此协议补丁。 diff --git a/scripts/compare-wk3-pair.mjs b/scripts/compare-wk3-pair.mjs new file mode 100644 index 00000000..ad808682 --- /dev/null +++ b/scripts/compare-wk3-pair.mjs @@ -0,0 +1,96 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { ByteBuffer } from "flatbuffers"; + +globalThis.$environment ??= { "surge-version": "test" }; +globalThis.$persistentStore ??= { read: () => null, write: () => true }; +const { Console } = await import("@nsnanocat/util"); +Console.logLevel = "OFF"; +const { default: WeatherKit3 } = await import("../src/class/WeatherKit3.mjs"); +// Retain the original three-product baseline; full-schema coverage lives in apple.WeatherKit. +const supported = ["airQuality", "forecastNextHour", "weatherAlerts"]; +const missing = ""; +const floatFields = new Set(["latitude", "longitude", "amount", "precipitationIntensity", "perceivedPrecipitationIntensity"]); +// Direct vtable reads independently check the decoder's integer fields and vector lengths. +function wireFacts(bb) { + const root = bb.readUint32(0); + const child = (table, slot) => { + const offset = bb.__offset(table, 4 + slot * 2); + return offset ? bb.__indirect(table + offset) : 0; + }; + const scalar = (table, slot) => { + const offset = bb.__offset(table, 4 + slot * 2); + return offset ? bb.readUint32(table + offset) : 0; + }; + const aqMetadata = child(child(root, 0), 0), + nextHour = child(root, 5); + return { aqExpiry: scalar(aqMetadata, 1), minuteCount: nextHour ? bb.readUint32(child(nextHour, 5)) : 0, forecastEnd: nextHour ? scalar(nextHour, 4) : 0, reportedTime: nextHour ? scalar(child(nextHour, 0), 8) : 0 }; +} + +function fields(value, prefix = "", out = {}) { + if (value && typeof value === "object" && !Array.isArray(value) && !(Array.isArray(value.bytes) && value.bytes.length === 16)) { + for (const [key, child] of Object.entries(value)) fields(child, prefix ? `${prefix}.${key}` : key, out); + } else if (Array.isArray(value) && value.length) { + for (const [index, child] of value.entries()) { + // The wire and JSON services return pollutants in opposite orders; compare species, not positions. + const key = child?.pollutantType ? `[${child.pollutantType}]` : `[${index}]`; + fields(child, `${prefix}${key}`, out); + } + } else out[prefix] = value; + return out; +} + +export function comparePair(directory) { + const apple = JSON.parse(fs.readFileSync(path.join(directory, "apple.json"))); + const wire = new ByteBuffer(new Uint8Array(fs.readFileSync(path.join(directory, "apple.bin")))); + const { json: decoded, failures } = WeatherKit3.decodeWithReport(wire, supported); + const a = fields(apple), + b = fields(decoded), + raw = wireFacts(wire); + const rows = [...new Set([...Object.keys(a), ...Object.keys(b)])].sort().map(field => { + const x = Object.hasOwn(a, field) ? a[field] : missing; + const y = Object.hasOwn(b, field) ? b[field] : missing; + let equal = JSON.stringify(x) === JSON.stringify(y), + reason = equal ? "一致" : "值不一致:需定位单位/枚举/偏移"; + const root = field.split(/[.[]/)[0]; + if (!supported.includes(root)) reason = "基线范围:该产品未纳入本脚本的三个产品比较"; + else if (floatFields.has(field.split(".").at(-1)) && typeof x === "number" && typeof y === "number" && Math.fround(x) === Math.fround(y)) { + equal = true; + reason = x === y ? "一致" : "一致:JSON 十进制与 float32 表示精度"; + } else if (Array.isArray(x?.bytes) && typeof y === "string") { + equal = x.bytes.map(byte => byte.toString(16).padStart(2, "0")).join("") === y.replaceAll("-", ""); + reason = equal ? "一致:同一 UUID 的字节/字符串表示" : "UUID 字节不一致"; + } else if (field === "airQuality.metadata.expireTime" && !equal && y === raw.aqExpiry) reason = "返回值差异:原始 Metadata 槽 1 的 uint32 与解码值一致"; + else if (field === "forecastNextHour.metadata.reportedTime" && !equal && y === raw.reportedTime) reason = `数据版本差异:原始 reportedTime 与 JSON 相差 ${y - x} 秒`; + else if (field === "forecastNextHour.forecastEnd" && !equal && y === raw.forecastEnd) reason = "返回值差异:原始 forecastEnd uint32 与解码值一致"; + else if (x === missing && /^forecastNextHour\.minutes\[/.test(field) && raw.minuteCount > (apple.forecastNextHour?.minutes?.length ?? 0)) { + reason = `返回值差异:JSON 分钟向量 ${apple.forecastNextHour.minutes.length} 条,原始 FlatBuffer 向量 ${raw.minuteCount} 条`; + } else if (x === missing && (y === null || y === 0 || y === false || (Array.isArray(y) && y.length === 0))) { + equal = true; + reason = "一致:JSON 省略缺省值,schema 返回缺省值"; + } else if (y === missing && /forecastNextHour\.summary\[\d+\]\.condition/.test(field)) reason = "缺字段:WK3 summary 槽 2 在当前 schema 标记 deprecated"; + else if (y === missing) reason = "缺字段:当前 codec 未输出此字段"; + return { field, json: x, flatbuffer: y, equal, reason }; + }); + const counts = {}; + for (const row of rows) counts[row.reason] = (counts[row.reason] ?? 0) + 1; + return { rows, failures, counts, decoded, raw }; +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + const output = path.resolve(process.argv[2] ?? "output/wk3-paired"); + fs.mkdirSync(output, { recursive: true }); + const all = [], + summaries = {}; + for (const city of ["hangzhou", "tianjin", "seattle", "seattle-repeat"]) { + const result = comparePair(fileURLToPath(new URL(`../tests/fixtures/wk3-paired/${city}/`, import.meta.url))); + all.push(...result.rows.map(row => ({ sample: city, ...row }))); + summaries[city] = { counts: result.counts, failures: result.failures, fields: result.rows.length, raw: result.raw }; + fs.writeFileSync(path.join(output, `${city}.decoded.json`), JSON.stringify(result.decoded, null, 2)); + } + const cell = value => `"${String(typeof value === "object" ? JSON.stringify(value) : value).replaceAll('"', '""')}"`; + fs.writeFileSync(path.join(output, "fields.csv"), "sample,field,JSON,FlatBuffer,equal,reason\n" + all.map(row => [row.sample, row.field, row.json, row.flatbuffer, row.equal, row.reason].map(cell).join(",")).join("\n") + "\n"); + fs.writeFileSync(path.join(output, "summary.json"), JSON.stringify(summaries, null, 2)); + console.log(JSON.stringify(summaries, null, 2)); +} diff --git a/src/class/ColorfulClouds.mjs b/src/class/ColorfulClouds.mjs index 0a7f1c6a..b3c78ca6 100644 --- a/src/class/ColorfulClouds.mjs +++ b/src/class/ColorfulClouds.mjs @@ -519,7 +519,7 @@ export default class ColorfulClouds { daytimeForecast: { forecastStart: timeStamp + 8 * 3600, // 8 hours forecastEnd: timeStamp + 8 * 3600 + 12 * 3600, // 8 + 12 hours - cloudCover: body?.result?.daily?.cloudrate?.[i]?.avg, + cloudCover: Math.round(body?.result?.daily?.cloudrate?.[i]?.avg * 100), // cloudCoverHighAltPct: 0, // Not given // cloudCoverLowAltPct: 0, // Not given // cloudCoverMidAltPct: 0, // Not given @@ -543,7 +543,7 @@ export default class ColorfulClouds { overnightForecast: { forecastStart: timeStamp + 20 * 3600, // 20 hours forecastEnd: timeStamp + 20 * 3600 + 12 * 3600, // 20 + 12 hours - cloudCover: body?.result?.daily?.cloudrate?.[i]?.avg, + cloudCover: Math.round(body?.result?.daily?.cloudrate?.[i]?.avg * 100), // cloudCoverHighAltPct: 0, // Not given // cloudCoverLowAltPct: 0, // Not given // cloudCoverMidAltPct: 0, // Not given @@ -839,7 +839,7 @@ export default class ColorfulClouds { return { metadata: this.#Metadata(hourly.result.server_time, hourly.location), hours: Array.from({ length: hourly.result.hourly.skycon.length }, (_, i) => ({ - cloudCover: hourly.result.hourly.cloudrate[i].value, + cloudCover: Math.round(hourly.result.hourly.cloudrate[i].value * 100), // cloudCoverHighAltPct: 0, // Not given // cloudCoverLowAltPct: 0, // Not given // cloudCoverMidAltPct: 0, // Not given diff --git a/src/function/weatherKit3JSON.mjs b/src/function/weatherKit3JSON.mjs new file mode 100644 index 00000000..a4155061 --- /dev/null +++ b/src/function/weatherKit3JSON.mjs @@ -0,0 +1,213 @@ +// WK3 JSON reader adaptations derived from hhhoratioxu/WeatherKit PR #107 +// (355b5f5dad9d01cb13167949f8334a489b301811), Copyright 2026 hhhoratioxu. +// SPDX-License-Identifier: Apache-2.0 +import { Console } from "@nsnanocat/util"; + +const weatherProducts = ["currentWeather", "forecastDaily", "forecastHourly"]; +const clientProducts = [...weatherProducts, "airQuality", "forecastNextHour", "weatherAlerts", "forecastPeriodic", "weatherChanges"]; +const clientContainers = new Set(["metadata", "days", "hours", "minutes", "daytimeForecast", "overnightForecast", "restOfDayForecast", "series", "forecasts", "periods", "condition", "parameters", "summary", "alerts", "pollutants"]); +const precipitationByType = /^precipitationAmount(?:(?:Previous|Next)\d+h)?ByType$/; +const maxBodyBytes = 4 * 1024 * 1024; +const object = value => value !== null && typeof value === "object" && !Array.isArray(value); +const header = (headers, name) => Object.entries(headers ?? {}).find(([key]) => key.toLowerCase() === name)?.[1]; + +/** Only negotiate JSON for main-app requests which actually need weather replacement. */ +export function useWeatherKit3JSON(request, parameters, settings) { + return ( + request.method === "GET" && + /^https:\/\/weatherkit\.apple\.com\/api\/v3\/weather\//.test(request.url) && + /^WeatherKit_Weather_(iOS|macOS)_/.test(header(request.headers, "user-agent")) && + parameters.version === "v3" && + [parameters.latitude, parameters.longitude].every(value => value !== undefined && Number.isFinite(Number(value))) && + Math.abs(Number(parameters.latitude)) <= 90 && + Math.abs(Number(parameters.longitude)) <= 180 && + ["ColorfulClouds", "QWeather"].includes(settings.Weather?.Provider) && + settings.Weather?.Replace?.includes(parameters.country) && + parameters.dataSets.some(name => weatherProducts.includes(name) && settings.DataSets.includes(name)) + ); +} + +/** Adapt Apple's JSON representation to the native main-app reader; unknown roots stay untouched. */ +export function adaptWeatherKit3JSON(product, name, provider = false) { + // Existing AQ algorithms use -1 for an optional pollutant index not defined by that standard. + // WK3 JSON uses unsigned integers: omit the sentinel, preserving the concentration and overall AQ. + if (provider && name === "airQuality") + for (const pollutant of product?.pollutants ?? []) { + if (pollutant.index === -1) delete pollutant.index; + if (pollutant.categoryIndex === -1) delete pollutant.categoryIndex; + } + let nodes = 0; + function visit(value, depth = 0, known = true) { + if (++nodes > 100000 || depth > 64) throw new RangeError("WK3 JSON exceeds traversal bounds"); + if (!value || typeof value !== "object") return; + if (Array.isArray(value)) { + if (value.length > 8192) throw new RangeError("WK3 JSON vector too large"); + for (const item of value) visit(item, depth + 1, known); + return; + } + for (const key of Object.keys(value)) { + let item = value[key]; + if (!known) { + visit(item, depth + 1, false); + continue; + } + if ((key === "conditionCode" || key === "precipitationType" || (key === "condition" && item?.length > 0)) && Array.isArray(item) && item.every(entry => typeof entry === "string")) { + const first = item.find(entry => entry); + if (first === undefined) delete value[key]; + else value[key] = first; + item = value[key]; + } + if ((key === "precipitationAmount" || key.startsWith("snowfallAmount")) && object(item) && Number.isFinite(item.expected)) value[key] = item = item.expected; + if (precipitationByType.test(key) && Array.isArray(item)) { + const scalar = key.slice(0, -"ByType".length); + if (value[scalar] === undefined && item.every(part => object(part) && Number.isFinite(part.expected) && part.expected >= 0)) { + const amount = item.reduce((sum, part) => sum + part.expected, 0); + if (Number.isFinite(amount)) value[scalar] = amount; + } + } + if (provider) { + if (item === undefined) { + delete value[key]; + continue; + } + if (typeof item === "number" && !Number.isFinite(Math.fround(item))) throw new RangeError("Invalid WK3 provider number"); + const range = integerRange(key); + if (range && item != null) { + if (typeof item !== "number" || item < 0 || item > range) throw new RangeError(`Invalid WK3 ${key}`); + value[key] = item = timeFields.has(key) ? Math.trunc(item) : Math.round(item); + } + if (item === 0 && optionalTimes.has(key)) { + delete value[key]; + continue; + } + if (key === "metadata" && object(item)) { + for (const coordinate of ["latitude", "longitude"]) + if (typeof item[coordinate] === "string" && item[coordinate].trim()) { + const number = Number(item[coordinate]); + if (!Number.isFinite(number)) throw new RangeError("Invalid WK3 coordinate"); + item[coordinate] = number; + } + } + } + visit(item, depth + 1, clientContainers.has(key) || precipitationByType.test(key)); + } + } + visit(product); + if (name === "weatherAlerts") + for (const alert of product?.alerts ?? []) { + const bytes = alert?.id?.bytes; + if (!Array.isArray(bytes) || bytes.length !== 16 || !bytes.every(byte => Number.isInteger(byte) && byte >= 0 && byte <= 255)) continue; + const hex = bytes.map(byte => byte.toString(16).padStart(2, "0")).join(""); + alert.id = `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`; + } + return product; +} + +const percentages = new Set(["cloudCover", "cloudCoverLowAltPct", "cloudCoverMidAltPct", "cloudCoverHighAltPct", "humidity", "humidityMax", "humidityMin", "precipitationChance"]); +const bytes = new Set(["uvIndex", "maxUvIndex", "uvIndexMax", "uvIndexMin", "categoryIndex"]); +const timeFields = new Set([ + "asOf", + "readTime", + "reportedTime", + "expireTime", + "forecastStart", + "forecastEnd", + "startTime", + "endTime", + "date", + "effectiveTime", + "issuedTime", + "eventOnsetTime", + "eventEndTime", + "moonrise", + "moonset", + "sunrise", + "sunset", + "sunriseCivil", + "sunsetCivil", + "sunriseNautical", + "sunsetNautical", + "sunriseAstronomical", + "sunsetAstronomical", + "solarNoon", + "solarMidnight", + "temperatureMaxTime", + "temperatureMinTime", +]); +const optionalTimes = new Set([ + "reportedTime", + "endTime", + "eventOnsetTime", + "eventEndTime", + "moonrise", + "moonset", + "sunrise", + "sunset", + "sunriseCivil", + "sunsetCivil", + "sunriseNautical", + "sunsetNautical", + "sunriseAstronomical", + "sunsetAstronomical", + "solarNoon", + "solarMidnight", + "temperatureMaxTime", + "temperatureMinTime", +]); +function integerRange(key) { + if (percentages.has(key)) return 100; + if (bytes.has(key)) return 255; + if (key === "index" || key === "windDirection") return 65535; + if (timeFields.has(key)) return 0xffffffff; +} + +/** Process each selected product independently, reusing existing injectors and preserving Apple on failure. */ +export async function replaceWeatherKit3JSON(response, dataSets, injectors) { + if (response.status !== 200 || !/^application\/json(?:;|$)/i.test(header(response.headers, "content-type"))) return response; + const raw = response.bodyBytes ?? response.body; + let body; + try { + if ((typeof raw === "string" ? raw.length : raw?.byteLength) > maxBodyBytes) return response; + const text = typeof raw === "string" ? raw : new TextDecoder("utf-8", { fatal: true }).decode(raw); + if (new TextEncoder().encode(text).byteLength > maxBodyBytes) return response; + body = JSON.parse(text); + if (!object(body)) return response; + // JSON negotiation changes representation even for unselected products in the same response. + // In particular forecastPeriodic and retained alert UUIDs still need reader compatibility. + for (const name of clientProducts) if (object(body[name])) adaptWeatherKit3JSON(body[name], name); + } catch { + return response; + } + const fallback = JSON.stringify(body); + await Promise.all( + [...new Set(dataSets)].map(async name => { + if (!injectors[name] || (!object(body[name]?.metadata) && name !== "forecastNextHour")) return; + const original = body[name]; + try { + const copy = original === undefined ? undefined : JSON.parse(JSON.stringify(original)); + const value = await injectors[name](copy); + if (object(value?.metadata) && value.metadata.temporarilyUnavailable !== true) { + const candidate = adaptWeatherKit3JSON(value, name, true); + // Bound and validate before assigning so an invalid provider graph cannot poison the whole response. + const encoded = JSON.stringify(candidate, (_key, item) => { + if (typeof item === "number" && !Number.isFinite(item)) throw new RangeError("Invalid WK3 provider number"); + return item; + }); + if (new TextEncoder().encode(encoded).byteLength > maxBodyBytes) throw new RangeError("WK3 product too large"); + body[name] = candidate; + } + } catch (error) { + body[name] = original; + Console.warn("WeatherKit.api.v3.weather.json", `preserved ${name}: ${error?.name ?? "Error"}`); + } + }), + ); + let encoded = JSON.stringify(body); + // Once JSON is negotiated, fallback must still satisfy the native JSON reader (not raw WK3 JSON). + if (new TextEncoder().encode(encoded).byteLength > maxBodyBytes) encoded = fallback; + const headers = { ...response.headers }; + for (const key of Object.keys(headers)) if (["content-length", "content-encoding", "etag", "last-modified", "content-md5", "digest", "cache-control"].includes(key.toLowerCase())) delete headers[key]; + headers["Cache-Control"] = "no-store"; + const { bodyBytes: _bodyBytes, ...result } = response; + return { ...result, headers, body: encoded }; +} diff --git a/src/process/Request.dev.mjs b/src/process/Request.dev.mjs index fba6aa36..e72dadf3 100644 --- a/src/process/Request.dev.mjs +++ b/src/process/Request.dev.mjs @@ -6,6 +6,7 @@ import WeatherAlerts from "../class/WeatherAlerts.mjs"; import database from "../function/database.mjs"; import parseWeatherKitURL from "../function/parseWeatherKitURL.mjs"; import setENV from "../function/setENV.mjs"; +import { useWeatherKit3JSON } from "../function/weatherKit3JSON.mjs"; /***************** Processing *****************/ export async function Request($request) { // 构造回复数据 @@ -165,6 +166,10 @@ export async function Request($request) { break; } $request.url = url.toString(); + if (useWeatherKit3JSON($request, parseWeatherKitURL(url), Settings)) { + for (const key of Object.keys($request.headers)) if (["accept", "if-modified-since"].includes(key.toLowerCase())) delete $request.headers[key]; + $request.headers.Accept = "application/json"; + } Console.debug(`$request.url: ${$request.url}`); return { $request, $response }; } diff --git a/src/process/Response.dev.mjs b/src/process/Response.dev.mjs index 59d33872..3261aba2 100644 --- a/src/process/Response.dev.mjs +++ b/src/process/Response.dev.mjs @@ -14,6 +14,7 @@ import mergeWeatherKitAvailability from "../function/mergeWeatherKitAvailability import parseWeatherKitURL from "../function/parseWeatherKitURL.mjs"; import providerNameToLogo from "../function/providerNameToLogo.mjs"; import setENV from "../function/setENV.mjs"; +import { replaceWeatherKit3JSON, useWeatherKit3JSON } from "../function/weatherKit3JSON.mjs"; /***************** Processing *****************/ export async function Response($request, $response) { // 解构URL @@ -31,6 +32,31 @@ export async function Response($request, $response) { */ const { Settings, Caches, Configs } = setENV("iRingo", "WeatherKit", database); Console.logLevel = Settings.LogLevel; + const parameters = parseWeatherKitURL(url); + if (useWeatherKit3JSON($request, parameters, Settings) && Object.entries($response.headers ?? {}).some(([key, value]) => key.toLowerCase() === "content-type" && /^application\/json(?:;|$)/i.test(value))) { + const providerParameters = { ...parameters }; + const enviroments = { + colorfulClouds: new ColorfulClouds(providerParameters, Settings?.API?.ColorfulClouds?.Token || "Y2FpeXVuX25vdGlmeQ=="), + qWeather: new QWeather(providerParameters, Settings?.API?.QWeather?.Token || "bdd98ec1d87747f3a2e8b1741a5af796", Settings?.API?.QWeather?.Host), + waqi: new WAQI(parameters, Settings?.API?.WAQI?.Token), + country: parameters.country, + }; + return replaceWeatherKit3JSON( + $response, + parameters.dataSets.filter(name => Settings.DataSets.includes(name)), + { + currentWeather: product => InjectCurrentWeather(product, Settings, enviroments), + forecastDaily: product => InjectForecastDaily(product, Settings, enviroments), + forecastHourly: product => InjectForecastHourly(product, Settings, enviroments), + airQuality: product => InjectAirQuality(product, Settings, Caches, enviroments), + forecastNextHour: product => InjectForecastNextHour(product, Settings, enviroments), + weatherAlerts: product => { + enviroments.qWeather.weatherKitLanguage = product?.metadata?.language ?? enviroments.qWeather.weatherKitLanguage; + return InjectWeatherAlerts(product, Settings, enviroments); + }, + }, + ); + } // 创建空数据 let body = {}; // 格式判断 diff --git a/tests/dailyPrecipitationTotals.test.mjs b/tests/dailyPrecipitationTotals.test.mjs index ed99a25a..1290a629 100644 --- a/tests/dailyPrecipitationTotals.test.mjs +++ b/tests/dailyPrecipitationTotals.test.mjs @@ -7,7 +7,8 @@ globalThis.$argument = { LogLevel: "OFF", Storage: "database" }; globalThis.$httpClient = { get(request, callback) { let body; - if (request.url.includes("api.caiyunapp.com")) body = colorfulCloudsDaily; + if (request.url.includes("/hourly?")) body = colorfulCloudsHourly; + else if (request.url.includes("api.caiyunapp.com")) body = colorfulCloudsDaily; else if (request.url.includes("/v7/weather/")) body = qWeatherDaily; else throw new Error(`unexpected request: ${request.url}`); callback(null, { headers: {}, status: 200 }, JSON.stringify(body)); @@ -35,6 +36,8 @@ test("ColorfulClouds keeps WeatherKit daily amount pairs while replacing support assert.equal(appleDay.precipitationChance, 80); assert.equal(appleDay.daytimeForecast.precipitationChance, 70); assert.equal(appleDay.overnightForecast.precipitationChance, 60); + assert.equal(appleDay.daytimeForecast.cloudCover, 80); + assert.equal(appleDay.overnightForecast.cloudCover, 80); }); test("QWeather keeps WeatherKit daily amount pairs instead of reusing one daily total", async () => { @@ -129,3 +132,36 @@ const qWeatherDaily = { fxLink: "https://www.qweather.com/", updateTime: "2026-07-16T08:00:00+08:00", }; + +// The provider boundary converts fractions once, before either WK2 encoding or WK3 JSON rounding. +const colorfulCloudsHourly = { + status: "ok", + location: [30, 120], + result: { + hourly: { + status: "ok", + skycon: [{ value: "CLOUDY", datetime: "2026-11-13T12:00:00Z" }], + cloudrate: [{ value: 0.5 }], + humidity: [{ value: 0.5 }], + precipitation: [{ value: 0, probability: 0 }], + pressure: [{ value: 101300 }], + temperature: [{ value: 20 }], + apparent_temperature: [{ value: 20 }], + visibility: [{ value: 10 }], + wind: [{ direction: 0, speed: 1 }], + }, + }, +}; +test("Caiyun hourly cloud-cover fractions are converted to percentages before rounding", async () => { + for (const [fraction, percent] of [ + [0, 0], + [0.005, 1], + [0.5, 50], + [0.8, 80], + [1, 100], + ]) { + colorfulCloudsHourly.result.hourly.cloudrate[0].value = fraction; + const forecast = await new ColorfulClouds({ ...parameters, version: "v3" }, "token").ForecastHourly(1); + assert.equal(forecast.hours[0].cloudCover, percent); + } +}); diff --git a/tests/fixtures/wk3-paired/README.md b/tests/fixtures/wk3-paired/README.md new file mode 100644 index 00000000..0bed8b0b --- /dev/null +++ b/tests/fixtures/wk3-paired/README.md @@ -0,0 +1,40 @@ +# macOS 27.3 paired WeatherKit responses + +Captured on 2026-11-13 from this macOS 27.3 (26C5035r) host, using its native Weather +request credentials only in memory. The rewrite module was disabled for the bounded +probe and restored in `finally`. Each pair uses the same public test location, URL, +User-Agent, schema header and datasets. Only `Accept` differs. Requests begin within +2 ms of each other; exact acquisition times and response media types are in each manifest. + +- `hangzhou`, `tianjin`: public city-centre requests, not device-location requests. +- `seattle`: includes 81 native minute records and one real weather alert. +- `seattle-repeat`: explicit no-cache repeat; both HTTP responses report the same Date + and MISS, but the JSON minute product is two minutes older (82 records versus 84). + This acquisition is retained rather than selecting only matching responses. + +Redaction: no request headers, credentials or device metadata are stored. Metadata +latitude/longitude are replaced with zero in JSON and in vtable-validated binary float +fields. Coordinates embedded in URLs are zeroed with same-length byte substitutions. +The binary is **not re-encoded**; original vtables, product slots, enum values, unknown +fields and timestamps remain. Manifest offsets audit exactly which floats changed. +JSON is compact to retain the wire-response style and avoid generated diff noise. + +Schema/codec under test: WeatherKit `dev@cd9d7927`, protocol `v2.0.5` source tag at +`NSRingo/apple.WeatherKit@c87c1dcd` (the local source-build procedure is in +`docs/wk3-weather-json.md`). The comparison does not interpret `unknownN:ubyte` root +placeholders as product schemas. + +```sh +node scripts/compare-wk3-pair.mjs output/wk3-paired +node --test tests/weatherKit3Paired.test.mjs +``` + +The generator writes every field to `fields.csv` (sample, field, JSON, FlatBuffer, +equal, reason), decoded JSON and summary counts. Pollutants are keyed by species +because the two formats return them in opposite orders. Float32 equivalence is used +only for schema float fields, never timestamps; UUIDs and omitted defaults have +explicit comparison rules. Integer mismatches are checked against direct vtable reads. + +Additional audit: the Seattle attribution URL coordinates and alert tokens are +redacted in JSON and in-place in the binary. Each manifest records the modified +string byte ranges and updated SHA-256 hashes. diff --git a/tests/fixtures/wk3-paired/hangzhou/apple.bin b/tests/fixtures/wk3-paired/hangzhou/apple.bin new file mode 100644 index 00000000..bcebe175 Binary files /dev/null and b/tests/fixtures/wk3-paired/hangzhou/apple.bin differ diff --git a/tests/fixtures/wk3-paired/hangzhou/apple.json b/tests/fixtures/wk3-paired/hangzhou/apple.json new file mode 100644 index 00000000..6c4dc0a8 --- /dev/null +++ b/tests/fixtures/wk3-paired/hangzhou/apple.json @@ -0,0 +1 @@ +{"news":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569406,"expireTime":1794573006,"temporarilyUnavailable":false,"sourceType":"APPLE_INTERNAL","reportedTime":1794569406},"placements":[]},"airQuality":{"metadata":{"language":"en","latitude":0,"longitude":0,"expireTime":1794573000,"sourceType":"STATION","attributionUrl":"https://www.qweather.com/air/a/00.0000,000.00?from=AppleWeatherService&lang=en","readTime":1794569400,"temporarilyUnavailable":false,"providerName":"QWeather","reportedTime":1794564000},"index":50,"pollutants":[{"amount":9.0,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"SO2"},{"amount":34.0,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"PM2_5"},{"amount":18.0,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"OZONE"},{"amount":0.7,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"CO"},{"amount":79.0,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"NO2"},{"amount":50.0,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"PM10"}],"scale":"HJ6332012.2604","primaryPollutant":"NOT_AVAILABLE","previousDayComparison":"UNKNOWN","categoryIndex":1,"isSignificant":true},"currentWeather":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569406,"expireTime":1794569706,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794569406},"daylight":false,"humidity":82,"pressure":1019.95459,"temperature":19.18985,"visibility":16033.438477,"asOf":1794569406,"precipitationIntensity":0.0,"temperatureDewPoint":16.042374,"windGust":3.437658,"temperatureApparent":20.357151,"snowfallAmountPrevious6h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"snowfallAmountPrevious24h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountNext6hByType":[{"expected":0.850883,"minimum":0.850883,"maximum":0.850883,"precipitationType":["RAIN"]}],"snowfallAmountNext24h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountPrevious6hByType":[],"snowfallAmountPrevious1h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"pressureTrend":"RISING","windDirection":116,"windSpeed":2.820522,"cloudCoverLowAltPct":1,"perceivedPrecipitationIntensity":0.0,"precipitationAmountPrevious1hByType":[],"cloudCover":95,"precipitationAmountNext1hByType":[],"snowfallAmountNext1h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountPrevious24hByType":[{"expected":0.644,"minimum":0.644,"maximum":0.644,"precipitationType":["RAIN"]}],"cloudCoverMidAltPct":93,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":83,"snowfallAmountNext6h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"uvIndex":0,"precipitationAmountNext24hByType":[{"expected":9.529001,"minimum":9.529001,"maximum":9.529001,"precipitationType":["RAIN"]}]},"weatherChanges":{"metadata":{"language":"en","latitude":0,"longitude":0,"expireTime":1794573006,"sourceType":"MODELED","attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569406,"temporarilyUnavailable":false,"reportedTime":1794567934},"changes":[{"forecastEnd":1794672000,"maxTemperatureChange":"STEADY","forecastStart":1794585600,"minTemperatureChange":"STEADY","dayPrecipitationChange":"INC","nightPrecipitationChange":"DEC"},{"forecastEnd":1794758400,"maxTemperatureChange":"STEADY","forecastStart":1794672000,"minTemperatureChange":"STEADY","dayPrecipitationChange":"DEC","nightPrecipitationChange":"STEADY"}],"forecastEnd":1794758400,"forecastStart":1794585600},"weatherAlerts":{"metadata":{"language":"en-US","latitude":0,"longitude":0,"expireTime":1794569706,"sourceType":"STATION","attributionUrl":"https://www.qweather.com","readTime":1794569406,"temporarilyUnavailable":false,"providerName":"National Early Warning Center","reportedTime":1794569406},"alerts":[]},"historicalComparisons":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569406,"expireTime":1794573006,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"comparisons":[{"condition":"TEMPERATURE_MAX","deviation":"NORMAL","baselineValue":17.244995,"baselineStartDate":0,"currentValue":20.800938,"baselineType":"MEAN"},{"condition":"PRECIPITATION","deviation":"NORMAL","baselineValue":0.0,"baselineStartDate":0,"currentValue":0.0,"baselineType":"MEAN"}]},"forecastDaily":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569406,"expireTime":1794573006,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"days":[{"moonrise":1794534809,"moonset":1794570807,"sunrise":1794522168,"sunset":1794560632,"forecastEnd":1794585600,"sunriseCivil":1794520650,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":20.800938,"solarMidnight":1794584614,"windSpeedAvg":3.814125,"temperatureMaxTime":1794549662,"windGustSpeedMax":9.5688,"sunriseAstronomical":1794517209,"forecastStart":1794499200,"moonPhase":"WAXING_CRESCENT","humidityMax":87,"precipitationAmountByType":[{"expected":0.622,"minimum":0.622,"maximum":0.622,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794565589,"visibilityMax":19238.0,"precipitationChance":56,"temperatureMin":15.190534,"maxUvIndex":4,"temperatureMinTime":1794517604,"sunsetNautical":1794563880,"daytimeForecast":{"humidity":77,"daylight":true,"forecastEnd":1794567600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":20.800938,"uvIndexMin":0,"temperatureApparentMin":15.483466,"windGustSpeedMax":9.5688,"perceivedPrecipitationIntensityMax":0.302,"uvIndexMax":4,"temperatureApparentMax":21.281811,"forecastStart":1794524400,"windDirection":32,"windSpeed":3.91425,"humidityMax":87,"precipitationAmountByType":[{"expected":0.125,"minimum":0.125,"maximum":0.125,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":8,"visibilityMax":19238.0,"cloudCover":96,"precipitationChance":31,"temperatureMin":15.25,"cloudCoverMidAltPct":82,"precipitationIntensityMax":0.067,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":89,"humidityMin":68,"visibilityMin":13129.0,"precipitationType":["RAIN"],"windSpeedMax":5.9364},"conditionCode":["DRIZZLE"],"humidityMin":68,"solarNoon":1794541398,"visibilityMin":10597.0,"overnightForecast":{"humidity":85,"daylight":false,"forecastEnd":1794610800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":19.432705,"uvIndexMin":0,"temperatureApparentMin":19.020458,"windGustSpeedMax":8.5572,"perceivedPrecipitationIntensityMax":1.189,"uvIndexMax":0,"temperatureApparentMax":20.606981,"forecastStart":1794567600,"windDirection":325,"windSpeed":3.1044,"humidityMax":86,"precipitationAmountByType":[{"expected":6.367,"minimum":6.367,"maximum":6.367,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":45,"visibilityMax":16314.0,"cloudCover":99,"precipitationChance":89,"temperatureMin":18.13209,"cloudCoverMidAltPct":95,"precipitationIntensityMax":1.376,"conditionCode":["RAIN"],"cloudCoverHighAltPct":96,"humidityMin":80,"visibilityMin":4861.0,"precipitationType":["RAIN"],"windSpeedMax":4.0608},"precipitationType":["RAIN"],"sunriseNautical":1794518920,"sunsetCivil":1794562152,"windSpeedMax":6.1056,"restOfDayForecast":{"humidity":84,"daylight":false,"forecastEnd":1794585600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":19.18985,"uvIndexMin":0,"temperatureApparentMin":19.723528,"windGustSpeedMax":4.9716,"perceivedPrecipitationIntensityMax":0.608,"uvIndexMax":0,"temperatureApparentMax":20.378323,"forecastStart":1794569406,"windDirection":118,"windSpeed":2.516788,"humidityMax":85,"precipitationAmountByType":[{"expected":0.4,"minimum":0.4,"maximum":0.4,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":16,"visibilityMax":16314.0,"cloudCover":97,"precipitationChance":44,"temperatureMin":18.43,"cloudCoverMidAltPct":95,"precipitationIntensityMax":0.291,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":90,"humidityMin":82,"visibilityMin":10597.0,"precipitationType":["RAIN"],"windSpeedMax":3.294}},{"moonrise":1794623929,"moonset":1794660566,"sunrise":1794608618,"sunset":1794647000,"forecastEnd":1794672000,"sunriseCivil":1794607096,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":20.408365,"solarMidnight":1794671023,"windSpeedAvg":4.6044,"temperatureMaxTime":1794633910,"windGustSpeedMax":16.74,"sunriseAstronomical":1794603652,"forecastStart":1794585600,"moonPhase":"WAXING_CRESCENT","humidityMax":98,"precipitationAmountByType":[{"expected":9.129001,"minimum":9.129001,"maximum":9.129001,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794651965,"visibilityMax":20319.0,"precipitationChance":98,"temperatureMin":15.61,"maxUvIndex":3,"temperatureMinTime":1794672000,"sunsetNautical":1794650253,"daytimeForecast":{"humidity":82,"daylight":true,"forecastEnd":1794654000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":20.408365,"uvIndexMin":0,"temperatureApparentMin":18.308884,"windGustSpeedMax":16.74,"perceivedPrecipitationIntensityMax":1.004,"uvIndexMax":3,"temperatureApparentMax":20.453522,"forecastStart":1794610800,"windDirection":1,"windSpeed":5.25075,"humidityMax":88,"precipitationAmountByType":[{"expected":3.162,"minimum":3.162,"maximum":3.162,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":73,"visibilityMax":20319.0,"cloudCover":98,"precipitationChance":93,"temperatureMin":17.6,"cloudCoverMidAltPct":82,"precipitationIntensityMax":0.908,"conditionCode":["RAIN"],"cloudCoverHighAltPct":82,"humidityMin":77,"visibilityMin":9793.0,"precipitationType":["RAIN"],"windSpeedMax":7.3512},"conditionCode":["RAIN"],"humidityMin":77,"solarNoon":1794627807,"visibilityMin":4861.0,"overnightForecast":{"humidity":96,"daylight":false,"forecastEnd":1794697200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":17.6,"uvIndexMin":0,"temperatureApparentMin":14.197939,"windGustSpeedMax":19.152,"perceivedPrecipitationIntensityMax":0.354,"uvIndexMax":0,"temperatureApparentMax":18.308884,"forecastStart":1794654000,"windDirection":3,"windSpeed":5.7861,"humidityMax":100,"precipitationAmountByType":[{"expected":0.126,"minimum":0.126,"maximum":0.126,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":93,"visibilityMax":18939.0,"cloudCover":96,"precipitationChance":26,"temperatureMin":14.570957,"cloudCoverMidAltPct":35,"precipitationIntensityMax":0.093,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":3,"humidityMin":87,"visibilityMin":12824.0,"precipitationType":["RAIN"],"windSpeedMax":8.496},"precipitationType":["RAIN"],"sunriseNautical":1794605364,"sunsetCivil":1794648523,"windSpeedMax":7.3512},{"moonrise":1794712720,"moonset":1794750377,"sunrise":1794695068,"sunset":1794733370,"forecastEnd":1794758400,"sunriseCivil":1794693543,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":20.175529,"solarMidnight":1794757433,"windSpeedAvg":7.4865,"temperatureMaxTime":1794716785,"windGustSpeedMax":22.395601,"sunriseAstronomical":1794690094,"forecastStart":1794672000,"moonPhase":"WAXING_CRESCENT","humidityMax":100,"precipitationAmountByType":[{"expected":0.126,"minimum":0.126,"maximum":0.126,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794738342,"visibilityMax":21906.0,"precipitationChance":43,"temperatureMin":14.570957,"maxUvIndex":4,"temperatureMinTime":1794687824,"sunsetNautical":1794736628,"daytimeForecast":{"humidity":69,"daylight":true,"forecastEnd":1794740400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":20.175529,"uvIndexMin":0,"temperatureApparentMin":15.482128,"windGustSpeedMax":22.395601,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":4,"temperatureApparentMax":19.936546,"forecastStart":1794697200,"windDirection":8,"windSpeed":8.397449,"humidityMax":87,"precipitationAmountByType":[],"cloudCoverLowAltPct":59,"visibilityMax":21906.0,"cloudCover":77,"precipitationChance":0,"temperatureMin":15.64,"cloudCoverMidAltPct":13,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":50,"humidityMin":62,"visibilityMin":18937.0,"precipitationType":["CLEAR"],"windSpeedMax":9.6408},"conditionCode":["DRIZZLE"],"humidityMin":62,"solarNoon":1794714216,"visibilityMin":12824.0,"overnightForecast":{"humidity":83,"daylight":false,"forecastEnd":1794783600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":17.07,"uvIndexMin":0,"temperatureApparentMin":13.370182,"windGustSpeedMax":15.9588,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":16.961658,"forecastStart":1794740400,"windDirection":29,"windSpeed":5.632051,"humidityMax":90,"precipitationAmountByType":[],"cloudCoverLowAltPct":51,"visibilityMax":20055.0,"cloudCover":84,"precipitationChance":0,"temperatureMin":13.299702,"cloudCoverMidAltPct":26,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":57,"humidityMin":74,"visibilityMin":17620.0,"precipitationType":["CLEAR"],"windSpeedMax":7.3404},"precipitationType":["RAIN"],"sunriseNautical":1794691809,"sunsetCivil":1794734895,"windSpeedMax":9.6408},{"moonrise":1794801228,"moonset":1794840185,"sunrise":1794781518,"sunset":1794819740,"forecastEnd":1794844800,"sunriseCivil":1794779990,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":18.463999,"solarMidnight":1794843843,"windSpeedAvg":6.425475,"temperatureMaxTime":1794807928,"windGustSpeedMax":16.7616,"sunriseAstronomical":1794776537,"forecastStart":1794758400,"moonPhase":"WAXING_CRESCENT","humidityMax":90,"precipitationAmountByType":[],"sunsetAstronomical":1794824720,"visibilityMax":21797.0,"precipitationChance":0,"temperatureMin":13.299702,"maxUvIndex":4,"temperatureMinTime":1794772656,"sunsetNautical":1794823005,"daytimeForecast":{"humidity":70,"daylight":true,"forecastEnd":1794826800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":18.463999,"uvIndexMin":0,"temperatureApparentMin":15.188446,"windGustSpeedMax":16.7616,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":4,"temperatureApparentMax":18.230001,"forecastStart":1794783600,"windDirection":59,"windSpeed":6.97035,"humidityMax":79,"precipitationAmountByType":[],"cloudCoverLowAltPct":44,"visibilityMax":21797.0,"cloudCover":76,"precipitationChance":0,"temperatureMin":14.93,"cloudCoverMidAltPct":21,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":53,"humidityMin":64,"visibilityMin":17620.0,"precipitationType":["CLEAR"],"windSpeedMax":8.856},"conditionCode":["MOSTLY_CLOUDY"],"humidityMin":64,"solarNoon":1794800627,"visibilityMin":17620.0,"overnightForecast":{"humidity":85,"daylight":false,"forecastEnd":1794870000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":16.74,"uvIndexMin":0,"temperatureApparentMin":11.96024,"windGustSpeedMax":14.7816,"perceivedPrecipitationIntensityMax":0.799,"uvIndexMax":0,"temperatureApparentMax":16.213861,"forecastStart":1794826800,"windDirection":50,"windSpeed":6.6612,"humidityMax":94,"precipitationAmountByType":[{"expected":0.224,"minimum":0.224,"maximum":0.224,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":45,"visibilityMax":19767.0,"cloudCover":63,"precipitationChance":22,"temperatureMin":12.188884,"cloudCoverMidAltPct":46,"precipitationIntensityMax":0.534,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":22,"humidityMin":72,"visibilityMin":13908.0,"precipitationType":["RAIN"],"windSpeedMax":7.4988},"precipitationType":["CLEAR"],"sunriseNautical":1794778254,"sunsetCivil":1794821269,"windSpeedMax":8.856},{"moonrise":1794889527,"moonset":1794929975,"sunrise":1794867969,"sunset":1794906113,"forecastEnd":1794931200,"sunriseCivil":1794866438,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":15.357734,"solarMidnight":1794930255,"windSpeedAvg":7.7052,"temperatureMaxTime":1794890602,"windGustSpeedMax":22.726801,"sunriseAstronomical":1794862980,"forecastStart":1794844800,"moonPhase":"WAXING_CRESCENT","humidityMax":100,"precipitationAmountByType":[{"expected":13.804999,"minimum":13.804999,"maximum":13.804999,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794911100,"visibilityMax":18315.0,"precipitationChance":84,"temperatureMin":11.48,"maxUvIndex":4,"temperatureMinTime":1794931200,"sunsetNautical":1794909383,"daytimeForecast":{"humidity":89,"daylight":true,"forecastEnd":1794913200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":15.357734,"uvIndexMin":0,"temperatureApparentMin":11.665749,"windGustSpeedMax":13.741199,"perceivedPrecipitationIntensityMax":1.144,"uvIndexMax":4,"temperatureApparentMax":15.194091,"forecastStart":1794870000,"windDirection":23,"windSpeed":7.07115,"humidityMax":100,"precipitationAmountByType":[{"expected":9.075,"minimum":9.075,"maximum":9.075,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":66,"visibilityMax":13908.0,"cloudCover":90,"precipitationChance":80,"temperatureMin":12.39,"cloudCoverMidAltPct":85,"precipitationIntensityMax":1.248,"conditionCode":["RAIN"],"cloudCoverHighAltPct":13,"humidityMin":82,"visibilityMin":4911.0,"precipitationType":["RAIN"],"windSpeedMax":9.8604},"conditionCode":["RAIN"],"humidityMin":82,"solarNoon":1794887038,"visibilityMin":4911.0,"overnightForecast":{"humidity":97,"daylight":false,"forecastEnd":1794956400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":12.74,"uvIndexMin":0,"temperatureApparentMin":7.883188,"windGustSpeedMax":33.530403,"perceivedPrecipitationIntensityMax":1.144,"uvIndexMax":0,"temperatureApparentMax":11.665749,"forecastStart":1794913200,"windDirection":339,"windSpeed":12.117449,"humidityMax":100,"precipitationAmountByType":[{"expected":5.712,"minimum":5.712,"maximum":5.712,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":78,"visibilityMax":19540.0,"cloudCover":97,"precipitationChance":72,"temperatureMin":10.771482,"cloudCoverMidAltPct":90,"precipitationIntensityMax":1.248,"conditionCode":["RAIN"],"cloudCoverHighAltPct":2,"humidityMin":83,"visibilityMin":4911.0,"precipitationType":["RAIN"],"windSpeedMax":14.191199},"precipitationType":["RAIN"],"sunriseNautical":1794864698,"sunsetCivil":1794907644,"windSpeedMax":12.492},{"moonrise":1794977686,"sunrise":1794954419,"sunset":1794992487,"forecastEnd":1795017600,"sunriseCivil":1794952885,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.762805,"solarMidnight":1795016667,"windSpeedAvg":16.059074,"temperatureMaxTime":1794981021,"windGustSpeedMax":45.216,"sunriseAstronomical":1794949423,"forecastStart":1794931200,"moonPhase":"FIRST_QUARTER","humidityMax":100,"precipitationAmountByType":[{"expected":1.206,"minimum":1.206,"maximum":1.206,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794997482,"visibilityMax":22986.0,"precipitationChance":65,"temperatureMin":7.876788,"maxUvIndex":4,"temperatureMinTime":1795007096,"sunsetNautical":1794995763,"daytimeForecast":{"humidity":58,"daylight":true,"forecastEnd":1794999600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.762805,"uvIndexMin":0,"temperatureApparentMin":4.5453,"windGustSpeedMax":45.216,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":4,"temperatureApparentMax":9.23888,"forecastStart":1794956400,"windDirection":321,"windSpeed":19.52055,"humidityMax":83,"precipitationAmountByType":[],"cloudCoverLowAltPct":18,"visibilityMax":22986.0,"cloudCover":51,"precipitationChance":0,"temperatureMin":9.46,"cloudCoverMidAltPct":26,"precipitationIntensityMax":0.0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":50,"humidityMin":44,"visibilityMin":19540.0,"precipitationType":["CLEAR"],"windSpeedMax":23.205599},"conditionCode":["DRIZZLE"],"humidityMin":44,"solarNoon":1794973450,"visibilityMin":7687.0,"overnightForecast":{"humidity":51,"daylight":false,"forecastEnd":1795042800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.46,"uvIndexMin":0,"temperatureApparentMin":3.700094,"windGustSpeedMax":30.510002,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":9.349154,"forecastStart":1794999600,"windDirection":327,"windSpeed":9.3105,"humidityMax":53,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":22302.0,"cloudCover":26,"precipitationChance":0,"temperatureMin":7.255062,"cloudCoverMidAltPct":4,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":44,"humidityMin":47,"visibilityMin":20816.0,"precipitationType":["CLEAR"],"windSpeedMax":15.1164},"precipitationType":["RAIN"],"sunriseNautical":1794951143,"sunsetCivil":1794994021,"windSpeedMax":23.205599},{"moonrise":1795065789,"moonset":1795019775,"sunrise":1795040869,"sunset":1795078863,"forecastEnd":1795104000,"sunriseCivil":1795039333,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":12.891275,"solarMidnight":1795103081,"windSpeedAvg":4.99455,"temperatureMaxTime":1795069099,"windGustSpeedMax":22.392,"sunriseAstronomical":1795035866,"forecastStart":1795017600,"moonPhase":"WAXING_GIBBOUS","humidityMax":64,"precipitationAmountByType":[],"sunsetAstronomical":1795083865,"visibilityMax":23706.0,"precipitationChance":0,"temperatureMin":7.255062,"maxUvIndex":4,"temperatureMinTime":1795033627,"sunsetNautical":1795082144,"daytimeForecast":{"humidity":42,"daylight":true,"forecastEnd":1795086000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":12.891275,"uvIndexMin":0,"temperatureApparentMin":9.349154,"windGustSpeedMax":12.2292,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":4,"temperatureApparentMax":15.174844,"forecastStart":1795042800,"windDirection":304,"windSpeed":4.40835,"humidityMax":55,"precipitationAmountByType":[],"cloudCoverLowAltPct":8,"visibilityMax":23706.0,"cloudCover":26,"precipitationChance":0,"temperatureMin":9.16,"cloudCoverMidAltPct":14,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":29,"humidityMin":37,"visibilityMin":20816.0,"precipitationType":["CLEAR"],"windSpeedMax":5.1228},"conditionCode":["MOSTLY_CLEAR"],"humidityMin":37,"solarNoon":1795059863,"visibilityMin":20816.0,"overnightForecast":{"humidity":64,"daylight":false,"forecastEnd":1795129200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.81,"uvIndexMin":0,"temperatureApparentMin":7.323315,"windGustSpeedMax":13.0968,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":9.450615,"forecastStart":1795086000,"windDirection":254,"windSpeed":3.9546,"humidityMax":69,"precipitationAmountByType":[],"cloudCoverLowAltPct":1,"visibilityMax":22262.0,"cloudCover":25,"precipitationChance":0,"temperatureMin":8.265721,"cloudCoverMidAltPct":7,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":35,"humidityMin":55,"visibilityMin":15087.0,"precipitationType":["CLEAR"],"windSpeedMax":5.1336},"precipitationType":["CLEAR"],"sunriseNautical":1795037588,"sunsetCivil":1795080400,"windSpeedMax":9.9396},{"moonrise":1795153911,"moonset":1795109638,"sunrise":1795127320,"sunset":1795165240,"forecastEnd":1795190400,"sunriseCivil":1795125781,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":17.98996,"solarMidnight":1795189495,"windSpeedAvg":4.918575,"temperatureMaxTime":1795153458,"windGustSpeedMax":17.6364,"sunriseAstronomical":1795122309,"forecastStart":1795104000,"moonPhase":"WAXING_GIBBOUS","humidityMax":72,"precipitationAmountByType":[],"sunsetAstronomical":1795170250,"visibilityMax":23946.0,"precipitationChance":0,"temperatureMin":7.89,"maxUvIndex":4,"temperatureMinTime":1795190400,"sunsetNautical":1795168527,"daytimeForecast":{"humidity":44,"daylight":true,"forecastEnd":1795172400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":17.98996,"uvIndexMin":0,"temperatureApparentMin":8.861073,"windGustSpeedMax":17.6364,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":4,"temperatureApparentMax":18.810215,"forecastStart":1795129200,"windDirection":316,"windSpeed":5.967,"humidityMax":69,"precipitationAmountByType":[],"cloudCoverLowAltPct":1,"visibilityMax":23946.0,"cloudCover":25,"precipitationChance":0,"temperatureMin":8.28,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":42,"humidityMin":33,"visibilityMin":12780.0,"precipitationType":["CLEAR"],"windSpeedMax":7.2972},"conditionCode":["MOSTLY_CLEAR"],"humidityMin":33,"solarNoon":1795146277,"visibilityMin":12780.0,"overnightForecast":{"humidity":70,"daylight":false,"forecastEnd":1795215600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":12.99,"uvIndexMin":0,"temperatureApparentMin":6.877808,"windGustSpeedMax":9.0792,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":11.841161,"forecastStart":1795172400,"windDirection":262,"windSpeed":3.2652,"humidityMax":78,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":21599.0,"cloudCover":14,"precipitationChance":0,"temperatureMin":7.439852,"cloudCoverMidAltPct":1,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":3,"humidityMin":52,"visibilityMin":16929.0,"precipitationType":["CLEAR"],"windSpeedMax":4.3056},"precipitationType":["CLEAR"],"sunriseNautical":1795124033,"sunsetCivil":1795166780,"windSpeedMax":7.2972},{"moonrise":1795242146,"moonset":1795199632,"sunrise":1795213770,"sunset":1795251619,"forecastEnd":1795276800,"sunriseCivil":1795212229,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":14.780753,"solarMidnight":1795275909,"windSpeedAvg":4.989,"temperatureMaxTime":1795243174,"windGustSpeedMax":16.621201,"sunriseAstronomical":1795208752,"forecastStart":1795190400,"moonPhase":"WAXING_GIBBOUS","humidityMax":78,"precipitationAmountByType":[],"sunsetAstronomical":1795256636,"visibilityMax":23325.0,"precipitationChance":0,"temperatureMin":7.439852,"maxUvIndex":4,"temperatureMinTime":1795197707,"sunsetNautical":1795254911,"daytimeForecast":{"humidity":59,"daylight":true,"forecastEnd":1795258800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":14.780753,"uvIndexMin":0,"temperatureApparentMin":8.329831,"windGustSpeedMax":16.621201,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":4,"temperatureApparentMax":16.340837,"forecastStart":1795215600,"windDirection":102,"windSpeed":5.97705,"humidityMax":78,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":23325.0,"cloudCover":34,"precipitationChance":0,"temperatureMin":7.769081,"cloudCoverMidAltPct":14,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":47,"visibilityMin":16929.0,"precipitationType":["CLEAR"],"windSpeedMax":6.7788},"conditionCode":["MOSTLY_CLEAR"],"humidityMin":47,"solarNoon":1795232692,"visibilityMin":16929.0,"overnightForecast":{"humidity":74,"daylight":false,"forecastEnd":1795302000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":14.28,"uvIndexMin":0,"temperatureApparentMin":8.172373,"windGustSpeedMax":11.3004,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":13.514278,"forecastStart":1795258800,"windDirection":7,"windSpeed":3.56895,"humidityMax":82,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":20158.0,"cloudCover":32,"precipitationChance":0,"temperatureMin":8.565985,"cloudCoverMidAltPct":23,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":57,"visibilityMin":15954.0,"precipitationType":["CLEAR"],"windSpeedMax":5.5764},"precipitationType":["CLEAR"],"sunriseNautical":1795210478,"sunsetCivil":1795253161,"windSpeedMax":6.7788},{"moonrise":1795330591,"moonset":1795289830,"sunrise":1795300221,"sunset":1795338000,"forecastEnd":1795363200,"sunriseCivil":1795298676,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":19.560213,"solarMidnight":1795362325,"windSpeedAvg":5.3463,"temperatureMaxTime":1795327302,"windGustSpeedMax":20.916,"sunriseAstronomical":1795295195,"forecastStart":1795276800,"moonPhase":"WAXING_GIBBOUS","humidityMax":89,"precipitationAmountByType":[{"expected":0.703,"minimum":0.703,"maximum":0.703,"precipitationType":["RAIN"]}],"sunsetAstronomical":1795343024,"visibilityMax":21599.0,"precipitationChance":11,"temperatureMin":8.565985,"maxUvIndex":3,"temperatureMinTime":1795285929,"sunsetNautical":1795341297,"daytimeForecast":{"humidity":53,"daylight":true,"forecastEnd":1795345200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":19.560213,"uvIndexMin":0,"temperatureApparentMin":9.880361,"windGustSpeedMax":20.916,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":3,"temperatureApparentMax":18.62365,"forecastStart":1795302000,"windDirection":6,"windSpeed":7.1967,"humidityMax":82,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":21599.0,"cloudCover":74,"precipitationChance":0,"temperatureMin":8.84,"cloudCoverMidAltPct":25,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":5,"humidityMin":42,"visibilityMin":15954.0,"precipitationType":["CLEAR"],"windSpeedMax":10.3716},"conditionCode":["MOSTLY_CLOUDY"],"humidityMin":42,"solarNoon":1795319107,"visibilityMin":11352.0,"overnightForecast":{"humidity":80,"daylight":false,"forecastEnd":1795388400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":15.23,"uvIndexMin":0,"temperatureApparentMin":9.337468,"windGustSpeedMax":11.779201,"perceivedPrecipitationIntensityMax":0.901,"uvIndexMax":0,"temperatureApparentMax":14.363777,"forecastStart":1795345200,"windDirection":343,"windSpeed":4.0209,"humidityMax":89,"precipitationAmountByType":[{"expected":4.412,"minimum":4.412,"maximum":4.412,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":0,"visibilityMax":19948.0,"cloudCover":82,"precipitationChance":25,"temperatureMin":8.858445,"cloudCoverMidAltPct":82,"precipitationIntensityMax":0.705,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":0,"humidityMin":56,"visibilityMin":8248.0,"precipitationType":["RAIN"],"windSpeedMax":6.5952},"precipitationType":["RAIN"],"sunriseNautical":1795296924,"sunsetCivil":1795339545,"windSpeedMax":10.3716}]},"forecastHourly":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569406,"expireTime":1794573006,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"hours":[{"daylight":false,"humidity":80,"pressure":1020.090027,"temperature":19.43,"visibility":15751.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":2,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":15.889755,"windGust":4.0824,"temperatureApparent":20.595747,"cloudCover":96,"precipitationChance":0,"cloudCoverMidAltPct":94,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":94,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794567600,"pressureTrend":"RISING","windDirection":137,"uvIndex":0,"windSpeed":2.844,"snowfallIntensity":0.0},{"daylight":false,"humidity":84,"pressure":1019.820007,"temperature":18.84,"visibility":16314.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":1,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":16.078018,"windGust":2.7972,"temperatureApparent":20.008615,"cloudCover":94,"precipitationChance":0,"cloudCoverMidAltPct":92,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":73,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794571200,"pressureTrend":"STEADY","windDirection":96,"uvIndex":0,"windSpeed":2.7972,"snowfallIntensity":0.0},{"daylight":false,"humidity":84,"pressure":1020.140015,"temperature":18.799999,"visibility":10747.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":8,"perceivedPrecipitationIntensity":0.608,"precipitationIntensity":0.291,"temperatureDewPoint":16.038864,"windGust":4.9716,"temperatureApparent":19.944382,"cloudCover":98,"precipitationChance":25,"cloudCoverMidAltPct":96,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":88,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.291,"minimum":0.291,"maximum":0.291},"forecastStart":1794574800,"pressureTrend":"STEADY","windDirection":141,"uvIndex":0,"windSpeed":3.294,"snowfallIntensity":0.0},{"daylight":false,"humidity":84,"pressure":1019.76001,"temperature":18.709999,"visibility":10597.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":20,"perceivedPrecipitationIntensity":0.382,"precipitationIntensity":0.109,"temperatureDewPoint":15.950729,"windGust":2.4372,"temperatureApparent":19.976105,"cloudCover":97,"precipitationChance":6,"cloudCoverMidAltPct":92,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":94,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.109,"minimum":0.109,"maximum":0.109},"forecastStart":1794578400,"pressureTrend":"STEADY","windDirection":155,"uvIndex":0,"windSpeed":2.4372,"snowfallIntensity":0.0},{"daylight":false,"humidity":84,"pressure":1019.530029,"temperature":18.620001,"visibility":14656.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":26,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":15.862625,"windGust":2.1492,"temperatureApparent":20.0208,"cloudCover":100,"precipitationChance":0,"cloudCoverMidAltPct":100,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":98,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794582000,"pressureTrend":"STEADY","windDirection":84,"uvIndex":0,"windSpeed":1.6092,"snowfallIntensity":0.0},{"daylight":false,"humidity":85,"pressure":1018.830017,"temperature":18.43,"visibility":13339.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":36,"perceivedPrecipitationIntensity":0.492,"precipitationIntensity":0.185,"temperatureDewPoint":15.861649,"windGust":3.492,"temperatureApparent":19.723528,"cloudCover":98,"precipitationChance":44,"cloudCoverMidAltPct":90,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":97,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.185,"minimum":0.185,"maximum":0.185},"forecastStart":1794585600,"pressureTrend":"FALLING","windDirection":45,"uvIndex":0,"windSpeed":2.3652,"snowfallIntensity":0.0},{"daylight":false,"humidity":85,"pressure":1018.440002,"temperature":18.42,"visibility":6740.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":38,"perceivedPrecipitationIntensity":0.796,"precipitationIntensity":0.53,"temperatureDewPoint":15.851852,"windGust":4.2948,"temperatureApparent":19.634636,"cloudCover":100,"precipitationChance":61,"cloudCoverMidAltPct":98,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.53,"minimum":0.53,"maximum":0.53},"forecastStart":1794589200,"pressureTrend":"FALLING","windDirection":311,"uvIndex":0,"windSpeed":3.06,"snowfallIntensity":0.0},{"daylight":false,"humidity":86,"pressure":1017.809998,"temperature":18.25,"visibility":5734.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":61,"perceivedPrecipitationIntensity":1.135,"precipitationIntensity":1.225,"temperatureDewPoint":15.868057,"windGust":5.4144,"temperatureApparent":19.452429,"cloudCover":100,"precipitationChance":74,"cloudCoverMidAltPct":98,"conditionCode":["RAIN"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":1.225,"minimum":1.225,"maximum":1.225},"forecastStart":1794592800,"pressureTrend":"FALLING","windDirection":317,"uvIndex":0,"windSpeed":3.1896,"snowfallIntensity":0.0},{"daylight":false,"humidity":86,"pressure":1017.450012,"temperature":18.23,"visibility":4861.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":63,"perceivedPrecipitationIntensity":1.168,"precipitationIntensity":1.315,"temperatureDewPoint":15.848434,"windGust":4.05,"temperatureApparent":19.313406,"cloudCover":100,"precipitationChance":78,"cloudCoverMidAltPct":97,"conditionCode":["RAIN"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":1.315,"minimum":1.315,"maximum":1.315},"forecastStart":1794596400,"pressureTrend":"FALLING","windDirection":296,"uvIndex":0,"windSpeed":3.8376,"snowfallIntensity":0.0},{"daylight":false,"humidity":85,"pressure":1017.02002,"temperature":18.5,"visibility":9299.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":82,"perceivedPrecipitationIntensity":1.049,"precipitationIntensity":1.009,"temperatureDewPoint":15.930283,"windGust":6.426,"temperatureApparent":19.531525,"cloudCover":100,"precipitationChance":76,"cloudCoverMidAltPct":98,"conditionCode":["RAIN"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":1.009,"minimum":1.009,"maximum":1.009},"forecastStart":1794600000,"pressureTrend":"FALLING","windDirection":312,"uvIndex":0,"windSpeed":4.0608,"snowfallIntensity":0.0},{"daylight":false,"humidity":84,"pressure":1017.219971,"temperature":18.58,"visibility":9277.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":93,"perceivedPrecipitationIntensity":1.189,"precipitationIntensity":1.376,"temperatureDewPoint":15.823471,"windGust":8.5572,"temperatureApparent":19.657303,"cloudCover":100,"precipitationChance":75,"cloudCoverMidAltPct":95,"conditionCode":["RAIN"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":1.376,"minimum":1.376,"maximum":1.376},"forecastStart":1794603600,"pressureTrend":"STEADY","windDirection":294,"uvIndex":0,"windSpeed":3.8052,"snowfallIntensity":0.0},{"daylight":false,"humidity":86,"pressure":1017.190002,"temperature":18.190001,"visibility":10314.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":75,"perceivedPrecipitationIntensity":0.641,"precipitationIntensity":0.327,"temperatureDewPoint":15.809158,"windGust":6.9408,"temperatureApparent":19.273407,"cloudCover":100,"precipitationChance":67,"cloudCoverMidAltPct":90,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.327,"minimum":0.327,"maximum":0.327},"forecastStart":1794607200,"pressureTrend":"STEADY","windDirection":345,"uvIndex":0,"windSpeed":3.8304,"snowfallIntensity":0.0},{"daylight":true,"humidity":86,"pressure":1017.469971,"temperature":18.139999,"visibility":10244.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":69,"perceivedPrecipitationIntensity":0.758,"precipitationIntensity":0.475,"temperatureDewPoint":15.760056,"windGust":6.678,"temperatureApparent":19.020458,"cloudCover":100,"precipitationChance":64,"cloudCoverMidAltPct":90,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.475,"minimum":0.475,"maximum":0.475},"forecastStart":1794610800,"pressureTrend":"STEADY","windDirection":267,"uvIndex":0,"windSpeed":3.0888,"snowfallIntensity":0.0},{"daylight":true,"humidity":86,"pressure":1017.77002,"temperature":18.25,"visibility":10016.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":61,"perceivedPrecipitationIntensity":0.929,"precipitationIntensity":0.756,"temperatureDewPoint":15.868057,"windGust":9.2916,"temperatureApparent":18.76442,"cloudCover":98,"precipitationChance":62,"cloudCoverMidAltPct":87,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":97,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.756,"minimum":0.756,"maximum":0.756},"forecastStart":1794614400,"pressureTrend":"STEADY","windDirection":321,"uvIndex":1,"windSpeed":3.42,"snowfallIntensity":0.0},{"daylight":true,"humidity":85,"pressure":1018.090027,"temperature":18.440001,"visibility":9793.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":61,"perceivedPrecipitationIntensity":1.004,"precipitationIntensity":0.908,"temperatureDewPoint":15.871475,"windGust":8.899199,"temperatureApparent":18.646,"cloudCover":100,"precipitationChance":60,"cloudCoverMidAltPct":95,"conditionCode":["RAIN"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.908,"minimum":0.908,"maximum":0.908},"forecastStart":1794618000,"pressureTrend":"RISING","windDirection":327,"uvIndex":2,"windSpeed":4.338,"snowfallIntensity":0.0},{"daylight":true,"humidity":83,"pressure":1017.890015,"temperature":18.93,"visibility":11005.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":62,"perceivedPrecipitationIntensity":0.782,"precipitationIntensity":0.509,"temperatureDewPoint":15.978439,"windGust":10.3932,"temperatureApparent":18.786564,"cloudCover":100,"precipitationChance":55,"cloudCoverMidAltPct":100,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.509,"minimum":0.509,"maximum":0.509},"forecastStart":1794621600,"pressureTrend":"STEADY","windDirection":335,"uvIndex":3,"windSpeed":5.904,"snowfallIntensity":0.0},{"daylight":true,"humidity":82,"pressure":1017.369995,"temperature":19.190001,"visibility":12224.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":78,"perceivedPrecipitationIntensity":0.467,"precipitationIntensity":0.166,"temperatureDewPoint":16.042526,"windGust":8.6904,"temperatureApparent":19.263245,"cloudCover":100,"precipitationChance":54,"cloudCoverMidAltPct":95,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.166,"minimum":0.166,"maximum":0.166},"forecastStart":1794625200,"pressureTrend":"STEADY","windDirection":335,"uvIndex":3,"windSpeed":5.0472,"snowfallIntensity":0.0},{"daylight":true,"humidity":80,"pressure":1016.469971,"temperature":19.74,"visibility":17514.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":84,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":16.191422,"windGust":10.166399,"temperatureApparent":20.002459,"cloudCover":100,"precipitationChance":0,"cloudCoverMidAltPct":89,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":100,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794628800,"pressureTrend":"FALLING","windDirection":6,"uvIndex":3,"windSpeed":4.1148,"snowfallIntensity":0.0},{"daylight":true,"humidity":77,"pressure":1015.669983,"temperature":20.35,"visibility":19293.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":87,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":16.184067,"windGust":11.739599,"temperatureApparent":20.444609,"cloudCover":100,"precipitationChance":0,"cloudCoverMidAltPct":91,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":100,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794632400,"pressureTrend":"FALLING","windDirection":9,"uvIndex":3,"windSpeed":4.9464,"snowfallIntensity":0.0},{"daylight":true,"humidity":77,"pressure":1015.049988,"temperature":20.299999,"visibility":18453.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":77,"perceivedPrecipitationIntensity":0.343,"precipitationIntensity":0.087,"temperatureDewPoint":16.135635,"windGust":13.7844,"temperatureApparent":20.213022,"cloudCover":100,"precipitationChance":46,"cloudCoverMidAltPct":78,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.087,"minimum":0.087,"maximum":0.087},"forecastStart":1794636000,"pressureTrend":"FALLING","windDirection":9,"uvIndex":2,"windSpeed":5.7816,"snowfallIntensity":0.0},{"daylight":true,"humidity":80,"pressure":1014.969971,"temperature":19.610001,"visibility":20319.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":81,"perceivedPrecipitationIntensity":0.138,"precipitationIntensity":0.014,"temperatureDewPoint":16.064926,"windGust":16.588799,"temperatureApparent":19.245304,"cloudCover":100,"precipitationChance":39,"cloudCoverMidAltPct":77,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":98,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.014,"minimum":0.014,"maximum":0.014},"forecastStart":1794639600,"pressureTrend":"FALLING","windDirection":20,"uvIndex":1,"windSpeed":7.1136,"snowfallIntensity":0.0},{"daylight":true,"humidity":83,"pressure":1014.900024,"temperature":19.0,"visibility":19439.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":71,"perceivedPrecipitationIntensity":0.379,"precipitationIntensity":0.107,"temperatureDewPoint":16.04686,"windGust":16.74,"temperatureApparent":18.782953,"cloudCover":98,"precipitationChance":37,"cloudCoverMidAltPct":74,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":43,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.107,"minimum":0.107,"maximum":0.107},"forecastStart":1794643200,"pressureTrend":"FALLING","windDirection":21,"uvIndex":0,"windSpeed":7.3512,"snowfallIntensity":0.0},{"daylight":true,"humidity":85,"pressure":1015.280029,"temperature":18.5,"visibility":16217.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":70,"perceivedPrecipitationIntensity":0.138,"precipitationIntensity":0.014,"temperatureDewPoint":15.930283,"windGust":14.3244,"temperatureApparent":19.127232,"cloudCover":95,"precipitationChance":32,"cloudCoverMidAltPct":68,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":26,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.014,"minimum":0.014,"maximum":0.014},"forecastStart":1794646800,"pressureTrend":"STEADY","windDirection":26,"uvIndex":0,"windSpeed":5.7384,"snowfallIntensity":0.0},{"daylight":false,"humidity":84,"pressure":1015.840027,"temperature":18.540001,"visibility":13897.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":70,"perceivedPrecipitationIntensity":0.41,"precipitationIntensity":0.126,"temperatureDewPoint":15.784317,"windGust":13.32,"temperatureApparent":19.140728,"cloudCover":84,"precipitationChance":32,"cloudCoverMidAltPct":59,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":42,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.126,"minimum":0.126,"maximum":0.126},"forecastStart":1794650400,"pressureTrend":"RISING","windDirection":18,"uvIndex":0,"windSpeed":5.166,"snowfallIntensity":0.0}]},"forecastPeriodic":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569406,"expireTime":1794573006,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"forecasts":[{"periods":[{"humidity":84,"daylight":false,"forecastEnd":1794582000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":19.432705,"uvIndexMin":0,"temperatureApparentMin":19.937618,"windGustSpeedMax":4.9716,"perceivedPrecipitationIntensityMax":0.608,"uvIndexMax":0,"temperatureApparentMax":20.606981,"forecastStart":1794567600,"windDirection":128,"windSpeed":2.68875,"humidityMax":84,"precipitationAmountByType":[{"expected":0.4,"minimum":0.4,"maximum":0.4,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":11,"visibilityMax":16314.0,"cloudCover":97,"precipitationChance":30,"temperatureMin":18.620001,"cloudCoverMidAltPct":94,"precipitationIntensityMax":0.291,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":88,"humidityMin":80,"visibilityMin":10597.0,"precipitationType":["RAIN"],"windSpeedMax":3.294},{"humidity":85,"daylight":false,"forecastEnd":1794596400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":18.620001,"uvIndexMin":0,"temperatureApparentMin":19.312206,"windGustSpeedMax":5.4144,"perceivedPrecipitationIntensityMax":1.168,"uvIndexMax":0,"temperatureApparentMax":20.0208,"forecastStart":1794582000,"windDirection":332,"windSpeed":2.83455,"humidityMax":86,"precipitationAmountByType":[{"expected":1.94,"minimum":1.94,"maximum":1.94,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":45,"visibilityMax":14656.0,"cloudCover":100,"precipitationChance":78,"temperatureMin":18.209585,"cloudCoverMidAltPct":96,"precipitationIntensityMax":1.315,"conditionCode":["RAIN"],"cloudCoverHighAltPct":99,"humidityMin":84,"visibilityMin":4861.0,"precipitationType":["RAIN"],"windSpeedMax":3.8376},{"humidity":85,"daylight":false,"forecastEnd":1794610800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":18.597536,"uvIndexMin":0,"temperatureApparentMin":19.020458,"windGustSpeedMax":8.5572,"perceivedPrecipitationIntensityMax":1.189,"uvIndexMax":0,"temperatureApparentMax":19.666948,"forecastStart":1794596400,"windDirection":309,"windSpeed":3.7899,"humidityMax":86,"precipitationAmountByType":[{"expected":4.027,"minimum":4.027,"maximum":4.027,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":79,"visibilityMax":10314.0,"cloudCover":100,"precipitationChance":90,"temperatureMin":18.13209,"cloudCoverMidAltPct":94,"precipitationIntensityMax":1.376,"conditionCode":["RAIN"],"cloudCoverHighAltPct":100,"humidityMin":84,"visibilityMin":4861.0,"precipitationType":["RAIN"],"windSpeedMax":4.0608},{"humidity":85,"daylight":true,"forecastEnd":1794625200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":19.190001,"uvIndexMin":0,"temperatureApparentMin":18.645838,"windGustSpeedMax":10.3932,"perceivedPrecipitationIntensityMax":1.004,"uvIndexMax":3,"temperatureApparentMax":19.263245,"forecastStart":1794610800,"windDirection":325,"windSpeed":4.432499,"humidityMax":86,"precipitationAmountByType":[{"expected":2.648,"minimum":2.648,"maximum":2.648,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":64,"visibilityMax":12224.0,"cloudCover":100,"precipitationChance":84,"temperatureMin":18.139999,"cloudCoverMidAltPct":94,"precipitationIntensityMax":0.908,"conditionCode":["RAIN"],"cloudCoverHighAltPct":99,"humidityMin":82,"visibilityMin":9793.0,"precipitationType":["RAIN"],"windSpeedMax":5.904},{"humidity":79,"daylight":true,"forecastEnd":1794639600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":20.408365,"uvIndexMin":1,"temperatureApparentMin":19.245304,"windGustSpeedMax":16.588799,"perceivedPrecipitationIntensityMax":0.467,"uvIndexMax":3,"temperatureApparentMax":20.453522,"forecastStart":1794625200,"windDirection":6,"windSpeed":5.2308,"humidityMax":82,"precipitationAmountByType":[{"expected":0.253,"minimum":0.253,"maximum":0.253,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":82,"visibilityMax":20319.0,"cloudCover":100,"precipitationChance":69,"temperatureMin":19.190001,"cloudCoverMidAltPct":86,"precipitationIntensityMax":0.166,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":100,"humidityMin":77,"visibilityMin":12224.0,"precipitationType":["RAIN"],"windSpeedMax":7.1136},{"humidity":84,"daylight":true,"forecastEnd":1794654000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":19.610001,"uvIndexMin":0,"temperatureApparentMin":18.308884,"windGustSpeedMax":16.74,"perceivedPrecipitationIntensityMax":0.41,"uvIndexMax":1,"temperatureApparentMax":19.245304,"forecastStart":1794639600,"windDirection":21,"windSpeed":6.08895,"humidityMax":88,"precipitationAmountByType":[{"expected":0.261,"minimum":0.261,"maximum":0.261,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":73,"visibilityMax":20319.0,"cloudCover":93,"precipitationChance":59,"temperatureMin":17.6,"cloudCoverMidAltPct":66,"precipitationIntensityMax":0.126,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":48,"humidityMin":80,"visibilityMin":13897.0,"precipitationType":["RAIN"],"windSpeedMax":7.3512}],"periodLength":4}]}} diff --git a/tests/fixtures/wk3-paired/hangzhou/manifest.json b/tests/fixtures/wk3-paired/hangzhou/manifest.json new file mode 100644 index 00000000..6afe3367 --- /dev/null +++ b/tests/fixtures/wk3-paired/hangzhou/manifest.json @@ -0,0 +1,82 @@ +{ + "schema": "lib-rc-3.0.0-b66", + "public_test_location": "Hangzhou city centre (not device location)", + "responses": [ + { + "file": "apple.json", + "started": 1794569406.314852, + "finished": 1794569406.575212, + "status": 200, + "content_type": "application/json", + "bytes": 52693 + }, + { + "file": "apple.bin", + "started": 1794569406.314922, + "finished": 1794569406.559099, + "status": 200, + "content_type": "application/vnd.apple.flatbuffer;messageType=WK3.Weather", + "bytes": 11332 + } + ], + "sanitization": { + "coordinate_fields": [ + { + "root_slot": 0, + "latitude_offset": 11208, + "longitude_offset": 11204 + }, + { + "root_slot": 1, + "latitude_offset": 10860, + "longitude_offset": 10856 + }, + { + "root_slot": 2, + "latitude_offset": 10376, + "longitude_offset": 10372 + }, + { + "root_slot": 3, + "latitude_offset": 5028, + "longitude_offset": 5024 + }, + { + "root_slot": 4, + "latitude_offset": 1832, + "longitude_offset": 1828 + }, + { + "root_slot": 6, + "latitude_offset": 184, + "longitude_offset": 180 + }, + { + "root_slot": 7, + "latitude_offset": 420, + "longitude_offset": 416 + }, + { + "root_slot": 9, + "latitude_offset": 572, + "longitude_offset": 568 + }, + { + "root_slot": 14, + "latitude_offset": 704, + "longitude_offset": 700 + } + ], + "coordinate_urls": 1, + "method": "Only vtable-validated Metadata latitude/longitude floats and same-length coordinate URL digits replaced. No re-encoding." + }, + "sha256": { + "apple.json": "47f2bdeaad2495dcfb2619ea5e7e379e3071c4f9f69fdb6aa417be1acc9f32a2", + "apple.bin": "59ea8fe207345aad6c8ccb04f22b49b648fce0fa2566a7aed8aa035e875ed0a4" + }, + "schema_source": { + "weatherkit_dev": "cd9d7927094aff79906fe3430f339f39970ca46e", + "protocol_tag": "v2.0.5", + "protocol_commit": "c87c1dcd851ea9293141bcfb32fde1b2cca564b9" + } +} diff --git a/tests/fixtures/wk3-paired/seattle-repeat/apple.bin b/tests/fixtures/wk3-paired/seattle-repeat/apple.bin new file mode 100644 index 00000000..9ef661b8 Binary files /dev/null and b/tests/fixtures/wk3-paired/seattle-repeat/apple.bin differ diff --git a/tests/fixtures/wk3-paired/seattle-repeat/apple.json b/tests/fixtures/wk3-paired/seattle-repeat/apple.json new file mode 100644 index 00000000..f2386971 --- /dev/null +++ b/tests/fixtures/wk3-paired/seattle-repeat/apple.json @@ -0,0 +1 @@ +{"news":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569673,"expireTime":1794573273,"temporarilyUnavailable":false,"sourceType":"APPLE_INTERNAL","reportedTime":1794569673},"placements":[{"priority":2,"articles":[{"id":"A6ajisfZkSH-TzT_j18WQJQ","phenomena":[],"locale":"en_US","supportedStorefronts":["143441","143444","143455","143460"],"headlineOverride":"How the UV index helps you know when it’s safe to have fun in the sun","alertIds":[]}],"placement":["UV_DETAILS"]},{"priority":2,"articles":[{"id":"AxDHjEelNRh-tGqDejqtusw","phenomena":[],"locale":"en_US","supportedStorefronts":["143441","143444","143455","143460"],"headlineOverride":"How to prepare and stay safe if high winds are in your forecast","alertIds":[]}],"placement":["WIND_DETAILS"]},{"priority":2,"articles":[{"id":"A2uodtK8zT8qsl9ZTqOSsuw","phenomena":[],"locale":"en_US","supportedStorefronts":["143441","143444","143455","143460"],"headlineOverride":"How to use the air quality index to protect yourself from pollution and smoke","alertIds":[]}],"placement":["AIR_QUALITY_DETAILS"]}]},"airQuality":{"metadata":{"latitude":0,"longitude":0,"expireTime":1794571287,"sourceType":"MODELED","attributionUrl":"https://dynamic-link.breezometer.com/?lat=00.000&lon=-000.000&partner=apple","readTime":1794569673,"temporarilyUnavailable":false,"providerName":"BreezoMeter","reportedTime":1794567600},"index":59,"pollutants":[{"amount":5.0,"units":"PARTS_PER_BILLION","pollutantType":"OZONE"},{"amount":14.707169,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"PM2_5"},{"amount":32.544106,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"PM10"},{"amount":300.0,"units":"PARTS_PER_BILLION","pollutantType":"CO"},{"amount":24.375298,"units":"PARTS_PER_BILLION","pollutantType":"NO2"},{"amount":5.560594,"units":"PARTS_PER_BILLION","pollutantType":"NO"},{"amount":0.8,"units":"PARTS_PER_BILLION","pollutantType":"SO2"},{"amount":23.047903,"units":"PARTS_PER_BILLION","pollutantType":"NOX"}],"scale":"EPA_NowCast.2604","primaryPollutant":"PM2_5","previousDayComparison":"SAME","categoryIndex":2,"isSignificant":false},"currentWeather":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569673,"expireTime":1794569973,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794569673},"daylight":false,"humidity":84,"pressure":992.896606,"temperature":11.151289,"visibility":20750.910156,"asOf":1794569673,"precipitationIntensity":0.0,"temperatureDewPoint":8.548309,"windGust":10.370301,"temperatureApparent":10.537592,"snowfallAmountPrevious6h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"snowfallAmountPrevious24h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountNext6hByType":[],"snowfallAmountNext24h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountPrevious6hByType":[],"snowfallAmountPrevious1h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"pressureTrend":"FALLING","windDirection":94,"windSpeed":7.099107,"cloudCoverLowAltPct":3,"perceivedPrecipitationIntensity":0.0,"precipitationAmountPrevious1hByType":[],"cloudCover":84,"precipitationAmountNext1hByType":[],"snowfallAmountNext1h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountPrevious24hByType":[],"cloudCoverMidAltPct":86,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":97,"snowfallAmountNext6h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"uvIndex":0,"precipitationAmountNext24hByType":[{"expected":5.984974,"minimum":5.984974,"maximum":5.984974,"precipitationType":["RAIN"]}]},"weatherChanges":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569673,"expireTime":1794573273,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"changes":[{"forecastEnd":1794729600,"maxTemperatureChange":"STEADY","forecastStart":1794643200,"minTemperatureChange":"STEADY","dayPrecipitationChange":"DEC","nightPrecipitationChange":"INC"},{"forecastEnd":1794816000,"maxTemperatureChange":"STEADY","forecastStart":1794729600,"minTemperatureChange":"STEADY","dayPrecipitationChange":"INC","nightPrecipitationChange":"DEC"}],"forecastEnd":1794816000,"forecastStart":1794643200},"weatherAlerts":{"metadata":{"language":"en","latitude":0,"longitude":0,"expireTime":1794569973,"sourceType":"STATION","attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569673,"temporarilyUnavailable":false,"providerName":"National Weather Service","reportedTime":1794538320},"alerts":[{"id":{"bytes":[190,12,153,147,104,54,83,236,165,44,137,30,51,142,238,251]},"description":"Coastal Flood Advisory","token":"REDACTED______________","phenomenon":"Flood","severity":"MINOR","significance":"ADVISORY","source":"National Weather Service","urgency":"EXPECTED","certainty":"LIKELY","importance":"LOW","responses":["MONITOR"],"eventOnsetTime":1794578400,"detailsUrl":"https://weatherkit.apple.com/alertDetails/index.html?ids=be0c9993-6836-53ec-a52c-891e338eeefb&lang=en-US&timezone=America/Los_Angeles&party=apple","effectiveTime":1794538320,"issuedTime":1794538320,"eventSource":"US","areaId":"waz315","expireTime":1794574800,"countryCode":"US","eventEndTime":1794600000,"attributionUrl":"https://www.weather.gov"}],"detailsUrl":"https://weatherkit.apple.com/alertDetails/index.html?ids=be0c9993-6836-53ec-a52c-891e338eeefb&lang=en-US&timezone=America/Los_Angeles&party=apple"},"historicalComparisons":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569673,"expireTime":1794573273,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"comparisons":[{"condition":"TEMPERATURE_MAX","deviation":"NORMAL","baselineValue":10.839996,"baselineStartDate":0,"currentValue":13.64015,"baselineType":"MEAN"},{"condition":"PRECIPITATION","deviation":"NORMAL","baselineValue":0.0,"baselineStartDate":0,"currentValue":0.0,"baselineType":"MEAN"}]},"forecastDaily":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569673,"expireTime":1794573273,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"days":[{"moonrise":1794598874,"moonset":1794627382,"sunrise":1794582705,"sunset":1794616505,"forecastEnd":1794643200,"sunriseCivil":1794580706,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.64015,"solarMidnight":1794642825,"windSpeedAvg":9.337426,"temperatureMaxTime":1794585564,"windGustSpeedMax":36.504002,"sunriseAstronomical":1794576293,"forecastStart":1794556800,"moonPhase":"WAXING_CRESCENT","humidityMax":90,"precipitationAmountByType":[{"expected":5.652,"minimum":5.652,"maximum":5.652,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794622910,"visibilityMax":34072.0,"precipitationChance":86,"temperatureMin":9.91,"maxUvIndex":1,"temperatureMinTime":1794643200,"sunsetNautical":1794620737,"daytimeForecast":{"humidity":83,"daylight":true,"forecastEnd":1794625200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.64015,"uvIndexMin":0,"temperatureApparentMin":9.132,"windGustSpeedMax":32.810402,"perceivedPrecipitationIntensityMax":1.135,"uvIndexMax":1,"temperatureApparentMax":11.529953,"forecastStart":1794582000,"windDirection":181,"windSpeed":10.008599,"humidityMax":90,"precipitationAmountByType":[{"expected":5.455,"minimum":5.455,"maximum":5.455,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":53,"visibilityMax":34072.0,"cloudCover":99,"precipitationChance":79,"temperatureMin":10.49,"cloudCoverMidAltPct":94,"precipitationIntensityMax":1.224,"conditionCode":["RAIN"],"cloudCoverHighAltPct":76,"humidityMin":70,"visibilityMin":6167.0,"precipitationType":["RAIN"],"windSpeedMax":15.263999},"conditionCode":["RAIN"],"humidityMin":70,"solarNoon":1794599599,"visibilityMin":6167.0,"overnightForecast":{"humidity":86,"daylight":false,"forecastEnd":1794668400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.49,"uvIndexMin":0,"temperatureApparentMin":5.112798,"windGustSpeedMax":36.504002,"perceivedPrecipitationIntensityMax":0.549,"uvIndexMax":0,"temperatureApparentMax":10.093671,"forecastStart":1794625200,"windDirection":183,"windSpeed":13.7466,"humidityMax":89,"precipitationAmountByType":[{"expected":0.71,"minimum":0.71,"maximum":0.71,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":49,"visibilityMax":28703.0,"cloudCover":74,"precipitationChance":65,"temperatureMin":9.009192,"cloudCoverMidAltPct":49,"precipitationIntensityMax":0.234,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":31,"humidityMin":82,"visibilityMin":16190.0,"precipitationType":["RAIN"],"windSpeedMax":16.65},"precipitationType":["RAIN"],"sunriseNautical":1794578462,"sunsetCivil":1794618498,"windSpeedMax":16.254,"restOfDayForecast":{"humidity":83,"daylight":false,"forecastEnd":1794643200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.64015,"uvIndexMin":0,"temperatureApparentMin":5.957533,"windGustSpeedMax":36.504002,"perceivedPrecipitationIntensityMax":1.135,"uvIndexMax":1,"temperatureApparentMax":11.864366,"forecastStart":1794569673,"windDirection":171,"windSpeed":10.008805,"humidityMax":90,"precipitationAmountByType":[{"expected":5.652,"minimum":5.652,"maximum":5.652,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":42,"visibilityMax":34072.0,"cloudCover":95,"precipitationChance":84,"temperatureMin":9.91,"cloudCoverMidAltPct":84,"precipitationIntensityMax":1.224,"conditionCode":["RAIN"],"cloudCoverHighAltPct":76,"humidityMin":70,"visibilityMin":6167.0,"precipitationType":["RAIN"],"windSpeedMax":16.254}},{"moonrise":1794687261,"moonset":1794717746,"sunrise":1794669195,"sunset":1794702834,"forecastEnd":1794729600,"sunriseCivil":1794667189,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.170017,"solarMidnight":1794729234,"windSpeedAvg":13.5183,"temperatureMaxTime":1794689975,"windGustSpeedMax":36.504002,"sunriseAstronomical":1794662769,"forecastStart":1794643200,"moonPhase":"WAXING_CRESCENT","humidityMax":88,"precipitationAmountByType":[{"expected":5.55,"minimum":5.55,"maximum":5.55,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794709253,"visibilityMax":29373.0,"precipitationChance":85,"temperatureMin":9.009192,"maxUvIndex":1,"temperatureMinTime":1794668269,"sunsetNautical":1794707078,"daytimeForecast":{"humidity":79,"daylight":true,"forecastEnd":1794711600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.170017,"uvIndexMin":0,"temperatureApparentMin":5.696423,"windGustSpeedMax":33.551998,"perceivedPrecipitationIntensityMax":0.503,"uvIndexMax":1,"temperatureApparentMax":9.767292,"forecastStart":1794668400,"windDirection":166,"windSpeed":13.39695,"humidityMax":86,"precipitationAmountByType":[{"expected":0.96,"minimum":0.96,"maximum":0.96,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":59,"visibilityMax":29373.0,"cloudCover":89,"precipitationChance":55,"temperatureMin":9.01,"cloudCoverMidAltPct":72,"precipitationIntensityMax":0.194,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":64,"humidityMin":76,"visibilityMin":15078.0,"precipitationType":["RAIN"],"windSpeedMax":14.860799},"conditionCode":["RAIN"],"humidityMin":76,"solarNoon":1794686008,"visibilityMin":4069.0,"overnightForecast":{"humidity":84,"daylight":false,"forecastEnd":1794754800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.48,"uvIndexMin":0,"temperatureApparentMin":5.973359,"windGustSpeedMax":38.952,"perceivedPrecipitationIntensityMax":1.473,"uvIndexMax":0,"temperatureApparentMax":9.430315,"forecastStart":1794711600,"windDirection":155,"windSpeed":14.398049,"humidityMax":88,"precipitationAmountByType":[{"expected":8.279,"minimum":8.279,"maximum":8.279,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":67,"visibilityMax":25393.0,"cloudCover":94,"precipitationChance":87,"temperatureMin":10.038711,"cloudCoverMidAltPct":84,"precipitationIntensityMax":2.419,"conditionCode":["RAIN"],"cloudCoverHighAltPct":31,"humidityMin":78,"visibilityMin":4069.0,"precipitationType":["RAIN"],"windSpeedMax":17.780401},"precipitationType":["RAIN"],"sunriseNautical":1794664941,"sunsetCivil":1794704834,"windSpeedMax":16.65},{"moonrise":1794775225,"moonset":1794808239,"sunrise":1794755684,"sunset":1794789166,"forecastEnd":1794816000,"sunriseCivil":1794753672,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":12.499799,"solarMidnight":1794815645,"windSpeedAvg":13.973175,"temperatureMaxTime":1794775537,"windGustSpeedMax":38.952,"sunriseAstronomical":1794749245,"forecastStart":1794729600,"moonPhase":"WAXING_CRESCENT","humidityMax":90,"precipitationAmountByType":[{"expected":7.270999,"minimum":7.270999,"maximum":7.270999,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794795599,"visibilityMax":27960.0,"precipitationChance":94,"temperatureMin":7.48,"maxUvIndex":1,"temperatureMinTime":1794816000,"sunsetNautical":1794793421,"daytimeForecast":{"humidity":79,"daylight":true,"forecastEnd":1794798000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":12.499799,"uvIndexMin":0,"temperatureApparentMin":5.879239,"windGustSpeedMax":35.748001,"perceivedPrecipitationIntensityMax":0.775,"uvIndexMax":1,"temperatureApparentMax":9.362825,"forecastStart":1794754800,"windDirection":199,"windSpeed":15.284551,"humidityMax":86,"precipitationAmountByType":[{"expected":3.041,"minimum":3.041,"maximum":3.041,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":37,"visibilityMax":27960.0,"cloudCover":64,"precipitationChance":79,"temperatureMin":9.24,"cloudCoverMidAltPct":53,"precipitationIntensityMax":0.499,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":0,"humidityMin":73,"visibilityMin":13909.0,"precipitationType":["RAIN"],"windSpeedMax":18.6192},"conditionCode":["RAIN"],"humidityMin":73,"solarNoon":1794772418,"visibilityMin":7974.0,"overnightForecast":{"humidity":89,"daylight":false,"forecastEnd":1794841200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.24,"uvIndexMin":0,"temperatureApparentMin":4.984706,"windGustSpeedMax":20.1276,"perceivedPrecipitationIntensityMax":1.093,"uvIndexMax":0,"temperatureApparentMax":8.094274,"forecastStart":1794798000,"windDirection":167,"windSpeed":7.06065,"humidityMax":90,"precipitationAmountByType":[{"expected":2.986,"minimum":2.986,"maximum":2.986,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":36,"visibilityMax":26518.0,"cloudCover":63,"precipitationChance":51,"temperatureMin":6.209458,"cloudCoverMidAltPct":37,"precipitationIntensityMax":1.117,"conditionCode":["RAIN"],"cloudCoverHighAltPct":4,"humidityMin":83,"visibilityMin":16329.0,"precipitationType":["RAIN"],"windSpeedMax":8.186399},"precipitationType":["RAIN"],"sunriseNautical":1794751419,"sunsetCivil":1794791172,"windSpeedMax":18.6192},{"moonrise":1794862879,"moonset":1794898770,"sunrise":1794842173,"sunset":1794875500,"forecastEnd":1794902400,"sunriseCivil":1794840154,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.921954,"solarMidnight":1794902056,"windSpeedAvg":8.8203,"temperatureMaxTime":1794864861,"windGustSpeedMax":25.909199,"sunriseAstronomical":1794835720,"forecastStart":1794816000,"moonPhase":"WAXING_CRESCENT","humidityMax":90,"precipitationAmountByType":[{"expected":4.384,"minimum":4.384,"maximum":4.384,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794881947,"visibilityMax":31734.0,"precipitationChance":67,"temperatureMin":6.209458,"maxUvIndex":1,"temperatureMinTime":1794840092,"sunsetNautical":1794879767,"daytimeForecast":{"humidity":80,"daylight":true,"forecastEnd":1794884400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.921954,"uvIndexMin":0,"temperatureApparentMin":5.026797,"windGustSpeedMax":25.909199,"perceivedPrecipitationIntensityMax":0.808,"uvIndexMax":1,"temperatureApparentMax":10.102699,"forecastStart":1794841200,"windDirection":182,"windSpeed":9.35655,"humidityMax":90,"precipitationAmountByType":[{"expected":0.895,"minimum":0.895,"maximum":0.895,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":45,"visibilityMax":31734.0,"cloudCover":73,"precipitationChance":36,"temperatureMin":6.24,"cloudCoverMidAltPct":57,"precipitationIntensityMax":0.548,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":16,"humidityMin":73,"visibilityMin":16596.0,"precipitationType":["RAIN"],"windSpeedMax":12.225599},"conditionCode":["RAIN"],"humidityMin":73,"solarNoon":1794858830,"visibilityMin":16329.0,"overnightForecast":{"humidity":88,"daylight":false,"forecastEnd":1794927600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":8.71,"uvIndexMin":0,"temperatureApparentMin":4.863145,"windGustSpeedMax":25.909199,"perceivedPrecipitationIntensityMax":0.751,"uvIndexMax":0,"temperatureApparentMax":6.049259,"forecastStart":1794884400,"windDirection":169,"windSpeed":8.89755,"humidityMax":89,"precipitationAmountByType":[{"expected":0.568,"minimum":0.568,"maximum":0.568,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":56,"visibilityMax":28122.0,"cloudCover":60,"precipitationChance":50,"temperatureMin":5.989975,"cloudCoverMidAltPct":7,"precipitationIntensityMax":0.465,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":1,"humidityMin":85,"visibilityMin":16596.0,"precipitationType":["RAIN"],"windSpeedMax":11.7612},"precipitationType":["RAIN"],"sunriseNautical":1794837897,"sunsetCivil":1794877511,"windSpeedMax":12.225599},{"moonrise":1794950358,"sunrise":1794928662,"sunset":1794961836,"forecastEnd":1794988800,"sunriseCivil":1794926635,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.845637,"solarMidnight":1794988468,"windSpeedAvg":6.440775,"temperatureMaxTime":1794953467,"windGustSpeedMax":18.273602,"sunriseAstronomical":1794922194,"forecastStart":1794902400,"moonPhase":"FIRST_QUARTER","humidityMax":89,"precipitationAmountByType":[{"expected":0.341,"minimum":0.341,"maximum":0.341,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794968297,"visibilityMax":29733.0,"precipitationChance":53,"temperatureMin":5.98,"maxUvIndex":1,"temperatureMinTime":1794988800,"sunsetNautical":1794966114,"daytimeForecast":{"humidity":72,"daylight":true,"forecastEnd":1794970800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.845637,"uvIndexMin":0,"temperatureApparentMin":5.082117,"windGustSpeedMax":17.006401,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":1,"temperatureApparentMax":12.097757,"forecastStart":1794927600,"windDirection":154,"windSpeed":5.87355,"humidityMax":87,"precipitationAmountByType":[],"cloudCoverLowAltPct":15,"visibilityMax":29733.0,"cloudCover":29,"precipitationChance":0,"temperatureMin":6.13,"cloudCoverMidAltPct":1,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":6,"humidityMin":62,"visibilityMin":17780.0,"precipitationType":["CLEAR"],"windSpeedMax":7.1748},"conditionCode":["DRIZZLE"],"humidityMin":62,"solarNoon":1794945242,"visibilityMin":17780.0,"overnightForecast":{"humidity":82,"daylight":false,"forecastEnd":1795014000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":7.72,"uvIndexMin":0,"temperatureApparentMin":2.451838,"windGustSpeedMax":25.2936,"perceivedPrecipitationIntensityMax":0.95,"uvIndexMax":0,"temperatureApparentMax":7.151364,"forecastStart":1794970800,"windDirection":133,"windSpeed":7.62915,"humidityMax":89,"precipitationAmountByType":[{"expected":4.327,"minimum":4.327,"maximum":4.327,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":31,"visibilityMax":25857.0,"cloudCover":92,"precipitationChance":50,"temperatureMin":4.947516,"cloudCoverMidAltPct":66,"precipitationIntensityMax":0.798,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":89,"humidityMin":74,"visibilityMin":10859.0,"precipitationType":["RAIN"],"windSpeedMax":12.204},"precipitationType":["RAIN"],"sunriseNautical":1794924375,"sunsetCivil":1794963853,"windSpeedMax":8.9928},{"moonrise":1795037733,"moonset":1794989335,"sunrise":1795015150,"sunset":1795048175,"forecastEnd":1795075200,"sunriseCivil":1795013116,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.020019,"solarMidnight":1795074881,"windSpeedAvg":10.204425,"temperatureMaxTime":1795042826,"windGustSpeedMax":25.7724,"sunriseAstronomical":1795008667,"forecastStart":1794988800,"moonPhase":"WAXING_GIBBOUS","humidityMax":94,"precipitationAmountByType":[{"expected":6.641001,"minimum":6.641001,"maximum":6.641001,"precipitationType":["RAIN"]}],"sunsetAstronomical":1795054649,"visibilityMax":30421.0,"precipitationChance":74,"temperatureMin":4.947516,"maxUvIndex":1,"temperatureMinTime":1795009991,"sunsetNautical":1795052464,"daytimeForecast":{"humidity":81,"daylight":true,"forecastEnd":1795057200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.020019,"uvIndexMin":0,"temperatureApparentMin":2.416847,"windGustSpeedMax":25.7724,"perceivedPrecipitationIntensityMax":0.866,"uvIndexMax":1,"temperatureApparentMax":6.052989,"forecastStart":1795014000,"windDirection":182,"windSpeed":12.15015,"humidityMax":88,"precipitationAmountByType":[{"expected":2.276,"minimum":2.276,"maximum":2.276,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":60,"visibilityMax":30421.0,"cloudCover":82,"precipitationChance":66,"temperatureMin":5.11,"cloudCoverMidAltPct":76,"precipitationIntensityMax":0.643,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":50,"humidityMin":76,"visibilityMin":14677.0,"precipitationType":["RAIN"],"windSpeedMax":13.608},"conditionCode":["DRIZZLE"],"humidityMin":76,"solarNoon":1795031654,"visibilityMin":10859.0,"overnightForecast":{"humidity":92,"daylight":false,"forecastEnd":1795100400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":7.9,"uvIndexMin":0,"temperatureApparentMin":5.690445,"windGustSpeedMax":17.945999,"perceivedPrecipitationIntensityMax":0.327,"uvIndexMax":0,"temperatureApparentMax":6.35371,"forecastStart":1795057200,"windDirection":158,"windSpeed":5.645401,"humidityMax":95,"precipitationAmountByType":[{"expected":0.478,"minimum":0.478,"maximum":0.478,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":62,"visibilityMax":25229.0,"cloudCover":82,"precipitationChance":50,"temperatureMin":5.36,"cloudCoverMidAltPct":55,"precipitationIntensityMax":0.079,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":8,"humidityMin":82,"visibilityMin":12901.0,"precipitationType":["RAIN"],"windSpeedMax":9.774},"precipitationType":["RAIN"],"sunriseNautical":1795010851,"sunsetCivil":1795050198,"windSpeedMax":13.608},{"moonrise":1795125079,"moonset":1795079963,"sunrise":1795101637,"sunset":1795134515,"forecastEnd":1795161600,"sunriseCivil":1795099597,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.116987,"solarMidnight":1795161294,"windSpeedAvg":5.149725,"temperatureMaxTime":1795135920,"windGustSpeedMax":12.852,"sunriseAstronomical":1795095140,"forecastStart":1795075200,"moonPhase":"WAXING_GIBBOUS","humidityMax":96,"precipitationAmountByType":[{"expected":0.141,"minimum":0.141,"maximum":0.141,"precipitationType":["RAIN"]}],"sunsetAstronomical":1795141003,"visibilityMax":32204.0,"precipitationChance":53,"temperatureMin":5.36,"maxUvIndex":1,"temperatureMinTime":1795082400,"sunsetNautical":1795138815,"daytimeForecast":{"humidity":83,"daylight":true,"forecastEnd":1795143600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.116987,"uvIndexMin":0,"temperatureApparentMin":6.35371,"windGustSpeedMax":12.852,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":1,"temperatureApparentMax":10.070161,"forecastStart":1795100400,"windDirection":4,"windSpeed":6.0135,"humidityMax":93,"precipitationAmountByType":[],"cloudCoverLowAltPct":44,"visibilityMax":32204.0,"cloudCover":63,"precipitationChance":0,"temperatureMin":6.03,"cloudCoverMidAltPct":2,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":0,"humidityMin":77,"visibilityMin":12901.0,"precipitationType":["CLEAR"],"windSpeedMax":7.8264},"conditionCode":["DRIZZLE"],"humidityMin":77,"solarNoon":1795118068,"visibilityMin":12901.0,"overnightForecast":{"humidity":94,"daylight":false,"forecastEnd":1795186800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":8.52,"uvIndexMin":0,"temperatureApparentMin":4.634763,"windGustSpeedMax":9.4392,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":8.422463,"forecastStart":1795143600,"windDirection":44,"windSpeed":3.7053,"humidityMax":97,"precipitationAmountByType":[],"cloudCoverLowAltPct":34,"visibilityMax":28680.0,"cloudCover":52,"precipitationChance":0,"temperatureMin":4.006788,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"humidityMin":85,"visibilityMin":9083.0,"precipitationType":["CLEAR"],"windSpeedMax":4.0212},"precipitationType":["RAIN"],"sunriseNautical":1795097328,"sunsetCivil":1795136544,"windSpeedMax":7.8264},{"moonrise":1795212479,"moonset":1795170725,"sunrise":1795188123,"sunset":1795220858,"forecastEnd":1795248000,"sunriseCivil":1795186076,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.763362,"solarMidnight":1795247709,"windSpeedAvg":4.930575,"temperatureMaxTime":1795216005,"windGustSpeedMax":14.482799,"sunriseAstronomical":1795181613,"forecastStart":1795161600,"moonPhase":"WAXING_GIBBOUS","humidityMax":97,"precipitationAmountByType":[],"sunsetAstronomical":1795227360,"visibilityMax":17821.0,"precipitationChance":0,"temperatureMin":4.006788,"maxUvIndex":1,"temperatureMinTime":1795177699,"sunsetNautical":1795225170,"daytimeForecast":{"humidity":77,"daylight":true,"forecastEnd":1795230000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.763362,"uvIndexMin":0,"temperatureApparentMin":5.128633,"windGustSpeedMax":14.482799,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":1,"temperatureApparentMax":11.921696,"forecastStart":1795186800,"windDirection":9,"windSpeed":6.00165,"humidityMax":92,"precipitationAmountByType":[],"cloudCoverLowAltPct":16,"visibilityMax":13937.0,"cloudCover":19,"precipitationChance":0,"temperatureMin":4.73,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":69,"visibilityMin":9448.0,"precipitationType":["CLEAR"],"windSpeedMax":7.5348},"conditionCode":["MOSTLY_CLEAR"],"humidityMin":69,"solarNoon":1795204483,"visibilityMin":9083.0,"overnightForecast":{"humidity":78,"daylight":false,"forecastEnd":1795273200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":7.8,"uvIndexMin":0,"temperatureApparentMin":3.108699,"windGustSpeedMax":12.618,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":6.648598,"forecastStart":1795230000,"windDirection":63,"windSpeed":5.1837,"humidityMax":84,"precipitationAmountByType":[],"cloudCoverLowAltPct":7,"visibilityMax":24474.0,"cloudCover":10,"precipitationChance":0,"temperatureMin":4.249874,"cloudCoverMidAltPct":1,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":3,"humidityMin":66,"visibilityMin":13937.0,"precipitationType":["CLEAR"],"windSpeedMax":6.4764},"precipitationType":["CLEAR"],"sunriseNautical":1795183803,"sunsetCivil":1795222893,"windSpeedMax":7.5348},{"moonrise":1795300029,"moonset":1795261701,"sunrise":1795274609,"sunset":1795307204,"forecastEnd":1795334400,"sunriseCivil":1795272555,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.168908,"solarMidnight":1795334124,"windSpeedAvg":5.365725,"temperatureMaxTime":1795299642,"windGustSpeedMax":13.5252,"sunriseAstronomical":1795268084,"forecastStart":1795248000,"moonPhase":"WAXING_GIBBOUS","humidityMax":83,"precipitationAmountByType":[],"sunsetAstronomical":1795313719,"visibilityMax":26954.0,"precipitationChance":0,"temperatureMin":4.249874,"maxUvIndex":1,"temperatureMinTime":1795255291,"sunsetNautical":1795311526,"daytimeForecast":{"humidity":62,"daylight":true,"forecastEnd":1795316400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.168908,"uvIndexMin":0,"temperatureApparentMin":4.959615,"windGustSpeedMax":12.459599,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":1,"temperatureApparentMax":13.309563,"forecastStart":1795273200,"windDirection":135,"windSpeed":4.4259,"humidityMax":66,"precipitationAmountByType":[],"cloudCoverLowAltPct":2,"visibilityMax":26954.0,"cloudCover":19,"precipitationChance":0,"temperatureMin":6.57,"cloudCoverMidAltPct":17,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":60,"visibilityMin":8662.0,"precipitationType":["CLEAR"],"windSpeedMax":6.372},"conditionCode":["MOSTLY_CLEAR"],"humidityMin":60,"solarNoon":1795290898,"visibilityMin":8662.0,"overnightForecast":{"humidity":71,"daylight":false,"forecastEnd":1795359600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":8.71,"uvIndexMin":0,"temperatureApparentMin":3.380726,"windGustSpeedMax":13.5252,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":7.205438,"forecastStart":1795316400,"windDirection":154,"windSpeed":5.82615,"humidityMax":77,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":22708.0,"cloudCover":12,"precipitationChance":0,"temperatureMin":4.10744,"cloudCoverMidAltPct":4,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":64,"visibilityMin":10503.0,"precipitationType":["CLEAR"],"windSpeedMax":7.487999},"precipitationType":["CLEAR"],"sunriseNautical":1795270278,"sunsetCivil":1795309245,"windSpeedMax":7.487999}]},"forecastHourly":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569673,"expireTime":1794573273,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"hours":[{"daylight":false,"humidity":85,"pressure":993.530029,"temperature":11.51,"visibility":22126.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":1,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":9.07518,"windGust":6.9768,"temperatureApparent":10.747892,"cloudCover":68,"precipitationChance":0,"cloudCoverMidAltPct":89,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":100,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794567600,"pressureTrend":"FALLING","windDirection":131,"uvIndex":0,"windSpeed":6.9768,"snowfallIntensity":0.0},{"daylight":false,"humidity":83,"pressure":992.429993,"temperature":10.95,"visibility":19738.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":4,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":8.174545,"windGust":12.87,"temperatureApparent":10.448459,"cloudCover":96,"precipitationChance":0,"cloudCoverMidAltPct":83,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":94,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794571200,"pressureTrend":"FALLING","windDirection":69,"uvIndex":0,"windSpeed":7.1892,"snowfallIntensity":0.0},{"daylight":false,"humidity":79,"pressure":990.97998,"temperature":11.34,"visibility":21981.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":4,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.828262,"windGust":15.4296,"temperatureApparent":9.860982,"cloudCover":74,"precipitationChance":0,"cloudCoverMidAltPct":48,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":67,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794574800,"pressureTrend":"FALLING","windDirection":83,"uvIndex":0,"windSpeed":9.0468,"snowfallIntensity":0.0},{"daylight":false,"humidity":74,"pressure":990.539978,"temperature":11.79,"visibility":26370.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":8,"perceivedPrecipitationIntensity":0.082,"precipitationIntensity":0.005,"temperatureDewPoint":7.305634,"windGust":17.848801,"temperatureApparent":11.730489,"cloudCover":100,"precipitationChance":5,"cloudCoverMidAltPct":95,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":98,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.005,"minimum":0.005,"maximum":0.005},"forecastStart":1794578400,"pressureTrend":"FALLING","windDirection":152,"uvIndex":0,"windSpeed":5.5908,"snowfallIntensity":0.0},{"daylight":false,"humidity":71,"pressure":989.789978,"temperature":12.85,"visibility":32347.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":6,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.719589,"windGust":25.4088,"temperatureApparent":11.529953,"cloudCover":97,"precipitationChance":0,"cloudCoverMidAltPct":92,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":97,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794582000,"pressureTrend":"FALLING","windDirection":107,"uvIndex":0,"windSpeed":9.324,"snowfallIntensity":0.0},{"daylight":true,"humidity":70,"pressure":990.130005,"temperature":13.64,"visibility":34072.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":24,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":8.26889,"windGust":31.352398,"temperatureApparent":10.051039,"cloudCover":99,"precipitationChance":0,"cloudCoverMidAltPct":96,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":56,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794585600,"pressureTrend":"FALLING","windDirection":131,"uvIndex":0,"windSpeed":15.263999,"snowfallIntensity":0.0},{"daylight":true,"humidity":80,"pressure":991.099976,"temperature":12.79,"visibility":20931.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":66,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":9.425461,"windGust":32.810402,"temperatureApparent":9.430462,"cloudCover":96,"precipitationChance":0,"cloudCoverMidAltPct":60,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":22,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794589200,"pressureTrend":"STEADY","windDirection":239,"uvIndex":0,"windSpeed":13.521601,"snowfallIntensity":0.0},{"daylight":true,"humidity":81,"pressure":991.559998,"temperature":13.02,"visibility":15831.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":39,"perceivedPrecipitationIntensity":0.568,"precipitationIntensity":0.252,"temperatureDewPoint":9.834717,"windGust":27.719999,"temperatureApparent":11.22246,"cloudCover":100,"precipitationChance":20,"cloudCoverMidAltPct":98,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":19,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.252,"minimum":0.252,"maximum":0.252},"forecastStart":1794592800,"pressureTrend":"RISING","windDirection":221,"uvIndex":1,"windSpeed":8.478,"snowfallIntensity":0.0},{"daylight":true,"humidity":82,"pressure":991.630005,"temperature":13.07,"visibility":16011.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":67,"perceivedPrecipitationIntensity":0.283,"precipitationIntensity":0.059,"temperatureDewPoint":10.066879,"windGust":30.477598,"temperatureApparent":10.538489,"cloudCover":100,"precipitationChance":23,"cloudCoverMidAltPct":96,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":34,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.059,"minimum":0.059,"maximum":0.059},"forecastStart":1794596400,"pressureTrend":"RISING","windDirection":194,"uvIndex":1,"windSpeed":10.5984,"snowfallIntensity":0.0},{"daylight":true,"humidity":82,"pressure":992.080017,"temperature":12.52,"visibility":16435.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":64,"perceivedPrecipitationIntensity":0.499,"precipitationIntensity":0.191,"temperatureDewPoint":9.529678,"windGust":31.676399,"temperatureApparent":9.236209,"cloudCover":100,"precipitationChance":35,"cloudCoverMidAltPct":97,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":99,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.191,"minimum":0.191,"maximum":0.191},"forecastStart":1794600000,"pressureTrend":"RISING","windDirection":206,"uvIndex":1,"windSpeed":12.549601,"snowfallIntensity":0.0},{"daylight":true,"humidity":84,"pressure":991.97998,"temperature":12.26,"visibility":12801.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":75,"perceivedPrecipitationIntensity":1.128,"precipitationIntensity":1.205,"temperatureDewPoint":9.634369,"windGust":28.656,"temperatureApparent":9.296653,"cloudCover":100,"precipitationChance":55,"cloudCoverMidAltPct":100,"conditionCode":["RAIN"],"cloudCoverHighAltPct":96,"precipitationType":["RAIN"],"precipitationAmount":{"expected":1.205,"minimum":1.205,"maximum":1.205},"forecastStart":1794603600,"pressureTrend":"STEADY","windDirection":201,"uvIndex":1,"windSpeed":11.6712,"snowfallIntensity":0.0},{"daylight":true,"humidity":85,"pressure":992.119995,"temperature":12.03,"visibility":9057.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":81,"perceivedPrecipitationIntensity":0.89,"precipitationIntensity":0.684,"temperatureDewPoint":9.585251,"windGust":26.046,"temperatureApparent":9.971526,"cloudCover":100,"precipitationChance":57,"cloudCoverMidAltPct":100,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":98,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.684,"minimum":0.684,"maximum":0.684},"forecastStart":1794607200,"pressureTrend":"STEADY","windDirection":194,"uvIndex":1,"windSpeed":9.234,"snowfallIntensity":0.0},{"daylight":true,"humidity":88,"pressure":992.48999,"temperature":11.76,"visibility":7366.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":60,"perceivedPrecipitationIntensity":1.135,"precipitationIntensity":1.224,"temperatureDewPoint":9.837143,"windGust":18.129601,"temperatureApparent":10.412608,"cloudCover":100,"precipitationChance":68,"cloudCoverMidAltPct":100,"conditionCode":["RAIN"],"cloudCoverHighAltPct":97,"precipitationType":["RAIN"],"precipitationAmount":{"expected":1.224,"minimum":1.224,"maximum":1.224},"forecastStart":1794610800,"pressureTrend":"STEADY","windDirection":180,"uvIndex":0,"windSpeed":7.7652,"snowfallIntensity":0.0},{"daylight":true,"humidity":90,"pressure":992.950012,"temperature":11.47,"visibility":6167.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":60,"perceivedPrecipitationIntensity":1.084,"precipitationIntensity":1.093,"temperatureDewPoint":9.886688,"windGust":17.326801,"temperatureApparent":11.056475,"cloudCover":100,"precipitationChance":68,"cloudCoverMidAltPct":100,"conditionCode":["RAIN"],"cloudCoverHighAltPct":96,"precipitationType":["RAIN"],"precipitationAmount":{"expected":1.093,"minimum":1.093,"maximum":1.093},"forecastStart":1794614400,"pressureTrend":"RISING","windDirection":158,"uvIndex":0,"windSpeed":6.4944,"snowfallIntensity":0.0},{"daylight":false,"humidity":90,"pressure":993.429993,"temperature":11.2,"visibility":10017.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":54,"perceivedPrecipitationIntensity":0.894,"precipitationIntensity":0.692,"temperatureDewPoint":9.620041,"windGust":19.764,"temperatureApparent":10.591156,"cloudCover":100,"precipitationChance":63,"cloudCoverMidAltPct":100,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.692,"minimum":0.692,"maximum":0.692},"forecastStart":1794618000,"pressureTrend":"RISING","windDirection":149,"uvIndex":0,"windSpeed":7.844399,"snowfallIntensity":0.0},{"daylight":false,"humidity":89,"pressure":993.809998,"temperature":10.85,"visibility":18581.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":39,"perceivedPrecipitationIntensity":0.274,"precipitationIntensity":0.055,"temperatureDewPoint":9.108414,"windGust":18.846001,"temperatureApparent":10.008285,"cloudCover":99,"precipitationChance":44,"cloudCoverMidAltPct":90,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":95,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.055,"minimum":0.055,"maximum":0.055},"forecastStart":1794621600,"pressureTrend":"RISING","windDirection":135,"uvIndex":0,"windSpeed":8.4348,"snowfallIntensity":0.0},{"daylight":false,"humidity":89,"pressure":994.340027,"temperature":10.49,"visibility":22953.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":17,"perceivedPrecipitationIntensity":0.203,"precipitationIntensity":0.03,"temperatureDewPoint":8.753342,"windGust":15.235199,"temperatureApparent":10.093671,"cloudCover":99,"precipitationChance":35,"cloudCoverMidAltPct":88,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":96,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.03,"minimum":0.03,"maximum":0.03},"forecastStart":1794625200,"pressureTrend":"RISING","windDirection":146,"uvIndex":0,"windSpeed":7.1712,"snowfallIntensity":0.0},{"daylight":false,"humidity":88,"pressure":994.849976,"temperature":10.28,"visibility":23999.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":23,"perceivedPrecipitationIntensity":0.352,"precipitationIntensity":0.092,"temperatureDewPoint":8.379349,"windGust":19.6416,"temperatureApparent":8.919455,"cloudCover":82,"precipitationChance":27,"cloudCoverMidAltPct":65,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":69,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.092,"minimum":0.092,"maximum":0.092},"forecastStart":1794628800,"pressureTrend":"RISING","windDirection":168,"uvIndex":0,"windSpeed":9.1692,"snowfallIntensity":0.0},{"daylight":false,"humidity":86,"pressure":995.400024,"temperature":10.2,"visibility":25448.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":38,"perceivedPrecipitationIntensity":0.192,"precipitationIntensity":0.027,"temperatureDewPoint":7.96196,"windGust":23.864401,"temperatureApparent":7.958751,"cloudCover":98,"precipitationChance":19,"cloudCoverMidAltPct":74,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":69,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.027,"minimum":0.027,"maximum":0.027},"forecastStart":1794632400,"pressureTrend":"RISING","windDirection":183,"uvIndex":0,"windSpeed":12.0564,"snowfallIntensity":0.0},{"daylight":false,"humidity":84,"pressure":996.070007,"temperature":10.02,"visibility":28076.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":33,"perceivedPrecipitationIntensity":0.242,"precipitationIntensity":0.043,"temperatureDewPoint":7.440018,"windGust":24.958801,"temperatureApparent":7.321218,"cloudCover":74,"precipitationChance":17,"cloudCoverMidAltPct":42,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":69,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.043,"minimum":0.043,"maximum":0.043},"forecastStart":1794636000,"pressureTrend":"RISING","windDirection":179,"uvIndex":0,"windSpeed":12.3696,"snowfallIntensity":0.0},{"daylight":false,"humidity":83,"pressure":996.48999,"temperature":9.93,"visibility":28703.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":62,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.176666,"windGust":28.184401,"temperatureApparent":7.196809,"cloudCover":93,"precipitationChance":0,"cloudCoverMidAltPct":58,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":69,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794639600,"pressureTrend":"RISING","windDirection":179,"uvIndex":0,"windSpeed":13.086,"snowfallIntensity":0.0},{"daylight":false,"humidity":82,"pressure":997.099976,"temperature":9.91,"visibility":22503.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":60,"perceivedPrecipitationIntensity":0.297,"precipitationIntensity":0.065,"temperatureDewPoint":6.980118,"windGust":36.504002,"temperatureApparent":5.957533,"cloudCover":83,"precipitationChance":13,"cloudCoverMidAltPct":68,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":31,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.065,"minimum":0.065,"maximum":0.065},"forecastStart":1794643200,"pressureTrend":"RISING","windDirection":188,"uvIndex":0,"windSpeed":16.254,"snowfallIntensity":0.0},{"daylight":false,"humidity":84,"pressure":997.929993,"temperature":9.78,"visibility":19336.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":62,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.20488,"windGust":36.144001,"temperatureApparent":5.642423,"cloudCover":79,"precipitationChance":0,"cloudCoverMidAltPct":45,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":20,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794646800,"pressureTrend":"RISING","windDirection":189,"uvIndex":0,"windSpeed":16.65,"snowfallIntensity":0.0},{"daylight":false,"humidity":86,"pressure":998.849976,"temperature":9.54,"visibility":16225.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":50,"perceivedPrecipitationIntensity":0.549,"precipitationIntensity":0.234,"temperatureDewPoint":7.313568,"windGust":32.5116,"temperatureApparent":5.722379,"cloudCover":59,"precipitationChance":24,"cloudCoverMidAltPct":30,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.234,"minimum":0.234,"maximum":0.234},"forecastStart":1794650400,"pressureTrend":"RISING","windDirection":186,"uvIndex":0,"windSpeed":14.9436,"snowfallIntensity":0.0}]},"forecastPeriodic":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569673,"expireTime":1794573273,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"forecasts":[{"periods":[{"humidity":79,"daylight":false,"forecastEnd":1794582000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":12.85,"uvIndexMin":0,"temperatureApparentMin":9.818448,"windGustSpeedMax":25.4088,"perceivedPrecipitationIntensityMax":0.082,"uvIndexMax":0,"temperatureApparentMax":11.864366,"forecastStart":1794567600,"windDirection":101,"windSpeed":7.4943,"humidityMax":85,"precipitationAmountByType":[{"expected":0.005,"minimum":0.005,"maximum":0.005,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":5,"visibilityMax":32347.0,"cloudCover":88,"precipitationChance":24,"temperatureMin":10.94799,"cloudCoverMidAltPct":79,"precipitationIntensityMax":0.005,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":89,"humidityMin":71,"visibilityMin":19738.0,"precipitationType":["RAIN"],"windSpeedMax":9.324},{"humidity":77,"daylight":true,"forecastEnd":1794596400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.64015,"uvIndexMin":0,"temperatureApparentMin":9.384322,"windGustSpeedMax":32.810402,"perceivedPrecipitationIntensityMax":0.568,"uvIndexMax":1,"temperatureApparentMax":11.529953,"forecastStart":1794582000,"windDirection":185,"windSpeed":11.8062,"humidityMax":82,"precipitationAmountByType":[{"expected":0.252,"minimum":0.252,"maximum":0.252,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":41,"visibilityMax":34072.0,"cloudCover":98,"precipitationChance":26,"temperatureMin":12.767698,"cloudCoverMidAltPct":87,"precipitationIntensityMax":0.252,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":41,"humidityMin":70,"visibilityMin":15831.0,"precipitationType":["RAIN"],"windSpeedMax":15.263999},{"humidity":84,"daylight":true,"forecastEnd":1794610800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.07,"uvIndexMin":0,"temperatureApparentMin":9.132,"windGustSpeedMax":31.676399,"perceivedPrecipitationIntensityMax":1.135,"uvIndexMax":1,"temperatureApparentMax":10.538489,"forecastStart":1794596400,"windDirection":198,"windSpeed":10.65915,"humidityMax":88,"precipitationAmountByType":[{"expected":2.139,"minimum":2.139,"maximum":2.139,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":71,"visibilityMax":16435.0,"cloudCover":100,"precipitationChance":68,"temperatureMin":11.76,"cloudCoverMidAltPct":99,"precipitationIntensityMax":1.224,"conditionCode":["RAIN"],"cloudCoverHighAltPct":90,"humidityMin":82,"visibilityMin":7366.0,"precipitationType":["RAIN"],"windSpeedMax":12.549601},{"humidity":89,"daylight":true,"forecastEnd":1794625200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.76,"uvIndexMin":0,"temperatureApparentMin":9.995345,"windGustSpeedMax":19.764,"perceivedPrecipitationIntensityMax":1.135,"uvIndexMax":0,"temperatureApparentMax":11.058412,"forecastStart":1794610800,"windDirection":151,"windSpeed":7.560449,"humidityMax":90,"precipitationAmountByType":[{"expected":3.064,"minimum":3.064,"maximum":3.064,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":48,"visibilityMax":22953.0,"cloudCover":100,"precipitationChance":82,"temperatureMin":10.49,"cloudCoverMidAltPct":96,"precipitationIntensityMax":1.224,"conditionCode":["RAIN"],"cloudCoverHighAltPct":97,"humidityMin":88,"visibilityMin":6167.0,"precipitationType":["RAIN"],"windSpeedMax":8.4348},{"humidity":86,"daylight":false,"forecastEnd":1794639600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.49,"uvIndexMin":0,"temperatureApparentMin":7.196809,"windGustSpeedMax":28.184401,"perceivedPrecipitationIntensityMax":0.352,"uvIndexMax":0,"temperatureApparentMax":10.093671,"forecastStart":1794625200,"windDirection":175,"windSpeed":10.93095,"humidityMax":89,"precipitationAmountByType":[{"expected":0.192,"minimum":0.192,"maximum":0.192,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":33,"visibilityMax":28703.0,"cloudCover":88,"precipitationChance":49,"temperatureMin":9.93,"cloudCoverMidAltPct":64,"precipitationIntensityMax":0.092,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":72,"humidityMin":83,"visibilityMin":22953.0,"precipitationType":["RAIN"],"windSpeedMax":13.086},{"humidity":84,"daylight":false,"forecastEnd":1794654000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.93,"uvIndexMin":0,"temperatureApparentMin":5.604858,"windGustSpeedMax":36.504002,"perceivedPrecipitationIntensityMax":0.549,"uvIndexMax":0,"temperatureApparentMax":7.196809,"forecastStart":1794639600,"windDirection":186,"windSpeed":15.3558,"humidityMax":88,"precipitationAmountByType":[{"expected":0.299,"minimum":0.299,"maximum":0.299,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":56,"visibilityMax":28703.0,"cloudCover":74,"precipitationChance":28,"temperatureMin":9.2,"cloudCoverMidAltPct":47,"precipitationIntensityMax":0.234,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":21,"humidityMin":82,"visibilityMin":16225.0,"precipitationType":["RAIN"],"windSpeedMax":16.65}],"periodLength":4}]},"forecastNextHour":{"metadata":{"latitude":0,"longitude":0,"expireTime":1794571473,"sourceType":"MODELED","attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569673,"temporarilyUnavailable":false,"providerName":"US National Oceanic & Atmospheric Administration","reportedTime":1794569160},"condition":[{"parameters":[],"startTime":1794569640,"beginCondition":"CLEAR","forecastToken":"CLEAR","endCondition":"CLEAR"}],"summary":[{"condition":["CLEAR"],"precipitationChance":0,"startTime":1794569640,"precipitationIntensity":0.0}],"minutes":[{"precipitationChance":0,"startTime":1794569640,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569700,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569760,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569820,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569880,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569940,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570000,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570060,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570120,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570180,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570240,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570300,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570360,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570420,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570480,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570540,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570600,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570660,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570720,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570780,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570840,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570900,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570960,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571020,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571080,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571140,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571200,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571260,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571320,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571380,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571440,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571500,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571560,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571620,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571680,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571740,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571800,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571860,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571920,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571980,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572040,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572100,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572160,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572220,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572280,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572340,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572400,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572460,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572520,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572580,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572640,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572700,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572760,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572820,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572880,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572940,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573000,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573060,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573120,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573180,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573240,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573300,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573360,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573420,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573480,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573540,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573600,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573660,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573720,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573780,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573840,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573900,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573960,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574020,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574080,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574140,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574200,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574260,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574320,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574380,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574440,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574500,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0}],"forecastEnd":1794574560,"forecastStart":1794569640}} diff --git a/tests/fixtures/wk3-paired/seattle-repeat/manifest.json b/tests/fixtures/wk3-paired/seattle-repeat/manifest.json new file mode 100644 index 00000000..911b31ef --- /dev/null +++ b/tests/fixtures/wk3-paired/seattle-repeat/manifest.json @@ -0,0 +1,129 @@ +{ + "schema": "lib-rc-3.0.0-b66", + "public_test_location": "seattle public city centre (not device location)", + "responses": [ + { + "headers": { + "date": [ + "Fri, 13 Nov 2026 11:34:33 GMT" + ], + "vary": [ + "Accept-Encoding", + "User-Agent, Accept" + ], + "cache-control": [ + "max-age=299, public, s-maxage=299" + ], + "x-cache": [ + "MISS KS-CLOUD" + ] + }, + "file": "apple.json", + "started": 1794569672.95542, + "finished": 1794569673.088546, + "status": 200, + "content_type": "application/json", + "bytes": 62455 + }, + { + "headers": { + "date": [ + "Fri, 13 Nov 2026 11:34:33 GMT" + ], + "vary": [ + "Accept-Encoding", + "User-Agent, Accept" + ], + "cache-control": [ + "max-age=299, public, s-maxage=299" + ], + "x-cache": [ + "MISS KS-CLOUD" + ] + }, + "file": "apple.bin", + "started": 1794569672.9554958, + "finished": 1794569673.074862, + "status": 200, + "content_type": "application/vnd.apple.flatbuffer;messageType=WK3.Weather", + "bytes": 13820 + } + ], + "sanitization": { + "coordinate_fields": [ + { + "root_slot": 0, + "latitude_offset": 13712, + "longitude_offset": 13708 + }, + { + "root_slot": 1, + "latitude_offset": 13324, + "longitude_offset": 13320 + }, + { + "root_slot": 2, + "latitude_offset": 12904, + "longitude_offset": 12900 + }, + { + "root_slot": 3, + "latitude_offset": 7748, + "longitude_offset": 7744 + }, + { + "root_slot": 4, + "latitude_offset": 4584, + "longitude_offset": 4580 + }, + { + "root_slot": 5, + "latitude_offset": 3404, + "longitude_offset": 3400 + }, + { + "root_slot": 6, + "latitude_offset": 188, + "longitude_offset": 184 + }, + { + "root_slot": 7, + "latitude_offset": 380, + "longitude_offset": 376 + }, + { + "root_slot": 9, + "latitude_offset": 1164, + "longitude_offset": 1160 + }, + { + "root_slot": 14, + "latitude_offset": 2132, + "longitude_offset": 2128 + } + ], + "coordinate_urls": 0, + "method": "Only vtable-validated Metadata latitude/longitude floats and same-length coordinate URL digits replaced. No re-encoding." + }, + "sha256": { + "apple.json": "e961c5e04f369cfd933b7b7c1065f87b918ffdaa8c01c55869226fc9925cd06a", + "apple.bin": "3614704af05dcb1c420ed4725bb8378f964e8df2b71150592389ae031e7d9ca1" + }, + "schema_source": { + "weatherkit_dev": "cd9d7927094aff79906fe3430f339f39970ca46e", + "protocol_tag": "v2.0.5", + "protocol_commit": "c87c1dcd851ea9293141bcfb32fde1b2cca564b9" + }, + "additional_redactions": [ + { + "kind": "coordinate-url", + "offset": 13744, + "bytes": 75 + }, + { + "kind": "weather-alert-token", + "offset": 864, + "bytes": 22 + } + ] +} diff --git a/tests/fixtures/wk3-paired/seattle/apple.bin b/tests/fixtures/wk3-paired/seattle/apple.bin new file mode 100644 index 00000000..e216730f Binary files /dev/null and b/tests/fixtures/wk3-paired/seattle/apple.bin differ diff --git a/tests/fixtures/wk3-paired/seattle/apple.json b/tests/fixtures/wk3-paired/seattle/apple.json new file mode 100644 index 00000000..b7206dd4 --- /dev/null +++ b/tests/fixtures/wk3-paired/seattle/apple.json @@ -0,0 +1 @@ +{"news":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"APPLE_INTERNAL","reportedTime":1794569484},"placements":[{"priority":2,"articles":[{"id":"A6ajisfZkSH-TzT_j18WQJQ","phenomena":[],"locale":"en_US","supportedStorefronts":["143441","143444","143455","143460"],"headlineOverride":"How the UV index helps you know when it’s safe to have fun in the sun","alertIds":[]}],"placement":["UV_DETAILS"]},{"priority":2,"articles":[{"id":"AxDHjEelNRh-tGqDejqtusw","phenomena":[],"locale":"en_US","supportedStorefronts":["143441","143444","143455","143460"],"headlineOverride":"How to prepare and stay safe if high winds are in your forecast","alertIds":[]}],"placement":["WIND_DETAILS"]},{"priority":2,"articles":[{"id":"A2uodtK8zT8qsl9ZTqOSsuw","phenomena":[],"locale":"en_US","supportedStorefronts":["143441","143444","143455","143460"],"headlineOverride":"How to use the air quality index to protect yourself from pollution and smoke","alertIds":[]}],"placement":["AIR_QUALITY_DETAILS"]}]},"airQuality":{"metadata":{"latitude":0,"longitude":0,"expireTime":1794571344,"sourceType":"MODELED","attributionUrl":"https://dynamic-link.breezometer.com/?lat=00.000&lon=-000.000&partner=apple","readTime":1794569484,"temporarilyUnavailable":false,"providerName":"BreezoMeter","reportedTime":1794567600},"index":59,"pollutants":[{"amount":5.0,"units":"PARTS_PER_BILLION","pollutantType":"OZONE"},{"amount":14.707169,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"PM2_5"},{"amount":32.544106,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"PM10"},{"amount":300.0,"units":"PARTS_PER_BILLION","pollutantType":"CO"},{"amount":24.375298,"units":"PARTS_PER_BILLION","pollutantType":"NO2"},{"amount":5.560594,"units":"PARTS_PER_BILLION","pollutantType":"NO"},{"amount":0.8,"units":"PARTS_PER_BILLION","pollutantType":"SO2"},{"amount":23.047903,"units":"PARTS_PER_BILLION","pollutantType":"NOX"}],"scale":"EPA_NowCast.2604","primaryPollutant":"PM2_5","previousDayComparison":"SAME","categoryIndex":2,"isSignificant":false},"currentWeather":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794569784,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794569484},"daylight":false,"humidity":84,"pressure":992.954346,"temperature":11.190001,"visibility":20876.279297,"asOf":1794569484,"precipitationIntensity":0.0,"temperatureDewPoint":8.586227,"windGust":10.060907,"temperatureApparent":10.568842,"snowfallAmountPrevious6h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"snowfallAmountPrevious24h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountNext6hByType":[],"snowfallAmountNext24h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountPrevious6hByType":[],"snowfallAmountPrevious1h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"pressureTrend":"FALLING","windDirection":97,"windSpeed":7.087955,"cloudCoverLowAltPct":3,"perceivedPrecipitationIntensity":0.0,"precipitationAmountPrevious1hByType":[],"cloudCover":83,"precipitationAmountNext1hByType":[],"snowfallAmountNext1h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountPrevious24hByType":[],"cloudCoverMidAltPct":86,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":97,"snowfallAmountNext6h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"uvIndex":0,"precipitationAmountNext24hByType":[{"expected":5.981877,"minimum":5.981877,"maximum":5.981877,"precipitationType":["RAIN"]}]},"weatherChanges":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"changes":[{"forecastEnd":1794729600,"maxTemperatureChange":"STEADY","forecastStart":1794643200,"minTemperatureChange":"STEADY","dayPrecipitationChange":"DEC","nightPrecipitationChange":"INC"},{"forecastEnd":1794816000,"maxTemperatureChange":"STEADY","forecastStart":1794729600,"minTemperatureChange":"STEADY","dayPrecipitationChange":"INC","nightPrecipitationChange":"DEC"}],"forecastEnd":1794816000,"forecastStart":1794643200},"weatherAlerts":{"metadata":{"language":"en","latitude":0,"longitude":0,"expireTime":1794569784,"sourceType":"STATION","attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"temporarilyUnavailable":false,"providerName":"National Weather Service","reportedTime":1794538320},"alerts":[{"id":{"bytes":[190,12,153,147,104,54,83,236,165,44,137,30,51,142,238,251]},"description":"Coastal Flood Advisory","token":"REDACTED______________","phenomenon":"Flood","severity":"MINOR","significance":"ADVISORY","source":"National Weather Service","urgency":"EXPECTED","certainty":"LIKELY","importance":"LOW","responses":["MONITOR"],"eventOnsetTime":1794578400,"detailsUrl":"https://weatherkit.apple.com/alertDetails/index.html?ids=be0c9993-6836-53ec-a52c-891e338eeefb&lang=en-US&timezone=America/Los_Angeles&party=apple","effectiveTime":1794538320,"issuedTime":1794538320,"eventSource":"US","areaId":"waz315","expireTime":1794574800,"countryCode":"US","eventEndTime":1794600000,"attributionUrl":"https://www.weather.gov"}],"detailsUrl":"https://weatherkit.apple.com/alertDetails/index.html?ids=be0c9993-6836-53ec-a52c-891e338eeefb&lang=en-US&timezone=America/Los_Angeles&party=apple"},"historicalComparisons":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"comparisons":[{"condition":"TEMPERATURE_MAX","deviation":"NORMAL","baselineValue":10.839996,"baselineStartDate":0,"currentValue":13.64015,"baselineType":"MEAN"},{"condition":"PRECIPITATION","deviation":"NORMAL","baselineValue":0.0,"baselineStartDate":0,"currentValue":0.0,"baselineType":"MEAN"}]},"forecastDaily":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"days":[{"moonrise":1794598874,"moonset":1794627382,"sunrise":1794582705,"sunset":1794616505,"forecastEnd":1794643200,"sunriseCivil":1794580706,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.64015,"solarMidnight":1794642825,"windSpeedAvg":9.337426,"temperatureMaxTime":1794585564,"windGustSpeedMax":36.504002,"sunriseAstronomical":1794576293,"forecastStart":1794556800,"moonPhase":"WAXING_CRESCENT","humidityMax":90,"precipitationAmountByType":[{"expected":5.652,"minimum":5.652,"maximum":5.652,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794622910,"visibilityMax":34072.0,"precipitationChance":86,"temperatureMin":9.91,"maxUvIndex":1,"temperatureMinTime":1794643200,"sunsetNautical":1794620737,"daytimeForecast":{"humidity":83,"daylight":true,"forecastEnd":1794625200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.64015,"uvIndexMin":0,"temperatureApparentMin":9.132,"windGustSpeedMax":32.810402,"perceivedPrecipitationIntensityMax":1.135,"uvIndexMax":1,"temperatureApparentMax":11.529953,"forecastStart":1794582000,"windDirection":181,"windSpeed":10.008599,"humidityMax":90,"precipitationAmountByType":[{"expected":5.455,"minimum":5.455,"maximum":5.455,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":53,"visibilityMax":34072.0,"cloudCover":99,"precipitationChance":79,"temperatureMin":10.49,"cloudCoverMidAltPct":94,"precipitationIntensityMax":1.224,"conditionCode":["RAIN"],"cloudCoverHighAltPct":76,"humidityMin":70,"visibilityMin":6167.0,"precipitationType":["RAIN"],"windSpeedMax":15.263999},"conditionCode":["RAIN"],"humidityMin":70,"solarNoon":1794599599,"visibilityMin":6167.0,"overnightForecast":{"humidity":86,"daylight":false,"forecastEnd":1794668400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.49,"uvIndexMin":0,"temperatureApparentMin":5.112798,"windGustSpeedMax":36.504002,"perceivedPrecipitationIntensityMax":0.549,"uvIndexMax":0,"temperatureApparentMax":10.093671,"forecastStart":1794625200,"windDirection":183,"windSpeed":13.7466,"humidityMax":89,"precipitationAmountByType":[{"expected":0.71,"minimum":0.71,"maximum":0.71,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":49,"visibilityMax":28703.0,"cloudCover":74,"precipitationChance":65,"temperatureMin":9.009192,"cloudCoverMidAltPct":49,"precipitationIntensityMax":0.234,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":31,"humidityMin":82,"visibilityMin":16190.0,"precipitationType":["RAIN"],"windSpeedMax":16.65},"precipitationType":["RAIN"],"sunriseNautical":1794578462,"sunsetCivil":1794618498,"windSpeedMax":16.254,"restOfDayForecast":{"humidity":83,"daylight":false,"forecastEnd":1794643200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.64015,"uvIndexMin":0,"temperatureApparentMin":5.957533,"windGustSpeedMax":36.504002,"perceivedPrecipitationIntensityMax":1.135,"uvIndexMax":1,"temperatureApparentMax":11.864366,"forecastStart":1794569484,"windDirection":171,"windSpeed":10.001331,"humidityMax":90,"precipitationAmountByType":[{"expected":5.652,"minimum":5.652,"maximum":5.652,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":42,"visibilityMax":34072.0,"cloudCover":95,"precipitationChance":84,"temperatureMin":9.91,"cloudCoverMidAltPct":84,"precipitationIntensityMax":1.224,"conditionCode":["RAIN"],"cloudCoverHighAltPct":76,"humidityMin":70,"visibilityMin":6167.0,"precipitationType":["RAIN"],"windSpeedMax":16.254}},{"moonrise":1794687261,"moonset":1794717746,"sunrise":1794669195,"sunset":1794702834,"forecastEnd":1794729600,"sunriseCivil":1794667189,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.170017,"solarMidnight":1794729234,"windSpeedAvg":13.5183,"temperatureMaxTime":1794689975,"windGustSpeedMax":36.504002,"sunriseAstronomical":1794662769,"forecastStart":1794643200,"moonPhase":"WAXING_CRESCENT","humidityMax":88,"precipitationAmountByType":[{"expected":5.55,"minimum":5.55,"maximum":5.55,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794709253,"visibilityMax":29373.0,"precipitationChance":85,"temperatureMin":9.009192,"maxUvIndex":1,"temperatureMinTime":1794668269,"sunsetNautical":1794707078,"daytimeForecast":{"humidity":79,"daylight":true,"forecastEnd":1794711600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.170017,"uvIndexMin":0,"temperatureApparentMin":5.696423,"windGustSpeedMax":33.551998,"perceivedPrecipitationIntensityMax":0.503,"uvIndexMax":1,"temperatureApparentMax":9.767292,"forecastStart":1794668400,"windDirection":166,"windSpeed":13.39695,"humidityMax":86,"precipitationAmountByType":[{"expected":0.96,"minimum":0.96,"maximum":0.96,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":59,"visibilityMax":29373.0,"cloudCover":89,"precipitationChance":55,"temperatureMin":9.01,"cloudCoverMidAltPct":72,"precipitationIntensityMax":0.194,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":64,"humidityMin":76,"visibilityMin":15078.0,"precipitationType":["RAIN"],"windSpeedMax":14.860799},"conditionCode":["RAIN"],"humidityMin":76,"solarNoon":1794686008,"visibilityMin":4069.0,"overnightForecast":{"humidity":84,"daylight":false,"forecastEnd":1794754800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.48,"uvIndexMin":0,"temperatureApparentMin":5.973359,"windGustSpeedMax":38.952,"perceivedPrecipitationIntensityMax":1.473,"uvIndexMax":0,"temperatureApparentMax":9.430315,"forecastStart":1794711600,"windDirection":155,"windSpeed":14.398049,"humidityMax":88,"precipitationAmountByType":[{"expected":8.279,"minimum":8.279,"maximum":8.279,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":67,"visibilityMax":25393.0,"cloudCover":94,"precipitationChance":87,"temperatureMin":10.038711,"cloudCoverMidAltPct":84,"precipitationIntensityMax":2.419,"conditionCode":["RAIN"],"cloudCoverHighAltPct":31,"humidityMin":78,"visibilityMin":4069.0,"precipitationType":["RAIN"],"windSpeedMax":17.780401},"precipitationType":["RAIN"],"sunriseNautical":1794664941,"sunsetCivil":1794704834,"windSpeedMax":16.65},{"moonrise":1794775225,"moonset":1794808239,"sunrise":1794755684,"sunset":1794789166,"forecastEnd":1794816000,"sunriseCivil":1794753672,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":12.499799,"solarMidnight":1794815645,"windSpeedAvg":13.973175,"temperatureMaxTime":1794775537,"windGustSpeedMax":38.952,"sunriseAstronomical":1794749245,"forecastStart":1794729600,"moonPhase":"WAXING_CRESCENT","humidityMax":90,"precipitationAmountByType":[{"expected":7.270999,"minimum":7.270999,"maximum":7.270999,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794795599,"visibilityMax":27960.0,"precipitationChance":94,"temperatureMin":7.48,"maxUvIndex":1,"temperatureMinTime":1794816000,"sunsetNautical":1794793421,"daytimeForecast":{"humidity":79,"daylight":true,"forecastEnd":1794798000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":12.499799,"uvIndexMin":0,"temperatureApparentMin":5.879239,"windGustSpeedMax":35.748001,"perceivedPrecipitationIntensityMax":0.775,"uvIndexMax":1,"temperatureApparentMax":9.362825,"forecastStart":1794754800,"windDirection":199,"windSpeed":15.284551,"humidityMax":86,"precipitationAmountByType":[{"expected":3.041,"minimum":3.041,"maximum":3.041,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":37,"visibilityMax":27960.0,"cloudCover":64,"precipitationChance":79,"temperatureMin":9.24,"cloudCoverMidAltPct":53,"precipitationIntensityMax":0.499,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":0,"humidityMin":73,"visibilityMin":13909.0,"precipitationType":["RAIN"],"windSpeedMax":18.6192},"conditionCode":["RAIN"],"humidityMin":73,"solarNoon":1794772418,"visibilityMin":7974.0,"overnightForecast":{"humidity":89,"daylight":false,"forecastEnd":1794841200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.24,"uvIndexMin":0,"temperatureApparentMin":4.984706,"windGustSpeedMax":20.1276,"perceivedPrecipitationIntensityMax":1.093,"uvIndexMax":0,"temperatureApparentMax":8.094274,"forecastStart":1794798000,"windDirection":167,"windSpeed":7.06065,"humidityMax":90,"precipitationAmountByType":[{"expected":2.986,"minimum":2.986,"maximum":2.986,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":36,"visibilityMax":26518.0,"cloudCover":63,"precipitationChance":51,"temperatureMin":6.209458,"cloudCoverMidAltPct":37,"precipitationIntensityMax":1.117,"conditionCode":["RAIN"],"cloudCoverHighAltPct":4,"humidityMin":83,"visibilityMin":16329.0,"precipitationType":["RAIN"],"windSpeedMax":8.186399},"precipitationType":["RAIN"],"sunriseNautical":1794751419,"sunsetCivil":1794791172,"windSpeedMax":18.6192},{"moonrise":1794862879,"moonset":1794898770,"sunrise":1794842173,"sunset":1794875500,"forecastEnd":1794902400,"sunriseCivil":1794840154,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.921954,"solarMidnight":1794902056,"windSpeedAvg":8.8203,"temperatureMaxTime":1794864861,"windGustSpeedMax":25.909199,"sunriseAstronomical":1794835720,"forecastStart":1794816000,"moonPhase":"WAXING_CRESCENT","humidityMax":90,"precipitationAmountByType":[{"expected":4.384,"minimum":4.384,"maximum":4.384,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794881947,"visibilityMax":31734.0,"precipitationChance":67,"temperatureMin":6.209458,"maxUvIndex":1,"temperatureMinTime":1794840092,"sunsetNautical":1794879767,"daytimeForecast":{"humidity":80,"daylight":true,"forecastEnd":1794884400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.921954,"uvIndexMin":0,"temperatureApparentMin":5.026797,"windGustSpeedMax":25.909199,"perceivedPrecipitationIntensityMax":0.808,"uvIndexMax":1,"temperatureApparentMax":10.102699,"forecastStart":1794841200,"windDirection":182,"windSpeed":9.35655,"humidityMax":90,"precipitationAmountByType":[{"expected":0.895,"minimum":0.895,"maximum":0.895,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":45,"visibilityMax":31734.0,"cloudCover":73,"precipitationChance":36,"temperatureMin":6.24,"cloudCoverMidAltPct":57,"precipitationIntensityMax":0.548,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":16,"humidityMin":73,"visibilityMin":16596.0,"precipitationType":["RAIN"],"windSpeedMax":12.225599},"conditionCode":["RAIN"],"humidityMin":73,"solarNoon":1794858830,"visibilityMin":16329.0,"overnightForecast":{"humidity":88,"daylight":false,"forecastEnd":1794927600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":8.71,"uvIndexMin":0,"temperatureApparentMin":4.863145,"windGustSpeedMax":25.909199,"perceivedPrecipitationIntensityMax":0.751,"uvIndexMax":0,"temperatureApparentMax":6.049259,"forecastStart":1794884400,"windDirection":169,"windSpeed":8.89755,"humidityMax":89,"precipitationAmountByType":[{"expected":0.568,"minimum":0.568,"maximum":0.568,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":56,"visibilityMax":28122.0,"cloudCover":60,"precipitationChance":50,"temperatureMin":5.989975,"cloudCoverMidAltPct":7,"precipitationIntensityMax":0.465,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":1,"humidityMin":85,"visibilityMin":16596.0,"precipitationType":["RAIN"],"windSpeedMax":11.7612},"precipitationType":["RAIN"],"sunriseNautical":1794837897,"sunsetCivil":1794877511,"windSpeedMax":12.225599},{"moonrise":1794950358,"sunrise":1794928662,"sunset":1794961836,"forecastEnd":1794988800,"sunriseCivil":1794926635,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.845637,"solarMidnight":1794988468,"windSpeedAvg":6.440775,"temperatureMaxTime":1794953467,"windGustSpeedMax":18.273602,"sunriseAstronomical":1794922194,"forecastStart":1794902400,"moonPhase":"FIRST_QUARTER","humidityMax":89,"precipitationAmountByType":[{"expected":0.341,"minimum":0.341,"maximum":0.341,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794968297,"visibilityMax":29733.0,"precipitationChance":53,"temperatureMin":5.98,"maxUvIndex":1,"temperatureMinTime":1794988800,"sunsetNautical":1794966114,"daytimeForecast":{"humidity":72,"daylight":true,"forecastEnd":1794970800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.845637,"uvIndexMin":0,"temperatureApparentMin":5.082117,"windGustSpeedMax":17.006401,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":1,"temperatureApparentMax":12.097757,"forecastStart":1794927600,"windDirection":154,"windSpeed":5.87355,"humidityMax":87,"precipitationAmountByType":[],"cloudCoverLowAltPct":15,"visibilityMax":29733.0,"cloudCover":29,"precipitationChance":0,"temperatureMin":6.13,"cloudCoverMidAltPct":1,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":6,"humidityMin":62,"visibilityMin":17780.0,"precipitationType":["CLEAR"],"windSpeedMax":7.1748},"conditionCode":["DRIZZLE"],"humidityMin":62,"solarNoon":1794945242,"visibilityMin":17780.0,"overnightForecast":{"humidity":82,"daylight":false,"forecastEnd":1795014000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":7.72,"uvIndexMin":0,"temperatureApparentMin":2.451838,"windGustSpeedMax":25.2936,"perceivedPrecipitationIntensityMax":0.95,"uvIndexMax":0,"temperatureApparentMax":7.151364,"forecastStart":1794970800,"windDirection":133,"windSpeed":7.62915,"humidityMax":89,"precipitationAmountByType":[{"expected":4.327,"minimum":4.327,"maximum":4.327,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":31,"visibilityMax":25857.0,"cloudCover":92,"precipitationChance":50,"temperatureMin":4.947516,"cloudCoverMidAltPct":66,"precipitationIntensityMax":0.798,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":89,"humidityMin":74,"visibilityMin":10859.0,"precipitationType":["RAIN"],"windSpeedMax":12.204},"precipitationType":["RAIN"],"sunriseNautical":1794924375,"sunsetCivil":1794963853,"windSpeedMax":8.9928},{"moonrise":1795037733,"moonset":1794989335,"sunrise":1795015150,"sunset":1795048175,"forecastEnd":1795075200,"sunriseCivil":1795013116,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.020019,"solarMidnight":1795074881,"windSpeedAvg":10.204425,"temperatureMaxTime":1795042826,"windGustSpeedMax":25.7724,"sunriseAstronomical":1795008667,"forecastStart":1794988800,"moonPhase":"WAXING_GIBBOUS","humidityMax":94,"precipitationAmountByType":[{"expected":6.641001,"minimum":6.641001,"maximum":6.641001,"precipitationType":["RAIN"]}],"sunsetAstronomical":1795054649,"visibilityMax":30421.0,"precipitationChance":74,"temperatureMin":4.947516,"maxUvIndex":1,"temperatureMinTime":1795009991,"sunsetNautical":1795052464,"daytimeForecast":{"humidity":81,"daylight":true,"forecastEnd":1795057200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.020019,"uvIndexMin":0,"temperatureApparentMin":2.416847,"windGustSpeedMax":25.7724,"perceivedPrecipitationIntensityMax":0.866,"uvIndexMax":1,"temperatureApparentMax":6.052989,"forecastStart":1795014000,"windDirection":182,"windSpeed":12.15015,"humidityMax":88,"precipitationAmountByType":[{"expected":2.276,"minimum":2.276,"maximum":2.276,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":60,"visibilityMax":30421.0,"cloudCover":82,"precipitationChance":66,"temperatureMin":5.11,"cloudCoverMidAltPct":76,"precipitationIntensityMax":0.643,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":50,"humidityMin":76,"visibilityMin":14677.0,"precipitationType":["RAIN"],"windSpeedMax":13.608},"conditionCode":["DRIZZLE"],"humidityMin":76,"solarNoon":1795031654,"visibilityMin":10859.0,"overnightForecast":{"humidity":92,"daylight":false,"forecastEnd":1795100400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":7.9,"uvIndexMin":0,"temperatureApparentMin":5.690445,"windGustSpeedMax":17.945999,"perceivedPrecipitationIntensityMax":0.327,"uvIndexMax":0,"temperatureApparentMax":6.35371,"forecastStart":1795057200,"windDirection":158,"windSpeed":5.645401,"humidityMax":95,"precipitationAmountByType":[{"expected":0.478,"minimum":0.478,"maximum":0.478,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":62,"visibilityMax":25229.0,"cloudCover":82,"precipitationChance":50,"temperatureMin":5.36,"cloudCoverMidAltPct":55,"precipitationIntensityMax":0.079,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":8,"humidityMin":82,"visibilityMin":12901.0,"precipitationType":["RAIN"],"windSpeedMax":9.774},"precipitationType":["RAIN"],"sunriseNautical":1795010851,"sunsetCivil":1795050198,"windSpeedMax":13.608},{"moonrise":1795125079,"moonset":1795079963,"sunrise":1795101637,"sunset":1795134515,"forecastEnd":1795161600,"sunriseCivil":1795099597,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.116987,"solarMidnight":1795161294,"windSpeedAvg":5.149725,"temperatureMaxTime":1795135920,"windGustSpeedMax":12.852,"sunriseAstronomical":1795095140,"forecastStart":1795075200,"moonPhase":"WAXING_GIBBOUS","humidityMax":96,"precipitationAmountByType":[{"expected":0.141,"minimum":0.141,"maximum":0.141,"precipitationType":["RAIN"]}],"sunsetAstronomical":1795141003,"visibilityMax":32204.0,"precipitationChance":53,"temperatureMin":5.36,"maxUvIndex":1,"temperatureMinTime":1795082400,"sunsetNautical":1795138815,"daytimeForecast":{"humidity":83,"daylight":true,"forecastEnd":1795143600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.116987,"uvIndexMin":0,"temperatureApparentMin":6.35371,"windGustSpeedMax":12.852,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":1,"temperatureApparentMax":10.070161,"forecastStart":1795100400,"windDirection":4,"windSpeed":6.0135,"humidityMax":93,"precipitationAmountByType":[],"cloudCoverLowAltPct":44,"visibilityMax":32204.0,"cloudCover":63,"precipitationChance":0,"temperatureMin":6.03,"cloudCoverMidAltPct":2,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":0,"humidityMin":77,"visibilityMin":12901.0,"precipitationType":["CLEAR"],"windSpeedMax":7.8264},"conditionCode":["DRIZZLE"],"humidityMin":77,"solarNoon":1795118068,"visibilityMin":12901.0,"overnightForecast":{"humidity":94,"daylight":false,"forecastEnd":1795186800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":8.52,"uvIndexMin":0,"temperatureApparentMin":4.634763,"windGustSpeedMax":9.4392,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":8.422463,"forecastStart":1795143600,"windDirection":44,"windSpeed":3.7053,"humidityMax":97,"precipitationAmountByType":[],"cloudCoverLowAltPct":34,"visibilityMax":28680.0,"cloudCover":52,"precipitationChance":0,"temperatureMin":4.006788,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"humidityMin":85,"visibilityMin":9083.0,"precipitationType":["CLEAR"],"windSpeedMax":4.0212},"precipitationType":["RAIN"],"sunriseNautical":1795097328,"sunsetCivil":1795136544,"windSpeedMax":7.8264},{"moonrise":1795212479,"moonset":1795170725,"sunrise":1795188123,"sunset":1795220858,"forecastEnd":1795248000,"sunriseCivil":1795186076,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.763362,"solarMidnight":1795247709,"windSpeedAvg":4.930575,"temperatureMaxTime":1795216005,"windGustSpeedMax":14.482799,"sunriseAstronomical":1795181613,"forecastStart":1795161600,"moonPhase":"WAXING_GIBBOUS","humidityMax":97,"precipitationAmountByType":[],"sunsetAstronomical":1795227360,"visibilityMax":17821.0,"precipitationChance":0,"temperatureMin":4.006788,"maxUvIndex":1,"temperatureMinTime":1795177699,"sunsetNautical":1795225170,"daytimeForecast":{"humidity":77,"daylight":true,"forecastEnd":1795230000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.763362,"uvIndexMin":0,"temperatureApparentMin":5.128633,"windGustSpeedMax":14.482799,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":1,"temperatureApparentMax":11.921696,"forecastStart":1795186800,"windDirection":9,"windSpeed":6.00165,"humidityMax":92,"precipitationAmountByType":[],"cloudCoverLowAltPct":16,"visibilityMax":13937.0,"cloudCover":19,"precipitationChance":0,"temperatureMin":4.73,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":69,"visibilityMin":9448.0,"precipitationType":["CLEAR"],"windSpeedMax":7.5348},"conditionCode":["MOSTLY_CLEAR"],"humidityMin":69,"solarNoon":1795204483,"visibilityMin":9083.0,"overnightForecast":{"humidity":78,"daylight":false,"forecastEnd":1795273200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":7.8,"uvIndexMin":0,"temperatureApparentMin":3.108699,"windGustSpeedMax":12.618,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":6.648598,"forecastStart":1795230000,"windDirection":63,"windSpeed":5.1837,"humidityMax":84,"precipitationAmountByType":[],"cloudCoverLowAltPct":7,"visibilityMax":24474.0,"cloudCover":10,"precipitationChance":0,"temperatureMin":4.249874,"cloudCoverMidAltPct":1,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":3,"humidityMin":66,"visibilityMin":13937.0,"precipitationType":["CLEAR"],"windSpeedMax":6.4764},"precipitationType":["CLEAR"],"sunriseNautical":1795183803,"sunsetCivil":1795222893,"windSpeedMax":7.5348},{"moonrise":1795300029,"moonset":1795261701,"sunrise":1795274609,"sunset":1795307204,"forecastEnd":1795334400,"sunriseCivil":1795272555,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.168908,"solarMidnight":1795334124,"windSpeedAvg":5.365725,"temperatureMaxTime":1795299642,"windGustSpeedMax":13.5252,"sunriseAstronomical":1795268084,"forecastStart":1795248000,"moonPhase":"WAXING_GIBBOUS","humidityMax":83,"precipitationAmountByType":[],"sunsetAstronomical":1795313719,"visibilityMax":26954.0,"precipitationChance":0,"temperatureMin":4.249874,"maxUvIndex":1,"temperatureMinTime":1795255291,"sunsetNautical":1795311526,"daytimeForecast":{"humidity":62,"daylight":true,"forecastEnd":1795316400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.168908,"uvIndexMin":0,"temperatureApparentMin":4.959615,"windGustSpeedMax":12.459599,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":1,"temperatureApparentMax":13.309563,"forecastStart":1795273200,"windDirection":135,"windSpeed":4.4259,"humidityMax":66,"precipitationAmountByType":[],"cloudCoverLowAltPct":2,"visibilityMax":26954.0,"cloudCover":19,"precipitationChance":0,"temperatureMin":6.57,"cloudCoverMidAltPct":17,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":60,"visibilityMin":8662.0,"precipitationType":["CLEAR"],"windSpeedMax":6.372},"conditionCode":["MOSTLY_CLEAR"],"humidityMin":60,"solarNoon":1795290898,"visibilityMin":8662.0,"overnightForecast":{"humidity":71,"daylight":false,"forecastEnd":1795359600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":8.71,"uvIndexMin":0,"temperatureApparentMin":3.380726,"windGustSpeedMax":13.5252,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":7.205438,"forecastStart":1795316400,"windDirection":154,"windSpeed":5.82615,"humidityMax":77,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":22708.0,"cloudCover":12,"precipitationChance":0,"temperatureMin":4.10744,"cloudCoverMidAltPct":4,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":64,"visibilityMin":10503.0,"precipitationType":["CLEAR"],"windSpeedMax":7.487999},"precipitationType":["CLEAR"],"sunriseNautical":1795270278,"sunsetCivil":1795309245,"windSpeedMax":7.487999}]},"forecastHourly":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"hours":[{"daylight":false,"humidity":85,"pressure":993.530029,"temperature":11.51,"visibility":22126.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":1,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":9.07518,"windGust":6.9768,"temperatureApparent":10.747892,"cloudCover":68,"precipitationChance":0,"cloudCoverMidAltPct":89,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":100,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794567600,"pressureTrend":"FALLING","windDirection":131,"uvIndex":0,"windSpeed":6.9768,"snowfallIntensity":0.0},{"daylight":false,"humidity":83,"pressure":992.429993,"temperature":10.95,"visibility":19738.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":4,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":8.174545,"windGust":12.87,"temperatureApparent":10.448459,"cloudCover":96,"precipitationChance":0,"cloudCoverMidAltPct":83,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":94,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794571200,"pressureTrend":"FALLING","windDirection":69,"uvIndex":0,"windSpeed":7.1892,"snowfallIntensity":0.0},{"daylight":false,"humidity":79,"pressure":990.97998,"temperature":11.34,"visibility":21981.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":4,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.828262,"windGust":15.4296,"temperatureApparent":9.860982,"cloudCover":74,"precipitationChance":0,"cloudCoverMidAltPct":48,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":67,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794574800,"pressureTrend":"FALLING","windDirection":83,"uvIndex":0,"windSpeed":9.0468,"snowfallIntensity":0.0},{"daylight":false,"humidity":74,"pressure":990.539978,"temperature":11.79,"visibility":26370.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":8,"perceivedPrecipitationIntensity":0.082,"precipitationIntensity":0.005,"temperatureDewPoint":7.305634,"windGust":17.848801,"temperatureApparent":11.730489,"cloudCover":100,"precipitationChance":5,"cloudCoverMidAltPct":95,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":98,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.005,"minimum":0.005,"maximum":0.005},"forecastStart":1794578400,"pressureTrend":"FALLING","windDirection":152,"uvIndex":0,"windSpeed":5.5908,"snowfallIntensity":0.0},{"daylight":false,"humidity":71,"pressure":989.789978,"temperature":12.85,"visibility":32347.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":6,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.719589,"windGust":25.4088,"temperatureApparent":11.529953,"cloudCover":97,"precipitationChance":0,"cloudCoverMidAltPct":92,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":97,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794582000,"pressureTrend":"FALLING","windDirection":107,"uvIndex":0,"windSpeed":9.324,"snowfallIntensity":0.0},{"daylight":true,"humidity":70,"pressure":990.130005,"temperature":13.64,"visibility":34072.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":24,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":8.26889,"windGust":31.352398,"temperatureApparent":10.051039,"cloudCover":99,"precipitationChance":0,"cloudCoverMidAltPct":96,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":56,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794585600,"pressureTrend":"FALLING","windDirection":131,"uvIndex":0,"windSpeed":15.263999,"snowfallIntensity":0.0},{"daylight":true,"humidity":80,"pressure":991.099976,"temperature":12.79,"visibility":20931.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":66,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":9.425461,"windGust":32.810402,"temperatureApparent":9.430462,"cloudCover":96,"precipitationChance":0,"cloudCoverMidAltPct":60,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":22,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794589200,"pressureTrend":"STEADY","windDirection":239,"uvIndex":0,"windSpeed":13.521601,"snowfallIntensity":0.0},{"daylight":true,"humidity":81,"pressure":991.559998,"temperature":13.02,"visibility":15831.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":39,"perceivedPrecipitationIntensity":0.568,"precipitationIntensity":0.252,"temperatureDewPoint":9.834717,"windGust":27.719999,"temperatureApparent":11.22246,"cloudCover":100,"precipitationChance":20,"cloudCoverMidAltPct":98,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":19,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.252,"minimum":0.252,"maximum":0.252},"forecastStart":1794592800,"pressureTrend":"RISING","windDirection":221,"uvIndex":1,"windSpeed":8.478,"snowfallIntensity":0.0},{"daylight":true,"humidity":82,"pressure":991.630005,"temperature":13.07,"visibility":16011.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":67,"perceivedPrecipitationIntensity":0.283,"precipitationIntensity":0.059,"temperatureDewPoint":10.066879,"windGust":30.477598,"temperatureApparent":10.538489,"cloudCover":100,"precipitationChance":23,"cloudCoverMidAltPct":96,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":34,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.059,"minimum":0.059,"maximum":0.059},"forecastStart":1794596400,"pressureTrend":"RISING","windDirection":194,"uvIndex":1,"windSpeed":10.5984,"snowfallIntensity":0.0},{"daylight":true,"humidity":82,"pressure":992.080017,"temperature":12.52,"visibility":16435.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":64,"perceivedPrecipitationIntensity":0.499,"precipitationIntensity":0.191,"temperatureDewPoint":9.529678,"windGust":31.676399,"temperatureApparent":9.236209,"cloudCover":100,"precipitationChance":35,"cloudCoverMidAltPct":97,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":99,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.191,"minimum":0.191,"maximum":0.191},"forecastStart":1794600000,"pressureTrend":"RISING","windDirection":206,"uvIndex":1,"windSpeed":12.549601,"snowfallIntensity":0.0},{"daylight":true,"humidity":84,"pressure":991.97998,"temperature":12.26,"visibility":12801.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":75,"perceivedPrecipitationIntensity":1.128,"precipitationIntensity":1.205,"temperatureDewPoint":9.634369,"windGust":28.656,"temperatureApparent":9.296653,"cloudCover":100,"precipitationChance":55,"cloudCoverMidAltPct":100,"conditionCode":["RAIN"],"cloudCoverHighAltPct":96,"precipitationType":["RAIN"],"precipitationAmount":{"expected":1.205,"minimum":1.205,"maximum":1.205},"forecastStart":1794603600,"pressureTrend":"STEADY","windDirection":201,"uvIndex":1,"windSpeed":11.6712,"snowfallIntensity":0.0},{"daylight":true,"humidity":85,"pressure":992.119995,"temperature":12.03,"visibility":9057.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":81,"perceivedPrecipitationIntensity":0.89,"precipitationIntensity":0.684,"temperatureDewPoint":9.585251,"windGust":26.046,"temperatureApparent":9.971526,"cloudCover":100,"precipitationChance":57,"cloudCoverMidAltPct":100,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":98,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.684,"minimum":0.684,"maximum":0.684},"forecastStart":1794607200,"pressureTrend":"STEADY","windDirection":194,"uvIndex":1,"windSpeed":9.234,"snowfallIntensity":0.0},{"daylight":true,"humidity":88,"pressure":992.48999,"temperature":11.76,"visibility":7366.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":60,"perceivedPrecipitationIntensity":1.135,"precipitationIntensity":1.224,"temperatureDewPoint":9.837143,"windGust":18.129601,"temperatureApparent":10.412608,"cloudCover":100,"precipitationChance":68,"cloudCoverMidAltPct":100,"conditionCode":["RAIN"],"cloudCoverHighAltPct":97,"precipitationType":["RAIN"],"precipitationAmount":{"expected":1.224,"minimum":1.224,"maximum":1.224},"forecastStart":1794610800,"pressureTrend":"STEADY","windDirection":180,"uvIndex":0,"windSpeed":7.7652,"snowfallIntensity":0.0},{"daylight":true,"humidity":90,"pressure":992.950012,"temperature":11.47,"visibility":6167.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":60,"perceivedPrecipitationIntensity":1.084,"precipitationIntensity":1.093,"temperatureDewPoint":9.886688,"windGust":17.326801,"temperatureApparent":11.056475,"cloudCover":100,"precipitationChance":68,"cloudCoverMidAltPct":100,"conditionCode":["RAIN"],"cloudCoverHighAltPct":96,"precipitationType":["RAIN"],"precipitationAmount":{"expected":1.093,"minimum":1.093,"maximum":1.093},"forecastStart":1794614400,"pressureTrend":"RISING","windDirection":158,"uvIndex":0,"windSpeed":6.4944,"snowfallIntensity":0.0},{"daylight":false,"humidity":90,"pressure":993.429993,"temperature":11.2,"visibility":10017.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":54,"perceivedPrecipitationIntensity":0.894,"precipitationIntensity":0.692,"temperatureDewPoint":9.620041,"windGust":19.764,"temperatureApparent":10.591156,"cloudCover":100,"precipitationChance":63,"cloudCoverMidAltPct":100,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":100,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.692,"minimum":0.692,"maximum":0.692},"forecastStart":1794618000,"pressureTrend":"RISING","windDirection":149,"uvIndex":0,"windSpeed":7.844399,"snowfallIntensity":0.0},{"daylight":false,"humidity":89,"pressure":993.809998,"temperature":10.85,"visibility":18581.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":39,"perceivedPrecipitationIntensity":0.274,"precipitationIntensity":0.055,"temperatureDewPoint":9.108414,"windGust":18.846001,"temperatureApparent":10.008285,"cloudCover":99,"precipitationChance":44,"cloudCoverMidAltPct":90,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":95,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.055,"minimum":0.055,"maximum":0.055},"forecastStart":1794621600,"pressureTrend":"RISING","windDirection":135,"uvIndex":0,"windSpeed":8.4348,"snowfallIntensity":0.0},{"daylight":false,"humidity":89,"pressure":994.340027,"temperature":10.49,"visibility":22953.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":17,"perceivedPrecipitationIntensity":0.203,"precipitationIntensity":0.03,"temperatureDewPoint":8.753342,"windGust":15.235199,"temperatureApparent":10.093671,"cloudCover":99,"precipitationChance":35,"cloudCoverMidAltPct":88,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":96,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.03,"minimum":0.03,"maximum":0.03},"forecastStart":1794625200,"pressureTrend":"RISING","windDirection":146,"uvIndex":0,"windSpeed":7.1712,"snowfallIntensity":0.0},{"daylight":false,"humidity":88,"pressure":994.849976,"temperature":10.28,"visibility":23999.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":23,"perceivedPrecipitationIntensity":0.352,"precipitationIntensity":0.092,"temperatureDewPoint":8.379349,"windGust":19.6416,"temperatureApparent":8.919455,"cloudCover":82,"precipitationChance":27,"cloudCoverMidAltPct":65,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":69,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.092,"minimum":0.092,"maximum":0.092},"forecastStart":1794628800,"pressureTrend":"RISING","windDirection":168,"uvIndex":0,"windSpeed":9.1692,"snowfallIntensity":0.0},{"daylight":false,"humidity":86,"pressure":995.400024,"temperature":10.2,"visibility":25448.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":38,"perceivedPrecipitationIntensity":0.192,"precipitationIntensity":0.027,"temperatureDewPoint":7.96196,"windGust":23.864401,"temperatureApparent":7.958751,"cloudCover":98,"precipitationChance":19,"cloudCoverMidAltPct":74,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":69,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.027,"minimum":0.027,"maximum":0.027},"forecastStart":1794632400,"pressureTrend":"RISING","windDirection":183,"uvIndex":0,"windSpeed":12.0564,"snowfallIntensity":0.0},{"daylight":false,"humidity":84,"pressure":996.070007,"temperature":10.02,"visibility":28076.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":33,"perceivedPrecipitationIntensity":0.242,"precipitationIntensity":0.043,"temperatureDewPoint":7.440018,"windGust":24.958801,"temperatureApparent":7.321218,"cloudCover":74,"precipitationChance":17,"cloudCoverMidAltPct":42,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":69,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.043,"minimum":0.043,"maximum":0.043},"forecastStart":1794636000,"pressureTrend":"RISING","windDirection":179,"uvIndex":0,"windSpeed":12.3696,"snowfallIntensity":0.0},{"daylight":false,"humidity":83,"pressure":996.48999,"temperature":9.93,"visibility":28703.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":62,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.176666,"windGust":28.184401,"temperatureApparent":7.196809,"cloudCover":93,"precipitationChance":0,"cloudCoverMidAltPct":58,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":69,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794639600,"pressureTrend":"RISING","windDirection":179,"uvIndex":0,"windSpeed":13.086,"snowfallIntensity":0.0},{"daylight":false,"humidity":82,"pressure":997.099976,"temperature":9.91,"visibility":22503.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":60,"perceivedPrecipitationIntensity":0.297,"precipitationIntensity":0.065,"temperatureDewPoint":6.980118,"windGust":36.504002,"temperatureApparent":5.957533,"cloudCover":83,"precipitationChance":13,"cloudCoverMidAltPct":68,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":31,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.065,"minimum":0.065,"maximum":0.065},"forecastStart":1794643200,"pressureTrend":"RISING","windDirection":188,"uvIndex":0,"windSpeed":16.254,"snowfallIntensity":0.0},{"daylight":false,"humidity":84,"pressure":997.929993,"temperature":9.78,"visibility":19336.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":62,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.20488,"windGust":36.144001,"temperatureApparent":5.642423,"cloudCover":79,"precipitationChance":0,"cloudCoverMidAltPct":45,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":20,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794646800,"pressureTrend":"RISING","windDirection":189,"uvIndex":0,"windSpeed":16.65,"snowfallIntensity":0.0},{"daylight":false,"humidity":86,"pressure":998.849976,"temperature":9.54,"visibility":16225.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":50,"perceivedPrecipitationIntensity":0.549,"precipitationIntensity":0.234,"temperatureDewPoint":7.313568,"windGust":32.5116,"temperatureApparent":5.722379,"cloudCover":59,"precipitationChance":24,"cloudCoverMidAltPct":30,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["RAIN"],"precipitationAmount":{"expected":0.234,"minimum":0.234,"maximum":0.234},"forecastStart":1794650400,"pressureTrend":"RISING","windDirection":186,"uvIndex":0,"windSpeed":14.9436,"snowfallIntensity":0.0}]},"forecastPeriodic":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"forecasts":[{"periods":[{"humidity":79,"daylight":false,"forecastEnd":1794582000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":12.85,"uvIndexMin":0,"temperatureApparentMin":9.818448,"windGustSpeedMax":25.4088,"perceivedPrecipitationIntensityMax":0.082,"uvIndexMax":0,"temperatureApparentMax":11.864366,"forecastStart":1794567600,"windDirection":101,"windSpeed":7.4943,"humidityMax":85,"precipitationAmountByType":[{"expected":0.005,"minimum":0.005,"maximum":0.005,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":5,"visibilityMax":32347.0,"cloudCover":88,"precipitationChance":24,"temperatureMin":10.94799,"cloudCoverMidAltPct":79,"precipitationIntensityMax":0.005,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":89,"humidityMin":71,"visibilityMin":19738.0,"precipitationType":["RAIN"],"windSpeedMax":9.324},{"humidity":77,"daylight":true,"forecastEnd":1794596400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.64015,"uvIndexMin":0,"temperatureApparentMin":9.384322,"windGustSpeedMax":32.810402,"perceivedPrecipitationIntensityMax":0.568,"uvIndexMax":1,"temperatureApparentMax":11.529953,"forecastStart":1794582000,"windDirection":185,"windSpeed":11.8062,"humidityMax":82,"precipitationAmountByType":[{"expected":0.252,"minimum":0.252,"maximum":0.252,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":41,"visibilityMax":34072.0,"cloudCover":98,"precipitationChance":26,"temperatureMin":12.767698,"cloudCoverMidAltPct":87,"precipitationIntensityMax":0.252,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":41,"humidityMin":70,"visibilityMin":15831.0,"precipitationType":["RAIN"],"windSpeedMax":15.263999},{"humidity":84,"daylight":true,"forecastEnd":1794610800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.07,"uvIndexMin":0,"temperatureApparentMin":9.132,"windGustSpeedMax":31.676399,"perceivedPrecipitationIntensityMax":1.135,"uvIndexMax":1,"temperatureApparentMax":10.538489,"forecastStart":1794596400,"windDirection":198,"windSpeed":10.65915,"humidityMax":88,"precipitationAmountByType":[{"expected":2.139,"minimum":2.139,"maximum":2.139,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":71,"visibilityMax":16435.0,"cloudCover":100,"precipitationChance":68,"temperatureMin":11.76,"cloudCoverMidAltPct":99,"precipitationIntensityMax":1.224,"conditionCode":["RAIN"],"cloudCoverHighAltPct":90,"humidityMin":82,"visibilityMin":7366.0,"precipitationType":["RAIN"],"windSpeedMax":12.549601},{"humidity":89,"daylight":true,"forecastEnd":1794625200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.76,"uvIndexMin":0,"temperatureApparentMin":9.995345,"windGustSpeedMax":19.764,"perceivedPrecipitationIntensityMax":1.135,"uvIndexMax":0,"temperatureApparentMax":11.058412,"forecastStart":1794610800,"windDirection":151,"windSpeed":7.560449,"humidityMax":90,"precipitationAmountByType":[{"expected":3.064,"minimum":3.064,"maximum":3.064,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":48,"visibilityMax":22953.0,"cloudCover":100,"precipitationChance":82,"temperatureMin":10.49,"cloudCoverMidAltPct":96,"precipitationIntensityMax":1.224,"conditionCode":["RAIN"],"cloudCoverHighAltPct":97,"humidityMin":88,"visibilityMin":6167.0,"precipitationType":["RAIN"],"windSpeedMax":8.4348},{"humidity":86,"daylight":false,"forecastEnd":1794639600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.49,"uvIndexMin":0,"temperatureApparentMin":7.196809,"windGustSpeedMax":28.184401,"perceivedPrecipitationIntensityMax":0.352,"uvIndexMax":0,"temperatureApparentMax":10.093671,"forecastStart":1794625200,"windDirection":175,"windSpeed":10.93095,"humidityMax":89,"precipitationAmountByType":[{"expected":0.192,"minimum":0.192,"maximum":0.192,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":33,"visibilityMax":28703.0,"cloudCover":88,"precipitationChance":49,"temperatureMin":9.93,"cloudCoverMidAltPct":64,"precipitationIntensityMax":0.092,"conditionCode":["DRIZZLE"],"cloudCoverHighAltPct":72,"humidityMin":83,"visibilityMin":22953.0,"precipitationType":["RAIN"],"windSpeedMax":13.086},{"humidity":84,"daylight":false,"forecastEnd":1794654000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.93,"uvIndexMin":0,"temperatureApparentMin":5.604858,"windGustSpeedMax":36.504002,"perceivedPrecipitationIntensityMax":0.549,"uvIndexMax":0,"temperatureApparentMax":7.196809,"forecastStart":1794639600,"windDirection":186,"windSpeed":15.3558,"humidityMax":88,"precipitationAmountByType":[{"expected":0.299,"minimum":0.299,"maximum":0.299,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":56,"visibilityMax":28703.0,"cloudCover":74,"precipitationChance":28,"temperatureMin":9.2,"cloudCoverMidAltPct":47,"precipitationIntensityMax":0.234,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":21,"humidityMin":82,"visibilityMin":16225.0,"precipitationType":["RAIN"],"windSpeedMax":16.65}],"periodLength":4}]},"forecastNextHour":{"metadata":{"latitude":0,"longitude":0,"expireTime":1794571284,"sourceType":"MODELED","attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"temporarilyUnavailable":false,"providerName":"US National Oceanic & Atmospheric Administration","reportedTime":1794568920},"condition":[{"parameters":[],"startTime":1794569460,"beginCondition":"CLEAR","forecastToken":"CLEAR","endCondition":"CLEAR"}],"summary":[{"condition":["CLEAR"],"precipitationChance":0,"startTime":1794569460,"precipitationIntensity":0.0}],"minutes":[{"precipitationChance":0,"startTime":1794569460,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569520,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569580,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569640,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569700,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569760,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569820,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569880,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794569940,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570000,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570060,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570120,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570180,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570240,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570300,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570360,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570420,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570480,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570540,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570600,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570660,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570720,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570780,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570840,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570900,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794570960,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571020,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571080,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571140,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571200,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571260,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571320,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571380,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571440,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571500,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571560,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571620,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571680,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571740,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571800,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571860,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571920,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794571980,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572040,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572100,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572160,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572220,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572280,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572340,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572400,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572460,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572520,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572580,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572640,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572700,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572760,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572820,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572880,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794572940,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573000,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573060,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573120,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573180,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573240,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573300,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573360,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573420,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573480,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573540,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573600,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573660,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573720,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573780,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573840,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573900,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794573960,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574020,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574080,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574140,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574200,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0},{"precipitationChance":0,"startTime":1794574260,"precipitationIntensity":0.0,"perceivedPrecipitationIntensity":0.0}],"forecastEnd":1794574320,"forecastStart":1794569460}} diff --git a/tests/fixtures/wk3-paired/seattle/manifest.json b/tests/fixtures/wk3-paired/seattle/manifest.json new file mode 100644 index 00000000..8b64fda3 --- /dev/null +++ b/tests/fixtures/wk3-paired/seattle/manifest.json @@ -0,0 +1,99 @@ +{ + "schema": "lib-rc-3.0.0-b66", + "public_test_location": "seattle public city centre (not device location)", + "responses": [ + { + "file": "apple.json", + "started": 1794569484.718946, + "finished": 1794569484.8511071, + "status": 200, + "content_type": "application/json", + "bytes": 62339 + }, + { + "file": "apple.bin", + "started": 1794569484.720716, + "finished": 1794569484.834561, + "status": 200, + "content_type": "application/vnd.apple.flatbuffer;messageType=WK3.Weather", + "bytes": 13784 + } + ], + "sanitization": { + "coordinate_fields": [ + { + "root_slot": 0, + "latitude_offset": 13676, + "longitude_offset": 13672 + }, + { + "root_slot": 1, + "latitude_offset": 13288, + "longitude_offset": 13284 + }, + { + "root_slot": 2, + "latitude_offset": 12868, + "longitude_offset": 12864 + }, + { + "root_slot": 3, + "latitude_offset": 7712, + "longitude_offset": 7708 + }, + { + "root_slot": 4, + "latitude_offset": 4548, + "longitude_offset": 4544 + }, + { + "root_slot": 5, + "latitude_offset": 3368, + "longitude_offset": 3364 + }, + { + "root_slot": 6, + "latitude_offset": 188, + "longitude_offset": 184 + }, + { + "root_slot": 7, + "latitude_offset": 380, + "longitude_offset": 376 + }, + { + "root_slot": 9, + "latitude_offset": 1164, + "longitude_offset": 1160 + }, + { + "root_slot": 14, + "latitude_offset": 2132, + "longitude_offset": 2128 + } + ], + "coordinate_urls": 0, + "method": "Only vtable-validated Metadata latitude/longitude floats and same-length coordinate URL digits replaced. No re-encoding." + }, + "sha256": { + "apple.json": "96cff738a4851b5a1f2658295b1c7bf66c067339bdfbe3dcd8b9d8fa4b6247c7", + "apple.bin": "f019b146b663ba1ab7a5c8388b4adb8f5e5680d582e3f120c5497001707e16a2" + }, + "schema_source": { + "weatherkit_dev": "cd9d7927094aff79906fe3430f339f39970ca46e", + "protocol_tag": "v2.0.5", + "protocol_commit": "c87c1dcd851ea9293141bcfb32fde1b2cca564b9" + }, + "additional_redactions": [ + { + "kind": "coordinate-url", + "offset": 13708, + "bytes": 75 + }, + { + "kind": "weather-alert-token", + "offset": 864, + "bytes": 22 + } + ] +} diff --git a/tests/fixtures/wk3-paired/tianjin/apple.bin b/tests/fixtures/wk3-paired/tianjin/apple.bin new file mode 100644 index 00000000..90d42083 Binary files /dev/null and b/tests/fixtures/wk3-paired/tianjin/apple.bin differ diff --git a/tests/fixtures/wk3-paired/tianjin/apple.json b/tests/fixtures/wk3-paired/tianjin/apple.json new file mode 100644 index 00000000..27959d4a --- /dev/null +++ b/tests/fixtures/wk3-paired/tianjin/apple.json @@ -0,0 +1 @@ +{"news":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"APPLE_INTERNAL","reportedTime":1794569484},"placements":[]},"airQuality":{"metadata":{"language":"en","latitude":0,"longitude":0,"expireTime":1794573000,"sourceType":"STATION","attributionUrl":"https://www.qweather.com/air/a/00.0000,000.0000?from=AppleWeatherService&lang=en","readTime":1794569400,"temporarilyUnavailable":false,"providerName":"QWeather","reportedTime":1794564000},"index":152,"pollutants":[{"amount":47.0,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"NO2"},{"amount":0.9,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"CO"},{"amount":74.0,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"OZONE"},{"amount":116.0,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"PM2_5"},{"amount":9.0,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"SO2"},{"amount":132.0,"units":"MICROGRAMS_PER_CUBIC_METER","pollutantType":"PM10"}],"scale":"HJ6332012.2604","primaryPollutant":"NOT_AVAILABLE","previousDayComparison":"UNKNOWN","categoryIndex":4,"isSignificant":true},"currentWeather":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794569784,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794569484},"daylight":false,"humidity":73,"pressure":1019.21283,"temperature":10.709241,"visibility":15433.75293,"asOf":1794569484,"precipitationIntensity":0.0,"temperatureDewPoint":6.065262,"windGust":7.254264,"temperatureApparent":9.943502,"snowfallAmountPrevious6h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"snowfallAmountPrevious24h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountNext6hByType":[],"snowfallAmountNext24h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountPrevious6hByType":[],"snowfallAmountPrevious1h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"pressureTrend":"RISING","windDirection":113,"windSpeed":4.486428,"cloudCoverLowAltPct":1,"perceivedPrecipitationIntensity":0.0,"precipitationAmountPrevious1hByType":[],"cloudCover":22,"precipitationAmountNext1hByType":[],"snowfallAmountNext1h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"precipitationAmountPrevious24hByType":[],"cloudCoverMidAltPct":23,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":16,"snowfallAmountNext6h":{"expected":0.0,"minimum":0.0,"maximum":0.0},"uvIndex":0,"precipitationAmountNext24hByType":[]},"weatherChanges":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"changes":[{"forecastEnd":1794672000,"maxTemperatureChange":"STEADY","forecastStart":1794585600,"minTemperatureChange":"STEADY","dayPrecipitationChange":"STEADY","nightPrecipitationChange":"STEADY"},{"forecastEnd":1794758400,"maxTemperatureChange":"STEADY","forecastStart":1794672000,"minTemperatureChange":"STEADY","dayPrecipitationChange":"STEADY","nightPrecipitationChange":"STEADY"}],"forecastEnd":1794758400,"forecastStart":1794585600},"weatherAlerts":{"metadata":{"language":"en-US","latitude":0,"longitude":0,"expireTime":1794569784,"sourceType":"STATION","attributionUrl":"https://www.qweather.com","readTime":1794569484,"temporarilyUnavailable":false,"providerName":"National Early Warning Center","reportedTime":1794569484},"alerts":[]},"historicalComparisons":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"comparisons":[{"condition":"TEMPERATURE_MAX","deviation":"HIGHER","baselineValue":11.869995,"baselineStartDate":0,"currentValue":15.948572,"baselineType":"MEAN"},{"condition":"PRECIPITATION","deviation":"NORMAL","baselineValue":0.0,"baselineStartDate":0,"currentValue":0.0,"baselineType":"MEAN"}]},"forecastDaily":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"days":[{"moonrise":1794537367,"moonset":1794569781,"sunrise":1794523897,"sunset":1794560337,"forecastEnd":1794585600,"sunriseCivil":1794522195,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":15.948572,"solarMidnight":1794585336,"windSpeedAvg":2.7225,"temperatureMaxTime":1794550308,"windGustSpeedMax":8.110801,"sunriseAstronomical":1794518371,"forecastStart":1794499200,"moonPhase":"WAXING_CRESCENT","humidityMax":80,"precipitationAmountByType":[],"sunsetAstronomical":1794565866,"visibilityMax":16892.0,"precipitationChance":0,"temperatureMin":6.036857,"maxUvIndex":3,"temperatureMinTime":1794523543,"sunsetNautical":1794563969,"daytimeForecast":{"humidity":65,"daylight":true,"forecastEnd":1794567600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":15.948572,"uvIndexMin":0,"temperatureApparentMin":7.483241,"windGustSpeedMax":8.110801,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":3,"temperatureApparentMax":17.519449,"forecastStart":1794524400,"windDirection":183,"windSpeed":2.9994,"humidityMax":80,"precipitationAmountByType":[],"cloudCoverLowAltPct":22,"visibilityMax":16892.0,"cloudCover":59,"precipitationChance":0,"temperatureMin":6.08,"cloudCoverMidAltPct":34,"precipitationIntensityMax":0.0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":24,"humidityMin":51,"visibilityMin":8767.0,"precipitationType":["CLEAR"],"windSpeedMax":4.8312},"conditionCode":["PARTLY_CLOUDY"],"humidityMin":51,"solarNoon":1794542116,"visibilityMin":8767.0,"overnightForecast":{"humidity":78,"daylight":false,"forecastEnd":1794610800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.33,"uvIndexMin":0,"temperatureApparentMin":6.06018,"windGustSpeedMax":8.866799,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":10.562979,"forecastStart":1794567600,"windDirection":61,"windSpeed":4.492949,"humidityMax":82,"precipitationAmountByType":[],"cloudCoverLowAltPct":29,"visibilityMax":15686.0,"cloudCover":28,"precipitationChance":0,"temperatureMin":6.229443,"cloudCoverMidAltPct":3,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":4,"humidityMin":71,"visibilityMin":7914.0,"precipitationType":["CLEAR"],"windSpeedMax":5.6232},"precipitationType":["CLEAR"],"sunriseNautical":1794520263,"sunsetCivil":1794562046,"windSpeedMax":4.8312,"restOfDayForecast":{"humidity":76,"daylight":false,"forecastEnd":1794585600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.709241,"uvIndexMin":0,"temperatureApparentMin":7.612356,"windGustSpeedMax":7.254264,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":9.928953,"forecastStart":1794569484,"windDirection":99,"windSpeed":3.721092,"humidityMax":80,"precipitationAmountByType":[],"cloudCoverLowAltPct":1,"visibilityMax":15449.0,"cloudCover":7,"precipitationChance":0,"temperatureMin":8.18,"cloudCoverMidAltPct":4,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":8,"humidityMin":73,"visibilityMin":14781.0,"precipitationType":["CLEAR"],"windSpeedMax":4.486428}},{"moonrise":1794626336,"moonset":1794659714,"sunrise":1794610363,"sunset":1794646689,"forecastEnd":1794672000,"sunriseCivil":1794608658,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.525146,"solarMidnight":1794671745,"windSpeedAvg":3.86175,"temperatureMaxTime":1794636251,"windGustSpeedMax":9.558,"sunriseAstronomical":1794604829,"forecastStart":1794585600,"moonPhase":"WAXING_CRESCENT","humidityMax":84,"precipitationAmountByType":[],"sunsetAstronomical":1794652227,"visibilityMax":17418.0,"precipitationChance":0,"temperatureMin":6.229443,"maxUvIndex":3,"temperatureMinTime":1794610254,"sunsetNautical":1794650328,"daytimeForecast":{"humidity":74,"daylight":true,"forecastEnd":1794654000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.525146,"uvIndexMin":0,"temperatureApparentMin":6.244272,"windGustSpeedMax":9.558,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":3,"temperatureApparentMax":16.761372,"forecastStart":1794610800,"windDirection":42,"windSpeed":3.7722,"humidityMax":80,"precipitationAmountByType":[],"cloudCoverLowAltPct":28,"visibilityMax":17418.0,"cloudCover":23,"precipitationChance":0,"temperatureMin":6.24,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":1,"humidityMin":66,"visibilityMin":8031.0,"precipitationType":["CLEAR"],"windSpeedMax":4.7484},"conditionCode":["MOSTLY_CLEAR"],"humidityMin":66,"solarNoon":1794628524,"visibilityMin":7914.0,"overnightForecast":{"humidity":82,"daylight":false,"forecastEnd":1794697200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.89,"uvIndexMin":0,"temperatureApparentMin":5.9612,"windGustSpeedMax":13.1256,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":9.523725,"forecastStart":1794654000,"windDirection":6,"windSpeed":4.05435,"humidityMax":86,"precipitationAmountByType":[],"cloudCoverLowAltPct":50,"visibilityMax":16701.0,"cloudCover":72,"precipitationChance":0,"temperatureMin":6.273458,"cloudCoverMidAltPct":23,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":66,"humidityMin":77,"visibilityMin":11806.0,"precipitationType":["CLEAR"],"windSpeedMax":7.5672},"precipitationType":["CLEAR"],"sunriseNautical":1794606722,"sunsetCivil":1794648401,"windSpeedMax":5.6232},{"moonrise":1794714894,"moonset":1794749778,"sunrise":1794696830,"sunset":1794733042,"forecastEnd":1794758400,"sunriseCivil":1794695121,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.048255,"solarMidnight":1794758155,"windSpeedAvg":9.0489,"temperatureMaxTime":1794721824,"windGustSpeedMax":26.888401,"sunriseAstronomical":1794691286,"forecastStart":1794672000,"moonPhase":"WAXING_CRESCENT","humidityMax":86,"precipitationAmountByType":[],"sunsetAstronomical":1794738589,"visibilityMax":18299.0,"precipitationChance":0,"temperatureMin":6.273458,"maxUvIndex":2,"temperatureMinTime":1794690768,"sunsetNautical":1794736688,"daytimeForecast":{"humidity":60,"daylight":true,"forecastEnd":1794740400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.048255,"uvIndexMin":0,"temperatureApparentMin":5.886318,"windGustSpeedMax":26.888401,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":2,"temperatureApparentMax":9.46348,"forecastStart":1794697200,"windDirection":62,"windSpeed":10.78515,"humidityMax":80,"precipitationAmountByType":[],"cloudCoverLowAltPct":22,"visibilityMax":18299.0,"cloudCover":96,"precipitationChance":0,"temperatureMin":6.95,"cloudCoverMidAltPct":55,"precipitationIntensityMax":0.0,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":77,"humidityMin":50,"visibilityMin":11806.0,"precipitationType":["CLEAR"],"windSpeedMax":13.932},"conditionCode":["CLOUDY"],"humidityMin":50,"solarNoon":1794714934,"visibilityMin":11806.0,"overnightForecast":{"humidity":60,"daylight":false,"forecastEnd":1794783600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":8.38,"uvIndexMin":0,"temperatureApparentMin":3.076332,"windGustSpeedMax":24.940802,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":6.39883,"forecastStart":1794740400,"windDirection":66,"windSpeed":10.083,"humidityMax":63,"precipitationAmountByType":[],"cloudCoverLowAltPct":24,"visibilityMax":17627.0,"cloudCover":100,"precipitationChance":0,"temperatureMin":4.944714,"cloudCoverMidAltPct":99,"precipitationIntensityMax":0.0,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":83,"humidityMin":57,"visibilityMin":15513.0,"precipitationType":["CLEAR"],"windSpeedMax":11.0052},"precipitationType":["CLEAR"],"sunriseNautical":1794693181,"sunsetCivil":1794734757,"windSpeedMax":13.932},{"moonrise":1794803128,"moonset":1794839885,"sunrise":1794783296,"sunset":1794819397,"forecastEnd":1794844800,"sunriseCivil":1794781584,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.400417,"solarMidnight":1794844566,"windSpeedAvg":7.362075,"temperatureMaxTime":1794806600,"windGustSpeedMax":23.972401,"sunriseAstronomical":1794777743,"forecastStart":1794758400,"moonPhase":"WAXING_CRESCENT","humidityMax":68,"precipitationAmountByType":[],"sunsetAstronomical":1794824953,"visibilityMax":22211.0,"precipitationChance":0,"temperatureMin":4.56,"maxUvIndex":2,"temperatureMinTime":1794844800,"sunsetNautical":1794823050,"daytimeForecast":{"humidity":47,"daylight":true,"forecastEnd":1794826800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.400417,"uvIndexMin":0,"temperatureApparentMin":2.732167,"windGustSpeedMax":19.915201,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":2,"temperatureApparentMax":9.868329,"forecastStart":1794783600,"windDirection":67,"windSpeed":7.23615,"humidityMax":63,"precipitationAmountByType":[],"cloudCoverLowAltPct":9,"visibilityMax":22211.0,"cloudCover":88,"precipitationChance":0,"temperatureMin":4.95,"cloudCoverMidAltPct":64,"precipitationIntensityMax":0.0,"conditionCode":["CLOUDY"],"cloudCoverHighAltPct":61,"humidityMin":39,"visibilityMin":17627.0,"precipitationType":["CLEAR"],"windSpeedMax":10.983601},"conditionCode":["CLOUDY"],"humidityMin":39,"solarNoon":1794801345,"visibilityMin":15513.0,"overnightForecast":{"humidity":65,"daylight":false,"forecastEnd":1794870000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":6.89,"uvIndexMin":0,"temperatureApparentMin":3.673543,"windGustSpeedMax":8.4492,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":7.503468,"forecastStart":1794826800,"windDirection":243,"windSpeed":4.4913,"humidityMax":72,"precipitationAmountByType":[],"cloudCoverLowAltPct":12,"visibilityMax":21431.0,"cloudCover":79,"precipitationChance":0,"temperatureMin":3.756924,"cloudCoverMidAltPct":46,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":68,"humidityMin":55,"visibilityMin":20002.0,"precipitationType":["CLEAR"],"windSpeedMax":5.1444},"precipitationType":["CLEAR"],"sunriseNautical":1794779641,"sunsetCivil":1794821116,"windSpeedMax":11.0052},{"moonrise":1794891114,"moonset":1794930001,"sunrise":1794869762,"sunset":1794905754,"forecastEnd":1794931200,"sunriseCivil":1794868047,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":7.708934,"solarMidnight":1794930977,"windSpeedAvg":9.268724,"temperatureMaxTime":1794894417,"windGustSpeedMax":42.264,"sunriseAstronomical":1794864201,"forecastStart":1794844800,"moonPhase":"WAXING_CRESCENT","humidityMax":80,"precipitationAmountByType":[{"expected":1.299,"minimum":1.299,"maximum":1.299,"precipitationType":["RAIN"]}],"sunsetAstronomical":1794911319,"visibilityMax":20751.0,"precipitationChance":33,"temperatureMin":3.756924,"maxUvIndex":2,"temperatureMinTime":1794851700,"sunsetNautical":1794909414,"daytimeForecast":{"humidity":67,"daylight":true,"forecastEnd":1794913200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":7.708934,"uvIndexMin":0,"temperatureApparentMin":4.476398,"windGustSpeedMax":23.410799,"perceivedPrecipitationIntensityMax":0.379,"uvIndexMax":2,"temperatureApparentMax":6.328387,"forecastStart":1794870000,"windDirection":236,"windSpeed":7.8021,"humidityMax":79,"precipitationAmountByType":[{"expected":0.549,"minimum":0.549,"maximum":0.549,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":28,"visibilityMax":20002.0,"cloudCover":86,"precipitationChance":22,"temperatureMin":5.65,"cloudCoverMidAltPct":79,"precipitationIntensityMax":0.107,"conditionCode":["MOSTLY_CLOUDY"],"cloudCoverHighAltPct":10,"humidityMin":62,"visibilityMin":17235.0,"precipitationType":["RAIN"],"windSpeedMax":11.019601},"conditionCode":["DRIZZLE"],"humidityMin":60,"solarNoon":1794887756,"visibilityMin":14655.0,"overnightForecast":{"humidity":58,"daylight":false,"forecastEnd":1794956400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":6.3,"uvIndexMin":0,"temperatureApparentMin":-6.986949,"windGustSpeedMax":55.332001,"perceivedPrecipitationIntensityMax":0.879,"uvIndexMax":0,"temperatureApparentMax":4.476398,"forecastStart":1794913200,"windDirection":313,"windSpeed":22.26795,"humidityMax":80,"precipitationAmountByType":[{"expected":0.75,"minimum":0.75,"maximum":0.75,"precipitationType":["RAIN"]}],"cloudCoverLowAltPct":28,"visibilityMax":22174.0,"cloudCover":36,"precipitationChance":25,"temperatureMin":2.302699,"cloudCoverMidAltPct":17,"precipitationIntensityMax":0.664,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":40,"visibilityMin":14655.0,"precipitationType":["RAIN"],"windSpeedMax":25.1208},"precipitationType":["RAIN"],"sunriseNautical":1794866100,"sunsetCivil":1794907476,"windSpeedMax":23.252399},{"moonrise":1794978953,"sunrise":1794956228,"sunset":1794992113,"forecastEnd":1795017600,"sunriseCivil":1794954510,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.80324,"solarMidnight":1795017389,"windSpeedAvg":19.258276,"temperatureMaxTime":1794981824,"windGustSpeedMax":56.231998,"sunriseAstronomical":1794950658,"forecastStart":1794931200,"moonPhase":"FIRST_QUARTER","humidityMax":62,"precipitationAmountByType":[],"sunsetAstronomical":1794997686,"visibilityMax":22228.0,"precipitationChance":0,"temperatureMin":0.23,"maxUvIndex":2,"temperatureMinTime":1795017600,"sunsetNautical":1794995780,"daytimeForecast":{"humidity":30,"daylight":true,"forecastEnd":1794999600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.80324,"uvIndexMin":0,"temperatureApparentMin":-6.672721,"windGustSpeedMax":56.231998,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":2,"temperatureApparentMax":7.315022,"forecastStart":1794956400,"windDirection":314,"windSpeed":20.3811,"humidityMax":45,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":22228.0,"cloudCover":4,"precipitationChance":0,"temperatureMin":2.35,"cloudCoverMidAltPct":1,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":22,"visibilityMin":21648.0,"precipitationType":["CLEAR"],"windSpeedMax":25.477201},"conditionCode":["CLEAR"],"humidityMin":22,"solarNoon":1794974168,"visibilityMin":18220.0,"overnightForecast":{"humidity":61,"daylight":false,"forecastEnd":1795042800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":3.24,"uvIndexMin":0,"temperatureApparentMin":-3.726878,"windGustSpeedMax":22.8204,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":-0.249791,"forecastStart":1794999600,"windDirection":251,"windSpeed":9.15675,"humidityMax":65,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":22056.0,"cloudCover":5,"precipitationChance":0,"temperatureMin":-0.395956,"cloudCoverMidAltPct":1,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":45,"visibilityMin":20939.0,"precipitationType":["CLEAR"],"windSpeedMax":9.813601},"precipitationType":["CLEAR"],"sunriseNautical":1794952558,"sunsetCivil":1794993838,"windSpeedMax":25.477201},{"moonrise":1795066723,"moonset":1795020142,"sunrise":1795042694,"sunset":1795078473,"forecastEnd":1795104000,"sunriseCivil":1795040972,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.503553,"solarMidnight":1795103803,"windSpeedAvg":12.2721,"temperatureMaxTime":1795076004,"windGustSpeedMax":27.108,"sunriseAstronomical":1795037114,"forecastStart":1795017600,"moonPhase":"WAXING_GIBBOUS","humidityMax":65,"precipitationAmountByType":[],"sunsetAstronomical":1795084056,"visibilityMax":21819.0,"precipitationChance":0,"temperatureMin":-0.395956,"maxUvIndex":2,"temperatureMinTime":1795027447,"sunsetNautical":1795082148,"daytimeForecast":{"humidity":41,"daylight":true,"forecastEnd":1795086000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.503553,"uvIndexMin":0,"temperatureApparentMin":-0.800893,"windGustSpeedMax":26.3664,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":2,"temperatureApparentMax":8.820073,"forecastStart":1795042800,"windDirection":315,"windSpeed":13.977901,"humidityMax":57,"precipitationAmountByType":[],"cloudCoverLowAltPct":3,"visibilityMax":21819.0,"cloudCover":7,"precipitationChance":0,"temperatureMin":1.95,"cloudCoverMidAltPct":3,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":33,"visibilityMin":20224.0,"precipitationType":["CLEAR"],"windSpeedMax":16.624802},"conditionCode":["CLEAR"],"humidityMin":33,"solarNoon":1795060582,"visibilityMin":20102.0,"overnightForecast":{"humidity":61,"daylight":false,"forecastEnd":1795129200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":5.49,"uvIndexMin":0,"temperatureApparentMin":-2.58954,"windGustSpeedMax":27.108,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":0.74367,"forecastStart":1795086000,"windDirection":314,"windSpeed":10.635301,"humidityMax":64,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":21805.0,"cloudCover":8,"precipitationChance":0,"temperatureMin":1.248407,"cloudCoverMidAltPct":6,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":51,"visibilityMin":20102.0,"precipitationType":["CLEAR"],"windSpeedMax":13.14},"precipitationType":["CLEAR"],"sunriseNautical":1795039017,"sunsetCivil":1795080202,"windSpeedMax":16.624802},{"moonrise":1795154498,"moonset":1795110359,"sunrise":1795129159,"sunset":1795164836,"forecastEnd":1795190400,"sunriseCivil":1795127434,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":7.197065,"solarMidnight":1795190217,"windSpeedAvg":11.036625,"temperatureMaxTime":1795163979,"windGustSpeedMax":37.152,"sunriseAstronomical":1795123571,"forecastStart":1795104000,"moonPhase":"WAXING_GIBBOUS","humidityMax":72,"precipitationAmountByType":[],"sunsetAstronomical":1795170428,"visibilityMax":21805.0,"precipitationChance":0,"temperatureMin":0.34,"maxUvIndex":2,"temperatureMinTime":1795190400,"sunsetNautical":1795168517,"daytimeForecast":{"humidity":50,"daylight":true,"forecastEnd":1795172400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":7.197065,"uvIndexMin":0,"temperatureApparentMin":-0.743232,"windGustSpeedMax":37.152,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":2,"temperatureApparentMax":4.868875,"forecastStart":1795129200,"windDirection":285,"windSpeed":12.71835,"humidityMax":62,"precipitationAmountByType":[],"cloudCoverLowAltPct":6,"visibilityMax":21441.0,"cloudCover":10,"precipitationChance":0,"temperatureMin":2.47,"cloudCoverMidAltPct":4,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":44,"visibilityMin":19909.0,"precipitationType":["CLEAR"],"windSpeedMax":16.7724},"conditionCode":["CLEAR"],"humidityMin":44,"solarNoon":1795146995,"visibilityMin":19909.0,"overnightForecast":{"humidity":66,"daylight":false,"forecastEnd":1795215600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":2.93,"uvIndexMin":0,"temperatureApparentMin":-3.410986,"windGustSpeedMax":24.893999,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":0.768481,"forecastStart":1795172400,"windDirection":7,"windSpeed":9.76455,"humidityMax":72,"precipitationAmountByType":[],"cloudCoverLowAltPct":18,"visibilityMax":20778.0,"cloudCover":34,"precipitationChance":0,"temperatureMin":-0.2056,"cloudCoverMidAltPct":17,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":56,"visibilityMin":19594.0,"precipitationType":["CLEAR"],"windSpeedMax":11.4156},"precipitationType":["CLEAR"],"sunriseNautical":1795125475,"sunsetCivil":1795166568,"windSpeedMax":16.7724},{"moonrise":1795242382,"moonset":1795200721,"sunrise":1795215624,"sunset":1795251200,"forecastEnd":1795276800,"sunriseCivil":1795213896,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.494344,"solarMidnight":1795276631,"windSpeedAvg":10.073925,"temperatureMaxTime":1795239531,"windGustSpeedMax":27.5436,"sunriseAstronomical":1795210027,"forecastStart":1795190400,"moonPhase":"WAXING_GIBBOUS","humidityMax":67,"precipitationAmountByType":[],"sunsetAstronomical":1795256801,"visibilityMax":20554.0,"precipitationChance":0,"temperatureMin":-0.2056,"maxUvIndex":2,"temperatureMinTime":1795205520,"sunsetNautical":1795254889,"daytimeForecast":{"humidity":35,"daylight":true,"forecastEnd":1795258800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.494344,"uvIndexMin":0,"temperatureApparentMin":-1.165541,"windGustSpeedMax":27.5436,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":2,"temperatureApparentMax":8.215144,"forecastStart":1795215600,"windDirection":337,"windSpeed":11.055901,"humidityMax":56,"precipitationAmountByType":[],"cloudCoverLowAltPct":19,"visibilityMax":20421.0,"cloudCover":48,"precipitationChance":0,"temperatureMin":1.13,"cloudCoverMidAltPct":30,"precipitationIntensityMax":0.0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"humidityMin":24,"visibilityMin":19541.0,"precipitationType":["CLEAR"],"windSpeedMax":14.292},"conditionCode":["PARTLY_CLOUDY"],"humidityMin":24,"solarNoon":1795233410,"visibilityMin":19541.0,"overnightForecast":{"humidity":38,"daylight":false,"forecastEnd":1795302000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":4.75,"uvIndexMin":0,"temperatureApparentMin":0.782133,"windGustSpeedMax":18.385199,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":2.097697,"forecastStart":1795258800,"windDirection":337,"windSpeed":7.31055,"humidityMax":42,"precipitationAmountByType":[],"cloudCoverLowAltPct":3,"visibilityMax":19952.0,"cloudCover":19,"precipitationChance":0,"temperatureMin":2.759989,"cloudCoverMidAltPct":18,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":9,"humidityMin":35,"visibilityMin":19690.0,"precipitationType":["CLEAR"],"windSpeedMax":8.128799},"precipitationType":["CLEAR"],"sunriseNautical":1795211933,"sunsetCivil":1795252936,"windSpeedMax":14.292},{"moonrise":1795330461,"moonset":1795291300,"sunrise":1795302089,"sunset":1795337567,"forecastEnd":1795363200,"sunriseCivil":1795300357,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.501335,"solarMidnight":1795363047,"windSpeedAvg":8.63325,"temperatureMaxTime":1795326106,"windGustSpeedMax":26.5716,"sunriseAstronomical":1795296483,"forecastStart":1795276800,"moonPhase":"WAXING_GIBBOUS","humidityMax":49,"precipitationAmountByType":[],"sunsetAstronomical":1795343177,"visibilityMax":19839.0,"precipitationChance":0,"temperatureMin":2.759989,"maxUvIndex":2,"temperatureMinTime":1795301984,"sunsetNautical":1795341262,"daytimeForecast":{"humidity":33,"daylight":true,"forecastEnd":1795345200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":10.501335,"uvIndexMin":0,"temperatureApparentMin":0.927409,"windGustSpeedMax":26.5716,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":2,"temperatureApparentMax":9.249003,"forecastStart":1795302000,"windDirection":284,"windSpeed":9.6852,"humidityMax":43,"precipitationAmountByType":[],"cloudCoverLowAltPct":11,"visibilityMax":19839.0,"cloudCover":11,"precipitationChance":0,"temperatureMin":2.76,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":26,"visibilityMin":18017.0,"precipitationType":["CLEAR"],"windSpeedMax":11.8944},"conditionCode":["CLEAR"],"humidityMin":26,"solarNoon":1795319826,"visibilityMin":17486.0,"overnightForecast":{"humidity":46,"daylight":false,"forecastEnd":1795388400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":4.48,"uvIndexMin":0,"temperatureApparentMin":0.112784,"windGustSpeedMax":18.594,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":1.530798,"forecastStart":1795345200,"windDirection":224,"windSpeed":8.20065,"humidityMax":49,"precipitationAmountByType":[],"cloudCoverLowAltPct":0,"visibilityMax":18017.0,"cloudCover":0,"precipitationChance":0,"temperatureMin":2.837352,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":43,"visibilityMin":16802.0,"precipitationType":["CLEAR"],"windSpeedMax":8.9244},"precipitationType":["CLEAR"],"sunriseNautical":1795298391,"sunsetCivil":1795339305,"windSpeedMax":11.8944}]},"forecastHourly":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"hours":[{"daylight":false,"humidity":71,"pressure":1019.150024,"temperature":11.33,"visibility":15686.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":3,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":6.259735,"windGust":7.7328,"temperatureApparent":10.562979,"cloudCover":34,"precipitationChance":0,"cloudCoverMidAltPct":38,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":15,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794567600,"pressureTrend":"RISING","windDirection":114,"uvIndex":0,"windSpeed":4.8312,"snowfallIntensity":0.0},{"daylight":false,"humidity":75,"pressure":1019.27002,"temperature":10.07,"visibility":15204.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":0,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":5.839401,"windGust":6.8184,"temperatureApparent":9.314911,"cloudCover":12,"precipitationChance":0,"cloudCoverMidAltPct":9,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":17,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794571200,"pressureTrend":"RISING","windDirection":113,"uvIndex":0,"windSpeed":4.1724,"snowfallIntensity":0.0},{"daylight":false,"humidity":75,"pressure":1019.469971,"temperature":9.73,"visibility":15449.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":0,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":5.510666,"windGust":4.716,"temperatureApparent":9.058333,"cloudCover":9,"precipitationChance":0,"cloudCoverMidAltPct":6,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":15,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794574800,"pressureTrend":"RISING","windDirection":107,"uvIndex":0,"windSpeed":3.7908,"snowfallIntensity":0.0},{"daylight":false,"humidity":76,"pressure":1019.549988,"temperature":9.42,"visibility":15086.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":1,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":5.401611,"windGust":3.942,"temperatureApparent":8.78021,"cloudCover":4,"precipitationChance":0,"cloudCoverMidAltPct":2,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":6,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794578400,"pressureTrend":"STEADY","windDirection":102,"uvIndex":0,"windSpeed":3.5028,"snowfallIntensity":0.0},{"daylight":false,"humidity":77,"pressure":1019.5,"temperature":8.91,"visibility":15302.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":1,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":5.095474,"windGust":4.1868,"temperatureApparent":8.290413,"cloudCover":1,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794582000,"pressureTrend":"STEADY","windDirection":86,"uvIndex":0,"windSpeed":3.4056,"snowfallIntensity":0.0},{"daylight":false,"humidity":80,"pressure":1019.27002,"temperature":8.18,"visibility":14781.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":6,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":4.93483,"windGust":5.292,"temperatureApparent":7.612356,"cloudCover":5,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794585600,"pressureTrend":"STEADY","windDirection":62,"uvIndex":0,"windSpeed":3.618,"snowfallIntensity":0.0},{"daylight":false,"humidity":81,"pressure":1019.030029,"temperature":7.63,"visibility":12829.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":24,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":4.576614,"windGust":6.9012,"temperatureApparent":6.926976,"cloudCover":18,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794589200,"pressureTrend":"STEADY","windDirection":50,"uvIndex":0,"windSpeed":4.6656,"snowfallIntensity":0.0},{"daylight":false,"humidity":82,"pressure":1018.849976,"temperature":7.26,"visibility":11536.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":52,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":4.390839,"windGust":8.28,"temperatureApparent":6.561325,"cloudCover":43,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794592800,"pressureTrend":"STEADY","windDirection":41,"uvIndex":0,"windSpeed":5.6232,"snowfallIntensity":0.0},{"daylight":false,"humidity":81,"pressure":1018.619995,"temperature":6.95,"visibility":8401.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":57,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":3.91304,"windGust":8.2044,"temperatureApparent":6.440799,"cloudCover":54,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794596400,"pressureTrend":"STEADY","windDirection":38,"uvIndex":0,"windSpeed":5.5152,"snowfallIntensity":0.0},{"daylight":false,"humidity":80,"pressure":1018.580017,"temperature":6.83,"visibility":8155.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":56,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":3.6194,"windGust":8.866799,"temperatureApparent":6.320585,"cloudCover":46,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794600000,"pressureTrend":"STEADY","windDirection":41,"uvIndex":0,"windSpeed":5.1804,"snowfallIntensity":0.0},{"daylight":false,"humidity":79,"pressure":1018.650024,"temperature":6.59,"visibility":7914.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":54,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":3.207336,"windGust":8.7372,"temperatureApparent":6.089649,"cloudCover":45,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794603600,"pressureTrend":"STEADY","windDirection":32,"uvIndex":0,"windSpeed":5.1156,"snowfallIntensity":0.0},{"daylight":false,"humidity":79,"pressure":1018.840027,"temperature":6.38,"visibility":7973.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":61,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":3.002991,"windGust":8.5932,"temperatureApparent":6.080156,"cloudCover":50,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794607200,"pressureTrend":"STEADY","windDirection":44,"uvIndex":0,"windSpeed":4.7808,"snowfallIntensity":0.0},{"daylight":true,"humidity":80,"pressure":1019.320007,"temperature":6.24,"visibility":8031.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":69,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":3.044449,"windGust":7.362,"temperatureApparent":6.244272,"cloudCover":56,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794610800,"pressureTrend":"RISING","windDirection":27,"uvIndex":0,"windSpeed":4.2588,"snowfallIntensity":0.0},{"daylight":true,"humidity":79,"pressure":1020.049988,"temperature":6.67,"visibility":10190.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":73,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":3.285187,"windGust":9.558,"temperatureApparent":7.484417,"cloudCover":58,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794614400,"pressureTrend":"RISING","windDirection":38,"uvIndex":0,"windSpeed":4.7448,"snowfallIntensity":0.0},{"daylight":true,"humidity":80,"pressure":1020.289978,"temperature":7.64,"visibility":11867.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":69,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":4.408691,"windGust":9.489599,"temperatureApparent":8.765293,"cloudCover":55,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794618000,"pressureTrend":"RISING","windDirection":39,"uvIndex":1,"windSpeed":4.7484,"snowfallIntensity":0.0},{"daylight":true,"humidity":79,"pressure":1020.359985,"temperature":8.83,"visibility":12727.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":58,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":5.386765,"windGust":9.396,"temperatureApparent":10.466646,"cloudCover":46,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794621600,"pressureTrend":"RISING","windDirection":36,"uvIndex":2,"windSpeed":4.3344,"snowfallIntensity":0.0},{"daylight":true,"humidity":80,"pressure":1020.299988,"temperature":9.58,"visibility":13540.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":50,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":6.298798,"windGust":8.686801,"temperatureApparent":11.396889,"cloudCover":39,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794625200,"pressureTrend":"STEADY","windDirection":33,"uvIndex":2,"windSpeed":4.5072,"snowfallIntensity":0.0},{"daylight":true,"humidity":74,"pressure":1019.429993,"temperature":11.24,"visibility":14931.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":35,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":6.77478,"windGust":8.186399,"temperatureApparent":13.491442,"cloudCover":27,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794628800,"pressureTrend":"FALLING","windDirection":36,"uvIndex":3,"windSpeed":3.9456,"snowfallIntensity":0.0},{"daylight":true,"humidity":69,"pressure":1018.390015,"temperature":12.77,"visibility":16874.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":13,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.224289,"windGust":7.0596,"temperatureApparent":15.818953,"cloudCover":10,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794632400,"pressureTrend":"FALLING","windDirection":51,"uvIndex":2,"windSpeed":2.7288,"snowfallIntensity":0.0},{"daylight":true,"humidity":66,"pressure":1017.789978,"temperature":13.52,"visibility":16975.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":4,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.2901,"windGust":6.6564,"temperatureApparent":16.76125,"cloudCover":4,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794636000,"pressureTrend":"FALLING","windDirection":51,"uvIndex":2,"windSpeed":2.3076,"snowfallIntensity":0.0},{"daylight":true,"humidity":68,"pressure":1017.72998,"temperature":13.07,"visibility":17103.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":4,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.297653,"windGust":6.2064,"temperatureApparent":15.873064,"cloudCover":3,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794639600,"pressureTrend":"FALLING","windDirection":33,"uvIndex":1,"windSpeed":3.1212,"snowfallIntensity":0.0},{"daylight":true,"humidity":72,"pressure":1018.01001,"temperature":12.48,"visibility":17164.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":0,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.569153,"windGust":4.824,"temperatureApparent":14.335525,"cloudCover":0,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794643200,"pressureTrend":"STEADY","windDirection":38,"uvIndex":0,"windSpeed":3.4488,"snowfallIntensity":0.0},{"daylight":false,"humidity":73,"pressure":1018.349976,"temperature":11.74,"visibility":17418.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":0,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":7.058548,"windGust":4.1112,"temperatureApparent":10.893111,"cloudCover":0,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":0,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794646800,"pressureTrend":"STEADY","windDirection":39,"uvIndex":0,"windSpeed":3.6504,"snowfallIntensity":0.0},{"daylight":false,"humidity":73,"pressure":1018.809998,"temperature":11.19,"visibility":17143.0,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"cloudCoverLowAltPct":1,"perceivedPrecipitationIntensity":0.0,"precipitationIntensity":0.0,"temperatureDewPoint":6.528549,"windGust":5.526,"temperatureApparent":10.390109,"cloudCover":5,"precipitationChance":0,"cloudCoverMidAltPct":0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":7,"precipitationType":["CLEAR"],"precipitationAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"forecastStart":1794650400,"pressureTrend":"RISING","windDirection":66,"uvIndex":0,"windSpeed":3.7584,"snowfallIntensity":0.0}]},"forecastPeriodic":{"metadata":{"latitude":0,"longitude":0,"attributionUrl":"https://developer.apple.com/weatherkit/data-source-attribution-internal/","readTime":1794569484,"expireTime":1794573084,"temporarilyUnavailable":false,"sourceType":"MODELED","reportedTime":1794567934},"forecasts":[{"periods":[{"humidity":75,"daylight":false,"forecastEnd":1794582000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":11.33,"uvIndexMin":0,"temperatureApparentMin":8.290413,"windGustSpeedMax":7.7328,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":10.562979,"forecastStart":1794567600,"windDirection":106,"windSpeed":3.8961,"humidityMax":77,"precipitationAmountByType":[],"cloudCoverLowAltPct":1,"visibilityMax":15686.0,"cloudCover":11,"precipitationChance":0,"temperatureMin":8.91,"cloudCoverMidAltPct":9,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":11,"humidityMin":71,"visibilityMin":15086.0,"precipitationType":["CLEAR"],"windSpeedMax":4.8312},{"humidity":81,"daylight":false,"forecastEnd":1794596400,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":8.91,"uvIndexMin":0,"temperatureApparentMin":6.440799,"windGustSpeedMax":8.28,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":8.290413,"forecastStart":1794582000,"windDirection":51,"windSpeed":4.5918,"humidityMax":82,"precipitationAmountByType":[],"cloudCoverLowAltPct":28,"visibilityMax":15302.0,"cloudCover":23,"precipitationChance":0,"temperatureMin":6.95,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":77,"visibilityMin":8401.0,"precipitationType":["CLEAR"],"windSpeedMax":5.6232},{"humidity":80,"daylight":false,"forecastEnd":1794610800,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":6.95,"uvIndexMin":0,"temperatureApparentMin":6.06018,"windGustSpeedMax":8.866799,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":0,"temperatureApparentMax":6.440799,"forecastStart":1794596400,"windDirection":38,"windSpeed":4.99095,"humidityMax":81,"precipitationAmountByType":[],"cloudCoverLowAltPct":59,"visibilityMax":8401.0,"cloudCover":49,"precipitationChance":0,"temperatureMin":6.229443,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"humidityMin":79,"visibilityMin":7914.0,"precipitationType":["CLEAR"],"windSpeedMax":5.5152},{"humidity":80,"daylight":true,"forecastEnd":1794625200,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":9.58,"uvIndexMin":0,"temperatureApparentMin":6.244272,"windGustSpeedMax":9.558,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":2,"temperatureApparentMax":11.396889,"forecastStart":1794610800,"windDirection":36,"windSpeed":4.55265,"humidityMax":80,"precipitationAmountByType":[],"cloudCoverLowAltPct":65,"visibilityMax":13540.0,"cloudCover":52,"precipitationChance":0,"temperatureMin":6.24,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["PARTLY_CLOUDY"],"cloudCoverHighAltPct":0,"humidityMin":79,"visibilityMin":8031.0,"precipitationType":["CLEAR"],"windSpeedMax":4.7484},{"humidity":71,"daylight":true,"forecastEnd":1794639600,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.525146,"uvIndexMin":1,"temperatureApparentMin":11.396889,"windGustSpeedMax":8.686801,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":3,"temperatureApparentMax":16.761372,"forecastStart":1794625200,"windDirection":41,"windSpeed":3.19905,"humidityMax":80,"precipitationAmountByType":[],"cloudCoverLowAltPct":20,"visibilityMax":17103.0,"cloudCover":16,"precipitationChance":0,"temperatureMin":9.58,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["MOSTLY_CLEAR"],"cloudCoverHighAltPct":0,"humidityMin":66,"visibilityMin":13540.0,"precipitationType":["CLEAR"],"windSpeedMax":4.5072},{"humidity":73,"daylight":true,"forecastEnd":1794654000,"snowfallAmount":{"expected":0.0,"minimum":0.0,"maximum":0.0},"temperatureMax":13.07,"uvIndexMin":0,"temperatureApparentMin":9.259305,"windGustSpeedMax":6.2064,"perceivedPrecipitationIntensityMax":0.0,"uvIndexMax":1,"temperatureApparentMax":15.873064,"forecastStart":1794639600,"windDirection":51,"windSpeed":3.5649,"humidityMax":77,"precipitationAmountByType":[],"cloudCoverLowAltPct":1,"visibilityMax":17418.0,"cloudCover":3,"precipitationChance":0,"temperatureMin":9.89,"cloudCoverMidAltPct":0,"precipitationIntensityMax":0.0,"conditionCode":["CLEAR"],"cloudCoverHighAltPct":3,"humidityMin":68,"visibilityMin":16701.0,"precipitationType":["CLEAR"],"windSpeedMax":3.7584}],"periodLength":4}]}} diff --git a/tests/fixtures/wk3-paired/tianjin/manifest.json b/tests/fixtures/wk3-paired/tianjin/manifest.json new file mode 100644 index 00000000..f8ddb6de --- /dev/null +++ b/tests/fixtures/wk3-paired/tianjin/manifest.json @@ -0,0 +1,82 @@ +{ + "schema": "lib-rc-3.0.0-b66", + "public_test_location": "tianjin public city centre (not device location)", + "responses": [ + { + "file": "apple.json", + "started": 1794569484.057322, + "finished": 1794569484.268827, + "status": 200, + "content_type": "application/json", + "bytes": 50569 + }, + { + "file": "apple.bin", + "started": 1794569484.057463, + "finished": 1794569484.256683, + "status": 200, + "content_type": "application/vnd.apple.flatbuffer;messageType=WK3.Weather", + "bytes": 10160 + } + ], + "sanitization": { + "coordinate_fields": [ + { + "root_slot": 0, + "latitude_offset": 10032, + "longitude_offset": 10028 + }, + { + "root_slot": 1, + "latitude_offset": 9684, + "longitude_offset": 9680 + }, + { + "root_slot": 2, + "latitude_offset": 9308, + "longitude_offset": 9304 + }, + { + "root_slot": 3, + "latitude_offset": 4368, + "longitude_offset": 4364 + }, + { + "root_slot": 4, + "latitude_offset": 1576, + "longitude_offset": 1572 + }, + { + "root_slot": 6, + "latitude_offset": 184, + "longitude_offset": 180 + }, + { + "root_slot": 7, + "latitude_offset": 376, + "longitude_offset": 372 + }, + { + "root_slot": 9, + "latitude_offset": 516, + "longitude_offset": 512 + }, + { + "root_slot": 14, + "latitude_offset": 648, + "longitude_offset": 644 + } + ], + "coordinate_urls": 1, + "method": "Only vtable-validated Metadata latitude/longitude floats and same-length coordinate URL digits replaced. No re-encoding." + }, + "sha256": { + "apple.json": "6265de605b8c576f9522fc8500e0fbb1e306b7c8b630f7742171f000f9b65952", + "apple.bin": "0b283195ef9d51d4e79e6126b63ceacb695cf31352aa325b2c1f58f1aab2181c" + }, + "schema_source": { + "weatherkit_dev": "cd9d7927094aff79906fe3430f339f39970ca46e", + "protocol_tag": "v2.0.5", + "protocol_commit": "c87c1dcd851ea9293141bcfb32fde1b2cca564b9" + } +} diff --git a/tests/platformBundleReplay.test.mjs b/tests/platformBundleReplay.test.mjs index c1446f4c..9698992e 100644 --- a/tests/platformBundleReplay.test.mjs +++ b/tests/platformBundleReplay.test.mjs @@ -130,3 +130,59 @@ test("release bundle keeps the WK2 path byte-identical without injections", { sk const output = await replay(releaseBundle, "release-v2"); assert.deepEqual(output.body, wk2Empty); }); + +test("dev request/response bundles negotiate WK3 JSON and replace weather via the real Caiyun adapter", { skip: !bundlesExist && "dist bundles not built" }, async () => { + const date = 1800000000; + const metadata = { providerName: "Apple", temporarilyUnavailable: false }; + const original = { currentWeather: { metadata, temperature: 20, conditionCode: ["RAIN"], retainedField: 42 }, unknown: { conditionCode: ["untouched"] } }; + replayGlobals({ url: "https://weatherkit.apple.com/api/v3/weather/zh-Hans/22.54/114.06?country=CN&dataSets=currentWeather", body: new Uint8Array() }); + globalThis.$request.headers = { "User-Agent": "WeatherKit_Weather_macOS_Version_27.3", accept: "application/vnd.apple.flatbuffer" }; + globalThis.$argument = { LogLevel: "OFF", Storage: "Argument", DataSets: "currentWeather", Weather: { Provider: "ColorfulClouds", Replace: "CN" } }; + const negotiated = await new Promise((resolve, reject) => { + globalThis.$done = resolve; + import(`${new URL("../dist/request.dev.bundle.js", import.meta.url).href}?replay=wk3-json`).catch(reject); + }); + assert.equal(negotiated.headers.Accept, "application/json"); + globalThis.$request = negotiated; + globalThis.$response = { status: 200, headers: { "Content-Type": "application/json", ETag: "old" }, body: new TextEncoder().encode(JSON.stringify(original)) }; + let calls = 0; + globalThis.$httpClient.get = (request, callback) => { + calls++; + assert.match(request.url, /realtime/); + const payload = { + status: "ok", + location: [22.54, 114.06], + result: { + server_time: date, + realtime: { + status: "ok", + cloudrate: 0.25, + skycon: "CLEAR_DAY", + humidity: 0.65, + precipitation: { local: { intensity: 0 } }, + pressure: 101300, + temperature: 27, + apparent_temperature: 28, + visibility: 10, + wind: { direction: 90, speed: 3 }, + }, + }, + }; + callback(null, { status: 200, headers: {} }, JSON.stringify(payload)); + }; + const output = await new Promise((resolve, reject) => { + globalThis.$done = resolve; + import(`${devBundle.href}?replay=wk3-json-weather`).catch(reject); + }); + assert.equal(calls, 1); + assert.equal(output.response, undefined, "Surge response-stage $done is unwrapped"); + assert.equal(output.bodyBytes, undefined); + const body = JSON.parse(output.body); + assert.equal(body.currentWeather.temperature, 27); + assert.equal(body.currentWeather.conditionCode, "CLEAR"); + assert.equal(body.currentWeather.humidity, 65); + assert.equal(body.currentWeather.metadata.providerName, "彩云天气"); + assert.equal(body.currentWeather.retainedField, 42); + assert.deepEqual(body.unknown, original.unknown); + assert.equal(output.headers.ETag, undefined); +}); diff --git a/tests/weatherKit3JSON.test.mjs b/tests/weatherKit3JSON.test.mjs new file mode 100644 index 00000000..f195264f --- /dev/null +++ b/tests/weatherKit3JSON.test.mjs @@ -0,0 +1,280 @@ +import assert from "node:assert/strict"; +import test, { beforeEach } from "node:test"; + +globalThis.$environment = { "surge-version": "test" }; +globalThis.$persistentStore = { read: () => null, write: () => true }; +globalThis.$argument = { LogLevel: "OFF", Storage: "Argument" }; + +const [{ Request }, { Response }, { default: WeatherKit3 }, { default: ColorfulClouds }, { default: QWeather }, { adaptWeatherKit3JSON, replaceWeatherKit3JSON }] = await Promise.all([ + import("../src/process/Request.dev.mjs"), + import("../src/process/Response.dev.mjs"), + import("../src/class/WeatherKit3.mjs"), + import("../src/class/ColorfulClouds.mjs"), + import("../src/class/QWeather.mjs"), + import("../src/function/weatherKit3JSON.mjs"), +]); + +const time = 1800000000; +const metadata = { providerName: "Apple", latitude: 22.54, longitude: 114.06, readTime: time, temporarilyUnavailable: false }; +const forecast = { forecastStart: time, forecastEnd: time + 3600, conditionCode: ["RAIN"], precipitationAmountByType: [{ precipitationType: ["RAIN"], expected: 6 }], temperature: 20, retainedField: "keep" }; +// Synthetic reader-contract fixture derived from PR #107's documented macOS observations, +// not a capture or proof of native acceptance on this Mac. +function appleBody() { + return structuredClone({ + currentWeather: { metadata, temperature: 20, conditionCode: ["RAIN"], humidity: 80, retainedField: "keep" }, + forecastDaily: { metadata, days: [{ ...forecast, daytimeForecast: forecast, overnightForecast: forecast }] }, + forecastHourly: { metadata, hours: [forecast] }, + forecastPeriodic: { metadata, series: [{ periods: [forecast] }] }, + weatherAlerts: { metadata, alerts: [{ id: { bytes: Array.from({ length: 16 }, (_, i) => i) }, description: "Apple alert" }] }, + unknownRoot: { conditionCode: ["DO_NOT_TOUCH"], nested: { expected: 17 } }, + }); +} +function request(dataSets = "currentWeather,forecastDaily,forecastHourly,forecastPeriodic,weatherAlerts") { + return { + method: "GET", + headers: { "User-Agent": "WeatherKit_Weather_macOS_Version_27.3", accept: "application/vnd.apple.flatbuffer", "If-None-Match": "old", "If-Modified-Since": "yesterday" }, + url: `https://weatherkit.apple.com/api/v3/weather/zh-Hans/22.54/114.06?country=CN&dataSets=${dataSets}`, + }; +} +function response(body = appleBody()) { + return { status: 200, headers: { "Content-Type": "application/json; charset=utf-8", ETag: "old", "content-length": "100", Digest: "old", "X-Keep": "yes" }, body: JSON.stringify(body) }; +} +beforeEach(() => { + globalThis.$argument = { + LogLevel: "OFF", + Storage: "Argument", + DataSets: ["currentWeather", "forecastDaily", "forecastHourly"], + Weather: { Provider: "ColorfulClouds", Replace: ["CN"] }, + }; + globalThis.$httpClient = { get: () => assert.fail("unexpected real provider request") }; +}); + +test("main-app WK3 request negotiates JSON without changing datasets or URL", async () => { + const input = request(); + const { $request: output } = await Request(input); + assert.equal(output.url, request().url); + assert.equal(output.headers.Accept, "application/json"); + assert.equal(output.headers.accept, undefined); + assert.equal(output.headers["If-None-Match"], undefined); + assert.equal(output.headers["If-Modified-Since"], undefined); +}); + +test("JSON negotiation is restricted by client, version, method, country, provider and dataset", async () => { + const cases = [ + input => { + input.headers["User-Agent"] = "WeatherWidget"; + }, + input => { + input.url = input.url.replace("v3", "v2"); + }, + input => { + input.method = "HEAD"; + }, + input => { + input.url = input.url.replace("country=CN", "country=US"); + }, + input => { + input.url = input.url.replace("22.54", "122.54"); + }, + input => { + input.url = input.url.replace(/dataSets=.*/, "dataSets=forecastPeriodic"); + }, + () => { + globalThis.$argument.Weather.Provider = "WeatherKit"; + }, + () => { + globalThis.$argument.DataSets = "[]"; + }, + ]; + for (const configure of cases) { + globalThis.$argument.Weather.Provider = "ColorfulClouds"; + globalThis.$argument.DataSets = ["currentWeather", "forecastDaily", "forecastHourly"]; + const input = request(); + configure(input); + const { $request: output } = await Request(input); + assert.equal(output.headers.accept, "application/vnd.apple.flatbuffer", configure.toString()); + assert.equal(output.headers.Accept, undefined); + } + const input = request(); + globalThis.$argument.DataSets = ["currentWeather"]; + input.headers["user-agent"] = input.headers["User-Agent"].replace("macOS", "iOS"); + delete input.headers["User-Agent"]; + assert.equal((await Request(input)).$request.headers.Accept, "application/json"); +}); + +test("WK3 JSON reuses Caiyun once per product without entering the binary codec", async t => { + t.mock.method(WeatherKit3, "decodeWithReport", () => assert.fail("negotiated JSON must not enter the FlatBuffer decoder")); + t.mock.method(WeatherKit3, "encode", () => assert.fail("negotiated JSON must not enter the FlatBuffer encoder")); + const now = t.mock.method(ColorfulClouds.prototype, "CurrentWeather", async () => ({ metadata: { providerName: "彩云天气" }, temperature: 27, humidity: 65.4, conditionCode: "CLEAR" })); + const daily = t.mock.method(ColorfulClouds.prototype, "Daily", async () => ({ metadata: { providerName: "彩云天气" }, days: [{ forecastStart: time, temperatureMax: 30, conditionCode: "CLEAR" }] })); + const hourly = t.mock.method(ColorfulClouds.prototype, "ForecastHourly", async () => ({ metadata: { providerName: "彩云天气" }, hours: [{ forecastStart: time, temperature: 28, conditionCode: "CLEAR" }] })); + const input = response(); + const outgoing = (await Request(request())).$request; + assert.equal(outgoing.headers.Accept, "application/json"); + const output = await Response(outgoing, input); + for (const provider of [now, daily, hourly]) assert.equal(provider.mock.callCount(), 1); + const body = JSON.parse(output.body); + assert.equal(body.currentWeather.temperature, 27); + assert.equal(body.currentWeather.humidity, 65); + assert.equal(body.currentWeather.metadata.providerName, "彩云天气"); + assert.equal(body.currentWeather.retainedField, "keep"); + assert.equal(body.forecastDaily.days[0].temperatureMax, 30); + assert.equal(body.forecastHourly.hours[0].temperature, 28); + assert.equal(body.forecastDaily.days[0].precipitationAmount, 6); + assert.equal(body.forecastDaily.days[0].daytimeForecast.precipitationAmount, 6); + assert.equal(body.forecastPeriodic.series[0].periods[0].conditionCode, "RAIN"); + assert.equal(body.weatherAlerts.alerts[0].id, "00010203-0405-0607-0809-0a0b0c0d0e0f"); + assert.deepEqual(body.unknownRoot, appleBody().unknownRoot); + assert.equal(output.headers.ETag, undefined); + assert.equal(output.headers.Digest, undefined); + assert.equal(output.headers["content-length"], undefined); + assert.equal(output.headers["Cache-Control"], "no-store"); + assert.equal(output.headers["X-Keep"], "yes"); + assert.equal(input.body, response().body, "input response stays unmodified"); +}); + +test("QWeather uses existing injectors; only requested AND enabled weather products are replaced", async t => { + globalThis.$argument.Weather.Provider = "QWeather"; + globalThis.$argument.DataSets = ["currentWeather", "forecastDaily"]; + const now = t.mock.method(QWeather.prototype, "WeatherNow", async () => ({ metadata: { providerName: "和风天气" }, temperature: 25 })); + t.mock.method(QWeather.prototype, "Daily", async () => assert.fail("daily not requested")); + t.mock.method(QWeather.prototype, "Hourly", async () => assert.fail("hourly disabled")); + const output = JSON.parse((await Response(request("currentWeather,forecastHourly"), response())).body); + assert.equal(output.currentWeather.temperature, 25); + assert.equal(now.mock.callCount(), 1); + assert.equal(output.forecastHourly.hours[0].temperature, 20); + assert.equal(output.forecastDaily.metadata.providerName, "Apple"); +}); + +test("provider failure/invalid number rolls back one product without discarding healthy replacements", async t => { + t.mock.method(ColorfulClouds.prototype, "CurrentWeather", async () => ({ metadata: { providerName: "Invalid" }, temperature: Number.NaN })); + t.mock.method(ColorfulClouds.prototype, "Daily", async () => { + throw new Error("offline"); + }); + t.mock.method(ColorfulClouds.prototype, "ForecastHourly", async () => ({ metadata: { providerName: "彩云天气" }, hours: [{ forecastStart: time, temperature: 29 }] })); + const output = JSON.parse((await Response(request(), response())).body); + assert.equal(output.currentWeather.temperature, 20); + assert.equal(output.currentWeather.metadata.providerName, "Apple"); + assert.equal(output.currentWeather.conditionCode, "RAIN", "fallback still satisfies the negotiated reader format"); + assert.equal(output.forecastDaily.metadata.providerName, "Apple"); + assert.equal(output.forecastHourly.hours[0].temperature, 29); +}); + +test("Surge binary-body-mode JSON bytes are decoded and stale bodyBytes removed", async t => { + t.mock.method(ColorfulClouds.prototype, "CurrentWeather", async () => ({ metadata: { providerName: "彩云天气" }, temperature: 27 })); + const input = response(); + input.bodyBytes = new TextEncoder().encode(input.body); + input.body = "stale"; + const output = await Response(request("currentWeather"), input); + assert.equal(JSON.parse(output.body).currentWeather.temperature, 27); + assert.equal(output.bodyBytes, undefined); +}); + +test("provider's unavailable result preserves usable Apple data and attribution", async t => { + t.mock.method(ColorfulClouds.prototype, "CurrentWeather", async () => ({ metadata: { providerName: "彩云天气", temporarilyUnavailable: true } })); + const output = JSON.parse((await Response(request("currentWeather"), response())).body); + assert.equal(output.currentWeather.metadata.providerName, "Apple"); + assert.equal(output.currentWeather.metadata.temporarilyUnavailable, false); + assert.equal(output.currentWeather.temperature, 20); +}); + +test("unknown nested fields retain their own shapes", () => { + const unknown = { conditionCode: ["FUTURE"], precipitationAmount: { expected: 8 } }; + const value = { conditionCode: ["RAIN"], unknown: structuredClone(unknown) }; + adaptWeatherKit3JSON(value, "currentWeather"); + assert.equal(value.conditionCode, "RAIN"); + assert.deepEqual(value.unknown, unknown); +}); + +test("native 27.3 precipitation ranges, rest-of-day and periodic forecasts use scalar conditions", () => { + const current = { precipitationAmountPrevious1hByType: [{ expected: 2, precipitationType: ["RAIN"] }] }; + adaptWeatherKit3JSON(current, "currentWeather"); + assert.equal(current.precipitationAmountPrevious1hByType[0].precipitationType, "RAIN"); + const daily = { days: [{ restOfDayForecast: { conditionCode: ["CLOUDY"], precipitationAmountByType: [{ expected: 3, precipitationType: ["RAIN"] }] } }] }; + adaptWeatherKit3JSON(daily, "forecastDaily"); + assert.equal(daily.days[0].restOfDayForecast.conditionCode, "CLOUDY"); + assert.equal(daily.days[0].restOfDayForecast.precipitationAmountByType[0].precipitationType, "RAIN"); + const periodic = { forecasts: [{ periods: [{ conditionCode: ["RAIN"] }] }] }; + adaptWeatherKit3JSON(periodic, "forecastPeriodic"); + assert.equal(periodic.forecasts[0].periods[0].conditionCode, "RAIN"); +}); + +test("EAQI's unsupported CO sub-index does not roll back the whole AQ product", async t => { + globalThis.$argument.DataSets = "airQuality,currentWeather"; + globalThis.$argument.AirQuality = { Calculate: { Algorithm: "EU_EAQI" }, Comparison: { ReplaceWhenCurrentChange: false } }; + t.mock.method(ColorfulClouds.prototype, "CurrentWeather", async () => undefined); + // Minimal synthetic copy of the native response shape that exposed the -1 sentinel regression. + const apple = appleBody(); + apple.airQuality = { + metadata: { ...metadata, providerName: "和风天气" }, + index: 40, + categoryIndex: 1, + scale: "HJ6332012.2604", + previousDayComparison: "UNKNOWN", + pollutants: [ + ["PM10", 40], + ["NO2", 57], + ["CO", 0.6], + ["OZONE", 46], + ["PM2_5", 26], + ["SO2", 9], + ].map(([pollutantType, amount]) => ({ pollutantType, amount, units: "MICROGRAMS_PER_CUBIC_METER" })), + }; + const body = JSON.parse((await Response(request("airQuality,currentWeather"), response(apple))).body); + assert.match(body.airQuality.scale, /^EU\.EAQI/); + const co = body.airQuality.pollutants.find(pollutant => pollutant.pollutantType === "CO"); + assert.equal(co.amount, 600, "existing provider-unit correction still applies"); + assert.equal(co.index, undefined); + assert.equal(co.categoryIndex, undefined); +}); + +test("oversized/invalid provider graphs keep normalized Apple data instead of raw WK3 JSON", async () => { + for (const extra of ["x".repeat(4 * 1024 * 1024), [Number.NaN], 1n]) { + const output = await replaceWeatherKit3JSON(response(), ["currentWeather"], { + currentWeather: product => ({ ...product, temperature: 27, extra }), + }); + const body = JSON.parse(output.body); + assert.equal(body.currentWeather.temperature, 20); + assert.equal(body.currentWeather.conditionCode, "RAIN"); + assert.equal(body.currentWeather.extra, undefined); + } +}); + +test("combined product size fallback keeps the reader-compatible response", async () => { + const extra = "x".repeat(3 * 1024 * 1024); + const output = await replaceWeatherKit3JSON(response(), ["currentWeather", "forecastHourly"], { + currentWeather: product => ({ ...product, temperature: 27, extra }), + forecastHourly: product => ({ ...product, extra }), + }); + const body = JSON.parse(output.body); + assert.equal(body.currentWeather.temperature, 20); + assert.equal(body.currentWeather.conditionCode, "RAIN"); + assert.equal(body.forecastHourly.extra, undefined); +}); + +test("malformed/error/oversized JSON responses are returned untouched without provider calls", async () => { + const cases = [ + { ...response(), status: 503 }, + { ...response(), body: "{" }, + { ...response(), body: "[]" }, + { ...response(), body: " ".repeat(4 * 1024 * 1024 + 1) }, + ]; + for (const input of cases) assert.equal(await Response(request(), input), input); +}); + +test("periodic-only normalization works independently of replaceable products", async () => { + const output = await replaceWeatherKit3JSON(response({ forecastPeriodic: appleBody().forecastPeriodic }), [], {}); + assert.equal(JSON.parse(output.body).forecastPeriodic.series[0].periods[0].precipitationAmount, 6); +}); + +test("condition record arrays stay intact; provider timestamp absence and integer rules are explicit", () => { + const condition = [{ startTime: time, forecastToken: "START" }]; + const value = { condition, humidity: 65.6, reportedTime: 0, temperature: -2, metadata: { latitude: "22.54", longitude: "114.06" } }; + adaptWeatherKit3JSON(value, "currentWeather", true); + assert.deepEqual(value.condition, condition); + assert.equal(value.humidity, 66); + assert.equal(value.reportedTime, undefined); + assert.equal(value.temperature, -2); + assert.equal(value.metadata.latitude, 22.54); + assert.throws(() => adaptWeatherKit3JSON({ humidity: 120 }, "currentWeather", true), RangeError); +}); diff --git a/tests/weatherKit3Paired.test.mjs b/tests/weatherKit3Paired.test.mjs new file mode 100644 index 00000000..3a4859ee --- /dev/null +++ b/tests/weatherKit3Paired.test.mjs @@ -0,0 +1,52 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import fs from "node:fs"; +import test from "node:test"; +import { WK3 } from "@nsringo/weatherkit"; +import { comparePair } from "../scripts/compare-wk3-pair.mjs"; + +for (const city of ["hangzhou", "tianjin", "seattle", "seattle-repeat"]) { + test(`27.3 paired ${city}: baseline products agree with the raw Apple fields apart from recorded gaps`, () => { + const dir = new URL(`./fixtures/wk3-paired/${city}/`, import.meta.url); + const result = comparePair(dir.pathname); + assert.deepEqual(result.failures, []); + assert.equal( + result.rows.some(row => row.reason.startsWith("值不一致") || row.reason === "缺字段:当前 codec 未输出此字段"), + false, + ); + const metadata = JSON.parse(fs.readFileSync(new URL("manifest.json", dir))); + assert.ok(Math.abs(metadata.responses[0].started - metadata.responses[1].started) < 0.002); + for (const [file, expected] of Object.entries(metadata.sha256)) + assert.equal( + createHash("sha256") + .update(fs.readFileSync(new URL(file, dir))) + .digest("hex"), + expected, + ); + const apple = JSON.parse(fs.readFileSync(new URL("apple.json", dir))); + for (const product of Object.values(apple)) + if (product.metadata) { + assert.equal(product.metadata.latitude, 0); + assert.equal(product.metadata.longitude, 0); + } + assert.equal(result.decoded.airQuality.metadata.expireTime, result.raw.aqExpiry); + const hasSummaryCondition = typeof WK3.ForecastPeriodSummary.prototype.condition === "function"; + assert.equal(result.rows.filter(row => !row.equal && row.reason.includes("deprecated")).length, !hasSummaryCondition && city.startsWith("seattle") ? 1 : 0); + if (hasSummaryCondition && city.startsWith("seattle")) + assert.deepEqual(result.decoded.forecastNextHour.summary.map(item => item.condition), apple.forecastNextHour.summary.map(item => item.condition)); + if (city === "seattle") { + assert.equal(result.raw.minuteCount, 81); + assert.equal(result.decoded.forecastNextHour.forecastEnd, apple.forecastNextHour.forecastEnd); + assert.equal(result.decoded.weatherAlerts.alerts.length, 1); + assert.equal(result.decoded.weatherAlerts.alerts[0].severity, "MINOR"); + assert.equal(result.decoded.weatherAlerts.alerts[0].importance, "LOW"); + assert.deepEqual(result.decoded.weatherAlerts.alerts[0].responses, ["MONITOR"]); + } + if (city === "seattle-repeat") { + assert.equal(apple.forecastNextHour.minutes.length, 82); + assert.equal(result.raw.minuteCount, 84); + assert.equal(result.raw.reportedTime - apple.forecastNextHour.metadata.reportedTime, 120); + assert.equal(result.raw.forecastEnd - apple.forecastNextHour.forecastEnd, 120); + } + }); +}