Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9164

Google Calendarから予定を取得してESP32のLCDに表示する

$
0
0
前回の投稿 Google Photosからランダムな1枚の画像を取得できるようにする にて、Google Photosに登録しておいた画像をランダムに表示されるフォトフレームを作りました。 今回は、少し拡張して、Google Calendarに登録しておいたイベントも合わせて表示するようにします。 Google PhotosのAPIの呼び出しの時には、REST呼び出しでしたが、Google CalendarはNode.js用のライブラリが用意されているのでそれを使います。 ですが、Googleアカウントログイン部分は、REST呼び出しのものを流用したいので、REST呼び出しで認証したGoogleアカウントの認証結果(アクセストークン)をGoogle Calendarライブラリに引き継ぐようにします。 ソースコードもろもろは、以下に上書きしています。 poruruba/GooglePhotosGallery Google Calendar APIを利用できるようにする Google Cloud Platformのコンソールから、Google Calendar APIを使えるように有効化します。 GCP:APIとライブラリ https://console.cloud.google.com/apis/library 検索のところに、Calendarと入力すると、Google Calendar APIが出てきますので、選択してEnableにします。 認証結果をGoogleライブラリに引き継ぐ 以前の投稿 GoogleAPIライブラリを使わずにGoogleアカウントでログインできるようにする で認証結果(アクセストークン・IDトークン・リフレッシュトークン)をファイル出力していました。 それを使います。 node.js/api/controllers/googlecalendar-api/index.js function get_calendar(token){ const oAuth2Client = new google.auth.OAuth2(CLIENT_ID); oAuth2Client.setCredentials(token); return google.calendar({ version: 'v3', auth: oAuth2Client }); } 上記のtokenには、access_tokenがあれば大丈夫です。 ただし、このトークンを取得したときのGoogleアカウント認証時のScopeに、以下が含まれている必要があります。 https://www.googleapis.com/auth/calendar.readonly CLIENT_IDは、認証時に利用していたクライアントIDを指定してください。 あとは、以下のように呼び出すだけです。 node.js/api/controllers/googlecalendar-api/index.js var result = await new Promise((resolve, reject) => { calendar.events.list(params, (err, res) => { if (err) return reject(err); resolve(res); }); }); paramsには、以下で示されるパラメータを指定します。 取得結果には、nextPageTokenが含まれている場合があります。その場合には、すべてのイベントを取得しきれていないので、pageTokenにnextPageTokenを指定して、続けて呼び出す必要があります。 結局、こんな感じです。 node.js/api/controllers/googlecalendar-api/index.js async function get_event_list(calendar, date){ var startTime = date.toISOString(); var endDate = new Date(date.getTime()); endDate.setHours(23); endDate.setMinutes(59); endDate.setSeconds(59); endDate.setMilliseconds(999); var endTime = endDate.toISOString(); var params = { calendarId: 'primary', timeMin: startTime, timeMax: endTime, singleEvents: true, orderBy: 'startTime', }; var result = await new Promise((resolve, reject) => { calendar.events.list(params, (err, res) => { if (err) return reject(err); resolve(res); }); }); var items = result.data.items || []; while (result.nextPageToken) { params.pageToken = result.nextPageToken; result = await new Promise((resolve, reject) => { calendar.events.list(params, (err, res) => { if (err) return reject(err); resolve(res); }); }); items = items.concat(result.data.items); } var list = []; for (const item of items) { var term = convert_date(item.start, item.end); list.push({ summary: item.summary, term: term }); } return list; } イベントの変更通知を受け取る Google Calendarのイベントは、他のGoogleアプリから編集されるため、イベントリスト取得後にも変更されている場合があります。 そこで、変更されたことをNotificationとして通知してくれる機能があります。以下のように登録します。 (参考) https://developers.google.com/calendar/api/guides/push?hl=ja node.js/api/controllers/googlecalendar-api/index.js const CALENDAR_WEBHOOK_URL = 'https://【Node.jsサーバのURL】/googlecalendar-webhooks'; ・・・ var params = { id: uuidv4(), type: "web_hook", address: CALENDAR_WEBHOOK_URL }; const decoded = jwt_decode(token.id_token); var result = await do_post_with_token('https://www.googleapis.com/calendar/v3/calendars/' + decoded.email + '/events/watch', params, token.access_token); 登録には、emailアドレスが必要であるため、Googleアカウント認証時のScopeに、以下を含めておく必要があります。 https://www.googleapis.com/auth/userinfo.email idには、毎回異なる値を指定します。今回はuuidにしました。 addressには、通知を受けたいWebAPIのURLを指定します。 戻り値はJsonです。戻り値のresultには、idとresourceIdが含まれており、登録解除時に必要ですので、ファイルに保存しておきます。 解除は以下の通りです。 node.js/api/controllers/googlecalendar-api/index.js var params = { id: json.notification.id, resourceId: json.notification.resourceId }; var result = await do_post_text_with_token('https://www.googleapis.com/calendar/v3/channels/stop', params, token.access_token); console.log(result); 戻り値は、Jsonではなくテキストです。 以下は、通知を受けた時の処理の例です。 node.js/api/controllers/googlecalendar-api/index.js case '/googlecalendar-webhooks': { console.log(event); if( event.headers['x-goog-resource-state'] == 'exists'){ var token = await read_token(); const calendar = get_calendar(token); var list = await read_event_list(calendar, true); client.publish(TOPIC_CMD, "1"); } return new Response({}); } 登録したURLに届く通知は、Google Calendarのイベントの変更時以外にも、いくつかのタイミングで届きます。HTTPヘッダー「x-goog-resource-state」がexistsの時が、イベント変更時の通知です。 上記の例では、イベントリストを更新したのち、MQTTにパブリッシュして他のデバイス(今回の場合は、LCD付のESP32がSubscribeしている)に通知しています。 ついでにGoogle Tasks(toDo)のタスクリストも取得してみます こちらも、Googleライブラリ化されていますので、それを使います。 毎度の通り、以下で、Tasks APIを有効にします。 GCP:APIとライブラリ https://console.cloud.google.com/apis/library また、Googleアカウント認証時には、以下をScopeに含めます。 https://www.googleapis.com/auth/tasks.readonly あとは、以下のようにtasksのインスタンスを生成して、呼び出すだけです。 node.js/api/controllers/googlecalendar-api/index.js case '/googletasks-list': { var token = await read_token(); const tasks = get_tasks(token); var list = await get_tasklist_list(tasks); console.log(list); for( var i = 0 ; i < list.length ; i++ ){ list[i].list = await get_task_list(tasks, list[i].id); } return new Response({ list: list }); } ・・・ function get_tasks(token) { const oAuth2Client = new google.auth.OAuth2(CLIENT_ID); oAuth2Client.setCredentials(token); return google.tasks({ version: 'v1', auth: oAuth2Client }); } async function get_tasklist_list(tasks){ var params = {}; var result = await new Promise((resolve, reject) => { tasks.tasklists.list(params, (err, res) => { if (err) return reject(err); resolve(res); }); }); var items = result.data.items; while (result.nextPageToken) { params.pageToken = result.nextPageToken; result = await new Promise((resolve, reject) => { tasks.tasklists.list(params, (err, res) => { if (err) return reject(err); resolve(res); }); }); items = items.concat(result.data.items); } var list = []; for (const item of items) { list.push({ title: item.title, id: item.id }); } return list; } async function get_task_list(tasks, id) { var params = { tasklist: id }; var result = await new Promise((resolve, reject) => { tasks.tasks.list(params, (err, res) => { if (err) return reject(err); resolve(res); }); }); var items = result.data.items || []; while (result.nextPageToken) { params.pageToken = result.nextPageToken; result = await new Promise((resolve, reject) => { tasks.tasks.list(params, (err, res) => { if (err) return reject(err); resolve(res); }); }); items = items.concat(result.data.items); } var list = []; for (const item of items) { list.push({ title: item.title, notes: item.notes, id: item.id, parent: item.parent || null, due: item.due ? new Date(item.due).getTime() : null }); } return list; } 流れとしては、タスクリストというタスクを束ねたもののリストを取得したのち、タスクリストごとに含まれるタスクのリストを取得する、という流れです。 流れさえわかれば、おおよそ理解できると思いますので、詳しくは、GitHubのソースコード参照してください。 poruruba/GooglePhotosGallery  https://github.com/poruruba/GooglePhotosGallery LCD付ESP32の実装 Google Calendarのイベントの取得は、以下のURLに対してJSON-POST呼び出ししているだけです。 https://【Node.jsサーバのホスト名】/googlephotos-image 一方で、イベントの変更検知するために、MQTTでトピック「calendar/notify」でSubscribeして待ち受けています。通知を受けたら、Google Calendarイベントの再取得を含むLCD再描画処理を実行しています。 詳しくは、GitHubのソースコード参照してください。 poruruba/GooglePhotosGallery  https://github.com/poruruba/GooglePhotosGallery 以上

Viewing all articles
Browse latest Browse all 9164

Trending Articles