diff --git a/.gitignore b/.gitignore index bbba259..0ccc3fb 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,3 @@ nunit-*.xml /Weather4Agents.API/appsettings.Development.json .DS_Store .continue -docs/ProviderExamples/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d0aab90 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog +## v1.1.0 +- Moved forecast reliability percentage from hourly slot level to day level (`DayWeather.ReliabilityPerc` / `DayForecastEntry.ReliabilityPerc`) +- Added per-slot precipitation probability (`HoursWeatherDetails.PrecipitationProbabilityPerc`), exposed when the provider supplies it +- Updated 3bMeteo scraper to support v3 website layout (new `table-previsioni-ora` table structure and today-page detection) +- Fixed temperature parsing to avoid false match with `unit-tempo` CSS class +## v1.0.5 +- Updated 3bmeteo web scraper (3bmeteo website was updated) +## v1.0.4 +- Updated 3bmeteo web scraper (3bmeteo website was refactored) +## v1.0.3 +- OpenApi definitions in `/openapi/v1.json` +- Refactoring of api endpoints definitions +## v1.0.2 +- Fixed bug with mapping of word "velature sparse" (3bmeteo) +- Custom integration for Home Assistant (Integrations/HomeAssistant) +## v1.0.1 +- Removed Mediatr dependency and implemented a base service to handle CQRS pattern +- Improved appsettings loading for development mode +- Docker image for arm64 (raspberry) and amd64 +- New endpoint to get 7 days forecast +## v1.0.0 +- Initial release diff --git a/Integrations/HomeAssistant/README.md b/Integrations/HomeAssistant/README.md index c58f0d6..1b8ace0 100644 --- a/Integrations/HomeAssistant/README.md +++ b/Integrations/HomeAssistant/README.md @@ -75,7 +75,7 @@ Add a weather forecast card to any dashboard: ```yaml type: weather-forecast -entity: weather.weather4agents_nembro +entity: weather.weather4agents_bergamo forecast_type: daily # or "hourly" ``` diff --git a/Integrations/HomeAssistant/custom_components/weather4agents/strings.json b/Integrations/HomeAssistant/custom_components/weather4agents/strings.json index d32077c..7abfcb3 100644 --- a/Integrations/HomeAssistant/custom_components/weather4agents/strings.json +++ b/Integrations/HomeAssistant/custom_components/weather4agents/strings.json @@ -11,7 +11,7 @@ }, "data_description": { "base_url": "Full URL of the Weather4Agents API, e.g. http://192.168.1.10:8080", - "location": "City name as recognised by the API, e.g. nembro", + "location": "City name as recognised by the API, e.g. bergamo", "scan_interval": "How often (in minutes) to refresh weather data. Min 5, max 1440." } } diff --git a/README.md b/README.md index fac4f13..1ec2baf 100644 --- a/README.md +++ b/README.md @@ -49,21 +49,5 @@ Yu can consume APIs to get weather data ![alt text](./docs/images/n8n-integration.png) # Changelog -## v1.0.5 -- Updated 3bmeteo web scraper (3bmeteo website was updated) -## v1.0.4 -- Updated 3bmeteo web scraper (3bmeteo website was refactored) -## v1.0.3 -- OpenApi definitions in `/openapi/v1.json` -- Refactoring of api endpoints definitions -## v1.0.2 -- Fixed bug with mapping of word "velature sparse" (3bmeteo) -- Custom integration for Home Assistant (Integrations/HomeAssistant) -## v1.0.1 -- Removed Mediatr dependency and implemented a base service to handle CQRS pattern -- Improved appsettings loading for development mode -- Docker image for arm64 (raspberry) and amd64 -- New endpoint to get 7 days forecast -## v1.0.0 -- Initial release +See [CHANGELOG.md](CHANGELOG.md) for the full version history. diff --git a/Weather4Agents.API/Controllers/WeatherController.cs b/Weather4Agents.API/Controllers/WeatherController.cs index c382733..c02bfb6 100644 --- a/Weather4Agents.API/Controllers/WeatherController.cs +++ b/Weather4Agents.API/Controllers/WeatherController.cs @@ -22,7 +22,7 @@ public WeatherController(IDispatcher dispatcher) } /// - /// Retrieves the weather forecast for the next days using the specified provider (or the default one if omitted). + /// Forecast for the next days /// /// Location name. If location contains spaces, use URL encoding. /// Number of days to return @@ -45,7 +45,7 @@ public async Task GetForecastByDays(string location, int numberOf } /// - /// Retrieves the 7-day weather forecast using the specified provider (or the default one if omitted). + /// 7-day weather forecast /// /// Location name. If location contains spaces, use URL encoding. /// Optional provider name. If omitted, the default provider is used. @@ -67,7 +67,7 @@ public async Task GetWeekForecast(string location, [FromQuery] st } /// - /// Retrieves the weather for a specific day using the specified provider (or the default one if omitted). + /// Weather for a specific day /// /// Location name. If location contains spaces, use URL encoding. /// Date for which to retrieve weather information. diff --git a/Weather4Agents.Application/DTOs/WeekForecastResponse.cs b/Weather4Agents.Application/DTOs/WeekForecastResponse.cs index 549cbc4..89eee90 100644 --- a/Weather4Agents.Application/DTOs/WeekForecastResponse.cs +++ b/Weather4Agents.Application/DTOs/WeekForecastResponse.cs @@ -4,7 +4,9 @@ namespace Weather4Agents.Application.DTOs; public class WeekForecastResponse { - // UTC Format + /// + /// UTC Format + /// public DateTimeOffset LastUpdatedAt { get; set; } public IEnumerable Forecast { get; set; } = []; } @@ -12,5 +14,9 @@ public class WeekForecastResponse public class DayForecastEntry { public DateOnly Date { get; set; } + /// + /// Accuracy (0-100%) + /// + public int ReliabilityPerc { get; set; } = 100; public IEnumerable HoursDetails { get; set; } = []; } diff --git a/Weather4Agents.Application/UseCases/GetWeekForecast/GetWeekForecastHandler.cs b/Weather4Agents.Application/UseCases/GetWeekForecast/GetWeekForecastHandler.cs index b1231fd..3fae845 100644 --- a/Weather4Agents.Application/UseCases/GetWeekForecast/GetWeekForecastHandler.cs +++ b/Weather4Agents.Application/UseCases/GetWeekForecast/GetWeekForecastHandler.cs @@ -29,6 +29,7 @@ public async Task HandleAsync(GetWeekForecastQuery query, .Select(d => new DayForecastEntry { Date = d.Date, + ReliabilityPerc = d.ReliabilityPerc, HoursDetails = d.HoursDetails }); diff --git a/Weather4Agents.Domain/Entities/DayWeather.cs b/Weather4Agents.Domain/Entities/DayWeather.cs index 1178025..c16cfd2 100644 --- a/Weather4Agents.Domain/Entities/DayWeather.cs +++ b/Weather4Agents.Domain/Entities/DayWeather.cs @@ -21,5 +21,10 @@ public class DayWeather /// Weather provider where date come from /// public WeatherProvider Provider { get; set; } = new WeatherProvider("NotSet"); + /// + /// Overall forecast reliability percentage (0-100) as reported by the provider. + /// Defaults to 100 when the provider does not expose a reliability indicator. + /// + public int ReliabilityPerc { get; set; } = 100; } } diff --git a/Weather4Agents.Domain/Entities/HoursWeatherDetails.cs b/Weather4Agents.Domain/Entities/HoursWeatherDetails.cs index e195b25..cd205c5 100644 --- a/Weather4Agents.Domain/Entities/HoursWeatherDetails.cs +++ b/Weather4Agents.Domain/Entities/HoursWeatherDetails.cs @@ -51,10 +51,9 @@ public class HoursWeatherDetails /// public string WindDirection { get; set; } = ""; /// - /// Forecast reliability percentage (0-100) extracted from provider page. - /// Defaults to 20 for probabilistic pages where the indicator is absent. - /// Defaults to 100 for standard hourly pages without an explicit reliability indicator. + /// Precipitation probability for this time slot (0-100), if provided by the source. + /// Null when the provider does not expose per-slot precipitation probability. /// - public int ReliabilityPerc { get; set; } = 100; + public int? PrecipitationProbabilityPerc { get; set; } } } diff --git a/Weather4Agents.Infrastructure/Scrapers/3bMeteoScraper.cs b/Weather4Agents.Infrastructure/Scrapers/3bMeteoScraper.cs index 8520c71..99aef9e 100644 --- a/Weather4Agents.Infrastructure/Scrapers/3bMeteoScraper.cs +++ b/Weather4Agents.Infrastructure/Scrapers/3bMeteoScraper.cs @@ -66,20 +66,28 @@ private DayWeather ParseDayPage(string html, DateOnly date) Provider = new WeatherProvider(ProviderName) }; - if (doc.GetElementbyId("table_orario_ita") is not null) + // The table layout is used for near-future days. It appears either as: + // - #table_orario_ita (days 1-2), or + // - .table-previsioni-ora without the wrapper id (today/day-0). + var hasTableLayout = + doc.GetElementbyId("table_orario_ita") is not null || + doc.DocumentNode.SelectSingleNode( + "//div[contains(@class,'table-previsioni-ora')]") is not null; + + if (hasTableLayout) { // Table layout (complete1/2/today-style): flat row-table rows - var reliability = ParseTableReliability(doc); - dayWeather.HoursDetails = ParseTableHourlyItems(doc, reliability); + dayWeather.ReliabilityPerc = ParseTableReliability(doc); + dayWeather.HoursDetails = ParseTableHourlyItems(doc); } else { // Accordion layout (complete4-style and legacy): fc-accordion-item elements var isComplicated = IsComplicatedPage(doc); - var reliability = isComplicated ? 20 : ParseReliability(doc); + dayWeather.ReliabilityPerc = isComplicated ? 20 : ParseReliability(doc); dayWeather.HoursDetails = isComplicated - ? ParseEsaSlots(doc, reliability) - : ParseHourlyItems(doc, reliability); + ? ParseEsaSlots(doc) + : ParseHourlyItems(doc); } return dayWeather; @@ -115,7 +123,7 @@ private static int ParseReliability(HtmlDocument doc) : 100; } - private static List ParseHourlyItems(HtmlDocument doc, int reliability) + private static List ParseHourlyItems(HtmlDocument doc) { var items = doc.DocumentNode.SelectNodes( "//div[@id='content-orario']//li[contains(@class,'fc-accordion-item')]"); @@ -131,7 +139,7 @@ private static List ParseHourlyItems(HtmlDocument doc, int if (!int.TryParse(timeText, out var hour) || hour is < 0 or > 23) continue; - var details = ParseAccordionItem(item, new TimeOnly(hour, 0), new TimeOnly(hour, 0).AddHours(1), reliability); + var details = ParseAccordionItem(item, new TimeOnly(hour, 0), new TimeOnly(hour, 0).AddHours(1)); if (details is not null) results.Add(details); } @@ -147,7 +155,7 @@ private static readonly (TimeOnly From, TimeOnly To)[] EsaSlotDefs = (new TimeOnly(18, 0), new TimeOnly(0, 0)), // Ser. - Sera ]; - private static List ParseEsaSlots(HtmlDocument doc, int reliability) + private static List ParseEsaSlots(HtmlDocument doc) { var items = doc.DocumentNode.SelectNodes( "//div[@id='content-esaorario']//li[contains(@class,'fc-accordion-item')]"); @@ -158,14 +166,14 @@ private static List ParseEsaSlots(HtmlDocument doc, int rel for (var i = 0; i < 4; i++) { var (timeFrom, timeTo) = EsaSlotDefs[i]; - var details = ParseAccordionItem(items[i], timeFrom, timeTo, reliability); + var details = ParseAccordionItem(items[i], timeFrom, timeTo); if (details is not null) results.Add(details); } return results; } - private static HoursWeatherDetails? ParseAccordionItem(HtmlNode item, TimeOnly timeFrom, TimeOnly timeTo, int reliability) + private static HoursWeatherDetails? ParseAccordionItem(HtmlNode item, TimeOnly timeFrom, TimeOnly timeTo) { // Prefer the detailed summary text; fall back to the short header label (new design) or condition span (legacy) var descNode = item.SelectSingleNode(".//div[contains(@class,'fc-accordion-summary-mobile')]") @@ -173,8 +181,10 @@ private static List ParseEsaSlots(HtmlDocument doc, int rel ?? item.SelectSingleNode(".//div[contains(@class,'fc-accordion-condition')]//span[contains(@class,'ds-body-medium')]"); var description = HtmlEntity.DeEntitize(descNode?.InnerText ?? string.Empty).Trim(); - // Temperature: data-temp-c attribute on span.unit-temp (class name varies across designs) - var tempSpan = item.SelectSingleNode(".//span[contains(@class,'unit-temp')]"); + // Temperature: data-temp-c attribute on span.unit-temp. + // Use word-boundary match to avoid matching span.unit-tempo (weather description). + var tempSpan = item.SelectSingleNode( + ".//span[contains(concat(' ', normalize-space(@class), ' '), ' unit-temp ')]"); double.TryParse( tempSpan?.GetAttributeValue("data-temp-c", "0").Trim(), NumberStyles.Any, CultureInfo.InvariantCulture, out var tempC); @@ -193,19 +203,22 @@ private static List ParseEsaSlots(HtmlDocument doc, int rel var pressureMbar = (int)ParseFirstDouble(GetParamValue(item, "pressione")); + var probText = GetParamValue(item, "probabilita").Replace("%", "").Trim(); + int? precipProb = int.TryParse(probText, NumberStyles.Integer, CultureInfo.InvariantCulture, out var p) ? p : null; + return new HoursWeatherDetails { - TimeFrom = timeFrom, - TimeTo = timeTo, - WeatherType = MapWeatherType(description), - WeatherTypeDescription = description, - TemperatureC = tempC, - PrecipitationMm = precipMm, - HumidityPerc = humidity, - PressionMbar = pressureMbar, - WindKmh = windKmh, - WindDirection = windDir, - ReliabilityPerc = reliability, + TimeFrom = timeFrom, + TimeTo = timeTo, + WeatherType = MapWeatherType(description), + WeatherTypeDescription = description, + TemperatureC = tempC, + PrecipitationMm = precipMm, + HumidityPerc = humidity, + PressionMbar = pressureMbar, + WindKmh = windKmh, + WindDirection = windDir, + PrecipitationProbabilityPerc = precipProb, }; } @@ -240,10 +253,11 @@ private static int ParseTableReliability(HtmlDocument doc) : 100; } - private static List ParseTableHourlyItems(HtmlDocument doc, int reliability) + private static List ParseTableHourlyItems(HtmlDocument doc) { var rows = doc.DocumentNode.SelectNodes( - "//div[@id='table_orario_ita']//div[contains(@class,'row-table') and contains(@class,'noPad')]"); + "//div[@id='table_orario_ita' or contains(@class,'table-previsioni-ora')]" + + "//div[contains(@class,'row-table') and contains(@class,'noPad')]"); if (rows is null) return []; @@ -305,7 +319,6 @@ private static List ParseTableHourlyItems(HtmlDocument doc, PressionMbar = pressureMbar, WindKmh = windKmh, WindDirection = windDir, - ReliabilityPerc = reliability, }); } diff --git a/Weather4Agents.Test/Scrapers/Meteo3bScraperTests.cs b/Weather4Agents.Test/Scrapers/Meteo3bScraperTests.cs index 0a1b9e5..12b5417 100644 --- a/Weather4Agents.Test/Scrapers/Meteo3bScraperTests.cs +++ b/Weather4Agents.Test/Scrapers/Meteo3bScraperTests.cs @@ -28,13 +28,17 @@ private static DayWeather InvokeParseDayPage(Meteo3bScraper scraper, string html } private static string Complete1Html => - File.ReadAllText(Path.Combine("ProviderExamples", "3bmeteo-complete1.html")); + File.ReadAllText(Path.Combine("ProviderExamples", "3bmeteo-v3-complete1.html")); - private static string Complete3Html => - File.ReadAllText(Path.Combine("ProviderExamples", "3bmeteo-complete3.html")); + // v3 HTML examples + private static string V3Complete1Html => + File.ReadAllText(Path.Combine("ProviderExamples", "3bmeteo-v3-complete1.html")); - private static string ComplicatedHtml => - File.ReadAllText(Path.Combine("ProviderExamples", "3bmeteo-complicated1.html")); + private static string V3Complete4Html => + File.ReadAllText(Path.Combine("ProviderExamples", "3bmeteo-v3-complete4.html")); + + private static string V3TodayHtml => + File.ReadAllText(Path.Combine("ProviderExamples", "3bmeteo-v3-today1.html")); // ------------------------------------------------------------------------- // Hourly page — slot count and ordering @@ -71,47 +75,6 @@ public void ParseDayPage_WithCompleteHtml_DoesNotReturnFourSlots() Assert.NotEqual(4, result.HoursDetails.Count); } - // ------------------------------------------------------------------------- - // Complicated page — slot count, time ranges, reliability - // ------------------------------------------------------------------------- - - [Fact] - public void ParseDayPage_WithComplicatedHtml_ReturnsFourEsaSlots() - { - var scraper = CreateScraper(); - - var result = InvokeParseDayPage(scraper, ComplicatedHtml, new DateOnly(2026, 5, 16)); - - Assert.Equal(4, result.HoursDetails.Count); - } - - [Fact] - public void ParseDayPage_WithComplicatedHtml_SlotsHaveCorrectTimeRanges() - { - var scraper = CreateScraper(); - - var result = InvokeParseDayPage(scraper, ComplicatedHtml, new DateOnly(2026, 5, 16)); - - Assert.Equal(new TimeOnly(0, 0), result.HoursDetails[0].TimeFrom); - Assert.Equal(new TimeOnly(6, 0), result.HoursDetails[0].TimeTo); - Assert.Equal(new TimeOnly(6, 0), result.HoursDetails[1].TimeFrom); - Assert.Equal(new TimeOnly(12, 0), result.HoursDetails[1].TimeTo); - Assert.Equal(new TimeOnly(12, 0), result.HoursDetails[2].TimeFrom); - Assert.Equal(new TimeOnly(18, 0), result.HoursDetails[2].TimeTo); - Assert.Equal(new TimeOnly(18, 0), result.HoursDetails[3].TimeFrom); - Assert.Equal(new TimeOnly(0, 0), result.HoursDetails[3].TimeTo); - } - - [Fact] - public void ParseDayPage_WithComplicatedHtml_AllSlotsHaveReliabilityTwenty() - { - var scraper = CreateScraper(); - - var result = InvokeParseDayPage(scraper, ComplicatedHtml, new DateOnly(2026, 5, 16)); - - Assert.All(result.HoursDetails, h => Assert.Equal(20, h.ReliabilityPerc)); - } - // ------------------------------------------------------------------------- // Reliability // ------------------------------------------------------------------------- @@ -125,19 +88,7 @@ public void ParseDayPage_WithCompleteHtml1_ReliabilityIs90() var result = InvokeParseDayPage(scraper, Complete1Html, new DateOnly(2026, 5, 14)); Assert.NotEmpty(result.HoursDetails); - Assert.All(result.HoursDetails, h => Assert.Equal(90, h.ReliabilityPerc)); - } - - [Fact] - public void ParseDayPage_WithCompleteHtml3_ReliabilityIs95() - { - // 3bmeteo-complete3.html footer shows "95%" - var scraper = CreateScraper(); - - var result = InvokeParseDayPage(scraper, Complete3Html, new DateOnly(2026, 5, 14)); - - Assert.NotEmpty(result.HoursDetails); - Assert.All(result.HoursDetails, h => Assert.Equal(95, h.ReliabilityPerc)); + Assert.Equal(90, result.ReliabilityPerc); } [Fact] @@ -181,25 +132,25 @@ public void ParseDayPage_WithNoReliabilityFooter_DefaultsTo100() var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); Assert.NotEmpty(result.HoursDetails); - Assert.All(result.HoursDetails, h => Assert.Equal(100, h.ReliabilityPerc)); + Assert.Equal(100, result.ReliabilityPerc); } // ------------------------------------------------------------------------- - // Data correctness — complete1.html hour 00 - // temp=10, precip=0mm, wind=6km/h NE, humidity=65%, pressure=1004mb + // Data correctness — 3bmeteo-v3-complete1.html (table layout) hour 00 + // temp=10.7, precip=0mm, wind=6km/h NNE, humidity=75%, pressure=1024mb // ------------------------------------------------------------------------- private static HoursWeatherDetails GetHourSlot(DayWeather day, int hour) => day.HoursDetails.Single(h => h.TimeFrom.Hour == hour); [Fact] - public void ParseDayPage_WithCompleteHtml_Hour00_TemperatureIs10() + public void ParseDayPage_WithCompleteHtml_Hour00_TemperatureIs10point7() { var scraper = CreateScraper(); var result = InvokeParseDayPage(scraper, Complete1Html, new DateOnly(2026, 5, 14)); - Assert.Equal(10, GetHourSlot(result, 0).TemperatureC); + Assert.Equal(10.7, GetHourSlot(result, 0).TemperatureC); } [Fact] @@ -213,7 +164,7 @@ public void ParseDayPage_WithCompleteHtml_Hour00_PrecipitationIsZero() } [Fact] - public void ParseDayPage_WithCompleteHtml_Hour00_WindIs6KmhNE() + public void ParseDayPage_WithCompleteHtml_Hour00_WindIs6KmhNNE() { var scraper = CreateScraper(); @@ -221,210 +172,215 @@ public void ParseDayPage_WithCompleteHtml_Hour00_WindIs6KmhNE() var slot = GetHourSlot(result, 0); Assert.Equal(6, slot.WindKmh); - Assert.Equal("NE", slot.WindDirection); + Assert.Equal("NNE", slot.WindDirection); } [Fact] - public void ParseDayPage_WithCompleteHtml_Hour00_HumidityIs65() + public void ParseDayPage_WithCompleteHtml_Hour00_HumidityIs75() { var scraper = CreateScraper(); var result = InvokeParseDayPage(scraper, Complete1Html, new DateOnly(2026, 5, 14)); - Assert.Equal(65, GetHourSlot(result, 0).HumidityPerc); + Assert.Equal(75, GetHourSlot(result, 0).HumidityPerc); } [Fact] - public void ParseDayPage_WithCompleteHtml_Hour00_PressureIs1004() + public void ParseDayPage_WithCompleteHtml_Hour00_PressureIs1024() { var scraper = CreateScraper(); var result = InvokeParseDayPage(scraper, Complete1Html, new DateOnly(2026, 5, 14)); - Assert.Equal(1004, GetHourSlot(result, 0).PressionMbar); + Assert.Equal(1024, GetHourSlot(result, 0).PressionMbar); } // ------------------------------------------------------------------------- - // Data correctness — complicated1.html slot 0 (Notte 00-06) - // temp=10, precip=5.4mm, wind=8km/h NNO, humidity=93%, pressure=1001mb + // Edge cases // ------------------------------------------------------------------------- [Fact] - public void ParseDayPage_WithComplicatedHtml_NotteSlot_TemperatureIs10() + public void ParseDayPage_WithComplicatedHtmlFewerThanFourSlots_ReturnsEmpty() { var scraper = CreateScraper(); + var html = """ + + + +
+
    +
  • +
  • +
