明日の天気を教えてくれるカスタムAlexaスキル

#はじめに
引越しを機に我が家にAlexaを導入しました。
定型アクションに含まれる「天気予報を読む」というアクションを利用し、毎朝9:00に今日の天気を教えてくれるよう設定しています。
今日の天気だけじゃなく、明日の天気を教えてくれる機能があったらいいな〜と思って開発しました。

#現状
アレクサに「明日の天気を教えて」と言えば天気、最高気温、最低気温を回答してもらえるのですが、こちらからの発言なしに定時実行することはできなさそうです。
定型アクションではスキルを発動することができるため、明日の天気を教えてくれるスキルを開発すれば設定した時刻に明日の天気を教えてくれるようになるはず！

※最終的に分かったのですが、現状のAlexaスキル開発では未公開のスキル（ステータスが開発中のスキル）は定型アクションで利用できないようです。[定型アクションが設定できない](https://forums.developer.amazon.com/questions/225112/定型アクションか設定てきない.html)

[このページ](https://alexa.uservoice.com/forums/906892-alexa-skills-developer-voice-and-vote/suggestions/39226321-allow-development-skills-to-be-used-in-routines)から投票できます。投票数が多ければ追加されるかもしれません！

__スキルを公開する予定はないため、この記事では明日の天気を教えてくれる関数の作成方法のみを残しています。__

#前提条件
[初めてのスキル開発(amazon alexa公式)](https://developer.amazon.com/ja-JP/docs/alexa/alexa-skills-kit-sdk-for-nodejs/develop-your-first-skill.html)

#天気予報APIで天気情報を取得する
まずは明日の天気を取得するために利用する天気予報APIを決めます。今回はOpenWeatherMapAPIを利用しました。無料会員で登録します。
https://openweathermap.org

未来の天気を知りたいため、``forecast``を利用します。
無料会員では３時間ごと５日分の天気しか取得できません。


>http://api.openweathermap.org/data/2.5/forecast


下記のように``郵便番号,国コード（jp）``を設定することで、地域を指定することができます。
緯度と経度で地域を指定する方法もありますが、今回は郵便番号で指定します。


>zip=123-4567,jp


何も設定しない場合、気温は華氏で表記されます。今回は摂氏で表記して欲しいため下記の文言を入れておきます。


>units=metric


``appid``にOpenWeatherMapAPIに登録した際に付与された``APPID``を入力します。


>appid="APP ID"


よって今回は以下のURLを利用して、明日の天気情報を取得しようと思います。


>http://api.openweathermap.org/data/2.5/forecast?zip={郵便番号},jp&units=metric&appid={APPID}


試しにWebブラウザ上でURLを叩いてみると、気温や気圧などの情報が取得できました。
この時に``dt``や``dt_txt``に入っている時刻は日本時間ではないため、後ほど変換する必要があります。

```json
	cod:200
 	message:0
 	cnt:40
 	list:[
     		0:{
         		dt:1607342400
         		main:{
             		temp:12.95
             		feels_like:10.44
             		temp_min:12.95
             		temp_max:13.12
             		pressure:1018
             		sea_level:1018
             		grnd_level:1017
             		humidity:55
             		temp_kf:-0.17
         		}
         		weather:[
             		0:{
                 		id:801
                 		main:Clouds
                 		description:few clouds
                 		icon:02n
             		}
         		]
         		clouds:{
             		all:24
         		}
         		wind:{
             		speed:1.74
             		deg:348
         		}
         		visibility:10000
         		pop:0
         		sys:{
             		pod:n
         		}
         		dt_txt:2020-12-07 12:00:00
     		}
     		1:{
     		"~~~~~~~以下略~~~~~~~"
```




#取得した情報を使って
３時間ごと５日分の天気を取得するURLは分かったため、次は上記の情報から必要な天気の情報を取得していきます。
明日の天気を知るにあたって最低限、最高気温と最低気温、雨の有無がわかれば良いと思ったので今回はそれを目標にします。


まずは、fetchでURLから情報を取得しresponseに結果を入力します。
（最初はfetchにawaitを入れておらずエラーが出ました。）

```javascript

const fetchResponse = await fetch('http://api.openweathermap.org/data/2.5/forecast?zip="郵便番号",jp&units=metric&appid="APPID"');
const response = await fetchResponse.json();
```

つぎに明日の日付を取得します。
今回利用している環境では、現在の日時がUTC（協定世界時）で取得されるため、JST（日本標準時）に直す必要があります。


```javascript
    const jstOffset = 9 * 60;
    const now = new Date();
    const offset = now.getTimezoneOffset() + jstOffset;
    //明日の日付を取得するため、取得した時間に24(時間)*60(分)*60(秒)*1000を追加する
    now.setTime(now.getTime() + offset * 60 * 1000 + 24*60*60*1000);
    const tomorrowDate = now.toLocaleDateString('ja-JP').replace(/\//g, '-');
```

これによって``tomorrowDate``で明日の日付を取得することができました。
今度は天気予報APIから取得した情報から明日の天気を抜き出します。


```javascript
    let weather = [];
    
    //天気予報APIで取得した時刻（response.list[i].dt）を、日本時間に変更しitemDateに格納する
    for(let i = 0; i < response.list.length; i++){  
        const itemDtTxt = new Date(response.list[i].dt * 1000);
        const itemOffset = itemDtTxt.getTimezoneOffset() + jstOffset;
        itemDtTxt.setTime(itemDtTxt.getTime() + itemOffset * 60 * 1000);
        const itemDate = itemDtTxt.toLocaleDateString('ja-JP').replace(/\//g, '-');
        
        //itemDateが明日であれば、気温と天気の情報を取得しweatherに格納する
        if (itemDate === tomorrowDate){
            const item = {
                temp: response.list[i].main.temp,
                weather: response.list[i].weather[0].main
            };
            weather.push(item);
        }
    }
    
    //最高気温と最低気温、天気にRainが含まれているかを判定する
    const maxTemp = Math.max.apply(null, weather.map(_ => _.temp));
    const minTemp = Math.min.apply(null, weather.map(_ => _.temp));
    const rain = weather.some(_ => _.weather === "Rain");
    
    let speakText = "";
    
    speakText = "明日の最高気温は、" + maxTemp +"度、最低気温は、" + minTemp +"度です。";
    if (rain === false){
        speakText +="雨の予報はありません。";
    }else{
        speakText +="雨の予報があります。出かける時は傘を忘れずに。";
    }
    return speakText;
```

以上で、明日の最高気温、最低気温と雨が降るかどうかを教えてくれるようになりました。

#おわりに
今後やりたいこと

+ 雨以外の天気も教えてほしい
+ 日中の天気に絞って教えて欲しい
+ 雨が降る時間を教えてくれるようにしたい