見出し画像

Googleカレンダーの予定をM5Paperに表示する

この欲望は以下の記事から連続しています。

いや絶対需要あるでしょ…Googleカレンダーの内容を自動更新表示してくれる壁掛け電子ペーパー端末は…と思って調べたらMagic Caledarというプロジェクトが過去にあったが滅んだらしい。超残念。

しかしですねえ、今の私はRemember the MilkのタスクくらいならM5Paperに表示できるようになったんです。つまりGoogleカレンダーのAPIとかあればカレンダーもできるはずです!

Google Calendar APIを調べる

というわけでGoogle Calendar APIを調べました。

Google CalendarにはCalendar APIもあるしCalDAVで取得することも出来そうです。ただし、両方ともOAuth 2.0で認証が必要です。
ここで躓きました。OAuth 2.0の認証の流れ自体は以前やったRemember the Milkと同じような感じですが…アクセストークンを受け取るのに認証画面からリダイレクトしてもらう必要がありそうです。

これがM5Paperにはちょっと問題です。M5Paperで認証画面へのURLを生成してQRコードでスマホに渡すことは出来そうです。しかしスマホ上の認証画面からのリダイレクトをM5Paper側に渡すのは…どうやればいいんだ!?

実はそういうブラウザが無いようなデバイス用のフローもあります。

でもねえ、下までスクロールしていくと分かるんですが、このタイプの認証で使えるのはGoogle DriveやYouTubeのAPIだけらしいんです。カレンダーは無い!

iCalendarファイルのURLを使う

そんな感じでAPIは暗礁に乗り上げましたが、他にGoogleカレンダーの内容を取得する方法はないのでしょうか?実はあります。
そう、カレンダーの設定画面にあるこれ。

このURLからは、指定したGoogleカレンダーの全予定がiCalendar形式でダウンロードできます。どうせ個人用なので、このURLをmicroSDに入れてM5Paperに教えれば、定期的にダウンロード/パースして予定を表示できるはずです。

この方式の問題点は、期間を指定してその間の予定を取得するようなことができず、全予定がダウンロードされてくることです。しかしまあ仕事用のカレンダーでもなければサイズ的にはなんとか行けるんじゃないかと思って…こちらを採用することにしました。

iCalendarファイルをパースする

iCalendar形式はカレンダーアプリ間で予定を受け渡すのによく使うやつです。

BEGIN:VEVENTからEND:VEVENTの間が一つの予定で、DTSTART:が開始時間、DTEND:が終了時間、SUMMARY:が予定の名前、大体そんな感じです。

実際にGoogleカレンダーからダウンロードしてきたファイルを見てみると、DTSTARTの形式は終日の予定と時刻指定の予定で別れており、終日の予定では
DTSTART;VALUE=DATE:20240226
のような日付だけの形式、時刻指定では
DTSTART:20240210T010000Z
のようにGMTで時刻まで入っているようです。

面倒なのが繰り返しの予定で、
DTSTART;VALUE=DATE:20240124
DTEND;VALUE=DATE:20240125

RRULE:FREQ=WEEKLY;BYDAY=WE
こんな形でRRULE:に繰り返しルールが書いてあります。上記の場合1/24から開始、毎週水曜日の予定です。

このように月毎、週毎、第X週、曜日など様々なパターンがある繰り返しを全て網羅し、かつ表示したい月に該当の予定があるかどうか判別していくのは大変そうです。なので今回は繰り返し予定の表示を見送りました。

現在の日付と時刻を取得する

さて、実際に今月のカレンダーを表示するには当然現在の日付を取得する必要があります。M5PaperにはRTCが内蔵されており、かつNTPを使用して時刻合わせを行う機能が標準でついているので、簡単です。

// Setup NTP
float timezone = 9.0f
configTime(60 * 60 * timezone, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");

configTimeでNTPサーバーを設定して、

struct tm timeinfo;
if (!getLocalTime(&timeinfo))
{
  Serial.println("Waiting getLocalTime");
  delay(500);
  return;
}

int year = timeinfo.tm_year + 1900;
int month = timeinfo.tm_mon + 1;
int day = timeinfo.tm_mday;

getLocalTimeでtm構造体に日付/時刻を取得します。

カレンダーを描画する

現在の月が分かったところで、見慣れた感じの曜日毎に日付を並べるカレンダーを表示するには何が必要か?まず、その月の1日が何曜日かの判別です。これはツェラーの公式を使ってみます。

int dayOfWeek(int year, int month, int day)
{
    if (month < 3)
    {
        year--;
        month += 12;
    }
    return (year + year / 4 - year / 100 + year / 400 + (13 * month + 8) / 5 + day) % 7;
}

グレゴリオ暦の範囲ならこんな感じで0〜6の数値で曜日が取得できるはずです。あとは折り返しながら日付を並べていくだけです。

その月が何日まであるか?も必要ですが、これはもう配列にしておいて2月だけ閏年判定で日数を変えれば…いいはずです。

int numberOfDaysInMonth(int year, int month)
{
    int numberOfDaysInMonthArray[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
    {
        numberOfDaysInMonthArray[1] = 29;
    }
    return numberOfDaysInMonthArray[month - 1];
}

こういうのどっかのライブラリに入ってるはずですよね。今回も楽しく車輪を再発明しました。

できたもの

これでなんとか動くものができました。例によってSDカードに必要情報を入れておく方式です。iCalendarファイルのURLは複数指定できるので、個人の予定と日本の祝日を両方表示させることができます。

埃がひどいな。恥ずかしくないの?
ちなみに背後の黒い板は100円ショップで見つけたスチールスタンドです。M5Paperをちょうど三枚貼り付けることができます

未来へ…

まあ動くは動くんですけど、このままだとGoogleカレンダーに溜まった過去の予定でiCalendarファイルが膨れ上がり、M5Paperが耐えられなくなる気がするんですよね…ちゃんとAPIを使いたい。予定の開始時刻だけ表示して期間を表示してないからそれもつけたい。繰り返し予定に対応したい。

そして何よりもっと画面が大きくなるべき。究極的には10インチくらいのうっすい電子ペーパーを壁にかけてね…そこにGoogleカレンダーの内容が自動更新表示されるわけですよ…もうそういうデバイスをGoogleが出せや…

この記事が気に入ったらサポートをしてみませんか?