- First WIP

This commit is contained in:
Dave M. 2026-03-17 15:49:32 -04:00
commit 62d5854bbe
8 changed files with 476 additions and 0 deletions

31
composer.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "mcnd/ulmus-api-weather",
"description": "A package made for mcnd/ulmus-api",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Dave Mc Nicoll",
"email": "info@mcnd.ca"
}
],
"require": {
"mcnd/ulmus": "dev-master",
"mcnd/ulmus-api": "dev-master"
},
"repositories": [
{
"type": "vcs",
"url": "https://git.mcnd.ca/mcndave/ulmus.git"
},
{
"type": "vcs",
"url": "https://git.mcnd.ca/mcndave/ulmus-api.git"
}
],
"autoload": {
"psr-4": {
"Ulmus\\Api\\Weather\\": "src/"
}
}
}

15
meta/config.php Normal file
View File

@ -0,0 +1,15 @@
<?php
return [
'ulmus' => [
'connections' => [
'api-weather' => [
'adapter' => getenv("WEATHER_API_ADAPTER"),
'url' => getenv("WEATHER_API_URL"),
'auth' => getenv('WEATHER_API_AUTH') ?: 'token',
'key' => getenv("WEATHER_API_KEY"),
'options' => [],
],
]
]
];

View File

@ -0,0 +1,10 @@
<?php
return [
'api.weather' => function($c) {
$adapter = new \Ulmus\Api\ConnectionAdapter($c->create(Ulmus\Api\Weather\ApiHandler::class)->constructor(getenv('WEATHER_API_UNIT_SCALE')), 'api-weather', $c->get('config')['ulmus'], false);
$adapter->resolveConfiguration();
return $adapter;
},
];

56
src/ApiHandler.php Normal file
View File

