feat(openclaw): add shopping, supermarket deals, geizhals, and ebay MCP tools
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import type { TimeSeriesPoint } from '../types/weather'
|
||||
|
||||
/**
|
||||
* Calculate dew point (°C) from temperature (°C) and relative humidity (%)
|
||||
* using the Magnus-Tetens formula.
|
||||
*/
|
||||
export function calculateDewPoint(
|
||||
temperature?: number | null,
|
||||
humidity?: number | null
|
||||
): number | null {
|
||||
if (
|
||||
temperature === undefined ||
|
||||
temperature === null ||
|
||||
humidity === undefined ||
|
||||
humidity === null ||
|
||||
humidity <= 0
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
const a = 17.625
|
||||
const b = 243.04
|
||||
|
||||
try {
|
||||
const alpha = (a * temperature) / (b + temperature) + Math.log(humidity / 100)
|
||||
const dewPoint = (b * alpha) / (a - alpha)
|
||||
return Math.round(dewPoint * 10) / 10
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return thermal sensation / comfort level based on dew point (°C)
|
||||
*/
|
||||
export function getDewPointComfort(dewPoint?: number | null): {
|
||||
text: string
|
||||
color: string
|
||||
} {
|
||||
if (dewPoint === undefined || dewPoint === null) {
|
||||
return { text: 'Unknown', color: 'var(--retro-text-dim)' }
|
||||
}
|
||||
if (dewPoint < 10) {
|
||||
return { text: 'Dry / Crisp', color: 'var(--retro-text-cyan)' }
|
||||
} else if (dewPoint <= 15) {
|
||||
return { text: 'Pleasant / Fresh', color: 'var(--retro-text-green)' }
|
||||
} else if (dewPoint <= 18) {
|
||||
return { text: 'Comfortable', color: 'var(--retro-text-amber)' }
|
||||
} else if (dewPoint <= 21) {
|
||||
return { text: 'Humid / Warm', color: 'var(--retro-text-amber)' }
|
||||
} else {
|
||||
return { text: 'Muggy / Oppressive', color: 'var(--retro-text-red)' }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert degrees (0-360°) to 16-wind compass rose direction
|
||||
*/
|
||||
export function getWindDirectionCardinal(degrees?: number | null): string {
|
||||
if (degrees === undefined || degrees === null) return 'N/A'
|
||||
const compassSectors = [
|
||||
'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
|
||||
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'
|
||||
]
|
||||
const val = Math.floor((degrees / 22.5) + 0.5)
|
||||
return compassSectors[val % 16]
|
||||
}
|
||||
|
||||
/**
|
||||
* Get UV Index category & retro color
|
||||
*/
|
||||
export function getUVIndexLevel(uv?: number | null): {
|
||||
label: string
|
||||
color: string
|
||||
} {
|
||||
if (uv === undefined || uv === null) {
|
||||
return { label: 'Low', color: 'var(--retro-text-green)' }
|
||||
}
|
||||
if (uv < 3) return { label: 'Low', color: 'var(--retro-text-green)' }
|
||||
if (uv < 6) return { label: 'Moderate', color: 'var(--retro-text-amber)' }
|
||||
if (uv < 8) return { label: 'High', color: 'var(--retro-text-amber)' }
|
||||
if (uv < 11) return { label: 'Very High', color: 'var(--retro-text-red)' }
|
||||
return { label: 'Extreme', color: 'var(--retro-accent-purple)' }
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate basic statistical properties (min, max, average, current) for a time series
|
||||
*/
|
||||
export function calculateHistoryStats(points?: TimeSeriesPoint[]): {
|
||||
min: number | null
|
||||
max: number | null
|
||||
avg: number | null
|
||||
current: number | null
|
||||
} {
|
||||
if (!points || points.length === 0) {
|
||||
return { min: null, max: null, avg: null, current: null }
|
||||
}
|
||||
|
||||
let min = points[0].value
|
||||
let max = points[0].value
|
||||
let sum = 0
|
||||
|
||||
for (const p of points) {
|
||||
if (p.value < min) min = p.value
|
||||
if (p.value > max) max = p.value
|
||||
sum += p.value
|
||||
}
|
||||
|
||||
const avg = Math.round((sum / points.length) * 10) / 10
|
||||
const current = points[points.length - 1].value
|
||||
|
||||
return {
|
||||
min: Math.round(min * 10) / 10,
|
||||
max: Math.round(max * 10) / 10,
|
||||
avg,
|
||||
current: Math.round(current * 10) / 10
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user