+
+ + """; - var result = InvokeParseDayPage(scraper, ComplicatedHtml, new DateOnly(2026, 5, 16)); + var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 16)); - Assert.Equal(10, result.HoursDetails[0].TemperatureC); + Assert.Empty(result.HoursDetails); } [Fact] - public void ParseDayPage_WithComplicatedHtml_NotteSlot_PrecipitationIs5point4() + public void ParseDayPage_WithMissingContentOrario_ReturnsEmpty() { var scraper = CreateScraper(); + var html = ""; - var result = InvokeParseDayPage(scraper, ComplicatedHtml, new DateOnly(2026, 5, 16)); + var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); - Assert.Equal(5.4, result.HoursDetails[0].PrecipitationMm); + Assert.Empty(result.HoursDetails); } + // ------------------------------------------------------------------------- + // MapWeatherType — singular/plural forms + // ------------------------------------------------------------------------- + [Fact] - public void ParseDayPage_WithComplicatedHtml_NotteSlot_WindIs8KmhNNO() + public void ParseDayPage_DescriptionWithNubiSparseConPossibiliPiogge_MapsToLightRain() { var scraper = CreateScraper(); + var html = BuildMinimalHourlyHtml("10", "Nubi sparse con possibili piogge"); - var result = InvokeParseDayPage(scraper, ComplicatedHtml, new DateOnly(2026, 5, 16)); + var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); - Assert.Equal(8, result.HoursDetails[0].WindKmh); - Assert.Equal("NNO", result.HoursDetails[0].WindDirection); + Assert.Equal(WeatherType.LightRain, result.HoursDetails[0].WeatherType); } [Fact] - public void ParseDayPage_WithComplicatedHtml_NotteSlot_HumidityIs93() + public void ParseDayPage_DescriptionWithPossibiliPioggeAlone_MapsToProbablyRainy() { var scraper = CreateScraper(); + var html = BuildMinimalHourlyHtml("10", "Possibili piogge"); - var result = InvokeParseDayPage(scraper, ComplicatedHtml, new DateOnly(2026, 5, 16)); + var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); - Assert.Equal(93, result.HoursDetails[0].HumidityPerc); + Assert.Equal(WeatherType.ProbablyRainy, result.HoursDetails[0].WeatherType); } [Fact] - public void ParseDayPage_WithComplicatedHtml_NotteSlot_PressureIs1001() + public void ParseDayPage_DescriptionWithSerenoOPocoNuvoloso_MapsToSunny() { var scraper = CreateScraper(); + var html = BuildMinimalHourlyHtml("10", "Sereno o poco nuvoloso"); - var result = InvokeParseDayPage(scraper, ComplicatedHtml, new DateOnly(2026, 5, 16)); + var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); - Assert.Equal(1001, result.HoursDetails[0].PressionMbar); + Assert.Equal(WeatherType.Sunny, result.HoursDetails[0].WeatherType); } [Fact] - public void ParseDayPage_WithComplicatedHtml_DescriptionIsNotEmpty() + public void ParseDayPage_DescriptionWithPocoNuvolosoAlone_MapsToPartlyCloudy() { var scraper = CreateScraper(); + var html = BuildMinimalHourlyHtml("10", "Poco nuvoloso"); - var result = InvokeParseDayPage(scraper, ComplicatedHtml, new DateOnly(2026, 5, 16)); + var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); - Assert.All(result.HoursDetails, h => Assert.NotEmpty(h.WeatherTypeDescription)); + Assert.Equal(WeatherType.PartlyCloudy, result.HoursDetails[0].WeatherType); } [Fact] - public void ParseDayPage_WithComplicatedHtml_WeatherTypeIsMapped() + public void ParseDayPage_DescriptionWithTemporali_MapsToThunderstorm() { var scraper = CreateScraper(); + var html = BuildMinimalHourlyHtml("10", "Molto nuvoloso con piogge e temporali"); - var result = InvokeParseDayPage(scraper, ComplicatedHtml, new DateOnly(2026, 5, 16)); + var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); - Assert.All(result.HoursDetails, h => Assert.NotEqual(WeatherType.Unknown, h.WeatherType)); + Assert.Equal(WeatherType.Thunderstorm, result.HoursDetails[0].WeatherType); } - // ------------------------------------------------------------------------- - // Edge cases - // ------------------------------------------------------------------------- - [Fact] - public void ParseDayPage_WithComplicatedHtmlFewerThanFourSlots_ReturnsEmpty() + public void ParseDayPage_DescriptionWithTemporale_MapsToThunderstorm() { var scraper = CreateScraper(); - var html = """ - - - -
-
    -
  • -
  • -
