Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion flexmeasures_weather/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ def add_weather_sensor(**args):
fm_sensor_specs["generic_asset"] = weather_station
fm_sensor_specs["timezone"] = args["timezone"]
fm_sensor_specs["name"] = fm_sensor_specs.pop("fm_sensor_name")
fm_sensor_specs.pop("weather_sensor_name")
fm_sensor_specs.pop("OWM_sensor_name")
fm_sensor_specs.pop("WAPI_sensor_name")
sensor = Sensor(**fm_sensor_specs)
sensor.attributes = fm_sensor_specs["attributes"]

Expand Down
20 changes: 15 additions & 5 deletions flexmeasures_weather/cli/tests/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import List
from datetime import datetime, timedelta

from flask import current_app
from flexmeasures.utils.time_utils import as_server_time, get_timezone


Expand All @@ -17,11 +17,21 @@ def mock_api_response(api_key, location):
mock_date_tz_aware = as_server_time(
datetime.fromtimestamp(mock_date.timestamp(), tz=get_timezone())
).replace(second=0, microsecond=0)

provider = str(current_app.config.get("WEATHER_PROVIDER", ""))
date_key = "dt"
temp_key = "temp"
wind_speed_key = "wind_speed"
if provider == "WAPI":
date_key = "time_epoch"
temp_key = "temp"
wind_speed_key = "wind_kph"

return mock_date_tz_aware, [
{"dt": mock_date.timestamp(), "temp": 40, "wind_speed": 100},
{date_key: mock_date.timestamp(), temp_key: 40, wind_speed_key: 100},
{
"dt": (mock_date + timedelta(hours=1)).timestamp(),
"temp": 42,
"wind_speed": 90,
date_key: (mock_date + timedelta(hours=1)).timestamp(),
temp_key: 42,
wind_speed_key: 90,
},
]
12 changes: 8 additions & 4 deletions flexmeasures_weather/sensor_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,32 @@
mapping = [
dict(
fm_sensor_name="temperature",
weather_sensor_name="temp",
OWM_sensor_name="temp",
WAPI_sensor_name="temp_c",
unit="°C",
event_resolution=timedelta(minutes=60),
attributes=weather_attributes,
),
dict(
fm_sensor_name="wind speed",
weather_sensor_name="wind_speed",
OWM_sensor_name="wind_speed",
WAPI_sensor_name="wind_kph",
unit="m/s",
event_resolution=timedelta(minutes=60),
attributes=weather_attributes,
),
dict(
fm_sensor_name="cloud cover",
weather_sensor_name="clouds",
OWM_sensor_name="clouds",
WAPI_sensor_name="cloud",
unit="%",
event_resolution=timedelta(minutes=60),
attributes=weather_attributes,
),
dict(
fm_sensor_name="irradiance", # in save_forecasts_to_db, we catch this name and do the actual computation to get to the irradiance
weather_sensor_name="clouds",
OWM_sensor_name="clouds",
WAPI_sensor_name="cloud",
unit="W/m²",
event_resolution=timedelta(minutes=60),
attributes=weather_attributes,
Expand Down
58 changes: 13 additions & 45 deletions flexmeasures_weather/utils/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,45 +68,7 @@ def process_weatherapi_data(
combined = first_day + second_day + third_day

relevant = combined[hour_no : hour_no + 48]
# relevant = combined

def map_weather_api_to_owm(weather_api_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Converts a single hour of WeatherAPI data to an OpenWeatherMap-style dictionary.

Args:
weather_api_data (Dict[str, Any]): A dictionary containing an hour's data from WeatherAPI.

Returns:
Dict[str, Any]: A dictionary with keys and structure similar to OpenWeatherMap's hourly forecast.
"""
game = {
"dt": weather_api_data["time_epoch"],
"temp": weather_api_data["temp_c"],
"feels_like": weather_api_data["feelslike_c"],
"pressure": weather_api_data["pressure_mb"],
"humidity": weather_api_data["humidity"],
"dew_point": weather_api_data["dewpoint_c"],
"uvi": weather_api_data["uv"],
"clouds": weather_api_data["cloud"],
"visibility": weather_api_data["vis_km"] * 1000,
"wind_speed": weather_api_data["wind_kph"] / 3.6,
"wind_deg": weather_api_data["wind_degree"],
"wind_gust": weather_api_data["gust_kph"] / 3.6,
"weather": [
{
"id": weather_api_data["condition"]["code"],
"main": weather_api_data["condition"]["text"].split()[0],
"description": weather_api_data["condition"]["text"],
"icon": weather_api_data["condition"]["icon"],
}
],
"pop": weather_api_data["chance_of_rain"] / 100,
}
return game

converted = [map_weather_api_to_owm(hour) for hour in relevant]
return converted
return relevant


def call_openweatherapi(
Expand Down Expand Up @@ -212,7 +174,7 @@ def call_api(
return call_weatherapi(api_key, location)


def save_forecasts_in_db(
def save_forecasts_in_db( # noqa: C901
api_key: str,
locations: List[Tuple[float, float]],
):
Expand All @@ -226,6 +188,7 @@ def save_forecasts_in_db(
"WEATHER_MAXIMAL_DEGREE_LOCATION_DISTANCE",
DEFAULT_MAXIMAL_DEGREE_LOCATION_DISTANCE,
)
provider = str(current_app.config.get("WEATHER_PROVIDER", ""))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My best guess for why tests might be failing is that this configuration setting is empty when the tests are run, so in line 212 you default to using fc["time_epoch"] (a WAPI key). It might be better to use OWM keys as the default, and/or to explicitly default the provider to OWM instead of to an empty string.

for location in locations:
click.echo("[FLEXMEASURES] %s, %s" % location)
weather_sensors: Dict[str, Sensor] = (
Expand All @@ -246,17 +209,18 @@ def save_forecasts_in_db(

# loop through forecasts, including the one of current hour (horizon 0)
for fc in forecasts:
time_key = fc["dt"] if provider == "OWM" else fc["time_epoch"]
fc_datetime = as_server_time(
datetime.fromtimestamp(fc["dt"], get_timezone())
datetime.fromtimestamp(time_key, get_timezone())
)
click.echo(
f"[FLEXMEASURES-WEATHER] Processing forecast for {fc_datetime} ..."
)
data_source = get_or_create_owm_data_source()
for sensor_specs in mapping:
sensor_name = str(sensor_specs["fm_sensor_name"])
owm_response_label = sensor_specs["weather_sensor_name"]
if owm_response_label in fc:
provider_response_label = sensor_specs[f"{provider}_sensor_name"]
if provider_response_label in fc:
weather_sensor = get_weather_sensor(
sensor_specs,
location,
Expand All @@ -270,7 +234,11 @@ def save_forecasts_in_db(
if weather_sensor not in db_forecasts.keys():
db_forecasts[weather_sensor] = []

fc_value = fc[owm_response_label]
fc_value = fc[provider_response_label]

if provider_response_label == "wind_kph":
# convert wind speed from kph to m/s
fc_value = fc[provider_response_label] / 3.6

# the irradiance is not available in Provider -> we compute it ourselves
if sensor_name == "irradiance":
Expand All @@ -297,7 +265,7 @@ def save_forecasts_in_db(
else:
# we will not fail here, but issue a warning
msg = "No label '%s' in response data for time %s" % (
owm_response_label,
provider_response_label,
fc_datetime,
)
click.echo("[FLEXMEASURES-WEATHER] %s" % msg)
Expand Down
Loading