Articoli

Una sveglia con Blazor e Raspberry PI (Terza parte)

da

Terza parte della nostra sveglia con Blazor: aggiungeremo una connessione ad un servizio meteo per visualizzare le condizioni del tempo nella località scelta.

Open Weather Map

Come servizio meteo, ho scelto Open Weather Map perché è abbastanza preciso e gratuito. Per prima cosa bisogna registrarsi per ottenere la API-KEY che useremo per le chiamate al servizio e recuperare i dati meteo.

Struttura del progetto

Dato che questa libreria potrebbe essere utilizzata anche in altri ambiti, ho voluto strutturarla in un modo un po’ più dinamico rispetto alla classica libreria di componenti. Ho aggiunto, per prima cosa, un progetto Weather che si occuperà di fornire la parte di front end. Questo progetto comprenderà quindi una struttura del genere:

Weather
	|_ Components
		|- WeatherLocation.razor
	|_ Services
		|- OpenWeatherService.cs
Program.cs
WeatherComponent.razor

In secondo luogo ho aggiunto un progetto Weather.Business che servirà da backend per il componente il quale avrà la seguente struttura

Weather.Business
	|_ Controllers
		|-WeatherController.cs
	|_ Data
		|-owm_cities.sql
	|_ Models
		|- WeatherLocationsBridge.cs
		|- WeatherLocationsContext.cs
Program.cs

Come ultimo progetto ho aggiunto Weather.Entities che conterrà i modelli di dati di scambio tra i due progetti precedenti e tra la nostra applicazione e i servizi di Open Weather Map.

OpenWeatherService

Iniziamo ad implementare una classe che farà da servizio di chiamata verso il backend e per tutto quello che sono le interazioni.

La parte principale del servizio è quella che chiama l’API esterna di Open Weather Map. Dovremo creare un client HTTP che si occuperà ogni volta di chiamare l’API con la seguente riga di codice

api_url = $@"{baseApiAddress}{weatherAddress}?lat={latitude.ToString().Replace(",", ".")}&lon={longitude.ToString().Replace(",", ".")}&units=metric&appid={apiKey}";

Ci sono due variabili in questo indirizzo: baseApiAddress e weatherAddress. Ci tornerà molto utile questa divisione in seguito perché avremo bisogno del baseApiAddress per recuperare le icone associate alle condizioni meteo. Ma andiamo per gradi…

Quindi, composto l’indirizzo web con le coordinate della città che vorremo recuperare, non ci resta che fare la chiamata.

Creiamo un nuovo client HTTP tramite IHttpClientFactory che avremo passato dalla dependency injection alla classe, facciamo la chiamata (NON DIMENTICATE L’API-KEY!!!) e recupereremo un json con tutti i dati che ci servono. Li deserializziamo e otterremo una classe MeteoPack pronta all’uso. Ecco il codice

// Open connection
var client = httpClientFactory.CreateClient("openweathermap");
using var weatherResponse = await client.GetAsync(api_url);
var stringResult = weatherResponse.Content.ReadAsStringAsync().Result;
if (stringResult.Length == 0)
{
    throw new ArgumentNullException("Empty response");
}

// Get json from OWM
MeteoPack = JsonConvert.DeserializeObject<MeteoPack>(stringResult);

Manca ancora l’immagine delle condizioni meteo da visualizzare e lo faremo tramite una chiamata separata. Io ho scelto di passare tutta la classe MeteoPack al metodo ma sarebbe bastato anche solo l’icona da cercare. In ogni caso il metodo GetIcon ci restituirà l’icona associata sotto forma di stringa che andremo poi ad utilizzare nel componente razor come source di un componente image in questo modo

<MudImage Src="@WeatherIcon" />

Dovremo, per correttezza, andare anche a recuperare l’altezza e la larghezza dell’immagine se la vogliamo a schermo con dimensioni appropriate.

L’effetto finale sarà più o meno il seguente

Mentre il codice razor completo del componente sarà questo

<MudGrid>
    <MudItem xs="12" Class="d-flex justify-content-center">
        <MudImage Src="@WeatherIcon" Height="@GetImageHeight()" Width="@GetImageHeight()" />
    </MudItem>
    <MudItem xs="12" Class="d-flex justify-content-center">
        <MudText Typo="Typo.h3" Style="font-weight:900">@GetTemperatureRounded() °C </MudText>
        <MudText Typo="Typo.h5" Class="ml-12" >@MeteoPack.name</MudText>
    </MudItem>
</MudGrid>

Vi consiglio comunque di dare un’occhiata al codice, sarà più chiaro.

Ho parlato in precedenza della classe MeteoPack ma non ho ben spiegato di cosa si tratta.

Questa classe e le classi associate, si trovano nel progetto Weather.Entities.Models e derivano direttamente dal json di risposta del servizio meteo. Molti campi come la città o le coordinate sono chiari, ma alte come Clouds che contiene solo una proprietà chiamata all il significato è un po’ oscuro. In ogni caso la documentazione di questi campi la troverete sul sito di Open Weather Map a questo indirizzo per la chiamata al meteo corrente. Per altre chiamate la documentazione è semplice da consultare.

Scegliere la località

Per scegliere la località abbiamo bisogno di un piccolo database che è fornito da Open Weather Map a questo indirizzo. Il problema è che viene fornito in formato JSON e contiene più di 200000 records con 2 milioni di linee di testo. Davvero una gran mole di dati. Dato che non era gestibile in questo modo, ho convertito il json in un database SQLite ma per comodità d’uso nel progetto è in formato SQL e il database verrà creato al primo avvio.

La nostra località la sceglieremo dal menù di Settings che richiamerà (per ora) solo la pagina di scelta della località. Scriveremo la nostra località e, se compare nell’elenco, cliccandoci sopra la selezioneremo.

Inseriamo il meteo

Essendo un componente a sé stante, tutta la dependency injection sarà costruita all’interno dei files Program.cs sia per la parte backend che per la pate frontend e andremo a richiamarla dai files Program.cs dell’applicazione principale.

Subito dopo andiamo a richiamare il componente meteo dalla pagina Index.razor in questo modo

<MudItem xs="6">
    <WeatherComponent />
</MudItem>

Infine aggiungiamo il componente che gestisce la scelta della città nella pagina Settings.razor aggiungendo semplicemente il tag <WeatherLocation /> all’interno della pagina.

Conclusioni

Ed ecco qui l’applicazione terminata

E se la sveglia suona avrà questo aspetto

Spero che questo progetto vi sia piaciuto e spero vi divertiate a costruirlo come mi sono divertito io. Come al solito il codice lo troverete su GitHub.