diff --git a/extensions/surf-check/CHANGELOG.md b/extensions/surf-check/CHANGELOG.md index 38b52bcfa12..49a0ce67ebe 100644 --- a/extensions/surf-check/CHANGELOG.md +++ b/extensions/surf-check/CHANGELOG.md @@ -1,5 +1,9 @@ # Surf Check Changelog +## [Update] - 2024-12-01 + +- Added unit configuration optionsfor wave height, swell height, tide, wind, and temperature. + ## [Update] - 2024-11-28 - Updated the extension description to better clarify its functionality diff --git a/extensions/surf-check/README.md b/extensions/surf-check/README.md index 6291b4b88e6..9a59d371666 100644 --- a/extensions/surf-check/README.md +++ b/extensions/surf-check/README.md @@ -11,9 +11,15 @@ Surf Check is a Raycast extension that provides quick access to surf conditions - Detailed view for each spot with comprehensive surf data - Access to forecaster reports and swell information - Direct links and quick actions to open Surfline reports for each spot +- Customizable measurement units: + - Surf height (ft/m) + - Swell height (ft/m) + - Tide height (ft/m) + - Wind speed (kts/km/h/m/s) + - Temperature (°F/°C) ## Screenshots - +![Configuration](./metadata/surf-check-3.png) ![List View](./metadata/surf-check-1.png) ![Detailed View](./metadata/surf-check-2.png) @@ -24,6 +30,12 @@ Surf Check is a Raycast extension that provides quick access to surf conditions 1. Install the Surf Check extension in Raycast 2. Set your preferred spot ID in the extension preferences 3. example: https://www.surfline.com/surf-report/lower-trestles/${spotId} | spotId is the numbers at the end of the url +4. Configure your preferred measurement units in the extension preferences: + - Choose between feet (ft) or meters (m) for surf heights + - Choose between feet (ft) or meters (m) for swell heights + - Choose between feet (ft) or meters (m) for tide heights + - Choose between knots (kts), km/h, or m/s for wind speed + - Choose between Fahrenheit (F) or Celsius (C) for temperature ### Checking Surf Conditions @@ -31,9 +43,9 @@ Surf Check is a Raycast extension that provides quick access to surf conditions 2. You'll see a list of nearby surf spots with basic information: - Spot name - Current conditions (e.g., Fair, Good) - - Wave height range - - Wind speed and description - - Water temperature + - Wave height range (in your preferred unit) + - Wind speed and description (in your preferred unit) + - Water temperature (in your preferred unit) ### Viewing Detailed Spot Information @@ -43,6 +55,7 @@ Surf Check is a Raycast extension that provides quick access to surf conditions - Tide information - Swell details - Forecaster report (if available) + All measurements are displayed in your chosen units. ### Additional Actions diff --git a/extensions/surf-check/metadata/surf-check-3.png b/extensions/surf-check/metadata/surf-check-3.png new file mode 100644 index 00000000000..3b1c0fa54c0 Binary files /dev/null and b/extensions/surf-check/metadata/surf-check-3.png differ diff --git a/extensions/surf-check/package.json b/extensions/surf-check/package.json index 3bfb21de9e6..f33abc1d966 100644 --- a/extensions/surf-check/package.json +++ b/extensions/surf-check/package.json @@ -23,6 +23,100 @@ "required": true, "title": "Spot ID", "description": "Enter the Spot ID for your preferred surf spot." + }, + { + "name": "surfHeightUnit", + "type": "dropdown", + "required": true, + "title": "Surf Height Unit", + "description": "Choose unit for wave heights", + "default": "ft", + "data": [ + { + "title": "Feet (ft)", + "value": "ft" + }, + { + "title": "Meters (m)", + "value": "m" + } + ] + }, + { + "name": "swellHeightUnit", + "type": "dropdown", + "required": true, + "title": "Swell Height Unit", + "description": "Choose unit for swell measurements", + "default": "ft", + "data": [ + { + "title": "Feet (ft)", + "value": "ft" + }, + { + "title": "Meters (m)", + "value": "m" + } + ] + }, + { + "name": "tideHeightUnit", + "type": "dropdown", + "required": true, + "title": "Tide Height Unit", + "description": "Choose unit for tide measurements", + "default": "ft", + "data": [ + { + "title": "Feet (ft)", + "value": "ft" + }, + { + "title": "Meters (m)", + "value": "m" + } + ] + }, + { + "name": "windSpeedUnit", + "type": "dropdown", + "required": true, + "title": "Wind Speed Unit", + "description": "Choose your preferred wind speed unit", + "default": "kts", + "data": [ + { + "title": "Knots (kts)", + "value": "kts" + }, + { + "title": "Kilometers per hour (km/h)", + "value": "kph" + }, + { + "title": "Meters per second (m/s)", + "value": "ms" + } + ] + }, + { + "name": "temperatureUnit", + "type": "dropdown", + "required": true, + "title": "Temperature Unit", + "description": "Choose temperature unit", + "default": "F", + "data": [ + { + "title": "Fahrenheit (°F)", + "value": "F" + }, + { + "title": "Celsius (°C)", + "value": "C" + } + ] } ] } diff --git a/extensions/surf-check/src/surf-check.tsx b/extensions/surf-check/src/surf-check.tsx index 659f9e38332..d4dc5fdc39f 100644 --- a/extensions/surf-check/src/surf-check.tsx +++ b/extensions/surf-check/src/surf-check.tsx @@ -2,8 +2,17 @@ import { ActionPanel, Detail, List, Action, Icon, Color, useNavigation } from "@ import { useState, useEffect } from "react"; import fetch from "node-fetch"; import { getPreferenceValues } from "@raycast/api"; +import { + HeightUnit, + WindSpeedUnit, + TemperatureUnit, + UnitPreferences, + formatHeight, + formatTemp, + formatWindSpeed, +} from "./utils"; -interface Preferences { +interface Preferences extends UnitPreferences { spotId: string; } @@ -53,8 +62,8 @@ function formatString(str: string): string { .replace(/\b\w/g, (c) => c.toUpperCase()); } -function formatSurfHeight(min: number, max: number): string { - return `${Math.round(min)}-${Math.round(max)}ft`; +function formatSurfHeight(min: number, max: number, unit: HeightUnit): string { + return `${formatHeight(min, unit)}-${formatHeight(max, unit)}`; } function getRatingIcon(rating: string): { icon: Icon; tintColor: Color } { @@ -95,7 +104,7 @@ function formatUrlName(name: string): string { export default function Command() { const [spots, setSpots] = useState([]); const [isLoading, setIsLoading] = useState(true); - const { spotId } = getPreferenceValues(); + const { spotId, surfHeightUnit, windSpeedUnit, temperatureUnit } = getPreferenceValues(); useEffect(() => { fetchSurfData(); @@ -123,15 +132,21 @@ export default function Command() { key={spot._id} icon={{ source: icon, tintColor }} title={spot.name} - subtitle={`${formatString(spot.rating.key)} (${formatSurfHeight(spot.waveHeight.min, spot.waveHeight.max)})`} + subtitle={`${formatString(spot.rating.key)} (${formatSurfHeight(spot.waveHeight.min, spot.waveHeight.max, surfHeightUnit)})`} accessories={[ { text: formatString(spot.waveHeight.humanRelation) }, - { icon: Icon.Wind, text: `${getWindDescription(spot.wind.speed)} ${spot.wind.speed}kts` }, - { icon: Icon.Temperature, text: `${spot.waterTemp.max}°F` }, + { + icon: Icon.Wind, + text: `${getWindDescription(spot.wind.speed)} ${formatWindSpeed(spot.wind.speed, windSpeedUnit)}`, + }, + { icon: Icon.Temperature, text: formatTemp(spot.waterTemp.max, temperatureUnit) }, ]} actions={ - } /> + } + /> } @@ -142,7 +157,17 @@ export default function Command() { ); } -function SpotDetails({ spot }: { spot: Spot }) { +interface SpotDetailsProps { + spot: Spot; + preferences: { + surfHeightUnit: HeightUnit; + windSpeedUnit: WindSpeedUnit; + temperatureUnit: TemperatureUnit; + }; +} + +function SpotDetails({ spot, preferences }: SpotDetailsProps) { + const { surfHeightUnit, windSpeedUnit, temperatureUnit } = preferences; const { pop } = useNavigation(); const [detailedSpot, setDetailedSpot] = useState(null); const [isLoading, setIsLoading] = useState(true); @@ -191,26 +216,25 @@ function SpotDetails({ spot }: { spot: Spot }) { const detailsMarkdown = ` # ${detailedSpot.spot.name} - ${formatString(detailedSpot.forecast.conditions.value)} (${formatSurfHeight(detailedSpot.forecast.waveHeight.min, detailedSpot.forecast.waveHeight.max)}) + ${formatString(detailedSpot.forecast.conditions.value)} (${formatSurfHeight(detailedSpot.forecast.waveHeight.min, detailedSpot.forecast.waveHeight.max, surfHeightUnit)}) ## Surf Conditions - Wave Height: ${detailedSpot.forecast.waveHeight.humanRelation} -- Wind: ${getWindDescription(detailedSpot.forecast.wind.speed)} ${detailedSpot.forecast.wind.speed}kts ${detailedSpot.forecast.wind.directionType} (${detailedSpot.forecast.wind.direction}°) -- Water Temperature: ${detailedSpot.forecast.waterTemp.min}°F - ${detailedSpot.forecast.waterTemp.max}°F - +- Wind: ${getWindDescription(detailedSpot.forecast.wind.speed)} ${formatWindSpeed(detailedSpot.forecast.wind.speed, windSpeedUnit)} ${detailedSpot.forecast.wind.directionType} (${detailedSpot.forecast.wind.direction}°) +- Water Temperature: ${formatTemp(detailedSpot.forecast.waterTemp.min, temperatureUnit)} - ${formatTemp(detailedSpot.forecast.waterTemp.max, temperatureUnit)} ## Tide Details | Type | Height | Time | |------|--------|------| -| Previous | ${detailedSpot.forecast.tide.previous.type} ${detailedSpot.forecast.tide.previous.height}ft | ${formatTime(detailedSpot.forecast.tide.previous.timestamp)} | -| Current | ${detailedSpot.forecast.tide.current.type} ${detailedSpot.forecast.tide.current.height}ft | ${formatTime(detailedSpot.forecast.tide.current.timestamp)} | -| Next | ${detailedSpot.forecast.tide.next.type} ${detailedSpot.forecast.tide.next.height}ft | ${formatTime(detailedSpot.forecast.tide.next.timestamp)} | +| Previous | ${detailedSpot.forecast.tide.previous.type} ${formatHeight(detailedSpot.forecast.tide.previous.height, surfHeightUnit)} | ${formatTime(detailedSpot.forecast.tide.previous.timestamp)} | +| Current | ${detailedSpot.forecast.tide.current.type} ${formatHeight(detailedSpot.forecast.tide.current.height, surfHeightUnit)} | ${formatTime(detailedSpot.forecast.tide.current.timestamp)} | +| Next | ${detailedSpot.forecast.tide.next.type} ${formatHeight(detailedSpot.forecast.tide.next.height, surfHeightUnit)} | ${formatTime(detailedSpot.forecast.tide.next.timestamp)} | ## Swells ${detailedSpot.forecast.swells .map( (swell, index) => - `${index + 1}. Height: ${swell.height}ft, Period: ${swell.period}s, Direction: ${swell.direction}°`, + `${index + 1}. Height: ${formatHeight(swell.height, surfHeightUnit)}, Period: ${swell.period}s, Direction: ${swell.direction}°`, ) .join("\n")} @@ -242,24 +266,27 @@ _Data provided by Surfline_ /> } actions={ - + } diff --git a/extensions/surf-check/src/utils.ts b/extensions/surf-check/src/utils.ts new file mode 100644 index 00000000000..9cd57a78f12 --- /dev/null +++ b/extensions/surf-check/src/utils.ts @@ -0,0 +1,75 @@ +export type HeightUnit = "ft" | "m"; +export type WindSpeedUnit = "kts" | "kph" | "ms"; +export type TemperatureUnit = "F" | "C"; + +export interface UnitPreferences { + windSpeedUnit: WindSpeedUnit; + surfHeightUnit: HeightUnit; + swellHeightUnit: HeightUnit; + tideHeightUnit: HeightUnit; + temperatureUnit: TemperatureUnit; +} + +export function convertHeight(value: number, from: HeightUnit, to: HeightUnit): number { + if (from === to) return value; + return from === "ft" ? Math.round(value * 0.3048 * 10) / 10 : Math.round((value / 0.3048) * 10) / 10; +} + +export function convertTemp(value: number, from: TemperatureUnit, to: TemperatureUnit): number { + if (from === to) return value; + return from === "F" ? Math.round(((value - 32) * 5) / 9) : Math.round((value * 9) / 5 + 32); +} + +export function convertWindSpeed(value: number, from: WindSpeedUnit, to: WindSpeedUnit): number { + if (from === to) return value; + // Convert to knots first as base unit + let kts = value; + if (from === "kph") kts = value / 1.852; + if (from === "ms") kts = value / 0.514444; + + // Convert from knots to target unit + switch (to) { + case "kts": + return Math.round(kts); + case "kph": + return Math.round(kts * 1.852); + case "ms": + return Math.round(kts * 0.514444 * 10) / 10; + } +} + +export function getUnitLabel(unit: HeightUnit | WindSpeedUnit | TemperatureUnit): string { + switch (unit) { + case "ft": + return "ft"; + case "m": + return "m"; + case "kts": + return "kts"; + case "kph": + return "km/h"; + case "ms": + return "m/s"; + case "F": + return "°F"; + case "C": + return "°C"; + default: + return unit; + } +} + +export function formatHeight(value: number, unit: HeightUnit, fromUnit: HeightUnit = "ft"): string { + const converted = fromUnit === unit ? value : convertHeight(value, fromUnit, unit); + return `${converted}${getUnitLabel(unit)}`; +} + +export function formatTemp(value: number, unit: TemperatureUnit, fromUnit: TemperatureUnit = "F"): string { + const converted = fromUnit === unit ? value : convertTemp(value, fromUnit, unit); + return `${converted}${getUnitLabel(unit)}`; +} + +export function formatWindSpeed(value: number, unit: WindSpeedUnit, fromUnit: WindSpeedUnit = "kts"): string { + const converted = fromUnit === unit ? value : convertWindSpeed(value, fromUnit, unit); + return `${converted}${getUnitLabel(unit)}`; +}