Androidのメモとか

ポキオの日記です。今日も遅延してない。

技術書典に向けてGoogle Drive上のファイル監視をGASからやってみる(後編)

なお、まだ執筆着手してない。

ポキオ Google Drive Google Apps Script enebular Node-RED Slack

自分を追い込むツール

どういうことかというと・・・

relativelayout.hatenablog.com

前回は、GASから技術書典向け原稿を格納してあるGoogle Driveの情報をGASから取得してみました。ただ、本当にやりたいことは「他の人がいかに進捗を出しているか通知することで、自分のモチベーションを向上させる」ことであるわけです。

そこで今回は、

  • 技術書典向け原稿が格納してあるGoogle Drive上のあるフォルダの情報をGASから取得できるようにする
  • それをこっそりWebAPIとして叩けるようにする
  • 定期的にenebular(Node-RED)からそのAPIを叩く
  • 誰かが進捗を出したときに、Slackに通知して自分を鼓舞する

みたいな仕組みを作ってみました。

まずはGAS側の実装

前回のように、DriveAppクラスDriveクラスの両方を使っていきます。

  • DriveAppクラスで特定のフォルダ内のファイルのリストを取得しそれぞれのIDを割り出す
  • DriveクラスでIDを使って「最終更新日」などの情報を取得
  • それらのデータをJSONに整形しWebAPIからGETできるようにする

こんな感じでセキュリティホール情報を取得できる術を作ってあげます。実際の実装はこんな感じ。

function doGet(e){
  return ContentService.createTextOutput(JSON.stringify(getFileList()))
}

function getFileList() {
  var id = 'xxxxx'
  var data = {}
  data.fileList = []
  
  var files = DriveApp.getFolderById(id).getFiles()
  while(files.hasNext()) {
    var file = files.next();
    var fileDetail = Drive.Files.get(file.getId())
    
    var fileInfo = {}
    fileInfo.name = file.getName()
    fileInfo.user = fileDetail.lastModifyingUserName
    fileInfo.date = fileDetail.modifiedDate
    
    data.fileList.push(fileInfo)
  }
  
  Logger.log(JSON.stringify(data))
  return data
}

そして、これをGASのメニューの「ウェブアプリケーションとして導入...」からWebAPI化します。GET時にはdoGet()が呼ばれ、JSONがレスポンスとして返るようになります。

次にenebular側の実装

フローはこんな感じです。

ポキオ Google Drive Google Apps Script enebular Node-RED Slack

  • 先程ののWebAPIを定期的に叩く
  • 最終更新日のタイムスタンプから最近更新されたファイルがあるかないかを判断
  • ある場合は特定のSlackに対してWebhookを投げて通知を行う

Functionノードでは、一応タイムスタンプを見ています。

var message = '';
var criteriaDate = new Date(Date.now() - 1 * 60 * 60 * 1000);

msg.payload.fileList.forEach(file => {
    if(criteriaDate < Date.parse(file.date)){
        message += file.user + 'さんが頑張っています!\n'
    }
});

msg.payload = message;
return msg;

大したことをしていないですが、ここでは1時間以内に更新されたファイルが有るかないかを検知しています。あった場合は、ここで生成されたmessageをSlackに投稿します。

動かしてみた

こんな感じで通知されます。

ポキオ Google Drive Google Apps Script enebular Node-RED Slack

ふふふ・・・みんな頑張ってますね・・・ふふふ・・・。

そろそろ僕も頑張って執筆します・・・。

Node.jsからSonosの状態を取得してみる

ヌー民。

ポキオ Sonos Node.js

Sonosで聞いてる曲をシェアしたい

そう思ったわけです。

relativelayout.hatenablog.com

Sonosのスピーカーで曲を聞いたことをTriggerにIFTTTを動かしたかったりしたんですが、残念ながらSonosはTriggerを提供しておらず、すべてActionとなっていました。そこで、今回はNode.jsの便利なパッケージを使って、Sonosで聞いた曲を取得してみようと思います。

その名もnode-sonos

そのままですね。

github.com

これがすごい便利。とりあえず、

npm i sonos

とかでインストールしてやって。ひとまず同じネットワーク内の機器探索から。

const Sonos = require('sonos')

Sonos.DeviceDiscovery((device) => {
    console.log('--------- device found ----------')
    console.log(device)
})

こんな感じで機器探索の機能が準備されています。これを実行すると・・・。

