Yado_tech

旅館+ITとはなんぞ

Raspberry PiでMultiroom Audio

f:id:devilmakelie:20200924234520p:plain


Spotifyを家中で聞きたい・・家中を音楽で満たしたい・・・そう思ったことはありませんか?私はあります。

でもケーブルを家中に敷設するのは見た目も悪いし・・・そう思ったことはありませんか?私はあります。

てことでRaspberry Piを利用してMultiRoom Audioをしてみました。

全体的なイメージはこんな感じです。

f:id:devilmakelie:20200924235840p:plain


順を追って行きます。

サーバー編

Raspotify のインストール

かなり簡単で、ワンライナーで行けます。

 curl -sL https://dtcooper.github.io/raspotify/install.sh | sh 

Snapcast Serverのインストール


バージョンなどはReleasesのページで適宜最新にして読み替えてください。

wget https://github.com/badaix/snapcast/releases/download/v0.21.0/snapserver_0.21.0-1_armhf.deb
sudo dpkg -i snapserver_0.21.0-1_armhf.deb

Raspotifyの設定

/etc/default/raspotify を編集します。

sudo nano /etc/default/raspotify 
# /etc/default/raspotify -- Arguments/configuration for librespot

# Device name on Spotify Connect
DEVICE_NAME="<好きな名前(日本語可)>"

# Bitrate, one of 96 (low quality), 160 (default quality), or 320 (high quality)
BITRATE="320"

# Additional command line arguments for librespot can be set below.
# See `librespot -h` for more info. Make sure whatever arguments you specify
# aren't already covered by other variables in this file. (See the daemon's
# config at `/lib/systemd/system/raspotify.service` for more technical details.)
#
# To make your device visible on Spotify Connect across the Internet add your
# username and password which can be set via "Set device password", on your
# account settings, use `--username` and `--password`.
#
# To choose a different output device (ie a USB audio dongle or HDMI audio out),
# use `--device` with something like `--device hw:0,1`. Your mileage may vary.
#
OPTIONS="--username <SPOTIFYのユーザーネーム> --password <SPOTIFYのパスワード> --zeroconf-port 65444"

# Uncomment to use a cache for downloaded audio files. Cache is disabled by
# default. It's best to leave this as-is if you want to use it, since
# permissions are properly set on the directory `/var/cache/raspotify'.
#CACHE_ARGS="--cache /var/cache/raspotify"

# By default, the volume normalization is enabled, add alternative volume
# arguments here if you'd like, but these should be fine.
#VOLUME_ARGS="--enable-volume-normalisation --linear-volume --initial-volume=100"

# Backend could be set to pipe here, but it's for very advanced use cases of
# librespot, so you shouldn't need to change this under normal circumstances.
BACKEND_ARGS="--backend pipe --device /tmp/snapfifo"

必要なのはDEVICE NAME、OPTIONS、BACKEND_ARGSの3つ
OPTIONSには開放するポート番号を--zeroconf-port以降に、
BACKEND_ARGSにはパイプを指定してあげましょう。

Snapcast Serverの設定

/etc/default/snapserver を編集しますが、今回は特に必要ありません。

 sudo nano /etc/default/snapserver 

次にサンプルレートを変更しましょう

サンプルレートは`/etc/snapserver.conf`で変更できます。
[stream]セクションに置くみたいです。

source = pipe:///tmp/snapfifo?name=default
sampleformat = 44100:16:2

それぞれ設定した後は再読込しておきましょう。

sudo service snapserver restart 
sudo service raspotify restart

ufwファイアウォール)の設定

ip-tablesのラッパー。とっても簡単なのでサクッとインストールしておきましょう。

sudo apt-get install ufw
sudo ufw allow 22 #SSH用のポートを開けておく
sudo ufw allow 1704 #snapserver用のポート①
sudo ufw allow 1705 #snapserver用のポート②
sudo ufw allow 1780 #snapeserver web用のポート
sudo ufw allow 65444 #raspotify用のポート

クライアント側のラズパイの設定

snapclientのインストール

wget https://github.com/badaix/snapcast/releases/download/v0.21.0/snapclient_0.21.0-1_armhf.deb
sudo dpkg -i snapclient_0.21.0-1_armhf.deb

Snapcast Clientの設定

/etc/default/snapclientの設定

sudo nano /etc/default/snapclient
START_SNAPCLIENT=true
SNAPCLIENT_OPTS="--host "

hostにはサーバーのIPアドレスを指定してください。

Snapclientのリスタート

