<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: yurfuwa</title>
    <description>The latest articles on Forem by yurfuwa (@yurfuwa).</description>
    <link>https://forem.com/yurfuwa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F44133%2Fbc9d0693-c043-46b1-bb5d-e0ea111f2e7c.jpg</url>
      <title>Forem: yurfuwa</title>
      <link>https://forem.com/yurfuwa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/yurfuwa"/>
    <language>en</language>
    <item>
      <title>Gdiff / Gblame ~,p,:,HEAD </title>
      <dc:creator>yurfuwa</dc:creator>
      <pubDate>Fri, 20 Apr 2018 04:59:22 +0000</pubDate>
      <link>https://forem.com/yurfuwa/gdiff--gblame-phead--5e5g</link>
      <guid>https://forem.com/yurfuwa/gdiff--gblame-phead--5e5g</guid>
      <description>&lt;p&gt;Vimで GdiffとかGblame 使ってて、いつも忘れるのでメモ&lt;br&gt;
Gblameでコミットログを出した後に、 ~,pで過去未来を辿れるが、そのあと現行のindexとdiffを出したいときは&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:Gdiff :
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;すると現在のファイルと比較できる。 Gdiff master とか head を指定してしまうと挙動がちょっとあれになる&lt;/p&gt;

&lt;p&gt;seealso : &lt;a href="http://cohama.hateblo.jp/entry/20120317/1331978764"&gt;http://cohama.hateblo.jp/entry/20120317/1331978764&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vim</category>
      <category>git</category>
    </item>
    <item>
      <title>Dockerfileで Github Enterprise or PrivateRepository から pip install する</title>
      <dc:creator>yurfuwa</dc:creator>
      <pubDate>Wed, 18 Apr 2018 06:05:24 +0000</pubDate>
      <link>https://forem.com/yurfuwa/dockerfile-github-enterprise-or-privaterepository--pip-install--4obo</link>
      <guid>https://forem.com/yurfuwa/dockerfile-github-enterprise-or-privaterepository--pip-install--4obo</guid>
      <description>&lt;p&gt;Enterpriseなgithubをつかっていると、pipで自社のModuleをインストールしたいことはよくある。&lt;br&gt;
privateなDockerImageをbuildするときに、privateなRepositoryからpipする的。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;python:3.6とかのdockerの場合、ssh-agentが立ち上がってないので立ち上げて鍵登録まで一気に持っていく。&lt;/li&gt;
&lt;li&gt;known_hostsに書き込まないと、知らんサイトとしてinteractiveにお伺いがきて蹴られる。
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;ssh-agent&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ssh-add &lt;span class="nt"&gt;-k&lt;/span&gt; any_your_key &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.ssh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ssh-keyscan &lt;span class="nt"&gt;-t&lt;/span&gt; rsa github.enterpise.domain &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.ssh/known_hosts &lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RUN後は鍵登録が飛ぶので、Dockerimage内に鍵を含めたくない場合は、pipした後に消すと良い気もする。&lt;/p&gt;

</description>
      <category>python</category>
      <category>docker</category>
      <category>ssh</category>
      <category>git</category>
    </item>
    <item>
      <title>TwitterのタイムラインからURLを抽出してRSSにしてFeedlyで読む</title>
      <dc:creator>yurfuwa</dc:creator>
      <pubDate>Tue, 03 Apr 2018 20:21:26 +0000</pubDate>
      <link>https://forem.com/yurfuwa/twitterurlrssfeedly-3b67</link>
      <guid>https://forem.com/yurfuwa/twitterurlrssfeedly-3b67</guid>
      <description>&lt;p&gt;昨今の界隈ではLDRことLive Dwango Reader (ex. Livedoor Reader)がサービス終了したことが話題になった。&lt;br&gt;
自分はRSS readerには Feedly Pro を長らく使っており、LDRを利用したことがないので特に感慨はない。&lt;/p&gt;
&lt;h2&gt;
  
  
  Feedly
&lt;/h2&gt;

&lt;p&gt;Feedlyについては簡単に説明しておきたい。&lt;br&gt;
Feedlyは使いやすくシンプルなRSSリーダであり、mobile appもある。PROにすると検索性が格段にアップするし、なによりポピュラーな記事をサマって今日見るべきカテゴリをまとめてくれるのが便利だ。&lt;br&gt;
さらに j で記事移動、v で記事移動、gg でカテゴリ移動のように、vimのようなショートカット配置になっているのも嬉しい。LDRより軽量とは言えないかもしれないが、見やすいしデザインはモダンだ。自分は多く時間を費やすものには金を払う価値があると思っている。&lt;/p&gt;
&lt;h2&gt;
  
  
  電子ドラッグ