--------- device found ----------
Sonos {
  host: '192.168.1.35',
  port: 1400,
  options: 
   { endpoints: 
      { transport: '/MediaRenderer/AVTransport/Control',
        rendering: '/MediaRenderer/RenderingControl/Control',
        device: '/DeviceProperties/Control' },
     spotify: { region: '3079' } },
  _events: { newListener: [AsyncFunction: implicitListen] },
  _eventsCount: 1 }

ここで取得できたIPアドレスとポート番号を使って、聞いている音楽の情報を取得してみます。

const Sonos = require('sonos')
const device = new Sonos.Sonos('192.168.1.35', '1400')

device.on('CurrentTrack', (track) => {
    console.log('--------- now playing... ---------')
    console.log(track)
})

すると、スクリプト実行時と曲が変わるタイミングで、情報が取得できます。

--------- now playing... ---------
{ id: null,
  parentID: null,
  title: 'McDonald Romance',
  artist: 'King Gnu',
  album: 'McDonald Romance',
  albumArtURI: 'https://i.scdn.co/image/xxxxxxxxxx',
  uri: 'x-sonos-spotify:spotify:track:xxxxxxxxxx',
  duration: 165,
  queuePosition: 1 }

たまに2回同じ曲名のイベントが発火してる気がしますが、そこはなんとかよしなに処理してあげましょう。

[asin:B07HSBKTFH:detail]

技術書典に向けてGoogle Drive上のファイル監視をGASからやってみる(前編)

久々のGAS案件。

ポキオ GoogleAppsScript GAS 技術書典

もうすぐ技術書典ですね(震)

執筆を加速させていきたいところですが、やっぱり締切駆動開発になりがちになってしまって、進歩しないなぁと思う今日このごろです。

ここで、この状況を打破すべく、他の人の進捗を監視して自分を戒めるシステムを構築してみようと思います!そんなシステム構築してる暇があったら、さっさと書けよ!

ポキオ GoogleAppsScript GAS 技術書典

某弊チームの技術書典向け原稿はGoogle Drive上で管理しているので、もしかしたらGoogle Apps Script(GAS)が使えるかもしれない!と思い、いろいろやってみたことをメモしていきます。

developers.google.com

GASで色々な情報をgetしてみる

まずはGoogle Drive上のファイルへのアクセスはDriveAppクラスが使えるはず。

developers.google.com

監視したいファイルやフォルダのIDが分かれば、そのIDから色々の情報が取得できます。

ポキオ GoogleAppsScript GAS 技術書典

たとえば、あるファイルの最終更新日を取得するには、こんな感じでコーディングしていきます。

function myFunction() {
  var id = 'xxxxx'
  var file = DriveApp.getFileById(id)
  Logger.log(file.getLastUpdated())
 }

これを実行すると、権限付与を求められますが、一度付与して実行すると・・・。

ポキオ GoogleAppsScript GAS 技術書典

こんな感じで最終更新日を取得することができます。一方で、技術書典を複数人で執筆していて、その進捗共有用のSpreadsheetの場合、最終更新日とともに誰が更新したのか気になるんですが、その情報はDriveAppクラスでは取得できないのです・・・

ポキオ GoogleAppsScript GAS 技術書典

実はDriveAppクラスとは別に、[リソース] > [Googleの拡張サービス]からDrive APIを有効にするとDriveクラスがGASから叩けるようになります。

developers.google.com

これは、いわゆるGoogle Drive APIで、APIでできることがGASから叩けるようになります。たとえば、先程の最終更新をしたのが誰かを取得するには、このDriveクラスを使っていきます。

function myFunction() {
  var id = 'xxxxx' 
  var file = Drive.Files.get(id)
  Logger.log(file.lastModifyingUserName)
 }

こんな感じ。で、実行すると・・・

ポキオ GoogleAppsScript GAS 技術書典

ちゃんと取得できましたー。(個人名なので伏せてますが・・・)

というわけで

なんとなくGoogle Drive上のファイルの情報を引き出せそうな気がしてきました。今後は、定期的にファイルをポーリングして監視したり、それを何かしらの形で通知する方法を考えていきたいところです。はやく執筆しろ。

enebular+IFTTTで夜になったらSonosスピーカーの音量を下げるようにしてみた

夜は静かに。

ポキオ Sonos IFTTT

ついつい音量上げたくなるやつ

Sonosのスピーカー「One」を使い始めて数週間。時間を見つけてはSonosで音楽を聞いてますが、ついつい調子に乗って音量上げがち。 でも、流石に夜は近所迷惑だったり、寝てる家族に申し訳ないので、ここはなんとか自動化で音量コントロールできないかと考えたわけです。

