feat(weather-widget): add imperial windspeed option (#4253)

This commit is contained in:
Meier Lukas
2025-10-10 19:59:05 +02:00
committed by GitHub
parent 6d0a1452a6
commit 50a23d76e3
6 changed files with 44 additions and 6 deletions

View File

@@ -47,3 +47,8 @@ export const humanFileSize = (size: number, concat = ""): string => {
} }
return "∞"; return "∞";
}; };
const IMPERIAL_MULTIPLIER = 1.609344;
export const metricToImperial = (metricValue: number) => metricValue / IMPERIAL_MULTIPLIER;
export const imperialToMetric = (imperialValue: number) => imperialValue * IMPERIAL_MULTIPLIER;

View File

@@ -88,6 +88,7 @@ const optionMapping: OptionMapping = {
location: (oldOptions) => oldOptions.location, location: (oldOptions) => oldOptions.location,
showCity: (oldOptions) => oldOptions.displayCityName, showCity: (oldOptions) => oldOptions.displayCityName,
dateFormat: (oldOptions) => (oldOptions.dateFormat === "hide" ? undefined : oldOptions.dateFormat), dateFormat: (oldOptions) => (oldOptions.dateFormat === "hide" ? undefined : oldOptions.dateFormat),
useImperialSpeed: () => undefined,
}, },
iframe: { iframe: {
embedUrl: (oldOptions) => oldOptions.embedUrl, embedUrl: (oldOptions) => oldOptions.embedUrl,

View File

@@ -1145,6 +1145,12 @@
"groupNameTaken": "Group name already taken" "groupNameTaken": "Group name already taken"
} }
} }
},
"unit": {
"speed": {
"kilometersPerHour": "km/h",
"milesPerHour": "mph"
}
} }
}, },
"section": { "section": {
@@ -1744,6 +1750,9 @@
"label": "Show current wind speed", "label": "Show current wind speed",
"description": "Only on current weather" "description": "Only on current weather"
}, },
"useImperialSpeed": {
"label": "Use mph for windspeed"
},
"location": { "location": {
"label": "Weather location" "label": "Weather location"
}, },
@@ -1762,12 +1771,12 @@
"description": "How the date should look like" "description": "How the date should look like"
} }
}, },
"currentWindSpeed": "{currentWindSpeed} km/h", "currentWindSpeed": "{currentWindSpeed} {unit}",
"dailyForecast": { "dailyForecast": {
"sunrise": "Sunrise", "sunrise": "Sunrise",
"sunset": "Sunset", "sunset": "Sunset",
"maxWindSpeed": "Max wind speed: {maxWindSpeed} km/h", "maxWindSpeed": "Max wind speed: {maxWindSpeed} {unit}",
"maxWindGusts": "Max wind gusts: {maxWindGusts} km/h" "maxWindGusts": "Max wind gusts: {maxWindGusts} {unit}"
}, },
"kind": { "kind": {
"clear": "Clear", "clear": "Clear",

View File

@@ -7,6 +7,7 @@ import dayjs from "dayjs";
import type { RouterOutputs } from "@homarr/api"; import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client"; import { clientApi } from "@homarr/api/client";
import { metricToImperial } from "@homarr/common";
import { useScopedI18n } from "@homarr/translation/client"; import { useScopedI18n } from "@homarr/translation/client";
import type { WidgetComponentProps } from "../definition"; import type { WidgetComponentProps } from "../definition";
@@ -52,6 +53,7 @@ interface WeatherProps extends Pick<WidgetComponentProps<"weather">, "options">
const DailyWeather = ({ options, weather }: WeatherProps) => { const DailyWeather = ({ options, weather }: WeatherProps) => {
const t = useScopedI18n("widget.weather"); const t = useScopedI18n("widget.weather");
const tCommon = useScopedI18n("common");
return ( return (
<> <>
@@ -78,7 +80,17 @@ const DailyWeather = ({ options, weather }: WeatherProps) => {
{options.showCurrentWindSpeed && ( {options.showCurrentWindSpeed && (
<Group className="weather-current-wind-speed-group" wrap="nowrap" gap="xs"> <Group className="weather-current-wind-speed-group" wrap="nowrap" gap="xs">
<IconWind size={16} /> <IconWind size={16} />
<Text fz={16}>{t("currentWindSpeed", { currentWindSpeed: String(weather.current.windspeed) })}</Text> <Text fz={16}>
{t("currentWindSpeed", {
currentWindSpeed: (options.useImperialSpeed
? metricToImperial(weather.current.windspeed)
: weather.current.windspeed
).toFixed(1),
unit: options.useImperialSpeed
? tCommon("unit.speed.milesPerHour")
: tCommon("unit.speed.kilometersPerHour"),
})}
</Text>
</Group> </Group>
)} )}
<Group className="weather-max-min-temp-group" wrap="nowrap" gap="sm"> <Group className="weather-max-min-temp-group" wrap="nowrap" gap="sm">
@@ -180,6 +192,7 @@ function Forecast({ weather, options }: WeatherProps) {
</HoverCard.Target> </HoverCard.Target>
<HoverCard.Dropdown> <HoverCard.Dropdown>
<WeatherDescription <WeatherDescription
useImperialSpeed={options.useImperialSpeed}
dateFormat={dateFormat} dateFormat={dateFormat}
time={dayWeather.time} time={dayWeather.time}
weatherCode={dayWeather.weatherCode} weatherCode={dayWeather.weatherCode}

View File

@@ -15,6 +15,7 @@ import {
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { metricToImperial } from "@homarr/common";
import type { TranslationObject } from "@homarr/translation"; import type { TranslationObject } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client"; import { useScopedI18n } from "@homarr/translation/client";
import type { TablerIcon } from "@homarr/ui"; import type { TablerIcon } from "@homarr/ui";
@@ -40,6 +41,7 @@ export const WeatherIcon = ({ code, size = 50 }: WeatherIconProps) => {
interface WeatherDescriptionProps { interface WeatherDescriptionProps {
weatherOnly?: boolean; weatherOnly?: boolean;
useImperialSpeed?: boolean;
dateFormat?: WidgetProps<"weather">["options"]["dateFormat"]; dateFormat?: WidgetProps<"weather">["options"]["dateFormat"];
time?: string; time?: string;
weatherCode: number; weatherCode: number;
@@ -66,6 +68,7 @@ interface WeatherDescriptionProps {
*/ */
export const WeatherDescription = ({ export const WeatherDescription = ({
weatherOnly, weatherOnly,
useImperialSpeed,
dateFormat, dateFormat,
time, time,
weatherCode, weatherCode,
@@ -96,12 +99,18 @@ export const WeatherDescription = ({
<List.Item icon={<IconMoon size={15} />}>{`${t("dailyForecast.sunset")}: ${sunset}`}</List.Item> <List.Item icon={<IconMoon size={15} />}>{`${t("dailyForecast.sunset")}: ${sunset}`}</List.Item>
{maxWindSpeed !== undefined && ( {maxWindSpeed !== undefined && (
<List.Item icon={<IconWind size={15} />}> <List.Item icon={<IconWind size={15} />}>
{t("dailyForecast.maxWindSpeed", { maxWindSpeed: String(maxWindSpeed) })} {t("dailyForecast.maxWindSpeed", {
maxWindSpeed: (useImperialSpeed ? metricToImperial(maxWindSpeed) : maxWindSpeed).toFixed(1),
unit: useImperialSpeed ? tCommon("unit.speed.milesPerHour") : tCommon("unit.speed.kilometersPerHour"),
})}
</List.Item> </List.Item>
)} )}
{maxWindGusts !== undefined && ( {maxWindGusts !== undefined && (
<List.Item icon={<IconWind size={15} />}> <List.Item icon={<IconWind size={15} />}>
{t("dailyForecast.maxWindGusts", { maxWindGusts: String(maxWindGusts) })} {t("dailyForecast.maxWindGusts", {
maxWindGusts: (useImperialSpeed ? metricToImperial(maxWindGusts) : maxWindGusts).toFixed(1),
unit: useImperialSpeed ? tCommon("unit.speed.milesPerHour") : tCommon("unit.speed.kilometersPerHour"),
})}
</List.Item> </List.Item>
)} )}
</List> </List>

View File

@@ -13,6 +13,7 @@ export const { definition, componentLoader } = createWidgetDefinition("weather",
isFormatFahrenheit: factory.switch(), isFormatFahrenheit: factory.switch(),
disableTemperatureDecimals: factory.switch(), disableTemperatureDecimals: factory.switch(),
showCurrentWindSpeed: factory.switch({ withDescription: true }), showCurrentWindSpeed: factory.switch({ withDescription: true }),
useImperialSpeed: factory.switch(),
location: factory.location({ location: factory.location({
defaultValue: { defaultValue: {
name: "Paris", name: "Paris",