&lt;/h2&gt;

&lt;p&gt;さて、話を戻そう&lt;br&gt;
そもそも最近のインターネットは受動的な情報選択をする傾向にある。TwitterやFacebookなどのソーシャルメディアが跋扈し、情報はほぼSNSによって共有される。&lt;br&gt;
RSSリーダを使っているのはもはや老害の域に達しており、RSSを吐いてくれるサイトも随分減ってしまった。嘆かわしいがこればかりは仕方がない。&lt;/p&gt;

&lt;p&gt;相対的にTwitterを見る時間が長くなってしまった人も多いだろう。流速のあるタイムラインでは、情報の取捨選択を瞬間的に行う必要がある。情報は必ずしも有益なものとは限らない。というより、ほとんどのツイートはノイズのようなもので、一時的に脳を麻薬のように刺激し、時間を浪費する。Twitterは電子ドラッグといっても過言ではない。&lt;/p&gt;
&lt;h2&gt;
  
  
  砂金拾い
&lt;/h2&gt;

&lt;p&gt;しかし中には素晴らしい情報も紛れている。自分が興味があり、ナレッジを蓄積し、知的好奇心を満たしてくれるような素敵な情報の共有が、玉石混交に確かに存在している。&lt;br&gt;
Twitterとは、砂金集めのようなものだ。そして自分の場合その砂金は、短文よりもURLの先にある。&lt;/p&gt;

&lt;p&gt;というわけで私は、大量の砂から砂金を分け、革袋にまとめることにした。つまりTwitterで流れる文章からURLを抜き出し、RSSにまとめ、いつも使っているFeedlyに落とし込む。&lt;/p&gt;

&lt;p&gt;アーキテクチャはこのようになる。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsteemitimages.com%2FDQmTW9vy1qmCCAVm8dJaU1TvT2KiY8AADYifqoAnHMsbjAJ%2FTwlclw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsteemitimages.com%2FDQmTW9vy1qmCCAVm8dJaU1TvT2KiY8AADYifqoAnHMsbjAJ%2FTwlclw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ポイントとしては&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS APIGateway + Lambda + Dynamodb サーバレスで運用する。&lt;/li&gt;
&lt;li&gt;Twitter API で stremに接続し、URLを抽出して投げる&lt;/li&gt;
&lt;li&gt;APIGatewayはエンドポイントを用意し、 GET ではRSSを返し、 POSTでデータを蓄積する&lt;/li&gt;
&lt;li&gt;TwitterのClientに Raspberry pi を使う&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  AWS APIGateway + Lambda + Dynamodb サーバレスで運用する。
&lt;/h2&gt;

&lt;p&gt;今回はサーバレスな構成にしてみた。&lt;br&gt;
まずは出力側から考えたい。&lt;br&gt;
Dynamodbのテーブルは以下のようになっている&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;url
timestamp
created_at
expired_at (TTL)
text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TTLを使用することで、dynamodbに無尽蔵に突っ込まれるデータを定期的に消去する。ここでは登録から2時間後にしている。Feedlyはおおよそ30分間隔、速いと10分でRSSを取得しに来る。取り込まれてしまえばRSSには情報は残っていなくても良い。よって、2時間も消費期限が持てば充分であり、それ以降は必要ないので消してしまう。&lt;br&gt;
Dynamodbはデータの蓄積にも金を取るし、バッチで一個一個消すのも面倒だ。TTLを使えば登録時に指定した時間に消えてくれる。&lt;br&gt;
テーブルは常に表示されるべきRSSのデータのみで完結する。これによってLambdaでは単にlambda_handlerにGETが来たらテーブルをscanしてjsonにくるむだけ良い。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;400&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DecimalEncoder&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DELETE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PUT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;httpMethod&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;operation&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unsupported method &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  APIGatewayを使って、エンドポイントを定義する。
&lt;/h2&gt;