ポキオ Sonos IFTTT

幸運なことに、IFTTTでSonosの音量設定(Set Volume)ができるので、これをつかって音量コントロールをしてみたいと思います。

ちょっぱやで作るよー!

このSonosのActionを毎度おなじみのWebhooksのTriggerから発火するように設定し、enebular(Node-RED)から毎晩23時にそのWebhooksURLを叩くようにするシンプルな仕組みです。

ポキオ Sonos IFTTT

毎晩23時に発火させるには、Injectionノードの設定をちょっといじるだけ。

ポキオ Sonos IFTTT

これだけで、毎晩発火するようになりました。実際にWebhooksの設定は、次のFunctionノードで行っています。

msg = {};
msg.method = 'POST';
msg.headers = 'Content-Type: application/json';
msg.url = 'http://maker.ifttt.com/trigger/xxxxx/with/key/xxxxx'
msg.payload = {'value1' : '10'};
return msg;

value1に10を設定し、Webhooks側ではここで指定された数値の音量を設定するようにしています。ここで、数値は0-100まで指定可能です。まぁ、10%くらいに音量を絞っておけば文句は言われないでしょう。

これで音量設定を自動化できました!

毎晩23時になると、急に音量が10%になるようになりました。

IFTTTのSonos Actionsには、まだまだいろんなコントロールが可能なので、自分のライフスタイルにあった自動化ができそうですね!

[asin:B07HSBKTFH:detail]

今回のフローはこんな感じですー。

[
    {
        "id": "c5495b36.8b97c8",
        "type": "tab",
        "label": "Sonos Flow",
        "disabled": false,
        "info": ""
    },
    {
        "id": "7451e07.f12dc2",
        "type": "inject",
        "z": "c5495b36.8b97c8",
        "name": "毎日23時に実行",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "00 23 * * *",
        "once": false,
        "onceDelay": 0.1,
        "x": 140,
        "y": 60,
        "wires": [
            [
                "44f2a101.c42ae"
            ]
        ]
    },
    {
        "id": "b7541cbb.283a4",
        "type": "http request",
        "z": "c5495b36.8b97c8",
        "name": "IFTTTにHTTP-POST",
        "method": "use",
        "ret": "txt",
        "url": "",
        "tls": "",
        "x": 560,
        "y": 60,
        "wires": [
            [
                "68b920c2.b7034"
            ]
        ]
    },
    {
        "id": "44f2a101.c42ae",
        "type": "function",
        "z": "c5495b36.8b97c8",
        "name": "IFTTT Webhooks設定",
        "func": "msg = {};\nmsg.method = 'POST';\nmsg.headers = 'Content-Type: application/json';\nmsg.url = 'http://maker.ifttt.com/trigger/xxxxx/with/key/xxxxx'\nmsg.payload = {'value1' : '10'};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 340,
        "y": 60,
        "wires": [
            [
                "b7541cbb.283a4"
            ]
        ]
    },
    {
        "id": "68b920c2.b7034",
        "type": "debug",
        "z": "c5495b36.8b97c8",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "x": 730,
        "y": 60,
        "wires": []
    }
]

SonosをIFTTT経由でコントロールしてみる(後編)

SonoSwitchをつくるお!

ポキオ Sonos IFTTT

前回までのあらすじ

SonosはIFTTTから制御できそうというところまでわかりました。

relativelayout.hatenablog.com

今回は、IFTTTのSonos Actionsを使うべく、WebhooksをTriggerとして、M5StackからIFTTT経由でSonosのスピーカーの制御をしてみようと思います。

M5Stackって?

詳しい説明は省きますが、ディスプレイやWi-Fi/BTなどがもろもろついた小さなマイコンボードです。

m5stack.com

プログラミング方法も色々ありますが、ブラウザ上でノンコーディングでプログラミングできるUI Flowを使って実装してみようと思います。

やりたいことは以下の通り。

  • Sonosのスピーカーを制御(Prev/Next/Pause)できるSonoSwitch
  • おしゃれなUIを表示
  • ボタンを押すと制御(Prev/Next/Pause)が行えるようにする
  • 裏ではIFTTTのWebhooksへHTTP-GETを行う

ポキオ Sonos IFTTT

IFTTT側の設定

先述の通り、TriggerをWebhooks、ActionをSonosとなるようにレシピを設定します。

ポキオ Sonos IFTTT

レシピは別々のWebhooksイベントで3つ作成します。今回は、それぞれのイベント名をこのようにしました。

  • sonoswitch1:Skip to previous
  • sonoswitch2:Pause
  • sonoswitch3:Skip to next