-
- - """; + var html = BuildMinimalHourlyHtml("10", "Temporale"); - var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 16)); + var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); - Assert.Empty(result.HoursDetails); + Assert.Equal(WeatherType.Thunderstorm, result.HoursDetails[0].WeatherType); } + // ------------------------------------------------------------------------- + // Precipitation probability + // ------------------------------------------------------------------------- + [Fact] - public void ParseDayPage_WithMissingContentOrario_ReturnsEmpty() + public void ParseDayPage_V3AccordionLayout_Hour00_PrecipitationProbabilityIsZero() { + // 3bmeteo-v3-complete4.html: hour 00 has data-param="probabilita" = 0% var scraper = CreateScraper(); - var html = ""; - var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); + var result = InvokeParseDayPage(scraper, V3Complete4Html, new DateOnly(2026, 5, 14)); - Assert.Empty(result.HoursDetails); + Assert.Equal(0, GetHourSlot(result, 0).PrecipitationProbabilityPerc); } - // ------------------------------------------------------------------------- - // MapWeatherType — singular/plural forms - // ------------------------------------------------------------------------- - [Fact] - public void ParseDayPage_DescriptionWithNubiSparseConPossibiliPiogge_MapsToLightRain() + public void ParseDayPage_TableLayout_SlotsHaveNullPrecipitationProbability() { + // Table layout does not expose per-slot precipitation probability var scraper = CreateScraper(); - var html = BuildMinimalHourlyHtml("10", "Nubi sparse con possibili piogge"); - var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); + var result = InvokeParseDayPage(scraper, V3Complete1Html, new DateOnly(2026, 5, 14)); - Assert.Equal(WeatherType.LightRain, result.HoursDetails[0].WeatherType); + Assert.NotEmpty(result.HoursDetails); + Assert.All(result.HoursDetails, h => Assert.Null(h.PrecipitationProbabilityPerc)); } + // ------------------------------------------------------------------------- + // v3 HTML — table layout (complete1) and accordion layout (complete4) + // ------------------------------------------------------------------------- + [Fact] - public void ParseDayPage_DescriptionWithPossibiliPioggeAlone_MapsToProbablyRainy() + public void ParseDayPage_V3TableLayout_Returns24HourlyForecasts() { var scraper = CreateScraper(); - var html = BuildMinimalHourlyHtml("10", "Possibili piogge"); - var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); + var result = InvokeParseDayPage(scraper, V3Complete1Html, new DateOnly(2026, 5, 14)); - Assert.Equal(WeatherType.ProbablyRainy, result.HoursDetails[0].WeatherType); + Assert.Equal(24, result.HoursDetails.Count); } [Fact] - public void ParseDayPage_DescriptionWithSerenoOPocoNuvoloso_MapsToSunny() + public void ParseDayPage_V3TableLayout_Hour00_TemperatureIsNonZero() { var scraper = CreateScraper(); - var html = BuildMinimalHourlyHtml("10", "Sereno o poco nuvoloso"); - var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); + var result = InvokeParseDayPage(scraper, V3Complete1Html, new DateOnly(2026, 5, 14)); - Assert.Equal(WeatherType.Sunny, result.HoursDetails[0].WeatherType); + Assert.NotEqual(0, GetHourSlot(result, 0).TemperatureC); } [Fact] - public void ParseDayPage_DescriptionWithPocoNuvolosoAlone_MapsToPartlyCloudy() + public void ParseDayPage_V3AccordionLayout_ReturnsHourlyForecasts() { var scraper = CreateScraper(); - var html = BuildMinimalHourlyHtml("10", "Poco nuvoloso"); - var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); + var result = InvokeParseDayPage(scraper, V3Complete4Html, new DateOnly(2026, 5, 14)); - Assert.Equal(WeatherType.PartlyCloudy, result.HoursDetails[0].WeatherType); + Assert.NotEmpty(result.HoursDetails); } [Fact] - public void ParseDayPage_DescriptionWithTemporali_MapsToThunderstorm() + public void ParseDayPage_V3AccordionLayout_Hour00_TemperatureIsNonZero() { var scraper = CreateScraper(); - var html = BuildMinimalHourlyHtml("10", "Molto nuvoloso con piogge e temporali"); - var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); + var result = InvokeParseDayPage(scraper, V3Complete4Html, new DateOnly(2026, 5, 14)); - Assert.Equal(WeatherType.Thunderstorm, result.HoursDetails[0].WeatherType); + Assert.NotEqual(0, GetHourSlot(result, 0).TemperatureC); } [Fact] - public void ParseDayPage_DescriptionWithTemporale_MapsToThunderstorm() + public void ParseDayPage_V3TodayPage_ReturnsHourlyForecasts() { var scraper = CreateScraper(); - var html = BuildMinimalHourlyHtml("10", "Temporale"); - var result = InvokeParseDayPage(scraper, html, new DateOnly(2026, 5, 14)); + var result = InvokeParseDayPage(scraper, V3TodayHtml, new DateOnly(2026, 5, 14)); - Assert.Equal(WeatherType.Thunderstorm, result.HoursDetails[0].WeatherType); + Assert.NotEmpty(result.HoursDetails); } private static string BuildMinimalHourlyHtml(string hour, string summaryDescription) => $""" diff --git a/Weather4Agents.Test/Weather4Agents.Test.csproj b/Weather4Agents.Test/Weather4Agents.Test.csproj index f1510b7..b9a84f9 100644 --- a/Weather4Agents.Test/Weather4Agents.Test.csproj +++ b/Weather4Agents.Test/Weather4Agents.Test.csproj @@ -20,20 +20,16 @@ - - ProviderExamples\3bmeteo-complete1.html + + ProviderExamples\3bmeteo-v3-complete1.html PreserveNewest - - ProviderExamples\3bmeteo-complete2.html + + ProviderExamples\3bmeteo-v3-complete4.html PreserveNewest - - ProviderExamples\3bmeteo-complete3.html - PreserveNewest - - - ProviderExamples\3bmeteo-complicated1.html + + ProviderExamples\3bmeteo-v3-today1.html PreserveNewest diff --git a/docs/ProviderExamples/3bmeteo-v1/3bmeteo-response-13.html b/docs/ProviderExamples/3bmeteo-v1/3bmeteo-response-13.html deleted file mode 100644 index 891ff37..0000000 --- a/docs/ProviderExamples/3bmeteo-v1/3bmeteo-response-13.html +++ /dev/null @@ -1,1440 +0,0 @@ - - - - Previsioni Meteo Bergamo Oggi - Fino a 15 Giorni ▷ 3B Meteo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - - - - - - - -
-
- -
- - -
-
- - -
- - -
- - - - - - - - - - -
- - -
-
-
-
-
-

