WebMite（MMBasic）でREST APIを利用する

# はじめに

PicoMiteのユーザマニュアル77ページにTCPクライアントの実装例として[openweathermap.org](https://openweathermap.org/)から現在のパリの気温を取得するプログラムが示されている。このプログラムは動作するが、OpenWeatherMapの現在のAPIは`[One Call API 3.0](https://openweathermap.org/api)`になっています。
ここではWebMite（MMBasic）のユーザマニュアルのプログラムをもとに`One Call API 3,.0`を利用してopenweathermap.orgからの気象情報の取得するプログラムについて説明しています。

# 実行環境

実行環境は次の通りです。
- ハードウェア：Raspberry Pi Pico 2 Wを搭載したPicoCalc
- ファームウェア：WebMiteバージョン6.0002RC26（https://github.com/madcock/PicoMiteAllVersions からダウンロードしたソースからRP2350用に自分でビルドしました）

MMBasicのプログラムはMac上で入力し、TFTPで転送しています。

# 準備〜OpenWeatherへユーザ登録

openweathermap.orgのAPIのOne Call API 3.0を利用するには次の作業が必要です。
1. OpenWeathermapへのユーザ登録
	- ユーザ名、メールアドレス、パスワードを登録します
	- 確認メール内のリンクをクリックして確認
	- プラン選択はないので登録完了で無料プラン（Free Plan）になります
2. APIキーの生成
	- ユーザ登録完了後にサインインし、メニューの`My API Keys`から生成します
3. One Call API 3.0のサブスクライブ
	- [Weather APIのページ](https://openweathermap.org/api)からSubscribeをクリックして支払い情報などを登録します。
	- 登録完了後、その情報がすぐには反映されないようで小一時間待ってOne Call API 3.0が利用できるようになりました

https://openweathermap.org/api の記載では`One Call API 3.0`を利用すると下記のデータにアクセスできると記されています。

- [現在の天気と予報](https://openweathermap.org/api/one-call-3#current)：
	- 1時間ごとの分予報
	- 48時間ごとの時間予報
	- 8日間の日次予報
	  および政府の気象警報
- 46年以上の過去の気象データアーカイブと4日後の予報を含む、[任意のタイムスタンプの気象データ](https://openweathermap.org/api/one-call-3#history)
- 46年以上のアーカイブと1.5年先の予報を含む、気象データの[日次集計](https://openweathermap.org/api/one-call-3#history_daily_aggregation)
- 今日と明日の予報を人間が読める形式で表示する[天気概要](https://openweathermap.org/api/one-call-3#weather_overview)

一日のAPI呼び出しのうち、最初の1000コールまでは無料で、それを越えると1コール0.0012GBPの費用が発生します。[https://openweathermap.org/price#weather](https://openweathermap.org/price#weather)に価格の記載があります。APIのコール数の集計はJSTではなくUTCのタイムゾーンで行われています。

# MMBasicでTCPクライアント

## 処理手順

MMBasicでTCPクライアントを実現するにはサーバからの応答を保存する配列（長い文字列変数）を用意し、次の手順で処理します。

1. `WEB OPEN TCP CLIENT domain$, port`コマンドで接続先ドメイン（domain$）にポート番号（port）で接続
2. `WEB TCP CLIENT REQUEST query$, inbuf`コマンドでリクエスト（query$）を送信して応答を変数（inbuf）に保存
3. `WEB CLOSE TCP CLIENT`コマンドでTCPのセッションを閉じる
4. 受信したデータの処理

## 作成したプログラム

ユーザマニュアル77ページの例題プログラムと[OpenWeathMapのAPI](https://openweathermap.org/api/one-call-3)を参考に、の現在の天気を取得するプログラムを作成しました。プログラムは次の通りです。

```BASIC:GetCurrentWeather.bas
  1	Const Key = "0123456789ABCDEFGHIJLKMNOPQRSTUV" ' API key
  2	Const CRLF$ = Chr$(13) + Chr$(10)              ' CR+LF
  3	Const Domain$ = "api.openweathermap.org"       ' Traget domain
  4	Const lat = "35.70"                            ' Latitude
  5	COnst lon = "139.77"                           ' Longitude
  6	Dim    Query$ = "GET /data/3.0/onecall?"       ' Query string
  7	Query$ = Query$ + "lat=" + lat + "&lon=" + lon
  8	Query$ = Query$ + "&exclude=minutely,hourly,daily"
  9	Query$ = Query$ + "&units=metric"
 10	Query$ = Query$ + "&APPID="+Key+CRLF$
 11	Dim buff%(4096/8)                              ' LongString variable 
 12
 13	WEB Open tcp client Domain$, 80                ' Connect to Server
 14	WEB tcp client request Query$, buff%()         ' Send sequest
 15	WEB close tcp client                           ' Close connection
 16	' Get value
 17	off  = Val(Json$(buff%(), "timezone_offset"))
 18	dt$  = DateTime$(Val(Json$(buff%(),"current.dt")) + off)
 19	sr$  = DateTime$(Val(Json$(buff%(),"current.sunrise")) + off)
 20	ss$  = DateTime$(Val(Json$(buff%(),"current.sunset")) + off)
 21	main$= Json$(buff%(),"current.weather[0].main")
 22	desc$= Json$(buff%(),"current.weather[0].description")
 23	temp = Val(Json$(buff%(), "current.temp"))
 24	pres = Val(Json$(buff%(),"current.pressure"))
 25	humi = Val(Json$(buff%(),"current.humidity"))
 26
 27	Print
 28	Print "=== Akihabara weather ==="
 29	print "Date   :";dt$
 30	Print "Sunrise:";sr$
 31	Print "Sunset :";ss$
 32	Print "main   :";main$
 33	Print "Temp   :";temp;"C"
 34	Print "Press  :";pres;"hPa"
 35	Print "Humid  :";humi;"%"
 36	Print "Desc   :";desc$
 ```

### 定数と変数定義

プログラムの1〜11行目でサーバへ接続し、問合せするために必要な値を定数および変数に設定しています。

|定数・変数|値など|
|:-:|-|
|Key|取得したAPIキー|
|CRLF$|改行コード|
|lat|情報を取得する場所の緯度|
|lon|情報を取得する場所の軽度|
|Query$|APIでの問合せの文字列|
|buff%()|問合せ結果を保存|

buff%()は配列の定義と同じですが長い文字列変数とするために添字を8で割る設定にし定ます。これにより配列の各要素が8バイトを占めるようしています。この定義で4096文字を保存できます。

### API

One Call API 3.0で現在の天気データと予報データを取得するAPIの形式は次のとおりです。

```
https://api.openweathermap.org/data/3.0/onecall?lat={lat}&lon={lon}&exclude={part}&appid={API key}
```

このAPIの`/data/3.0/onecall?lat={lat}&lon={lon}&exclude={part}&appid={API key}`の部分をGETメソッドで取得するリソースとして変数`Query$`に格納します。
APIのパラメータを表に示します。

|パラメータ|必須か|内容|
|:-:|:-:|-|
|lat|必須|緯度（-90〜90）|
|lon|必須|軽度（-180〜180）|
|appid|必須|取得したAPIキー|
|exclude|オプション|気象データからの除外項目指定。current（現在）,minutely（分単位）,hourly（時間単位）,daily（日単位）,alerts（警告）|
|units|オプション|測定単位。stanard（温度：ケルビン、風速：m/s）,metric（温度：摂氏、風速：m/s）,imperial（温度：華氏、風速：マイル/時）。デフォルトはstandard|
|lang|オプション|出力（descriptionフィールド）の言語指定（日本語はja）|

13〜15行目でOpenWeatherMapに接続し、GETメソッドを使ってAPIを呼び出し、その応答をbuff%()に格納し、接続を閉じます。OpenWeatherMapからの応答はJSON形式の文字列データで、下記に取得したデータを読みやすい形式に整形したリストを表示します。


```JSON
{
  "lat": 35.7,
  "lon": 139.77,
  "timezone": "Asia/Tokyo",
  "timezone_offset": 32400,
  "current": {
    "dt": 1749366544,
    "sunrise": 1749324297,
    "sunset": 1749376508,
    "temp": 299.28,
    "feels_like": 299.28,
    "pressure": 1009,
    "humidity": 61,
    "dew_point": 291.17,
    "uvi": 1.31,
    "clouds": 0,
    "visibility": 10000,
    "wind_speed": 4.63,
    "wind_deg": 190,
    "weather": [
      {
        "id": 500,
        "main": "Rain",
        "description": "light rain",
        "icon": "10d"
      }
    ],
    "rain": {
      "1h": 0.53
    }
  }
}
```

### JSON形式データから値の取り出し

17〜25行目でJSON$関数を使って上記のJSON形式のデータから下記のデータを取り出しています。

JSON$関数のパラメータは長い文字列変数（buff%()と取り出すデータのキーの文字列です。

```
JSON$(buff%(), key$)
```

|キー|内容|変数|
|:-|-|:-|
|timezone_offset|タイムゾーンのオフセットの秒数|off|
|dt|現在の日時（UTC）|dt$|
|current.sunrise|日出時間（UTC）|sr$|
|current.sunset|日没時間（UTC）|ss$|
|current.weather[0].main|現在の天気|main$|
|current.weather[0].description|天気の説明|desc$|
|current.temp|温度（℃）|temp|
|current.pressure|気圧（hPa）|pres|
|current.humidity|湿度（%）|humi|

27〜36行目で取得したデータを表示しています。

### 実行結果

プログラムは秋葉原付近の現在の天気情報を取得し、現在日時、日出、日没、天気、気温、気圧、湿度、説明を表示しています。

```
> RUN
tcp address 15.235.226.167
Connected

=== Akihabara weather ===
Date   :08-06-2025 21:45:05
Sunrise:08-06-2025 04:24:57
Sunset :08-06-2025 18:55:08
main   :Clear
Temp   : 23.5C
Press  : 1011hPa
Humid  : 75%
Desc   :clear sky
>
```

# 最後に

最初は気象庁の防災データを取得しようとしましたがHTTPで気象庁のデータを取得しようとすると301エラーとなり、HTTPでのデータ取得ができません。これはMMBasicのTCPクライアントはHTTP（80番ポート）接続のみでHTTPS（443番ポート）接続をサポートしていないのが原因でした。TLS接続をサポートしたファームウェアの登場を期待したいところです。


# 参考サイト

- [PicoMite User Manual](https://geoffg.net/Downloads/picomite/PicoMite_User_Manual.pdf)