To trigger an Event
Make a POST or GET web request to:

https://maker.ifttt.com/trigger/{event}/with/key/{key}

それぞれのイベントごとにURLが決まり、ここにHTTP-GETをするとSonosのActionが発火するようになりました。今度は、M5StackからこれらのURLをHTTP-GETできるようにしていきます。

つぎにM5Stack側の実装

とはいえ、まずは(本質的ではないですが)UIを作ってみます。

ポキオ Sonos IFTTT

macOSKeynoteでこんな感じのUIを作ってJPGで保存し、UI Flow経由でM5Stackに流し込みます。SonosのロゴのフォントはGIll Sansが一番近かったです(笑)

UI Flowでは、こんな感じに実装していきます。

ポキオ Sonos IFTTT

MicroPythonのコードは、この記事の後ろの方で晒します。やっていることはざっくりこんな感じで、

  • 起動時にWi-Fi接続
  • ボタン押下時にそれぞれのレシピへのWebhooksへHTTP-GETを送るようにする
  • HTTP-GET成功/失敗時に効果音を出す

これをUI Flowで組んでいきます。

つかってみますー

これが、ふつーに使えます(笑)

ポキオ Sonos IFTTT

Sonosのスピーカーに直接コマンドを送っているわけではないので、1〜2秒のタイムラグはあるものの、Sonosのスピーカーの制御がちゃんとできています!

逆に言えば、インターネット回線さえあれば、理論上は地球の裏側からもSonosのスピーカーを制御できるということですねー。すごいー。

[asin:B07HSBKTFH:detail]

コードはこんな感じです

from m5stack import *
from m5ui import *
from uiflow import *
import wifiCfg
import urequests

setScreenColor(0x222222)




image0 = M5Img(0, 0, "res/SonoSwitch.jpg", True)

statusCode = None


def buttonB_wasReleased():
  global statusCode
  if not (wifiCfg.wlan_sta.isconnected()):
    wifiCfg.reconnect()
    while not (wifiCfg.wlan_sta.isconnected()):
      pass
  try:
    req = urequests.request(method='GET', url='http://maker.ifttt.com/trigger/sonoswitch2/with/key/xxxxxxx', headers={})
  except:
    pass
  statusCode = req.status_code
  pass
btnB.wasReleased(buttonB_wasReleased)

def buttonA_wasReleased():
  global statusCode
  if not (wifiCfg.wlan_sta.isconnected()):
    wifiCfg.reconnect()
    while not (wifiCfg.wlan_sta.isconnected()):
      pass
  try:
    req = urequests.request(method='GET', url='http://maker.ifttt.com/trigger/sonoswitch1/with/key/xxxxxxx', headers={})
  except:
    pass
  statusCode = req.status_code
  pass
btnA.wasReleased(buttonA_wasReleased)

def buttonC_wasReleased():
  global statusCode
  if not (wifiCfg.wlan_sta.isconnected()):
    wifiCfg.reconnect()
    while not (wifiCfg.wlan_sta.isconnected()):
      pass
  try:
    req = urequests.request(method='GET', url='http://maker.ifttt.com/trigger/sonoswitch3/with/key/xxxxxxx', headers={})
  except:
    pass
  statusCode = req.status_code
  pass
btnC.wasReleased(buttonC_wasReleased)


statusCode = -1
wifiCfg.autoConnect(lcdShow = False)
while not (wifiCfg.wlan_sta.isconnected()):
  pass
speaker.tone(1800, 100)
while True:
  if statusCode > 0:
    if statusCode == 200:
      speaker.tone(1800, 100)
      speaker.tone(2400, 100)
    else:
      speaker.tone(1800, 300)
    statusCode = -1
  wait_ms(2)

SonosをIFTTT経由でコントロールしてみる(前編)

みんな大好きIFTTT!

ポキオ Sonos IFTTT

IFTTTって?

知っている人も多いハズのこのサービス。

ポキオ Sonos IFTTT

いろいろなサービス同士を結びつけてオートメーション化するサービス。実はSonosのスピーカーはIFTTTに対応しているんです。今回は、IFTTTで何ができるのか?というところを軽く探ってみようと思います。

連携はかんたん

ExploreからSonosと検索すると、かんたんにみつかりました。

ポキオ Sonos IFTTT

このページのConnectを押してやると、Sonosアカウントと紐付けができます。

ポキオ Sonos IFTTT