&lt;p&gt;GETの統合レスポンスに application/rss+xml を指定し、本分マッピングテンプレートで以下のように登録する。&lt;br&gt;
これによってLambda側で処理されたJSONをRSSにマッピングできる。Lambdaはあくまでデータ処理だけを考えればよく、APIGatewayが最終出荷物をコントロールしている。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;#set($inputRoot = $util.parseJson($input.path('$.body')))
&lt;span class="cp"&gt;&amp;lt;?xml version='1.0' encoding='UTF-8'?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;rss&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;'2.0'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;channel&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;yurfuwa's twitter&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;link&amp;gt;&lt;/span&gt;http://twitter.com/yurfuwa&lt;span class="nt"&gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;extract timeline-share-links&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
        #foreach($item in $inputRoot.Items)
        &lt;span class="nt"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;$item.text&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;link&amp;gt;&lt;/span&gt;$item.url&lt;span class="nt"&gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;$item.text&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;pubDate&amp;gt;&lt;/span&gt;$item.created_at&lt;span class="nt"&gt;&amp;lt;/pubDate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
        #end

    &lt;span class="nt"&gt;&amp;lt;/channel&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/rss&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;これで、AWSから発行されたURLのエンドポイントを叩けば、dynamodbに登録されているデータのRSSが出力されるようになった。個人で利用するだけなので、ドメインを取得する必要もない。&lt;/p&gt;

&lt;h2&gt;
  
  
  Raspberry piでTwitterのクライアントを作る
&lt;/h2&gt;

&lt;p&gt;自分は自宅サーバーをとうの昔に捨ててしまった。その代わりにRaspberry piを使っている。EC2も考えられるが、AWSは富豪的だ。たかが個人のタイムライン監視に、EC2でお金を払うのは辛い。個人用のSlack botなんかも、Raspberry piでやっている。&lt;/p&gt;

&lt;p&gt;ここでも、特に難しいことはしないが、Lambdaではpython3を使ったがClientはnodeにしている。&lt;br&gt;
Twitterのストリームに接続したら、expanded_urlが含まれているか見る。含まれていたら、指定のURLを除外して、整形して、APIGatewayで作ったエンドポイントに POST で投げる。&lt;br&gt;
先程のLambdaはすでにPOSTで来たらDynamodbのテーブルにput_itemするようになっているから、これで完成。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;twitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;twitter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{...})&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url-regex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;with&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;followings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ja&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expanded_url&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sr"&gt;/instagram.com|twitter.com|twitpic.com|ift.tt|swarmapp.com|nico.ms|pixiv.net|bit.ly|this.kiji.is|nhknews.jp|fb.me|tenki.jp|j.mp|melonbooks|ask.fm/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expanded_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)){&lt;/span&gt;

        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_TWEET_SHARE_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expanded_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/RT &lt;/span&gt;&lt;span class="se"&gt;\@[&lt;/span&gt;&lt;span class="sr"&gt;A-z|a-z|0-9|&lt;/span&gt;&lt;span class="se"&gt;\-&lt;/span&gt;&lt;span class="sr"&gt;|&lt;/span&gt;&lt;span class="se"&gt;\_]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\:\ &lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;urlRegex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;created_at&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timestamp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expired_at&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7200&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  運用費
&lt;/h2&gt;

&lt;p&gt;さて、AWSのサービスを使っているので、一月のお値段が気になる。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsteemitimages.com%2FDQmSXnAv7YJWA3LRmGdGWUEScEkV7k8x7wYrKKXYFctYLE5%2Fbilling.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsteemitimages.com%2FDQmSXnAv7YJWA3LRmGdGWUEScEkV7k8x7wYrKKXYFctYLE5%2Fbilling.PNG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;このような感じ。EC2ではこうはいかない。Dynamodbは無料枠の範囲内で収まる。&lt;br&gt;
TTL設定しないと爆死するし、Twitterのフォローが1万人とか監視いる人は知らん。&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedlyで表示してみる
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsteemitimages.com%2F0x0%2Fhttps%3A%2F%2Fsteemitimages.com%2FDQmWrWcBFo8FjvQubzbkexQRvGuBeDzETjAdSqdQavWiRsd%2Ffeedly.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsteemitimages.com%2F0x0%2Fhttps%3A%2F%2Fsteemitimages.com%2FDQmWrWcBFo8FjvQubzbkexQRvGuBeDzETjAdSqdQavWiRsd%2Ffeedly.PNG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;実際運用してみると素晴らしく、良い。&lt;br&gt;
Feedlyは勝手に話題っぽいURLを優先してくれるので、無尽蔵にURLを突っ込んでも見るべきものから表示される。&lt;br&gt;
Twitterを漠然と眺めている時間は減る上に、RSSは10分間隔で更新されるので速報系のニュースにも強い。&lt;/p&gt;

