Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/morning-briefing

src/sources/health.ts

44 lines · 1.3 KB · TypeScript
import { skippedSection } from '../format.ts'
import { fetchHealth } from '../open-meteo.ts'
import type { BriefingInput, BriefingSection, Severity } from '../types.ts'

function severityForAqi(aqi: number | null): Severity {
	if (aqi === null) return 'info'
	if (aqi >= 150) return 'critical'
	if (aqi >= 100) return 'warning'
	if (aqi >= 50) return 'notice'
	return 'good'
}

export async function healthSection(input: BriefingInput): Promise<BriefingSection> {
	const location =
		(typeof input.health?.location === 'string' && input.health.location.trim()) ||
		(typeof input.location === 'string' && input.location.trim()) ||
		''
	if (!location) {
		return skippedSection(
			'health',
			'Pass location or health.location. Health uses Open-Meteo air quality (no OAuth).',
		)
	}

	const health = await fetchHealth(location)
	const aqi = health.usAqi
	return {
		id: 'health',
		title: 'Health',
		status: aqi !== null && aqi >= 100 ? 'warning' : 'ok',
		summary:
			aqi === null
				? `${health.place} · air quality unavailable`
				: `${health.place} · US AQI ${aqi} (${health.category})`,
		items: [
			{
				title: aqi === null ? 'Air quality unavailable' : `US AQI ${aqi} · ${health.category}`,
				detail: `PM2.5 ${health.pm25 ?? '—'}, PM10 ${health.pm10 ?? '—'}. ${health.attribution}`,
				label: 'air',
				severity: severityForAqi(aqi),
			},
		],
	}
}