このあたりがGrantされるPermissionたち。See what...とありますが、執筆時時点ではそれを利用した機能はなさそうです。(詳しくは後述)

使い方

レシピのアクションにSonosを指定するだけ。

ポキオ Sonos IFTTT

…そう、SonosはIFTTTに対応しているものの、Triggerとしては使えないようです。なので、先程のSee what...のように「いま聞いている楽曲情報をTweetする」みたいなSonosがTriggerとなるレシピは作成することができません。必ず、他のサービスがTriggerとなりSonosが動き出すといった形になります。

では、IFTTTのアクションでは何ができるのかというと・・・

ポキオ Sonos IFTTT

  • Play Favorite
  • Play Stream
  • Skip to Next/Previous Track
  • Pause/Resume
  • Volume Up/Down
  • Set Volume
  • Mute/Unmute

このような、一通りの操作が可能です。Triggerにはなれないものの、これだけ幅広い制御がActionとして可能なので、夢が広がりそうですね。

ポキオ Sonos IFTTT

ちなみに、Actionは実行するGroupやスピーカーを設定できます。家のSonosスピーカーの制御がIFTTT経由でできるってちょっとすごいですよね・・・。Triggerは同じネットワーク上にいなくて良いわけですから・・・。次回は、このActionを使った例を紹介します!続く!

[asin:B07HSBKTFH:detail]

GeekservoでLEGOを自走させてみた

うごくぞー!

ポキオ Geekservo LEGO サーボモーター

LEGO、いいよLEGO

先日、LEGO互換のサーボモーターと手に入れてニヤニヤしていました(笑)

relativelayout.hatenablog.com

せっかくなので、これをLEGOで作ったクルマに取り付けてみて、走らせてみます。

車体はこんな感じ

もう購入から20年近く経っているLEGOをかき集めて、こんな感じの車体を作りました。

ポキオ Geekservo LEGO サーボモーター

サーボモーターに中くらいのタイヤを直付けしています。このタイヤはLEGO TECHNIC用のやつなので、シャフトが刺さるようになってます。

ポキオ Geekservo LEGO サーボモーター

フロントのタイヤは小さめのタイヤに、自由に回転するパーツを組み合わせることで、小回りが効くようになっています。ステアリング自体をサーボモーターで制御するのではなく、リアにある2つの独立したサーボモーターを制御することで左右に曲がれるようにしています。

マイコンを載せて、走り初め!

この車体に、Arduino UNOと、モバイルバッテリーを積んで、サーボモーターと接続してみます。

[asin:B0044X2E5S:detail]

この投稿をInstagramで見る

#LEGO を #Arduino で走らせてみた。なんかかわいい。

pokio(@pokiiiiio)がシェアした投稿 -

後述のコードをArduino UNOに焼くと、こんな感じで走ります。今回は、動き方自体をハードコードさせているので、ラジコンのように意のままに動かすことはできませんが、通信部材がついているような別のマイコンで動かせれば、ラジコン的な使い方も可能です。

というわけで、

とりあえず、チョッパやで作ってみました。

  • サーボモーターに個体差があり、回転速度が異なる
  • マイコンとモバイルバッテリーをオシャレに取り付けたい
  • USBケーブルが長くてダサい

など、いろいろ改善の余地はありますが、まずは動いたのでOKとさせてください(笑)

コードはこんな感じ

A0A1につないでますが、多分どのピンでも大丈夫なはずです。

#include <Servo.h>

Servo servo1;
Servo servo2;

void setup() {
  servo1.attach(A0);
  servo2.attach(A1);
}

void loop() {
  servo1.write(0);
  servo2.write(180);
  delay(2000);

  servo1.write(90);
  servo2.write(180);
  delay(2000);
  
  servo1.write(180);
  servo2.write(0);
  delay(2000);

  servo1.write(180);
  servo2.write(90);
  delay(2000);
}

レゴ(LEGO) テクニック ランドローバー・ディフェンダー 42110

レゴ(LEGO) テクニック ランドローバー・ディフェンダー 42110

  • 発売日: 2019/10/04
  • メディア: おもちゃ&ホビー

「Androidのメモとか」は、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、Amazonアソシエイト・プログラムの参加者です。

このブログは個人的なメモ書きであったり、考えを書く場所であります。執筆者の所属する団体や企業のコメントや意向とは無関係であります。また、このブログは必ずしも正しいことが書かれているとは限らず、誤字脱字や意図せず誤った情報を載せる場合がありえます。それが原因で読者が不利益を被ったとしても、執筆者はいかなる責任も負いません。ありがとうございます。