TEMP

°C °F -
-

PREC

mm/cm -
-

VENTI

km/h kn -
-

UMIDITÀ

Relativa -
-

PERCEPITA

°C °F -
- - - -

NEVE A
QUOTA 0°C

-

PRESSIONE

mbar -
-

UV

Intensità -
-

WINDCHILL

°C °F -
-
-
-
- - - - - - - - -
-
-
-
11:00
- sereno -
- sereno -
-
-
-
-
- 12.7° 54.9° -
- - assenti - -
- - 5 - - 3 -  ESE -
68%
- 13.0° 55.4° -
-
2750 m
3024 m
1027.4
- Debole (2) -
- 12.7° 54.9° -
-
-
-
- - - - - - -
-
-
-
12:00
- poco nuvoloso -
- poco nuvoloso -
-
-
-
-
- 13.6° 56.5° -
- - assenti - -
- - 6 - - 3 -  SSO -
66%
- 14.0° 57.2° -
-
2750 m
3025 m
1027.2
- Media (3) -
- 13.6° 56.5° -
-
-
-
- - - - -
-
-
-
13:00
- poco nuvoloso -
- poco nuvoloso -
-
-
-
-
- 14.4° 57.9° -
- - assenti - -
- - 6 - - 3 -  SSO -
64%
- 15.0° 59.0° -
-
2750 m
3023 m
1026.6
- Debole (2) -
- 14.4° 57.9° -
-
-
-
- - - - -
-
-
-
14:00
- poco nuvoloso -
- poco nuvoloso -
-
-
-
-
- 15.0° 59.0° -
- - assenti - -
- - 6 - - 3 -  SO -
63%
- 15.0° 59.0° -
-
2750 m
3019 m
1025.9
- Debole (2) -
- 15.0° 59.0° -
-
-
-
- - - - -
-
-
-
15:00
- poco nuvoloso -
- poco nuvoloso -
-
-
-
-
- 15.3° 59.5° -
- - assenti - -
- - 6 - - 3 -  SO -
63%
- 15.0° 59.0° -
-
2750 m
3028 m
1025.3
- Debole (2) -
- 15.3° 59.5° -
-
-
-
- - - - -
-
-
-
16:00
- poco nuvoloso -
- poco nuvoloso -
-
-
-
-
- 15.2° 59.4° -
- - assenti - -
- - 6 - - 3 -  SO -
64%
- 15.0° 59.0° -
-
2750 m
3036 m
1025.1
- Debole (1) -
- 15.0° 59.0° -
-
-
-
- - - - -
-
-
-
17:00
- poco nuvoloso -
- poco nuvoloso -
-
-
-
-
- 14.5° 58.1° -
- - assenti - -
- - 5 - - 3 -  SO -
68%
- 15.0° 59.0° -
-
2800 m
3049 m
1025
- Assente (0) -
- 14.0° 57.2° -
-
-
-
- - - - - - -
-
-
-
18:00
- poco nuvoloso -
- poco nuvoloso -
-
-
-
-
- 12.1° 53.8° -
- - assenti - -
- - 4 - - 2 -  SO -
77%
- 12.0° 53.6° -
-
2800 m
3061 m
1025.3
- Assente (0) -
- 12.0° 53.6° -
-
-
-
- - - - -
-
-
-
19:00
- poco nuvoloso -
- poco nuvoloso -
-
-
-
-
- 10.6° 51.1° -
- - assenti - -
- - 2 - - 1 -  NNO -
80%
- 8.3° 46.9° -
-
2800 m
3057 m
1025.5
- Assente (0) -
- 10.0° 50.0° -
-
-
-
- - - - -
-
-
-
20:00
- poco nuvoloso -
- poco nuvoloso -
-
-
-
-
- 9.9° 49.8° -
- - assenti - -
- - 3 - - 2 -  NNE -
81%
- 7.5° 45.4° -
-
2800 m
3065 m
1025.6
- Assente (0) -
- 9.0° 48.2° -
-
-
-
- - - - -
-
-
-
21:00
- poco nuvoloso -
- poco nuvoloso -
-
-
-
-
- 9.3° 48.7° -
- - assenti - -
- - 2 - - 1 -  NNE -
82%
- 6.8° 44.2° -
-
2750 m
3066 m
1025.9
- Assente (0) -
- 9.0° 48.2° -
-
-
-
- - - - -
-
-
-
22:00
- parz nuvoloso -
- parz nuvoloso -
-
-
-
-
- 8.8° 47.8° -
- - assenti - -
- - 2 - - 1 -  NE -
83%
- 6.2° 43.1° -
-
2750 m
3062 m
1026.2
- Assente (0) -
- 8.0° 46.4° -
-
-
-
- - - - -
-
-
-
23:00
- parz nuvoloso -
- parz nuvoloso -
-
-
-
-
- 8.4° 47.1° -
- - assenti - -
- - 4 - - 2 -  NNE -
84%
- 5.7° 42.2° -
-
2750 m
3056 m
1026.3
- Assente (0) -
- 8.0° 46.4° -
-
-
-
- - - - - - -
-
-
- Indice di qualità previsionale -
Indice di qualità previsionale
-
-
Ultimo giorno
96%
Ottima
Ultima settimana
95%
Ottima
Ultimo mese
90%
Ottima
-
-
- - - - - - - - -
-
-
-
- MEDIA 3ª DECADE - Dati degli ultimi 15 anni -
-
-
- Temperatura Media Minima 3°C -
Media Minima -
-
- Temperatura Media Massima 11°C -
Media Massima -
-
- Giorni di Precipitazioni 3 -
Giorni Precipitazioni -
-
-
-
-
LO STESSO GIORNO UN ANNO FA
-
- - pioviggine - - 0.5 mm Precipitazioni 6 km/h 3 Kn SO Venti - -
-
- pioviggine -
12°C 7°C
-
-
-
-
-
ESTREMI STORICI DEL GIORNO
-
-
- Temperatura Minima -6°C -
2018 -
-
- Temperatura Massima 19°C -
2021 -
-
- Estremi Piogge
- 28 mm di pioggia -
-
2024 -
-
-
-
-
ASTRONOMIA
-
- Sole
-
- Alba
Alba 07:04
-
- Tramonto
Tramonto 18:03
-
-
- Ore di luce Ore di luce 11 - -
- Luna
-
- Luna Alba
Alba 11:58
-
- Luna Tramonto
Tramonto 04:05
-
-
- Gibbosa crescente Fase Lunare - -
-
-
-
ALTRI DATI
-
- Qualità dell'aria
- Scadente Scadente -
-
-
-
-
-
-
- - -
Indice di rischio parassitario
3 più alti della giornata
-
-
-
Zanzara tigre
Assente
Pappataci
Assente
Dirofilaria
Assente
-
-
-
-
- -
Dati località
-
-
-