&lt;p&gt;最終的にはディープラーニングでTwitterからのソースをクラスタリングして、カテゴリごとにRSSを吐くとなお良さそう。教師データは既にあるカテゴライズされたRSSのソースを使えばいいし。&lt;/p&gt;

</description>
      <category>python</category>
      <category>node</category>
      <category>aws</category>
      <category>japanese</category>
    </item>
    <item>
      <title>RaspberryPi + AmazonAlexa を使って部屋の湿度を喋らせる</title>
      <dc:creator>yurfuwa</dc:creator>
      <pubDate>Thu, 04 Jan 2018 07:22:57 +0000</pubDate>
      <link>https://forem.com/yurfuwa/raspberrypi--amazonalexa--41do</link>
      <guid>https://forem.com/yurfuwa/raspberrypi--amazonalexa--41do</guid>
      <description>&lt;p&gt;部屋の乾燥が気になる季節。&lt;br&gt;
加湿器を使って部屋を加湿している人も多いだろう。&lt;br&gt;
自分はほしい加湿器(通称&lt;a href="https://www.balmuda.com/jp/rain/"&gt;壺&lt;/a&gt;)があるものの、どの程度加湿されるのか怪しい。&lt;br&gt;
まずは部屋がどの程度乾燥しているのか、そして加湿するとどのような変化があるのか定量的に視覚化したい。&lt;/p&gt;

&lt;p&gt;作る。&lt;/p&gt;

&lt;p&gt;センサは&lt;a href="http://amzn.to/2lS6X7o"&gt;これ&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6s8pjhCU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0bvb81pzqkgh68wavnpy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6s8pjhCU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0bvb81pzqkgh68wavnpy.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;配線は&lt;a href="http://osoyoo.com/ja/2017/07/06/dht11/"&gt;これ&lt;/a&gt;とか&lt;a href="http://www.kabegiwablog.com/entry/2017/09/21/214548"&gt;これ&lt;/a&gt;あたりを参考に。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z-QVg4wU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-ak.f.st-hatena.com/images/fotolife/k/kabegiwakun/20170921/20170921212028.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z-QVg4wU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-ak.f.st-hatena.com/images/fotolife/k/kabegiwakun/20170921/20170921212028.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;自分のRaspberryPiのモデルは2011 12のModel B。GPIO配列はこう。&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sSEKHx4h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/DSjUdprUMAAxixh.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--4z86hwpS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/891683361034338304/ssg6CgHK_normal.jpg" alt="ふわこ profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        ふわこ
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/yurfuwa"&gt;@yurfuwa&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      自宅rasp,いつもモデルとgpio配列を忘れがちだから貼っておこう 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      17:15 PM - 02 Jan 2018
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=948241332539027456" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=948241332539027456" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=948241332539027456" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;実行コードはここの&lt;a href="http://osoyoo.com/driver/pi3_start_learning_kit_lesson_17/dht11.py"&gt;サンプル&lt;/a&gt;やつを流用。&lt;/p&gt;

&lt;p&gt;フムー。干からびてしまう。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python dhht11.py
humidity: 6 %,  Temperature: 24 C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;湿度5%というのは&lt;a href="//monsieurshibata.cocolog-nifty.com/blog/2010/03/5-f4e3.html"&gt;中東バーレーン&lt;/a&gt;と同等らしい。アラブ人だから仕方ないね。&lt;/p&gt;

&lt;p&gt;次にAlexaに接続する。アーキテクチャ。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TJUmbfZZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iyjejwiwbp828z4c0fms.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TJUmbfZZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iyjejwiwbp828z4c0fms.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;raspの実行コードからdynamodbにデータを格納し、AWS Lambdaを経由してAlexa Skill kitで喋らせる。&lt;/p&gt;

&lt;p&gt;実行コードに以下を追加し、boto3からdynamodbのテーブルにアップデートを掛ける。一分毎の update_itemなので、キャパシティは全く気にする必要がない。DIYのデータストアに最適なのがDynamodbなんだよな。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'dynamodb'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'room-status'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'room'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;AttributeUpdates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;'temp'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;'Value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Action'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'PUT'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s"&gt;'humidity'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;'Value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Action'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'PUT'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alexa Skill Kit 用の AWS Lambda の実行コードは &lt;a href="https://github.com/arcward/echokit"&gt;Echokit&lt;/a&gt; を使う。デコレータとインテントがうまく紐付いており、かなり使いやすい。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;echokit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EchoKit&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;boto3.session&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;boto3.dynamodb.conditions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Attr&lt;/span&gt;