sudo service snapclient restart

Tips

コマンドラインでのボリュームの変更

alsamixerを使用します。

alsamixer

nano(ラズパイ用のコマンドラインエディタ)を終了する方法

CTRL+Xで終了し、Yを押して保存します。

WEB上で音量を調整したいとき

snapserverのIPアドレス:1780でWEBにもアクセスできます。

f:id:devilmakelie:20200925161721p:plain




あとは適当なスマホSpotifyを実行し、ConnectでSnapcastを選べばMultiroom Audioが出来ます。

これで音楽に包まれた生活ができますね。最高!

車のQOLが爆上がりして優勝できるやつつくった。

M5Stackを買いました。

www.switch-science.com


これはArduino+ボタン+液晶+SDカードリーダーを1つにまとめたもの

ブレッドボードなしに色々できるので電子工作のハードルを一気に下げてくれるすぐれものです。

こちらで画像のようなものを作りました。

http://pic.twitter.com/6MBtczmH2wpic.twitter.com

仕組みはM5Stackをブルートゥースキーボードとして動かしてキーを送信しているだけです。
Bluetoothキーボードはこちらのライブラリをインクルードして使いました。

github.com

あとはこのような画像を用意して

f:id:devilmakelie:20200523083150j:plain

以下のようなコードを作るだけです。

#include <BleConnectionStatus.h>
#include <BleKeyboard.h>
#include <KeyboardOutputCallbacks.h>

#include <M5Stack.h>
BleKeyboard bleKeyboard("Spotify Controller");
void setup() {
  M5.begin();
  M5.Lcd.drawJpgFile(SD,"/spotify.jpg");
 
  bleKeyboard.begin();

}