ABITANTI

11194 -

ALTITUDINE

309 m

LATITUDINE

45.74

LONGITUDINE

9.76
-
-
-
-
-
vota
4.9 su 799
-
-
-
- -
-
-
La Previsione per giovedì, 26 febbraio

Il Meteo a Bergamo e le temperature

-

- A Bergamo oggi cieli in prevalenza sereni o poco nuvolosi per l'intera giornata, non sono previste piogge nelle prossime ore. Durante la giornata di oggi la temperatura massima registrata sarà di 15°C, - la minima di 6°C, lo zero termico si attesterà a 3025m. I venti saranno al mattino deboli e proverranno da Nordest, al pomeriggio moderati e proverranno da Sud-Sudovest. Nessuna allerta meteo presente. -

Commento del previsore Lombardia

-

GIOVEDI': un campo di alte pressioni abbraccia la Regione garantendo tempo stabile ed assolato ovunque. Nello specifico su basse pianure occidentali, pedemontane-alte pianure, Prealpi occidentali, Orobie e Alpi Retiche cieli in prevalenza sereni o poco nuvolosi per l'intera giornata; sulle basse pianure orientali tempo perlopiù soleggiato, salvo nubi basse o banchi di nebbia al mattino; sulle Prealpi orientali cieli inizialmente poco o parzialmente nuvolosi ma con nuvolosità in aumento a partire dal pomeriggio. Venti molto deboli meridionali in rotazione a occidentali; Zero termico nell'intorno di 3050 metri.