&lt;span class="n"&gt;application_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'APP_ID'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;'APP_ID'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;'amzn1.ask.skill.000'&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EchoKit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify_app_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;

&lt;span class="c1"&gt;# LaunchRequest
&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;launch&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"部屋の温度は、{}度、湿度は{}%です"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'temp'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'humidity'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'dynamodb'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'room-status'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'room'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;'Item'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;room&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;読み取って喋るだけ。Alexaは喋りたい内容をJSONで適切に返す必要があるが、Echokitはうまく隠蔽してくれるのが良い。flask的な発想で実装されている。&lt;/p&gt;

&lt;p&gt;Alexa Skillkitの登録周りは割愛。インテントモデルモクソもない。AWSLambdaのARNを登録するだけでいい。このタイプは本来スマートホームAPIを使うべきだが、スマートホームAPIにはEchoの高級なモデルか、Bluetoothのハブとなる端末を別途実装or買う必要がある。&lt;br&gt;
インターネットに我が家の湿度情報を放流するほうが簡単である。&lt;br&gt;
しかし恐らくこの実装は審査に通るまい。（Alexa Skill storeに登録するには審査が必要なのだ）&lt;/p&gt;

&lt;p&gt;なのでここでスキルの実機端末へのクローズベータテストの機能を使う。これを使うと、特定のAmazonアカウントに対してクローズドベータと称してスキルをばらまくことができる。&lt;/p&gt;

&lt;p&gt;テレーン&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/5khHjIZobXY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;「うちの温度を&lt;em&gt;開いて&lt;/em&gt;」&lt;/p&gt;

&lt;p&gt;となっているのは、スマートホームAPIを使っていないからである。件の機能を使うと「うちの温度いくつ？」等でイケると思う。&lt;/p&gt;

&lt;h2&gt;
  
  
  鍋加湿ソリューション
&lt;/h2&gt;

&lt;p&gt;さて、Alexaに湿度を喋らせることが出来たので、いつでも部屋の湿度はわかるが、湿度は全く改善していない。&lt;/p&gt;

&lt;p&gt;加湿器を買えという意見もあるが、水を加熱すると蒸気が出ることが知られています。&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zGtVvEgh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/DSn2ZlFVQAEDZJg.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--4z86hwpS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/891683361034338304/ssg6CgHK_normal.jpg" alt="ふわこ profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        ふわこ
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/yurfuwa"&gt;@yurfuwa&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      鍋加湿ソリューション 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      14:22 PM - 03 Jan 2018
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=948560124288802816" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=948560124288802816" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=948560124288802816" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;日本の家はよくできており、匂いがリビングに充満しないように熱い空気がキッチン上空に停滞するような構造になっている。なのでサーキュレータで無理やりリビングに空気をデリバリーした。&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4muQt7Tg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/DSn6FzuU0AAyt14.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--4z86hwpS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/891683361034338304/ssg6CgHK_normal.jpg" alt="ふわこ profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        ふわこ
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/yurfuwa"&gt;@yurfuwa&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      ソリューションによって得られた結果 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      14:38 PM - 03 Jan 2018
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=948564232274001922" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=948564232274001922" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=948564232274001922" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;鍋加湿ソリューションで短時間に55%を達成したが、水の補給と火の始末がめんどくさい。&lt;br&gt;
寝るときは止める必要があるのが課題。&lt;/p&gt;

&lt;p&gt;なお止めると&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gx-HDg_U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/DSrLH8bVMAA1b2U.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--4z86hwpS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/891683361034338304/ssg6CgHK_normal.jpg" alt="ふわこ profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        ふわこ
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/yurfuwa"&gt;@yurfuwa&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      05:51 AM - 04 Jan 2018
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=948794018535632896" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=948794018535632896" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=948794018535632896" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;ものの1.5時間で湿度が半分になり、5%に向けて突き進む。&lt;br&gt;
鍋加湿ソリューションにはサステナビリティがない。&lt;/p&gt;