void loop() {
  M5.update();
  if(bleKeyboard.isConnected()){
    //Backword
    if(M5.BtnA.wasPressed()){
      bleKeyboard.write(KEY_MEDIA_PREVIOUS_TRACK);
            }
    //Play, Pause
    if(M5.BtnB.wasPressed()){
      bleKeyboard.write(KEY_MEDIA_PLAY_PAUSE);
      }
    //FastForword
    if(M5.BtnC.wasPressed()){
      bleKeyboard.write(KEY_MEDIA_NEXT_TRACK);
      
      }
    //wait

    delay(100);
    
    }

Arduinoだと起動が早いので車との相性が抜群だと思いました。

お風呂に温度異状があった場合にスマートライトの色を変える。GAS版

tuya Smart Life がIFTTTと連携しなくなるということでびっくりした話。

youtu.be

同時にこういった記事も見つけた。

qiita.com

こちらの記事ではnode.jsからtuya apiにアクセスしていたが、サーバーを用意するのもまだるっこしいので

GASで書いてみた。

必要なもの

  • おんどとりの温度計

データロガー おんどとり | SMART VALVE | T&D Corporation

  • スマートLED(私は2つで3,000円のこちらを購入した)



この辺は先程挙げた記事の中にやり方は書いてあります。

これで以下のようなGASを書いて5分ごとに実行すればOKです。

//TUYA API 設定 環境に応じて書き換えて下さい。
const clientId = "[CLIENTID]";
const clientSecret = "CLIENTSECRET";
const device_id = ["DEVICE_A", "DEVICE_B"];

//おんどとりweb storage API 設定
//環境に応じて書き換えて下さい。
const api_key = "API_KEY";
const login_id = "LOGIN_ID";
const password = "PASSWORD";
//>>>>【重要】最後にtemps関数内の風呂温度の内容を書き換えてください。<<<<<
//

//温度を配列で返す関数
function temps() {
  const ondotori_url = "https://api.webstorage.jp/v1/devices/current";

  const furo_temp = () => {
    var paylord = {
      "api-key": api_key,
      "login-id": login_id,
      "login-pass": password,
    };
    var headers = {
      "X-HTTP-Method-Override": "GET",
      "Content-Type": "application/json",
    };
    var options = {
      method: "post",
      payload: JSON.stringify(paylord),
      headers: headers,
      muteHttpExceptions: true,
    };

    return UrlFetchApp.fetch(ondotori_url, options);
  };
  return JSON.parse(furo_temp());
}

//ここまで関数

//ここから本文
function myfunction() {
  //温度のJSONを取得する
  var tmprs = temps();

  //デバッグ用 女子風呂の温度
  //Logger.log("女子:" + tmprs["devices"][1]["channel"][0].value)

  //↓↓↓男子風呂の温度 環境に応じて切り替えて下さい
  var danshi = tmprs["devices"][2]["channel"][0].value;

  //↓↓↓女子風呂の温度 環境に応じて切り替えて下さい
  var joshi = tmprs["devices"][1]["channel"][0].value;

  if (Math.max(danshi, joshi) > 43) {
    Logger.log(`熱い:男子:${danshi} 女子:${joshi}`);

    setColour(colour.red);
  } else if (Math.min(danshi, joshi) < 40) {
    Logger.log(`冷たい:男子:${danshi} 女子:${joshi}`);

    setColour(colour.blue);
  } else {
    Logger.log(`普通:男子:${danshi} 女子:${joshi}`);
    setColour(colour.green);
  }

  //if (Math.min(danshi,joshi)<38 && Math.max(danshi,joshi)>44){
  //  Logger.log("なんか変")
  //  status = "somethingwrong"
  //}
}
//ここまで本文

//以降はTUYA API用スクリプト
//setColour(colour.red or colour.blue or colour.green)以外はエラーとなります。
//単純に動かないだけ。

var url = "https://openapi.tuyaus.com/v1.0/token?grant_type=1";
var command_url = `https://openapi.tuyaus.com/v1.0/devices/${device_id}/commands`;
var command_urls = device_id.map(
  (key) => `https://openapi.tuyaus.com/v1.0/devices/${key}/commands`
);
var colour = {
  blue: { h: 240, s: 255, v: 255 },
  red: { h: 360, s: 255, v: 255 },
  green: { h: 120, s: 255, v: 100 },
};

function setColour(colors) {
  var timeStamp = getTime().toString();

  var token = retreiveAccessToken(clientId, clientSecret, timeStamp, url);
  var sign = calcSignWithToken(clientId, token, clientSecret, timeStamp);
  var headers = {
    client_id: clientId,
    access_token: token,
    t: timeStamp,
    sign: sign,
    sign_method: "HMAC-SHA256",
    "Content-Type": "application/json",
  };

  var options = {
    method: "POST",
    headers: headers,
    payload: JSON.stringify({
      commands: [
        { code: "switch_led", value: true },
        { code: "colour_data", value: colors },
      ],
    }),
  };
  // Logger.log(`access_token: ${token}`);
  // Logger.log(`sign :${sign}`);
  // Logger.log(`timestamp:${timeStamp}`);
  res = command_urls.map((key) => UrlFetchApp.fetch(key, options));
  Logger.log(res);
}

function retreiveAccessToken(clientId, clientSecret, timeStamp, url) {
  var easy_sign = calcSign(clientId, clientSecret, timeStamp);
  var headers = {
    client_id: clientId,
    sign: easy_sign,
    t: timeStamp,
    sign_method: "HMAC-SHA256",
    // Authorization: `Bearer $clientSecret`,
  };

  var options = {
    method: "GET",
    headers: headers,
    payload: {},
  };

  res = UrlFetchApp.fetch(url, options);
  return JSON.parse(res).result.access_token;
}

function getTime() {
  var now = new Date().getTime();
  return now;
}

function calcSign(clientId, clientSecret, timeStamp) {
  var str = clientId + timeStamp;
  var hash = Utilities.computeHmacSha256Signature(str, clientSecret);
  var signature = hash.reduce(function (str, chr) {
    chr = (chr < 0 ? chr + 256 : chr).toString(16);
    return str + (chr.length == 1 ? "0" : "") + chr;
  }, "");
  return signature.toUpperCase();
}

function calcSignWithToken(clientId, accessToken, clientSecret, timeStamp) {
  var str = clientId + accessToken + timeStamp;
  var hash = Utilities.computeHmacSha256Signature(str, clientSecret);
  var signature = hash.reduce(function (str, chr) {
    chr = (chr < 0 ? chr + 256 : chr).toString(16);
    return str + (chr.length == 1 ? "0" : "") + chr;
  }, "");
  return signature.toUpperCase();
}

ログインにめっちゃ苦労したけどなんとか出来た。よかったよかった。
iot.tuya.comのドキュメントの中にPOSTMANの使い方のなかにSHA256形式で暗号化してログインするというスクリプトが合ったのでそれをGASに合わせて書き換えるのが苦労した。

お風呂に温度異状があった場合にスマートライトの色を変える。

前回まででGASで異状検知した。

 

yadotech.hateblo.jp

 次はセルが更新された場合に電球の色を変える。

 

つかったのはこちら。

 

 大体いっこ1,500円くらい

 

あとはこれらをiftttでつなぐ。

 

IFTTTのつなぎ方は

SpreadsheetとSmartLife(スマートLEDのアプリ)をIFTTTに登録し、

 

Sheetsが更新されたらSmart Lifeで色を変えるというアクションを作る。

今回は3つの場合(熱い・冷たい・普通)で赤・青・緑に変更されるようにした。

 

f:id:devilmakelie:20200416173002p:plain

 

最後にGASをトリガー登録(時間主導)で10分ごとに登録する。

 

おしまい。

 

本日作ったばかりなので制限等はよくわからないが、そこそこいけるのではないかと考えている。

 

 

GASでおんどとりのAPIを叩く②

yadotech.hateblo.jp

ここから次は異状があったときに特定のセルを更新するまで

//環境に応じて書き換えるものはapi_key , login_id , password , 温度のところ(danshi , joshiのところ)です。
//

//温度を配列で返す関数
function temps(){  
  // api_key login_id password は書き直して下さい。
  const url = "https://api.webstorage.jp/v1/devices/current"
  const api_key = ""
  const login_id =""
  const password = ""
  const furo_temp = () => {
    var paylord = {'api-key':api_key,"login-id":login_id,'login-pass':password}
    var headers = {
      "X-HTTP-Method-Override":'GET',
      "Content-Type" : "application/json",}
    var options = {
     "method" : "post",   
     "payload" : JSON.stringify(paylord),
     "headers" : headers,
     "muteHttpExceptions":true
    }
    
    return UrlFetchApp.fetch(url,options);
  
  }
  return JSON.parse(furo_temp())
  
}

//ここまで関数

//ここから本文

//温度のJSONを取得する
var tmprs = temps()

//デバッグ用 女子風呂の温度
//Logger.log("女子:" + tmprs["devices"][1]["channel"][0].value)

//男子風呂の温度 環境に応じて切り替えて下さい
var danshi = tmprs["devices"][2]["channel"][0].value

//女子風呂の温度 環境に応じて切り替えて下さい
var joshi = tmprs["devices"][1]["channel"][0].value

var nowtime = new Date()
var status 
var sht = SpreadsheetApp.getActiveSheet()

if (Math.max(danshi,joshi)>44) {
  Logger.log("熱い")
//デバッグ用  sht.getRange("B1").setValue(nowtime)
  status = "hot"
}

if (Math.min(danshi,joshi)<38){
  Logger.log("冷たい")
//デバッグ用  sht.getRange("B3").setValue(nowtime)
  status = "cold" 
}

if (Math.min(danshi,joshi)<38 && Math.max(danshi,joshi)>44){
  Logger.log("なんか変")
  status = "somethingwrong"
}

//最後のswitch文
switch(status){
  case "hot":
    sht.getRange("D5").setValue(nowtime)
    break
  case "cold":
    sht.getRange("F5").setValue(nowtime)
    break
  case "somethingwrong":  
    sht.getRange("E5").setValue(nowtime)
    break
  default:
    sht.getRange("G5").setValue(nowtime)
    break    
  
    
}

GASでおんどとりWeb Storage APIを利用する。

Python版はこちら

yadotech.hateblo.jp


こんどはGASでおんどとりWEB STORAGE APIを利用してみた。

function temps(){  
  const url = "https://api.webstorage.jp/v1/devices/current"
  const api_key = "【API_KEY】"
  const login_id ="【ID】"
  const password = "【PASSWORD】"
  const furo_temp = () => {
    var paylord = {'api-key':api_key,"login-id":login_id,'login-pass':password}
    var headers = {
      "X-HTTP-Method-Override":'GET',
      "Content-Type" : "application/json",}
    var options = {
     "method" : "post",   
     "payload" : JSON.stringify(paylord),
     "headers" : headers,
     "muteHttpExceptions":true
    }
    
    return UrlFetchApp.fetch(url,options);
  
  }
  return JSON.parse(furo_temp())
  
}

Logger.log(temps())

後はお好きにすると良いと思います。

家で一番簡単にSpotifyをブロードキャストする方法

f:id:devilmakelie:20200126201034p:plain

 

以前はChromecast Audioという物があったのですが、現在は販売停止になっていて結構高くなっちゃってたので。

 

必要なもの

1.Chromecast (5,000円くらい)

store.google.com

2.HDMI分離器 (2,500円くらい)

 

 

この2個があれば後はGoogle Homeでオーディオグループを作成するだけ。

 

意外と簡単に行けたのでオススメ