Consulta il Meteo Lombardia

-

Ultimo aggiornamento il 26 febbraio ore 10:50

-
- - -
-
-

Le migliori webcam vicino a Bergamo segnalate alla community di 3bmeteo

-
-
-
- -
- - ▲
- - - - - - - - -
- - - diff --git a/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complete1.html b/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complete1.html deleted file mode 100644 index 01f68eb..0000000 --- a/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complete1.html +++ /dev/null @@ -1,1492 +0,0 @@ - - - - Meteo Bergamo Domani - Dettagli Orari e Esa - 3B Meteo - - - - - - - - - - - - - - - - - - - - - - - - - - - Vai al contenuto principale
- -
- - - - -

Previsioni Meteo

-
-
-
-
    - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • -
    - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - -
  • -
- -
- - - - -
-
Qualità previsionale
La nostra percentuale di successo e affidabilità.
Ieri
95%
Ottima -
Sett. scorsa
87%
Buona -
Mese scorso
91%
Ottima -
- -
- - - - -
-
- - - - -

Altri dati

-
- - - - -
-
Ottima
19.2 µg/m³ - PM10
-
- - - - -
-
- - - - -
-
Media
Chenopodiacee -
-
- - - - -
-
- - - - -
-
Basso
Zanzara tigre -
-
- - - - -
-
- - - - -
-
Non previsti
-
- - - - -