&lt;p&gt;やはり&lt;a href="https://www.balmuda.com/jp/rain/"&gt;壺&lt;/a&gt;が欲しいので誰か買ってください&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>ai</category>
      <category>python</category>
      <category>aws</category>
    </item>
    <item>
      <title>Flask + Tensorflow + Keras で モデルからのpredictが動作しない問題 </title>
      <dc:creator>yurfuwa</dc:creator>
      <pubDate>Thu, 16 Nov 2017 08:01:02 +0000</pubDate>
      <link>https://forem.com/yurfuwa/flask--tensorflow--keras--predict-bnk</link>
      <guid>https://forem.com/yurfuwa/flask--tensorflow--keras--predict-bnk</guid>
      <description>&lt;h2&gt;
  
  
  機械学習ライブラリを使ったPredictなAPI開発
&lt;/h2&gt;

&lt;p&gt;WebサービスでAIを活用する場合、多くはmicroservices的にVPC内にAPIを独立してデプロイし、インターナルに利用することが増えてきた。ことChainerやTensorflowといった機械学習系のライブラリは、その歴史的背景や数学ライブラリからPythonと相性がよいため、必然的にPythonのWAFを選択することになる。&lt;/p&gt;

&lt;h2&gt;
  
  
  pythonでmicroservicesなAPIでのWAF
&lt;/h2&gt;

&lt;p&gt;PythonでWebアプリケーションを開発するには、WAFとしてDjango,Flaskの二大巨頭が台頭しているが、概ねシンプルな推論のAPI開発にDjangoは大して冗長であって、Flask辺りがつかっていて小気味良い。&lt;/p&gt;

&lt;h2&gt;
  
  
  マルチスレッドにおけるモデルデータの参照問題
&lt;/h2&gt;

&lt;p&gt;Tensorflowにしろ、Chainerにしろ、APIでの推論は、機械学習の成果として得られるモデルの読み込みが必要になる。&lt;br&gt;
Tensorflow + Keras では、 サーバ起動時に事前にモデルをメモリに展開し、グラフを構築しておく。リクエスト時にはそれを利用する形になる。&lt;/p&gt;

&lt;p&gt;この時、マルチスレッドで動作していると、構築されたグラフのスレッドとは別のスレッドでリクエストを受け付けることになるので、グラフをスレッド間で共有する必要がある&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_make_predict_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_default_graph&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;another&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_default&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;labels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;see also : &lt;a href="https://github.com/fchollet/keras/issues/2397"&gt;https://github.com/fchollet/keras/issues/2397&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  サーバレスアーキテクチャにおけるTensorflowの利用
&lt;/h2&gt;

&lt;p&gt;昨今流行りのサーバレスであるが、GCP Cloudfunctionはそもそもnodejsしか使えないので除外、 AWS Lambdaが選択肢となる。&lt;/p&gt;

&lt;blockquote data-lang="ja"&gt;
&lt;p&gt;Tensorflowガンガン肥大していくが、学習はともかく認識時に必要最低限のモジュールだけを切り出したい&lt;/p&gt;— ふわこ (&lt;a class="mentioned-user" href="https://dev.to/yurfuwa"&gt;@yurfuwa&lt;/a&gt;
) &lt;a href="https://twitter.com/yurfuwa/status/928094652288221184?ref_src=twsrc%5Etfw"&gt;2017年11月8日&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;このようなニーズがある。しかしAWS Lambdaには 50MB制限というのがあり、ひとつのLambda関数においての容量制限が厳しい。&lt;/p&gt;

&lt;blockquote data-lang="ja"&gt;
&lt;p&gt;ServerlessでTensorflowやろうと躍起になってたんですが、tensorflowどころかnumpyやらscipyやらが既に重すぎてaws lambdaで死ぬし、.soをstripしたり.pyc消しても焼け石に水だった&lt;/p&gt;— ふわこ (&lt;a class="mentioned-user" href="https://dev.to/yurfuwa"&gt;@yurfuwa&lt;/a&gt;
) &lt;a href="https://twitter.com/yurfuwa/status/923608036609900544?ref_src=twsrc%5Etfw"&gt;2017年10月26日&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;素直にサーバーレスは諦めて、GCP AppEngineあたりで手を打つのが打倒っぽい。&lt;br&gt;
AWSさん、なんとかなりませんか、これ。&lt;/p&gt;

</description>
      <category>flask</category>
      <category>python</category>
      <category>tensorflow</category>
      <category>japanese</category>
    </item>
  </channel>
</rss>