@ -0,0 +1,56 @@
<?php
namespace Ulmus\Api\Weather;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\{ RequestInterface, ResponseInterface };
use Ulmus\Api\ApiHandlerInterface;
use Ulmus\Api\Attribute\Obj\Api\{ Read, Collection };
use Ulmus\Api\Common\Uri;
use Ulmus\Api\Response\JsonResponse;
use Ulmus\Api\Stream\JsonStream;
use Ulmus\Api\Stream\Stream;
class ApiHandler implements ApiHandlerInterface
{
public function __construct(
protected ? string $unitScale,
) {}
public function handleRequest(RequestInterface $request, \Ulmus\Api\Attribute\Obj\Api\ApiAction $attribute) : RequestInterface
{
if ($this->unitScale) {
$uriParams['unitScale'] = $this->unitScale;
}
if ($attribute->fields) {
$uriParams['fields'] = implode(',', $attribute->fields) ;
}
if ($uriParams ?? false) {
$request = $request->withUri(
( new Uri($request->getUri()) )->withQuery($uriParams)
);
}
return $request;
}
public function handleResponse(ResponseInterface $response, \Ulmus\Api\Attribute\Obj\Api\ApiAction $attribute) : ResponseInterface
{
try {
$json = $response->getParsedBody();
if (! empty($json['forecast']) ) {
if ($attribute instanceof Collection) {
$response = $response->withParsedBody($json['forecast']);
}
}
}
catch(\Throwable $e) {
throw new \Exception(sprintf("WeatherSource error: '%s'", $json['errorMessage']), $json['errorCode']);
}
return JsonResponse::fromResponse($response);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Ulmus\Api\Weather\Attribute\Obj\Api;
use Ulmus\Api\Common\MethodEnum;
#[\Attribute(\Attribute::TARGET_CLASS)]
class Collection extends \Ulmus\Api\Attribute\Obj\Api\Collection {
public function __construct(
public string $url,
public MethodEnum $method = MethodEnum::Get,
public int $timeout = 15,
public array $fields = [ "all" ],
public MethodEnum $searchMethod = MethodEnum::Get,
) {}
}

224
src/Entity/ForecastDay.php Normal file
View File

@ -0,0 +1,224 @@
<?php
namespace Ulmus\Api\Weather\Entity;
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\{Api\Attribute\Property\Entity,
Attribute\Property\Field,
Entity\EntityInterface,
Entity\Field\Date,
Entity\Field\Datetime,
EntityTrait,
SearchRequest\SearchRequestInterface,
SearchRequest\SearchRequestPaginationTrait};
use Ulmus\Api\Attribute\Obj\Api;
# Daily forecast response for a Latitude/Longitude point.
#[Api(adapter: "api-weather")]
#[Api\Collection(url: "/points/{latitude},{longitude}/days")]
class ForecastDay implements EntityInterface
{
use EntityTrait;
# Average Cloud Cover as a percent value.
#[Field(name: "cldCvrAvg")]
public int $cloudCoverAverage;
# Minimum Cloud Cover as a percent value.
#[Field(name: "cldCvrMax")]
public int $cloudCoverMaximum;
# Maximum Cloud Cover as a percent value.
#[Field(name: "cldCoverMin")]
public int $cloudCoverMinimum;
# date formatted as an RFC3339 date value.
#[Field]
public Date $date;
# Average Dew Point in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "dewPtAvg")]
public float $dewPointAverage;
# Minimum Dew Point in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "dewPtMin")]
public float $dewPointMinimum;
# Maxumum Dew Point in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "dewPtMax")]
public float $dewPointMaximum;
#[Field(name: "feelsLikeAvg")]
public float $feelsLikeAverage;
#[Field(name: "feelsLikeMin")]
public float $feelsLikeMinimum;
#[Field(name: "feelsLikeMax")]
public float $feelsLikeMaximum;
#[Field(name: "heatIndexAvg")]
public float $heatIndexAverage;
#[Field(name: "heatIndexMin")]
public float $heatIndexMinimum;
#[Field(name: "heatIndexMax")]
public float $heatIndexMaximum;
# Average Mean Sea Level Pressure in millibars.
#[Field(name: "mslPresAvg")]
public float $meanSeaLevelPressureAverage;
# Minimum Mean Sea Level Pressure in millibars.
#[Field(name: "mslPresMin")]
public float $meanSeaLevelPressureMinimum;
# Maximum Mean Sea Level Pressure in millibars.
#[Field(name: "mslPresMax")]
public float $meanSeaLevelPressureMaximum;
# Total precipitation in inches (imperial) or centimeters (metric, si).
#[Field(name: "precip")]
public float $precipitation;
# Probability of precipitation as a percent value.
#[Field(name: "precipProb")]
public float $precipitationProbability;
# Average Solar Radiation in watts per square meter.
#[Field(name: "radSolarAvg")]
public float $solarRadiationAverage;
# Minimum Solar Radiation in watts per square meter.
#[Field(name: "radSolarMin")]
public float $solarRadiationMinimum;
# Maximum Solar Radiation in watts per square meter.
#[Field(name: "radSolarMax")]
public float $solarRadiationMaximum;
# Total Solar Radiation in watts per square meter.
#[Field(name: "radSolarTot")]
public float $solarRadiationTotal;
# Average Relative humidity as a percent value.
#[Field(name: "relHumAvg")]
public float $relativeHumidityAverage;
# Minimum Relative humidity as a percent value.
#[Field(name: "relHumMin")]
public float $relativeHumidityMinimum;
# Maximum Relative humidity as a percent value.
#[Field(name: "relHumMax")]
public float $relativeHumidityMaximum;
# Total snowfall in inches (imperial) or centimeters (metric, si).
#[Field]
public float $snowfall;
# Probability of snowfall as a percent value.
#[Field(name: "snowfallProb")]
public float $snowfallProbability;
# Average Surface pressure in millibars.
#[Field(name: "sfcPresAvg")]
public float $surfacePressureAverage;
# Minimum Surface pressure in millibars.
#[Field(name: "sfcPresMin")]
public float $surfacePressureMinimum;
# Maximum Surface pressure in millibars.
#[Field(name: "sfcPresMax")]
public float $surfacePressureMaximum;
# Average Temperature in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "tempAvg")]
public float $temperatureAverage;
# Minimum Temperature in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "tempMin")]
public float $temperatureMinimum;
# Maximum Temperature in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "tempMax")]
public float $temperatureMaximum;
# The model initialization timestamp formatted as an RFC3339 date-time value.
#[Field(name: "timestampInit")]
public Datetime $timestampInitialization;
# Average Wet Bulb Temperature in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "wetBulbAvg")]
public float $wetBulbeAverage;
# Minimum Wet Bulb Temperature in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "wetBulbMin")]
public float $wetBulbeMinimum;
# Maximum Wet Bulb Temperature in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "wetBulbMax")]
public float $wetBulbeMaximum;
# Average Wind Chill in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "windChillAvg")]
public float $windChillAverage;
# Minimum Wind Chill in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "windChillMin")]
public float $windChillMinimum;
# Maximum Wind Chill in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "windChillMax")]
public float $windChillMaximum;
# Average Wind direction at 10 meters in Degrees (0 or 360 = North, 90 = East, South = 180, West = 270).
#[Field(name: "windDirAvg")]
public float $windDirectionAverage;
# Average Wind direction at 80 meters in Degrees (0 or 360 = North, 90 = East, South = 180, West = 270).
#[Field(name: "windDir80mAvg")]
public float $windDirection80mAvg;
# Average Wind direction at 80 meters in Degrees (0 or 360 = North, 90 = East, South = 180, West = 270).
#[Field(name: "windDir100mAvg")]
public float $windDirection100mAvg;
# Average Wind speed at 10 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpdAvg")]
public float $windSpeedAverage;
# Minimum Wind speed at 10 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpdMin")]
public float $windSpeedMinimum;
# Maximum Wind speed at 10 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpdMax")]
public float $windSpeedMaximum;
# Average Wind speed at 80 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpd80mAvg")]
public float $windSpeed80mAverage;
# Minimum Wind speed at 80 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpd80mMin")]
public float $windSpeed80mMinimum;
# Maximum Wind speed at 80 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpd80mMax")]
public float $windSpeed80mMaximum;
# Average Wind speed at 100 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpd100mAvg")]
public float $windSpeed100mAverage;
# Minimum Wind speed at 100 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpd100mMin")]
public float $windSpeed100mMinimum;
# Maximum Wind speed at 100 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpd100mMax")]
public float $windSpeed100mMaximum;
}

116
src/Entity/ForecastHour.php Normal file
View File

@ -0,0 +1,116 @@
<?php
namespace Ulmus\Api\Weather\Entity;
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\{Api\Attribute\Property\Entity,
Attribute\Property\Field,
Entity\EntityInterface,
Entity\Field\Date,
Entity\Field\Datetime,
EntityTrait,
SearchRequest\SearchRequestInterface,
SearchRequest\SearchRequestPaginationTrait};
use Ulmus\Api\Attribute\Obj\Api;
# An hourly weather forecast object.
#[Api(adapter: "api-weather")]
#[Api\Collection(url: "/points/{latitude},{longitude}/hours")]
class ForecastHour implements EntityInterface
{
use EntityTrait;
# Cloud Cover as a percent value.
#[Field(name: "cldCvr")]
public int $cloudCover;
# Dew Point in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "dewPt")]
public float $dewPoint;
#[Field]
public float $feelsLike;
#[Field]
public float $heatIndex;
# Mean Sea Level Pressure in millibars.
#[Field(name: "mslPres")]
public float $meanSeaLevelPressure;
# Total precipitation in inches (imperial) or centimeters (metric, si).
#[Field(name: "precip")]
public float $precipitation;
# Probability of precipitation as a percent value.
#[Field(name: "precipProb")]
public float $precipitationProbability;
# Average Solar Radiation in watts per square meter.
#[Field(name: "radSolar")]
public float $solarRadiation;
# Average Relative humidity as a percent value.
#[Field(name: "relHum")]
public float $relativeHumidity;
# Average Surface pressure in millibars.
#[Field(name: "sfcPres")]
public float $surfacePressure;
# Total snowfall in inches (imperial) or centimeters (metric, si).
#[Field]
public float $snowfall;
# Probability of snowfall as a percent value.
#[Field(name: "snowfallProb")]
public float $snowfallProbability;
# Specific humidity in grams per kilograms.
#[Field(name: "spcHum")]
public float $specificHumidity;
# Temperature in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "temp")]
public float $temperature;
# The model initialization timestamp formatted as an RFC3339 date-time value.
#[Field(name: "timestampInit")]
public Datetime $timestampInitialization;
# The model initialization timestamp formatted as an RFC3339 date-time value.
#[Field]
public Datetime $timestamp;
# Wet Bulb Temperature in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field(name: "wetBulb")]
public float $wetBulbe;
# Wind Chill in Degrees Fahrenheit (imperial), Celsius (metric), or Kelvin (si).
#[Field]
public float $windChill;
# Wind direction at 10 meters in Degrees (0 or 360 = North, 90 = East, South = 180, West = 270).
#[Field(name: "windDir")]
public float $windDirection;
# Average Wind direction at 80 meters in Degrees (0 or 360 = North, 90 = East, South = 180, West = 270).
#[Field(name: "windDir80m")]
public float $windDirection80m;
# Average Wind direction at 80 meters in Degrees (0 or 360 = North, 90 = East, South = 180, West = 270).
#[Field(name: "windDir100m")]
public float $windDirection100m;
# Average Wind speed at 10 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpd")]
public float $windSpeed;
# Average Wind speed at 80 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpd80m")]
public float $windSpeed80m;
# Average Wind speed at 100 meters in miles per hour (imperial) or km/hour (metric, si).
#[Field(name: "windSpd100m")]
public float $windSpeed100m;
}

8
src/Lib/EntityTrait.php Normal file
View File

@ -0,0 +1,8 @@
<?php
namespace Ulmus\Api\Weather\Lib;
trait EntityTrait
{
use \Ulmus\EntityTrait;
}