Oltre il meteo

-
- - - - -
-
Astronomia
Sole - - 15 ore di luce
05:51 Alba
20:44 Tramonto
Luna - Luna calante
-
04:09 Sorge
18:06 Cala
- - - - -
-
- - - - -
-
Dati località
-
Località Bergamo
-
  • Provincia BG
  • Regione Lombardia
  • Abitanti 119846
  • Altitudine 249 s.l.m.
  • Latitudine 45.6983
  • Longitudine 9.6773
  • -
- - - - -
-
- - - - -
- - - - -
Carta interattiva -
-
Un altro modo per visualizzare la tua località -
- Inizia a navigare - - - - - -
- - - - -
-
-
- -
- - - -
Media 2 ª decade di maggio
12°
Min -
22°
Max -
Precipitazioni
6 Giorni
- - - - -
-
-
- -
- - - -
Oggi 1 anno fa
13°/22°

Nubi sparse con pioviggine

Precipitazioni 0.3 mm -
Venti 7 km/h NNE -
- - - - -
-
-
- -
- - - -
Estremi storici del giorno
7°
2012
Min -
28°
2015
Max -
Precipitazioni
2024 -
0.3 mm
- - - - -
- - - - - -

Il Meteo a Bergamo e le temperature -

-

La previsione per giovedì, 14 maggio -

-
Redazione 3BMeteo
13 maggio ore 20:18
Giornata perturbata con piogge e rovesci diffusi, anche a carattere temporalesco, sono previsti 13mm di pioggia. Durante la giornata di domani la temperatura massima registrata sarà di 18°C, -la minima di 12°C, lo zero termico si attesterà a 2104m. I venti saranno al mattino moderati e proverranno da Sud-Sudest, al pomeriggio tesi e proverranno da Sud. Nessuna allerta meteo presente.
-
-
- -

Previsioni del tempo nelle altre località della provincia di Bergamo

- - - \ No newline at end of file diff --git a/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complete2.html b/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complete2.html deleted file mode 100644 index 1d00d20..0000000 --- a/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complete2.html +++ /dev/null @@ -1,1492 +0,0 @@ - - - - Meteo Bergamo Dopodomani - 3B Meteo - - - - - - - - - - - - - - - - - - - - - - - - - - - Vai al contenuto principale
- -
- - - - -

Previsioni Meteo

-
-
-
-
    - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • -
    - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - -
  • -
- -
- - - - -
-
Qualità previsionale
La nostra percentuale di successo e affidabilità.
Ieri
95%
Ottima -
Sett. scorsa
87%
Buona -
Mese scorso
91%
Ottima -
- -
- - - - -
-
- - - - -

Altri dati

-
- - - - -
-
Ottima
24.1 µg/m³ - PM10
-
- - - - -
-
- - - - -
-
Media
Chenopodiacee -
-
- - - - -
-
- - - - -
-
Basso
Zecca del cane -
-
- - - - -
-
- - - - -
-
Non previsti
-
- - - - -

Oltre il meteo

-
- - - - -
-
Astronomia
Sole - - 15 ore di luce
05:49 Alba
20:45 Tramonto
Luna - Luna calante
-
04:31 Sorge
19:30 Cala
- - - - -
-
- - - - -
-
Dati località
-
Località Bergamo
-
  • Provincia BG
  • Regione Lombardia
  • Abitanti 119846
  • Altitudine 249 s.l.m.
  • Latitudine 45.6983
  • Longitudine 9.6773
  • -
- - - - -
-
- - - - -
- - - - -
Carta interattiva -
-
Un altro modo per visualizzare la tua località -
- Inizia a navigare - - - - - -
- - - - -
-
-
- -
- - - -
Media 2 ª decade di maggio
12°
Min -
22°
Max -
Precipitazioni
6 Giorni
- - - - -
-
-
- -
- - - -
Oggi 1 anno fa
12°/26°

Sereno o poco nuvoloso

Precipitazioni Assenti -
Venti 9 km/h E -
- - - - -
-
-
- -
- - - -
Estremi storici del giorno
5°
2019
Min -
28°
2022
Max -
Precipitazioni
2024 -
0 mm
- - - - -
- - - - - -

Il Meteo a Bergamo e le temperature -

-

La previsione per venerdì, 15 maggio -

-
Redazione 3BMeteo
13 maggio ore 20:47
Cieli molto nuvolosi o coperti con deboli piogge, in temporanea intensificazione pomeridiana, sono previsti 13mm di pioggia. Durante la giornata di dopodomani la temperatura massima registrata sarà di 17°C, -la minima di 9°C, lo zero termico si attesterà a 1979m. I venti saranno al mattino moderati e proverranno da Sud-Sudest, al pomeriggio moderati e proverranno da Sud-Sudest. Nessuna allerta meteo presente.
-
-
- -

Previsioni del tempo nelle altre località della provincia di Bergamo

