Help us understand the problem. What is going on with this article?

心臓が止まったらSNSに「死にました」と投稿する

More than 1 year has passed since last update.

概要

スクリーンショット 2017-08-14 20.10.02.png

fitbit(alta HR)で心拍数をモニタリングして、心拍数が0になったらSNSに「死にました」と投稿するスクリプトを書きました。

リポジトリ

https://github.com/inamori/physical_health_checker

僕はほぼ24時間fitbitを付けっぱなしにしているので、fitbitごと壊れる死に方でない限り大体カバーできるはずです。

fitbit alta HRとは
  • リストバンド型ウェアラブル端末の1つで、常時心拍数などを測れます
  • Pure Pulseという技術で従来のデバイスより正確に測れるらしいです
  • APIが充実しています

Fitbit APIで心拍数を監視する

Fitbitアプリを登録する

  • こちらから登録できます
  • 心拍数データを取るためには「OAuth 2.0 Application Type」を「Personal」にする必要があります

心拍数を取得する

  • python-fitbitを使わせてもらいました。使い方は他に多くの解説記事があるので割愛
  • access_tokenには有効期限があるので、fitbit.Fitbitをコールする時、refresh_cbを指定して自動更新されるようにしておく
  • 時間と心拍数を分けて取り出してるのは、後述の回帰分析で使うためです
class FitbitClient(object):
    def __init__(self):
        self.client_id = os.getenv('FITBIT_CLIENT_ID')
        self.client_secret = os.getenv('FITBIT_CLIENT_SECRET')
        token_file_name = os.getenv('TOKEN_FILE_NAME')
        tokens = open(token_file_name).read()
        token_dict = literal_eval(tokens)
        self.access_token = token_dict['access_token']
        self.refresh_token = token_dict['refresh_token']

        def refresh_call_back(token):
            with open(token_file_name, 'w') as f:
                f.write(str(token))
            self.access_token = token['access_token']
            self.refresh_token = token['refresh_token']

        self.client = fitbit.Fitbit(
            self.client_id, self.client_secret,
            access_token=self.access_token, refresh_token=self.refresh_token,
            refresh_cb=refresh_call_back
        )

    def request_heart_beats(self, date, limit=30):
        data_sec = self.client.intraday_time_series('activities/heart', date, detail_level='1sec')
        heart_sec = data_sec['activities-heart-intraday']['dataset']
        heart_sec = heart_sec[-limit:]
        seconds = map(self.fetch_sec, heart_sec)
        beats = map(lambda data: data['value'], heart_sec)
        return seconds, beats

    @staticmethod
    def fetch_sec(data):
        t = time.strptime(data['time'], '%H:%M:%S')
        return t.tm_hour * 3600 + t.tm_min * 60 + t.tm_sec

シャワーに行くたび死ぬのを防ぐ

心拍数を取得するところまでは簡単にできます。
あとはどうやって死んだことを確認するかですが、安直に「心拍数が0か、あるいはデータが返ってこなかったら死亡」とやってしまうと、シャワー浴びにちょっと外しただけで死にかねません。

そこで「直近30個のデータから回帰分析し、60秒後の心拍数が10未満と予測されたら死んだと見なす」というちょっとややこしいロジックで判定することにしました。
衰弱死でも病死でも失血死でも、だいたい死ぬ時は徐々に心拍数が下がってから死ぬだろう、という前提に基づいてます。
サンプルを取りすぎると、急激な運動の後のクールダウンでも誤判定してしまう弱点がありますが、まあ30データ(≒30〜60秒分のデータ)なら大丈夫でしょう。

class PhysicalHealthChecker(object):
    def __init__(self, provider, event_emitter):
        self.event_emitter = event_emitter
        self.heart_beat_provider = provider
        seconds, beats = self.heart_beat_provider.request_heart_beats('today')
        expected_beat = np.polyval(np.polyfit(seconds, beats, 1), seconds[-1] + 60)
        if self.is_alive(expected_beat):
            print 'still alive.'
        else:
            timestamp_file_name = 'death_time.txt'
            death_time = open(timestamp_file_name, 'r').read()
            if not death_time:
                print 'dead.'
                self.event_emitter.emit('death')
                with open(timestamp_file_name, 'w') as file:
                    file.write(datetime.now().strftime("%Y/%m/%d %H:%M:%S"))

    @staticmethod
    def is_alive(heart_beat):
        return heart_beat >= 10


if __name__ == '__main__':
    ifttt_key = os.getenv('IFTTT_WEB_HOOK_KEY')
    fitbitHealthChecker = PhysicalHealthChecker(FitbitClient(), IftttEventEmitter(ifttt_key))

死んだらSNSに投稿する

IFTTTに「If {Webhook} Then {Twitter/Facebook POST}」なAppletを事前に作り、webhookをコールするだけです

スクリーンショット 2017-08-14 23.14.00.png

class IftttEventEmitter(object):
    def __init__(self, secret_key):
        self.secret_key = secret_key

    def emit(self, event_name):
        requests.get('https://maker.ifttt.com/trigger/' + event_name + '/with/key/' + self.secret_key)

  • あとはcronなりで回すだけです

既知の問題

  • APIで当日分の心拍数しか取ってないので、日付をまたいで死んだら誤作動しそうです

応用編

  • 闇の組織と交渉する時に「やめときな。俺が死んだら例のファイルは信頼できる仲間の所に転送される事になってる」というやつができます
  • 僕には全く関係無い話ですが、死ぬ時いかがわしいファイルを自動で消したい!という用途にも使えそうです
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away