AWS Cloud9が東京リージョンでサポートされました
今朝方、AWS Cloud9 でEC2タイプのUbuntu環境を検証していたのですが。。
おもむろにリージョンを選択すると、東京リージョンが選べるではないですか。
今までは東京リージョンのサービスを使う場合、シンガポール等で環境を構築し、そこを経由して東京リージョンへアクセスしていました。
東京リージョンがサポートされたことにより、よりレイテンシが小さくなります。
これでサポートされるリージョンは6つ。
* 米国東部 (バージニア北部)
* 米国東部 (オハイオ)
* 米国西部 (オレゴン)
* アジアパシフィック (シンガポール)
* アジアパシフィック (東京)
* EU (アイルランド)
少し前に、EC2環境でUbuntuを選択出来るようにもなりました。
今年は、AWS Cloud9 が、そしてブラウザ開発が熱くなりそうですね。
詳細は後ほどまとめようと思います。
4/6 追記
公式に発表されました。
Raspberry Pi タクトスイッチでポチッとな 〜LEDをON/OFF〜
前回は、最初の一歩として Lチカ を作成しました。
今回は、入力に タクトスイッチ を使って、LEDのON/OFFにチャレンジします。
- スイッチを押したらLEDが点灯 (ON)
- スイッチを離したらLEDが消灯 (OFF)
用意したもの
- ブレッドボード
- タクトスイッチ
- ジャンパワイヤ (オス-メス) 4本
- ジャンパワイヤ (U字型単線タイプ) 2本
- LED 1本
- 抵抗 (200Ω)
タクトスイッチは、押すと「カチッ」というクリック感があり、それが心地よく、ついつい何度も押してしまいます。
カバンに入れて持ち歩き、暇な時に連打するといいかもしれません。
プルダウン抵抗とプルアップ抵抗
タクトスイッチを使う場合、入力値が不定にならないように、プルダウン抵抗かプルアップ抵抗を用いる必要があります。
- プルダウン抵抗
- スイッチOFF: LOW (0V)
- スイッチON: HIGH (3.3V)
- プルアップ抵抗
- スイッチOFF: HIGH (3.3V)
- スイッチON: LOW (0V)
ブレッドボード上に大きめの抵抗を配置することで実現できますが、Raspberry Pi の場合、本体側にプルダウン抵抗・プルアップ抵抗が用意されています。
今回は、Raspberry Pi 本体のプルダウン抵抗を利用します。 その分、回路図がシンプルになります。
回路図
3.3V と GPIO24 の間にタクトスイッチを接続します。
ブレッドボード
前回より少し複雑になっています。
今回は横ラインに電源用とGND用に利用しました。
プログラム
import RPi.GPIO as GPIO import time PIN_IN = 24 PIN_OUT = 25 GPIO.setmode(GPIO.BCM) GPIO.setup(PIN_IN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(PIN_OUT, GPIO.OUT) try: while True: flag = GPIO.input(PIN_IN) == GPIO.HIGH GPIO.output(PIN_OUT, flag) time.sleep(0.01) except KeyboardInterrupt: pass finally: GPIO.cleanup()
入力設定
入力時にプルダウン抵抗を利用するため、setup関数にキーワード引数 pull_up_down
を指定します。
ループ内
GPIO24 の入力値に電流が流れてるかどうかをフラグにして、GPIO25 の出力を制御します。
CTRL+C
で処理を中断できます。
ラズパイで確認
スイッチがOFFの状態です。LEDはOFFの状態です。
ポチッとな!!
LEDが光りました。 まるで天津飯の太陽拳のようです。
参考
- Raspberry Pi公式
- カラー図解-Raspberry-Piで学ぶ電子工作-作って動かしてしくみがわかる
- 5章 タクトスイッチによる入力
Raspberry Pi にチャレンジ 〜LEDでチカチカ〜
今更ですが、Raspberry Pi (ラズパイ) を購入しました。
前々から興味はあったのですが、ラズパイを使って何かをする目的がないので、購入にためらっていました。
昔、ちょっとだけ触って、そのままフェードアウトしていった、玄人志向のKURO-BOXを思い出します。
しかし、友達にラズパイの勉強会に誘われたのを機に購入。
考え方を変えて
まず触ってみる。触ってから目的を考える。
ことにしました。
目的が出来るまで、ちょっとずつ触って、チャレンジしてみようかと思います。
購入したもの
- Raspberry Pi 3 Model B+ スターターセット BASIC
- OSOYOO(オソヨー) Raspberry Pi 学ぶ電子工作キット
- シリコンパワー microSD 32GB
- RedCloud カードリーダー
- ジャンパワイヤ (オス-メス)
環境構築
- Raspbian OSをインストール
- Python3環境の構築
- SSH接続
SDカードは相性があるようですが、シリコンパワーのSDカードでも普通にいけました。
Lチカって
まずは、最初のステップとして Lチカ
にチャレンジ。
Lチカとは、LEDをプログラムで制御して点滅させる、シンプルな構造。
LEDをチカチカさせるのでLチカ
。ローソンのはLチキ
。
最初にトライするには、うってつけです。
用意したもの
- ブレッドボード
- ジャンパワイヤ (オス-メス) 2本
- LED 1本
- 抵抗 (200Ω)
抵抗は300前後であれば問題ありません。
GPIOポート
ラズパイの GPIO
から電源を取ります。
GPIOとは汎用入出力インターフェイスのこと。General-Purpose Input/Output の略。
ラズパイの場合、40本のピンがあり、図の左下から順に
2
4
6
8
… 38
40
1
3
5
7
… 37
39
と、ピン番号が割り当てられています。
各GPIOには、用途が決まっています。
例えば、ピン番号 1
は 3V の電源が取れます。
ずっと光らせるだけであれば、このピン番号 1
にジャンパワイヤを接続して電源を取得するだけです。
ですが、LEDをチカチカさせたい場合は、プログラム上からON/OFFを制御します。 このような場合は、黄色い丸の GPIOから取得します。
用途 | ピン番号 | ピン番号 | 用途 |
---|---|---|---|
3V | (1) | (2) | 5V |
GPIO2 | (3) | (4) | 5V |
GPIO3 | (5) | (6) | GND |
GPIO4 | (7) | (8) | GPIO14 |
GND | (9) | (10) | GPIO15 |
GPIO17 | (11) | (12) | GPIO18 |
GPIO27 | (13) | (14) | GND |
GPIO22 | (15) | (16) | GPIO23 |
3V | (17) | (18) | GPIO24 |
GPIO10 | (19) | (20) | GND |
GPIO9 | (21) | (22) | GPIO25 |
GPIO11 | (23) | (24) | GPIO8 |
GND | (25) | (26) | GPIO7 |
GPIO0 | (27) | (28) | GPIO1 |
GPIO5 | (29) | (30) | GND |
GPIO6 | (31) | (32) | GPIO12 |
GPIO13 | (33) | (34) | GND |
GPIO19 | (35) | (36) | GPIO16 |
GPIO26 | (37) | (38) | GPIO20 |
GND | (39) | (40) | GPIO21 |
Lチカ
fritzing を利用して回路図を作成します。
今回は GPIO24
から電源をとり、LEDの間に抵抗を置きます。
回路図からブレッドボード図を微調整して、イメージを抑えます。
プログラム
PythonのGPIOモジュールを利用して、1秒ごとに点灯、消灯を10回繰り返すプログラムを作成します。
import RPi.GPIO as GPIO import time PIN = 24 GPIO.setmode(GPIO.BCM) GPIO.setup(PIN, GPIO.OUT) try: for i in range(10): GPIO.output(PIN, GPIO.HIGH) time.sleep(1.0) GPIO.output(PIN, GPIO.LOW) time.sleep(1.0) except KeyboardInterrupt: pass finally: GPIO.cleanup()
GPIOモード
ピン番号で指定する BOARD
と、GPIOの数字で指定する BCM
があります。
今回は BCM
を利用します。
GPIO24
を出力として利用するため、セットアップでは 24
を指定します。
もし、BOARD
であれば、18
を指定します。
点灯・消灯
GPIO.output メソッドの第二引数で電流のON/OFFを設定します。
- GPIO.HIGH (True)
- GPIO.LOW (False)
ラズパイで確認
ブレッドボード図をもとに、実際に接続し、SSH経由でプログラムを実行します。
無事、LEDをチカチカすることができました!
まとめ
大学時代に、半田ごてを使って回路を作ったはずなのですが、遠い昔のことで、すっかり忘れておりました。
楽しかった記憶もありません。
しかし、実際にLEDが点灯したときは、純粋に感動し、楽しいと感じました。 今回はLチカですが、次のステップを試してみたいと思います。
ラズパイを始めるにあたり、カラー図解-Raspberry-Piで学ぶ電子工作-作って動かしてしくみがわかる が、大変わかりやすく、とても参考になりました。
おすすめの本です。
参考
- Raspberry Pi公式
- カラー図解-Raspberry-Piで学ぶ電子工作-作って動かしてしくみがわかる
- 4章 プログラミングによるLEDの点滅
2019年はチャレンジの年 〜筑波連山縦走〜
新年明けましておめでとうございます。
俺事(私事)ですが、昨年は、やりたい事のために、意を決して転職活動をしました。
縁あって、2月から新しい職場、環境になります。
大きく環境が変わるため、早く仕事に慣れて、色々とチャレンジをしたい。
今から楽しみです。
今月はすべて有給休暇を取れたので、勉強や体力作り、リフレッシュ等に充てたいと思います。
『一年の計は元旦にあり』
ということで、さっそく元旦から体力作りにチャレンジをしました!
『筑波連山縦走』
水戸線の岩瀬駅から御嶽山に登り、尾根伝いに筑波山へ目指します。前からチャレンジしたかったことの1つです。
筑波連山縦走
4時に家を出発。
元旦なのに、家にいなくて家族に申し訳ない。。。お土産買って帰るから許してね。
岩瀬駅
暗い、誰もいない、そして寒い。。。 トイレの水が凍ってました。
ここから筑波山へ目指します。
御嶽山登山口
登山口に来ました。まだ暗いです。
看板右上に筑波山が。
ヘッドライトをしながら登って行きます。
御嶽山 (230m)
おんたけさん
と読みます。
木曽の御嶽と関係があるのでしょうか。
しかし、こちらの御嶽はあっけなく山頂に到着です。
空が少しずつ明るくなってきました。
ここから尾根伝いにひたすら歩きます。
雨引山 (409m)
本日のベストショット。
右側の山が筑波山。遠いなぁ。
燕山 (701m)
苦労して登った割には展望なし。
途中、茂みの方から大きなガサガサ音がして、心臓バクバク。
何のフレンズだったんだろう。。
加波山 (709m)
加波山神社で初めて人に遭遇。 少しホッとしました。
ここで初詣。
神々しい道を通って頂上付近。たばこ神社という、珍しい神社がありました。
加波山に到着。
ここまでは順調でした。ここまでは。
桜観音
丸山を経て一本杉峠へ行く予定でしたが、道を間違えて、加波山登山口の桜観音へ。
完全に別方向で、しかも入口まで降りてしまい、へこみました。
_| ̄|○
リタイアするか続行するか?
悩んだ末に、続行を決意。
ただし、同じ道は戻りたくないので、林道経由で一本杉峠へ目指します。
この辺りは採石場のようです。
一本杉峠 (430m)
何とか一本杉峠に到着。 しかし、かなりの体力と時間をロスしてしまいました。
今は冬なので、16:30には暗くなってしまいます。
- 筑波山からはケーブルカーで降りる
- ガスを使った昼食を諦めて、携帯食とお菓子でさっと済ます
この作戦でロスした時間を補うことにしました。
足尾山 (627m)
足尾山 山頂から筑波山方面を望む。
今日中にあそこにたどり着けるのか。。
きのこ山 (528m)
展望なし。今日の山の中で1番期待はずれ。
とっとと先を進みます。
上曽峠〜裏筑波登山口
ひたすら林道をず〜っと歩く。長い。
コンクリなので、かえって足が痛い。
裏筑波登山口〜筑波高原キャンプ場
倒木だらけで進みにくい道。
あまり使われてないルートなのかな。
倒木「ヒャッハー!ここは通さねえぜ」
筑波高原キャンプ場に到着。
疲労でかなり足にきてるけど、後は気合で登るだけ。
筑波山 (877m)
やっと到着!!
疲れたけど、充実感があって、やってよかったです。
でも来年の元旦は家族と過ごします(笑)
時間がないので女体山だけ登って、ケーブルカーで降りました。ギリギリ、つくば駅への最終バスに間に合いました。 お腹減った〜。
さいごに
今回の工程とタイムです。
工程 | 標高 | タイム |
---|---|---|
岩瀬駅 | 6:00 | |
御嶽山登山口 | 6:15 | |
御嶽山 | 230m | 6:25 |
雨引山 | 409m | 7:10 |
燕山 | 701m | 8:45 |
加波山 | 709m | 9:15 |
丸山 | 528m | ※道を間違える |
一本杉峠 | 430m | 11:05 |
足尾山 | 627m | 11:35 |
きのこ山 | 528m | 12:20 |
上曽峠 | 320m | 12:50 |
裏筑波登山口 | 13:38 | |
筑波高原キャンプ場 | 14:35 | |
筑波山 | 877m | 15:40 |
1日でこんなに歩いたのは久しぶりです。
ZappaでKappaを表示してみた
このエントリは、AWS #2 Advent Calendar 2018 の23日目です。
Zappaとは
Amazon API Gateway と AWS Lambda でWebアプリケーションを簡単に構築、デプロイできるPython用のサーバーレスフレームワークです。
FlaskやDjango、Bottleなど WSGI に対応したフレームワーク上で作成したアプリケーションを、Lambdaで利用できます。
Zappa.io や Zappatista! が 実際に Zappa を使ってWebサイトを構築しています。
Kappaとは
頭にお皿を乗せた緑色の妖怪。キュウリが大好き。
一部の河童は地下で強制的に寿司を…略
環境構築
前準備として、ローカル環境に以下の環境を構築します。
- AWS Credential 設定
- pyenv + pyenv-virtualenv 環境
- Python 3.6
Flask
ミニマムなFlaskアプリを準備します。
$ pip install flask
画面に Hello World
を表示するだけのサンプルです。
hello.py
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'Hello World' # We only need this for local development. if __name__ == '__main__': app.run()
ローカル上で確認をします。
$ python hello.py
ブラウザ上で http://127.0.0.1:5000/
にアクセスして Hello World
が表示されることを確認します。
Zappa
Zappa をインストール、構築します。
$ pip install zappa $ zappa init
███████╗ █████╗ ██████╗ ██████╗ █████╗ ╚══███╔╝██╔══██╗██╔══██╗██╔══██╗██╔══██╗ ███╔╝ ███████║██████╔╝██████╔╝███████║ ███╔╝ ██╔══██║██╔═══╝ ██╔═══╝ ██╔══██║ ███████╗██║ ██║██║ ██║ ██║ ██║ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ Welcome to Zappa! Zappa is a system for running server-less Python web applications on AWS Lambda and AWS API Gateway. This `init` command will help you create and configure your new Zappa deployment. Let's get started! Your Zappa configuration can support multiple production stages, like 'dev', 'staging', and 'production'.
今回は全てデフォルト、Enterキー
を押します。
- 環境
- What do you want to call this environment (default 'dev'): {Enterキー}
- デプロイ時に利用するS3バケット名
- What do you want to call your bucket? (default 'zappa-*********'): {Enterキー}
- 関数名
- Where is your app's function? (default 'hello.hello.app'): {Enterキー}
- グローバル展開するか
- Would you like to deploy this application globally? (default 'n') [y/n/(p)rimary]: {Enterキー}
- 確認
- Does this look okay? (default 'y') [y/n]: {Enterキー}
zappa_settings.json が作成されます。
{ "dev": { "app_function": "hello.hello.app", "aws_region": "ap-northeast-1", "profile_name": "default", "project_name": "zappa-sample", "runtime": "python3.6", "s3_bucket": "zappa-*********" } }
ここまでで、ディレクトリ構造は下記のようになります。
zappa-sample/ ├── .python-version ├── hello │ └── hello.py └── zappa_settings.json
デプロイ
それではdev環境にデプロイしてみましょう。
$ zappa deploy dev
成功(Deployment complete)すると、URLが表示されるので、ブラウザで確認をします。
デプロイ簡単ですね。
更新
テンプレートの利用
Flaskアプリをテンプレートから読み込むように変更します。
templates ディレクトリに index.html を用意します。
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>sample</title> </head> <body> <p>{{ message }}</p> </body> </html>
hello.py
from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): return render_template('index.html', message="Merry Christmas!!") # We only need this for local development. if __name__ == '__main__': app.run()
ディレクトリ構造
zappa-sample/ ├── .python-version ├── hello │ ├── hello.py │ └── templates │ └── index.html └── zappa_settings.json
updateコマンドで、dev環境に更新を反映します。
$ zappa update dev
Merry Christmas!!
文字が変わったでしょうか?
静的ファイルの利用
今度は画像ファイルを表示してみます。 staticディレクトリに画像ファイルを配置します。
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>sample</title> </head> <body> <p>{{ message }}</p> <img src="{{ url_for('static', filename='youkai_kappa.png') }}"> </body> </html>
ディレクトリ構造
zappa-sample/ ├── .python-version ├── hello │ ├── hello.py │ ├── static │ │ └── youkai_kappa.png │ └── templates │ └── index.html └── zappa_settings.json
ZappaでKappaを表示できました!!
静的ファイルは、S3上に配置する方が低コストです。
Flaskの場合だと、Flask-S3 を利用すれば、簡単に実現できます。
アンデプロイ
undeploy で削除します。
$ $ zappa undeploy dev
まとめ
語呂がよかったので勢いで書きましたが、虚しくなってきたので、このへんで勘弁してください。
参考
Fitbit API から睡眠データを取得してみた
Fitbit Charge2 を装着したまま洗顔をしたら、水がかかりお亡くなりになりました。。。
ちょうど、Fitbit Charge3 が出たので購入。 今度は防水なので大丈夫でしょう!!
せっかくなので、睡眠データを取得して何かをしたいと思います。
睡眠レベル
一言に睡眠と言っても、状態によっていくつかレベルがあります。
- 覚醒状態 (wake)
- 毎晩10-30回ほど発生
- 期間が非常に短い
- レム睡眠 (rem)
- 浅い眠り、体の眠り
- 夢を見ることがある
- 心拍数が上がったり、呼吸が早くなったり
- ノンレム睡眠
- 深い眠り、脳の眠り
- 第1段階〜第4段階に分かれる
眠りの深さは
覚醒状態 -> レム睡眠 -> ノンレム睡眠 第1段階 -> ノンレム睡眠 第4段階
の順に深くなります。
Fitbitでは、ノンレム睡眠を浅い睡眠と深い睡眠の2つに分け、4つの睡眠レベルに分けています。
- 目覚めた状態 (wake)
- レム睡眠 (rem)
- 浅い睡眠 (light)
- ノンレム睡眠 第1段階
- ノンレム睡眠 第2段階
- 深い睡眠 (deep)
- ノンレム睡眠 第3段階
- ノンレム睡眠 第4段階
俺の睡眠データ
基本、早寝早起きです。たまに小学生の子供達より早く寝ます。
手っ取り早く、curlで叩いて睡眠データを取得します。
Fitbit APIをcurlで叩く方法は、下記のエントリを参照してください。
《2018-11-16》
ver1.1以前(classic) と 1.2(stages) ではフォーマットが異なるので注意です。
ver1.1の場合
curl -H "Authorization: Bearer **********.**********" https://api.fitbit.com/1.1/user/-/sleep/date/2018-11-16.json | jq . > 2018-11-16-v1.1.json
2018-11-16-v1.1.json
{ "sleep": [ { "awakeCount": 0, "awakeDuration": 0, "awakeningsCount": 17, "dateOfSleep": "2018-11-16", "duration": 30000000, "efficiency": 95, "endTime": "2018-11-16T05:39:00.000", "isMainSleep": true, "logId": 20170274799, "minuteData": [ { "dateTime": "21:18:30", "value": "1" }, { "dateTime": "21:19:30", "value": "1" }, 略 { "dateTime": "05:36:30", "value": "1" }, { "dateTime": "05:37:30", "value": "1" } ], "minutesAfterWakeup": 0, "minutesAsleep": 473, "minutesAwake": 27, "minutesToFallAsleep": 0, "restlessCount": 17, "restlessDuration": 27, "startTime": "2018-11-15T21:18:30.000", "timeInBed": 500 } ], "summary": { "stages": { "deep": 71, "light": 215, "rem": 85, "wake": 41 }, "totalMinutesAsleep": 473, "totalSleepRecords": 1, "totalTimeInBed": 500 } }
v1.2の場合
curl -H "Authorization: Bearer **********.**********" https://api.fitbit.com/1.2/user/-/sleep/date/2018-11-16.json | jq . > 2018-11-16-v1.2.json
2018-11-16-v1.2.json
{ "sleep": [ { "dateOfSleep": "2018-11-16", "duration": 30000000, "efficiency": 95, "endTime": "2018-11-16T05:39:00.000", "infoCode": 0, "isMainSleep": true, "levels": { "data": [ { "dateTime": "2018-11-15T21:18:30.000", "level": "wake", "seconds": 30 }, { "dateTime": "2018-11-15T21:19:00.000", "level": "light", "seconds": 300 }, 略 { "dateTime": "2018-11-16T05:23:00.000", "level": "wake", "seconds": 510 }, { "dateTime": "2018-11-16T05:31:30.000", "level": "light", "seconds": 450 } ], "shortData": [ { "dateTime": "2018-11-15T21:18:30.000", "level": "wake", "seconds": 150 }, { "dateTime": "2018-11-15T21:59:00.000", "level": "wake", "seconds": 60 }, 略 { "dateTime": "2018-11-16T05:10:30.000", "level": "wake", "seconds": 30 }, { "dateTime": "2018-11-16T05:33:30.000", "level": "wake", "seconds": 30 } ], "summary": { "deep": { "count": 6, "minutes": 79, "thirtyDayAvgMinutes": 72 }, "light": { "count": 35, "minutes": 264, "thirtyDayAvgMinutes": 196 }, "rem": { "count": 10, "minutes": 92, "thirtyDayAvgMinutes": 64 }, "wake": { "count": 38, "minutes": 65, "thirtyDayAvgMinutes": 43 } } }, "logId": 20170274799, "minutesAfterWakeup": 0, "minutesAsleep": 435, "minutesAwake": 65, "minutesToFallAsleep": 0, "startTime": "2018-11-15T21:18:30.000", "timeInBed": 500, "type": "stages" } ], "summary": { "stages": { "deep": 71, "light": 215, "rem": 85, "wake": 41 }, "totalMinutesAsleep": 435, "totalSleepRecords": 1, "totalTimeInBed": 500 } }
ver1.2の方が睡眠レベル単位でまとめているため、データ容量が少なくなる傾向になります。
以後、ver1.2形式で取得します。
《2018-11-17》
Fitbitを外したまま寝てしまった。。。
しかし、エラーにならず結果が取得できることがわかりました。
curl -H "Authorization: Bearer **********.**********" https://api.fitbit.com/1.2/user/-/sleep/date/2018-11-17.json | jq . > 2018-11-17-v1.2.json
2018-11-17-v1.2.json
{ "sleep": [], "summary": { "totalMinutesAsleep": 0, "totalSleepRecords": 0, "totalTimeInBed": 0 } }
《2018-11-18》
curl -H "Authorization: Bearer **********.**********" https://api.fitbit.com/1.2/user/-/sleep/date/2018-11-18.json | jq . > 2018-11-18-v1.2.json
2018-11-18-v1.2.json
{ "sleep": [ { "dateOfSleep": "2018-11-18", "duration": 24840000, "efficiency": 89, "endTime": "2018-11-18T05:30:00.000", "infoCode": 0, "isMainSleep": true, "levels": { "data": [ { "dateTime": "2018-11-17T22:36:00.000", "level": "wake", "seconds": 30 }, { "dateTime": "2018-11-17T22:36:30.000", "level": "light", "seconds": 1020 }, 略 { "dateTime": "2018-11-18T05:18:00.000", "level": "light", "seconds": 510 }, { "dateTime": "2018-11-18T05:26:30.000", "level": "wake", "seconds": 210 } ], "shortData": [ { "dateTime": "2018-11-17T22:36:00.000", "level": "wake", "seconds": 120 }, { "dateTime": "2018-11-17T22:45:00.000", "level": "wake", "seconds": 30 }, 略 { "dateTime": "2018-11-18T04:32:00.000", "level": "wake", "seconds": 30 }, { "dateTime": "2018-11-18T04:35:00.000", "level": "wake", "seconds": 120 } ], "summary": { "deep": { "count": 4, "minutes": 77, "thirtyDayAvgMinutes": 73 }, "light": { "count": 23, "minutes": 203, "thirtyDayAvgMinutes": 209 }, "rem": { "count": 8, "minutes": 75, "thirtyDayAvgMinutes": 70 }, "wake": { "count": 26, "minutes": 59, "thirtyDayAvgMinutes": 47 } } }, "logId": 20192347613, "minutesAfterWakeup": 0, "minutesAsleep": 355, "minutesAwake": 59, "minutesToFallAsleep": 0, "startTime": "2018-11-17T22:36:00.000", "timeInBed": 414, "type": "stages" } ], "summary": { "stages": { "deep": 71, "light": 215, "rem": 85, "wake": 41 }, "totalMinutesAsleep": 355, "totalSleepRecords": 1, "totalTimeInBed": 414 } }
睡眠データの項目
公式ドキュメントを見ても項目についての詳細な説明がなく、項目名と実データから推測。間違っていたらコメントください。
睡眠データ
睡眠データ(sleep)は配列形式です。
昼寝をして夜寝た場合は2件登録されます。複数件ある場合は、Fitbit側が判定して1件だけisMainSleepフラグをtrueにします。
項目 | サンプル | 説明 | |
---|---|---|---|
dateOfSleep | '2018-09-18' | 睡眠日 | |
duration | 21960000 | ベッド(布団)にいた合計時間(ミリ秒) | |
efficiency | 97 | 睡眠効率(%)。のび太は100。計算方法はここを参考 | |
endTime | '2018-09-18T05:06:00.000' | 起床時間 | |
infoCode | 0 | ||
isMainSleep | true | メインの睡眠かどうか | |
levels | 明細データのレベル | ||
data | {"dateTime": "2018-09-18T02:27:00.000", "level": "rem", "seconds": 840} | 睡眠レベル単位のデータ配列。wake, rem, light, deepの4種類。睡眠レベル、開始時間、持続時間(秒) | |
shortData | {"dateTime": "2018-09-17T23:04:00.000", "level": "wake", "seconds": 150} | 覚醒データ配列。目覚めた状態が3分以内のデータ | |
summary | "deep": {"count": 4, "minutes": 78, "thirtyDayAvgMinutes": 57} | 睡眠レベル単位の要約データ。合計回数、合計時間(分)、30日間の平均時間(分) | |
logId | 19548152255 | ログID | |
minutesAfterWakeup | 5 | 起床した後の時間(分) | |
minutesAsleep | 334 | 睡眠時間(分) | |
minutesAwake | 32 | 起きていた時間(分) | |
minutesToFallAsleep | 0 | 最初の眠るまでの時間(分) | |
startTime | '2018-09-17T23:00:00.000' | 就寝時間 | |
timeInBed | 366 | ベッド(布団)にいた時間(分)。minutesAsleep + minutesAwake | |
type | 'stages' | 睡眠データの種類 |
timeInBed = minutesToFallAsleep + minutesAsleep + minutesAwake
要約データ
項目 | サンプル | 説明 | |
---|---|---|---|
stages | 睡眠レベル | ||
deep | 38 | 深い睡眠(分) | |
light | 163 | 浅い睡眠(分) | |
rem | 82 | レム睡眠(分) | |
wake | 31 | 目覚めた状態(分) | |
totalMinutesAsleep | 334 | 睡眠時間の合(分) | |
totalSleepRecords | 1 | 合計睡眠回数(sleep配列の件数) | |
totalTimeInBed | 366 | ベッド(布団)にいた合計時間(分)。totalTimeInBed - totalMinutesAsleep が起きている時間 |
Python3の場合
Fitbit用のモジュールを利用すると簡単に取得できます。
クライアントID (OAuth 2.0 Client ID)、クライアントシークレット、アクセストークン、リフレッシュトークンは https://dev.fitbit.com/apps で登録したアプリから取得した値を設定します。
API_VERSIONに1.2
を指定しています。(デフォルト1.0)
import datetime import fitbit def main(): client = fitbit.Fitbit(CLIENT_ID, CLIENT_SECRET, access_token=ACCESS_TOKEN, refresh_token=REFRESH_TOKEN) client.API_VERSION = 1.2 dt = datetime.date(2018, 11, 18) sleep = client.get_sleep(dt) print(sleep) if __name__ == "__main__": main()
参考
ブログに3日間書き込みしなかったら催促メールを送る
AWS Lambdaを使って、定期的にブログのトップページをチェック。 3日間書き込みがなかったら、催促メールを送り続ける。
あまり嬉しくないプログラムを作成します。
概要
大まかな流れは以下になります。
- CloudWatch Events が定期的にLambda関数を実行
- ブログのトップページから最新(最終)エントリ日を取得
- 3日間経過していたらメール送信
Lambda関数を作成する前に、下準備をします。
メール送信の準備
メールが送信できるように、Amazon SNSでトピックを作成してメール用のサブスクリプションを準備します。
トピックの作成
マネジメントコンソールから、サービス [アプリケーション統合] - [Simple Notification Service] を選択します。
トピックを選択して、[新しいトピックの作成] ボタンをクリック
トピック名を入力して[トピックの作成] ボタンをクリック
トピックが作成されます。
サブスクリプションの作成
トピック一覧から作成したトピックのARN
リンクをクリックして、トピックの詳細を開きます。
[サブスクリプションの作成] ボタンをクリックします。
プロトコルとエンドポイントを入力します。
- プロトコル:
Email
- エンドポイント:
送信したいメールアドレス
[サブスクリプションの作成]ボタンをクリックすると、確認用のメールが送信されます。
メール内のConfirm subscription
リンクをクリックすると確認済となり、これで利用可能となります。
メールの確認
メールが受信できるか確認します。
トピックの詳細画面、左上の [トピックに発行] ボタンをクリック
件名とメッセージを入力して [メッセージの発行] ボタンをクリック
メールが届いたら成功です。
トピックARN
は後で利用するので、メモ(コピー)しておいてください。
ロールの準備
Lambdaからメール送信ができるように、IAMロールを作成します。
マネジメントコンソールから、サービス [セキュリティ、 アイデンティティ、 コンプライアンス] - [IAM] を選択します。
サイドパネルの [ロール] から、[ロールの作成] ボタンをクリックします。
ロールを使用するサービスを選択
AWSサービスのLambdaを選択して、[次のステップ:アクセス権限] ボタンをクリックします。
ポリシーの割り当て
Lambdaからメール送信ができるように、以下のポリシーにチェックをつけます。
- AWSLambdaBasicExecutionRole
- AmazonSNSFullAccess
ポリシーのフィルタで絞り込むと探しやすいです。
ロールの確認と作成
ロール名 lambda-check-entry-date
を入力し、ポリシーを確認。
問題がなければ、[ロールの作成] ボタンをクリックします。
以上で下準備は終わりです。
今回もAWS Cloud9上から作成していきます。
ローカル上にLambda関数を作成
ローカル上にLambda関数を作成します。
右側サイドバーにある、[AWS Resources] を選択して [AWSリソース] ウィンドウを開き、[λ+] ボタンをクリックします。
- 関数名
- checkEntryDate
- アプリケーション名
- CheckEntryDate
- ランタイム
- Python 3.6
- 設計図
- hello-world-python3
- 関数トリガー
- none
- メモリ
- 128MB
- ロール
- Choose an existing role
- lambda-check-entry-date (下準備で作成したロール)
- Choose an existing role
外部モジュールのインストール
Pythonの外部モジュールをインストールします。
- Requests
- 使い勝手のよいHTTPライブラリ
- Beautiful Soup
- スクレイピングでよく使われるHTMLパーサー
- dateutils
- 日付操作用ライブラリ
- pytz
- タイムゾーンライブラリ
ターミナルから、CheckBlog
ディレクトリ直下にインストールします。
$ cd CheckBlog/ $ pip-3.6 install requests -t . $ pip-3.6 install beautifulsoup4 -t . $ pip-3.6 install dateutils -t . $ pip-3.6 install pytz -t .
-t
はどこにインストールするかのターゲットを、.
はカレントディレクトリを意味します。
必要なモジュールがインストールされました。
template.yaml
template.yaml に環境変数とスケジュールを登録します。
環境変数の使い方は、下記のエントリを参照してください。
AWSTemplateFormatVersion: '2010-09-09' Transform: 'AWS::Serverless-2016-10-31' Description: An AWS Serverless Specification template describing your function. Resources: checkBlog: Type: 'AWS::Serverless::Function' Properties: Handler: checkBlog/lambda_function.lambda_handler Runtime: python3.6 Description: '' MemorySize: 128 Timeout: 15 Role: 'arn:aws:iam::123456789012:role/lambda-check-entry-date' CodeUri: .debug/ Environment: Variables: site: 'https://oreout.hatenablog.com' period: '3' topic: 'arn:aws:sns:ap-northeast-1:123456789012:check_blog' subject: '俺の通知' body: | 何怠けとるねん! そろそろブログを更新して! Events: CheckBlogScheduledEvent: Type: Schedule Properties: Schedule: rate(6 hours)
環境変数
- site
- チェックするブログのトップページ
- period
- 最終投稿日からの何日間で判定するか
- topic
- メール送信の準備て作成した
トピックARN
- メール送信の準備て作成した
- subject
- メール件名
- body
- メール本文 (改行する場合はパイプ
|
を定義して次行に本文)
- メール本文 (改行する場合はパイプ
スケジュール
起動する間隔をRate式で定義します。
rate(数値 単位)
単位は 分
、時
、日
を指定できます。
注意点として、数値が複数の場合は、単位が複数形になります。
Rate式は Rate または Cron を使用したスケジュール式 を参照してください。
Lambda関数
下記コードに差し替えて、保存 (Command+S) します。
サンプルのため、例外処理は考慮していません。
lambda_function.py
import boto3 import os import requests from bs4 import BeautifulSoup from datetime import datetime from dateutil import relativedelta from dateutil import parser from pytz import timezone SITE = os.environ['site'] PERIOD = int(os.environ['period']) TOPIC = os.environ['topic'] SUBJECT = os.environ['subject'] BODY = os.environ['body'] def lambda_handler(event, context): r = requests.get(SITE) soup = BeautifulSoup(r.text, 'html.parser') entry = soup.find('time').get('datetime') # 最後の投稿日 latest = parser.parse(entry).date() # N日前 target = datetime.now(timezone('Asia/Tokyo')) - relativedelta.relativedelta(days=PERIOD) target = target.date() if target > latest: sns = boto3.client('sns') sns.publish(TopicArn=TOPIC, Subject=SUBJECT, Message=BODY) return False else: return True
- requestsでブログのトップページをGET
- レスポンス結果をBeautifulSoupに渡してHTMLパース
- 最初のtimeタグ(投稿日の降順)を取得して、datetime属性の値(日付)を取得
- 現在日と比較してN日間経過している場合はメール送信
実行とデプロイ
ローカルで実行確認、問題がなければデプロイします。
ローカルの実行、デプロイの仕方は、下記のエントリを参照してください。
これで、ブログをさぼっていたら、催促メールが飛んでくるようになりました。