- - - \ No newline at end of file diff --git a/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complete3.html b/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complete3.html deleted file mode 100644 index f78f929..0000000 --- a/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complete3.html +++ /dev/null @@ -1,1492 +0,0 @@ - - - - Il Meteo Bergamo Fra 4 Giorni - 3B Meteo - - - - - - - - - - - - - - - - - - - - - - - - - - - Vai al contenuto principale
- -
- - - - -

Previsioni Meteo

-
-
-
-
    - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - -
  • -
    - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - -
  • -
- -
- - - - -
-
Qualità previsionale
La nostra percentuale di successo e affidabilità.
Ieri
95%
Ottima -
Sett. scorsa
87%
Buona -
Mese scorso
91%
Ottima -
- -
- - - - -
-
- - - - -

Altri dati

-
- - - - -
-
N.D.
Solo per giorni 1-3 -
-
- - - - -
-
- - - - -
-
Media
Oleacee -
-
- - - - -
-
- - - - -
-
Basso
Zecca del cane -
-
- - - - -
-
- - - - -
-
Non previsti
-
- - - - -

Oltre il meteo

-
- - - - -
-
Astronomia
Sole - - 15 ore di luce
05:47 Alba
20:47 Tramonto
Luna - Luna nuova
-
05:38 Sorge
22:20 Cala
- - - - -
-
- - - - -
-
Dati località
-
Località Bergamo
-
  • Provincia BG
  • Regione Lombardia
  • Abitanti 119846
  • Altitudine 249 s.l.m.
  • Latitudine 45.6983
  • Longitudine 9.6773
  • -
- - - - -
-
- - - - -
- - - - -
Carta interattiva -
-
Un altro modo per visualizzare la tua località -
- Inizia a navigare - - - - - -
- - - - -
-
-
- -
- - - -
Media 2 ª decade di maggio
12°
Min -
22°
Max -
Precipitazioni
6 Giorni
- - - - -
-
-
- -
- - - -
Oggi 1 anno fa
10°/21°

Nubi irregolari con temporali

Precipitazioni 5.3 mm -
Venti 7 km/h SO -
- - - - -
-
-
- -
- - - -
Estremi storici del giorno
7°
2012
Min -
30°
2022
Max -
Precipitazioni
2013 -
5.3 mm
- - - - -
- - - - - -

Il Meteo a Bergamo e le temperature -

-

La previsione per domenica, 17 maggio -

-
Redazione 3BMeteo
13 maggio ore 20:20
Bel tempo con sole splendente per l'intera giornata, non sono previste piogge. Durante la giornata la temperatura massima registrata sarà di 20°C, -la minima di 8°C, lo zero termico si attesterà a 2312m. I venti saranno al mattino deboli e proverranno da Sud-Sudovest, al pomeriggio deboli e proverranno da Sud-Sudovest. Nessuna allerta meteo presente.
-
-
- -

Previsioni del tempo nelle altre località della provincia di Bergamo

- - - \ No newline at end of file diff --git a/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complicated1.html b/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complicated1.html deleted file mode 100644 index 10c387f..0000000 --- a/docs/ProviderExamples/3bmeteo-v2/3bmeteo-complicated1.html +++ /dev/null @@ -1,973 +0,0 @@ - - - - Meteo Bergamo Fra 3 Giorni - 3B Meteo - - - - - - - - - - - - - - - - - - - - - - - - - - - Vai al contenuto principale
- -
- - - - -

Previsioni Meteo

- - - -
- - -

Previsione complicata

Peggioramento confermato, ma sono incerte le tempistiche e l'intensità delle precipitazioni

-
-
Stefano Ghisu
- -
- - - -
-
-
    - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - - - - - - - - - - - - - -
  • -
- -
- - - - -
-
Qualità previsionale
La nostra percentuale di successo e affidabilità.
Ieri
95%
Ottima -
Sett. scorsa
87%
Buona -
Mese scorso
91%
Ottima -
- -
- - - - -
-
- - - - -

Altri dati

-
- - - - -
-
Ottima
13.5 µg/m³ - PM10
-
- - - - -
-
- - - - -
-
Bassa
Chenopodiacee -
-
- - - - -
-
- - - - -
-
Basso
Zecca del cane -
-
- - - - -
-
- - - - -
-
Non previsti
-
- - - - -

Oltre il meteo

-
- - - - -
-
Astronomia
Sole - - 15 ore di luce
05:48 Alba
20:46 Tramonto
Luna - Luna calante
-
05:00 Sorge
20:57 Cala
- - - - -
-
- - - - -
-
Dati località
-
Località Bergamo
-
  • Provincia BG
  • Regione Lombardia
  • Abitanti 119846
  • Altitudine 249 s.l.m.
  • Latitudine 45.6983
  • Longitudine 9.6773
  • -
- - - - -
-
- - - - -
- - - - -
Carta interattiva -
-
Un altro modo per visualizzare la tua località -
- Inizia a navigare - - - - - -
- - - - -
-
-
- -
- - - -
Media 2 ª decade di maggio
12°
Min -
22°
Max -
Precipitazioni
6 Giorni
- - - - -
-
-
- -
- - - -
Oggi 1 anno fa
12°/20°

Nubi sparse con pioviggine

Precipitazioni 1.4 mm -
Venti 15 km/h ESE -
- - - - -
-
-
- -
- - - -
Estremi storici del giorno
5°
2019
Min -
30°
2022
Max -
Precipitazioni
2013 -
1.4 mm
- - - - -
- - - - - -

Il Meteo a Bergamo e le temperature -

-

La previsione per sabato, 16 maggio -

-
Redazione 3BMeteo
13 maggio ore 21:00
Cieli molto nuvolosi al mattino con deboli piogge. Graduale attenuazione della nuvolosità in giornata fino a cieli poco nuvolosi in serata, sono previsti 11mm di pioggia. Durante la giornata la temperatura massima registrata sarà di 19°C, -la minima di 8°C, lo zero termico si attesterà a 2063m. I venti saranno al mattino moderati e proverranno da Nord-Nordovest, al pomeriggio moderati e proverranno da Sudovest. Nessuna allerta meteo presente.
-
-
- -

Previsioni del tempo nelle altre località della provincia di Bergamo

- - - \ No newline at end of file diff --git a/docs/ProviderExamples/3bmeteo-v1/3bmeteo-response-24.html b/docs/ProviderExamples/3bmeteo-v3/3bmeteo-complete1.html similarity index 72% rename from docs/ProviderExamples/3bmeteo-v1/3bmeteo-response-24.html rename to docs/ProviderExamples/3bmeteo-v3/3bmeteo-complete1.html index c824d78..2d0b091 100644 --- a/docs/ProviderExamples/3bmeteo-v1/3bmeteo-response-24.html +++ b/docs/ProviderExamples/3bmeteo-v3/3bmeteo-complete1.html @@ -1,6 +1,7 @@ -Meteo Bergamo Domani - Dettagli Orari e Esa ▷ 3B Meteo - - + +Meteo Bergamo Dopodomani ▷ 3B Meteo + + -
+