高木のブログ

GASでGoogle ToDoリストにデイリーミッションを追加する

タイトルがちょっと何言ってるかわからないかもしれない

毎日の自己研鑽タイムでやることが最近偏ってきてるなと思ったので、やることをランダムで決めてもらうことにした
そのプロトタイプ的なものをGASで作ってみた

仕様 🔗

  • 毎日Tasks APIを使って4つ選んでToDoリストに追加する
  • 前日分の消化していないミッションは最初に消す

コード 🔗

function myFunction() {
  const list_id = 'hogehoge';
  const missions = [
    '読書',
    'エンジニア学習',
    '英語学習',
    'ブログ活動',
    '筋トレ',
    'ウォーキング'
  ]

  const response = Tasks.Tasks.list(list_id)
  const tasks = response.items
  if(tasks) {
    tasks.forEach(function(task){
      Tasks.Tasks.remove(list_id, task.id)
    });
  }

  sample(missions, 4).forEach(function(mission){
    Tasks.Tasks.insert({ title: mission }, list_id)
  });
}

function shuffle([...array]) {
  for (let i = array.length - 1; i >= 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

function sample(array, num) {
  return shuffle(array).slice(0, num);
}

結果 🔗

こんな感じに ToDoリスト

補足 🔗

list_id(リストID)はTasks.Tasklists.list()で取得できる

Logger.log(Tasks.Tasklists.list());

参考 🔗

【RuboCop】Gemspec/RequiredRubyVersionの警告の対応

問題 🔗

作っているGemのGitHub ActionsでRuboCopのジョブがコケた

switchbot.gemspec:15:32: C: Gemspec/RequiredRubyVersion: required_ruby_version (2.5, declared in switchbot.gemspec) and TargetRubyVersion (2.4, which may be specified in .rubocop.yml) should be equal.
  spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')

原因 🔗

GemがRuby 2.5以上を必要としているのに対し、RuboCopの対象Rubyバージョンが2.4を指定しているためコケていた
ローカルではなぜか警告が出ていなかった

解決方法 🔗

.rubocop.ymlにTargetRubyVersionの設定を追加して無事解決

AllCops:
  TargetRubyVersion: 2.5

補足 🔗

RuboCopのデフォルトの設定項目は以下ファイルで確認することができる
https://github.com/rubocop/rubocop/blob/master/config/default.yml

参考 🔗

RuboCopの設定アレコレ - Qiita

Hugoでブログを作り直した

t1 🔗

できるといいな

puts :hoge

aaaa

Test aaa text.

t1.1 🔗

aaaa

t1.2 🔗

aaaa

t1.2.1 🔗

aaaa

t1.2.2 🔗

aaaa

t2 🔗

aaaa

t3 🔗

  1. One

/ testing some code /

  1. Two

  2. Three

超宽显示 var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text";var a = "text"; 超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示超宽显示 var a = "text";

GAS(JavaScript)で配列からランダムで指定した数の要素を取り出す

GASで配列からランダムにいくつか取り出したいという状況があり、調べてもこれが正解というものに出会えなかったので自分の中での結論をメモしておく
Rubyでいうsampleメソッドにあたるものを実現する

Rubyのsampleメソッド 🔗

items = ['Item1', 'Item2', 'Item3', 'Item4', 'Item5']
p items.sample(3)
#=> ["Item5", "Item4", "Item2"]

GAS(JavaScript)の場合 🔗

function myFunction() {
  const items = ['Item1', 'Item2', 'Item3', 'Item4', 'Item5'];
  Logger.log(sample(items, 3));
}

const shuffle = ([...array]) => {
  for (let i = array.length - 1; i >= 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

const sample = (array, num) => {
  return shuffle(array).slice(0, num);
}

実行ログ 🔗

20:47:06	お知らせ	実行開始
20:47:06	情報	[Item2, Item1, Item5]
20:47:06	情報	[Item3, Item4, Item2]
20:47:06	情報	[Item1, Item4, Item5]
20:47:06	情報	[Item3, Item2, Item1]
20:47:06	情報	[Item4, Item2, Item5]

参考 🔗

【Ruby/Rails】JSONのキーをキャメルケースからスネークケースに、文字列からシンボルに変換する

Tags: Ruby Rails API

問題 🔗

APIを叩いて返ってきたレスポンスがJSON形式でキーがキャメルケースの文字列だった
普通にJSON.parseしても問題ないが、Rubyだったらできればスネークケースのシンボルで扱いたい

SwitchBotのAPIレスポンス 🔗

{
    "statusCode": 100,
    "body": {
        "deviceList": [
            {
                "deviceId": "500291B269BE",
                "deviceName": "Living Room Humidifier",
                "deviceType": "Humidifier",
                "enableCloudService": true,
                "hubDeviceId": "000000000000"
            }
        ],
        "infraredRemoteList": [
            {
                "deviceId": "02-202008110034-13",
                "deviceName": "Living Room TV",
                "remoteType": "TV",
                "hubDeviceId": "FA7310762361"
            }
        ]
    },
    "message": "success"
}

JSON.parseした結果 🔗

JSON.parse(File.open('devices.json').read)
#=> {"statusCode"=>100, "body"=>{"deviceList"=>[{"deviceId"=>"500291B269BE", "deviceName"=>"Living Room Humidifier", "deviceType"=>"Humidifier", "enableCloudService"=>true, "hubDeviceId"=>"000000000000"}], "infraredRemoteList"=>[{"deviceId"=>"02-202008110034-13", "deviceName"=>"Living Room TV", "remoteType"=>"TV", "hubDeviceId"=>"FA7310762361"}]}, "message"=>"success"}

解決方法 🔗

deep_transform_keys(&:underscore)でスネークケースにして、deep_symbolize_keysでシンボルにする

require 'json'
require 'active_support/all' # Rubyの場合

JSON.parse(File.open('devices.json').read).deep_transform_keys(&:underscore).deep_symbolize_keys
#=> {:status_code=>100, :body=>{:device_list=>[{:device_id=>"500291B269BE", :device_name=>"Living Room Humidifier", :device_type=>"Humidifier", :enable_cloud_service=>true, :hub_device_id=>"000000000000"}], :infrared_remote_list=>[{:device_id=>"02-202008110034-13", :device_name=>"Living Room TV", :remote_type=>"TV", :hub_device_id=>"FA7310762361"}]}, :message=>"success"}

解説的なもの 🔗

キーを文字列からシンボルにする方法 🔗

方法は2つある

【Redshift】テーブルのコピー

Tags: Redshift AWS

手順 🔗

create table users_like (like users);
insert into users_like (select * from users);

権限はコピーされないので別途付与

grant select on users_like to username;

参考 🔗

Sinatra関連の小ネタ

Tags: Sinatra

Sinatraに関する小ネタを随時更新していく

小ネタ 🔗

開発環境か本番環境か 🔗

get '/' do
  if settings.development?
    "development!"
  else
    "not development!"
  end
end

ホットリロード 🔗

  source 'https://rubygems.org'

  ruby '2.7.2'

  gem 'sinatra'
+ gem 'sinatra-contrib'
  require 'sinatra'
+ require 'sinatra/reloader'

  get '/' do
    'Hello, World!!'
  end

CORS の設定 🔗

【Sinatra】CORS の設定をする | 高木のブログ

404 Not Found のエラーハンドリング 🔗

【Sinatra】404 Not Found のエラーハンドリング | 高木のブログ

コントローラーを分割する 🔗

【Sinatra】Rack::URLMap を使ってコントローラーを分割する | 高木のブログ

参考 🔗

【Git】git addしたあとに差分を確認する

Tags: Git

git addしたあとにgit diffしても差分を確認することができないが、
オプション(--cached)付けたら確認することができる。

$ git diff --cached

変更ファイルだけじゃなく新規ファイルの中身も確認できるので便利。

$ git diff --cached
diff --git a/.env.sample b/.env.sample
index ac2557a..117f039 100644
--- a/.env.sample
+++ b/.env.sample
@@ -1,4 +1,3 @@
-RACK_ENV=
 NATURE_REMO_API_TOKEN=
 NATURE_REMO_LIGHT_ON_SIGNAL=
 NATURE_REMO_LIGHT_OFF_SIGNAL=
diff --git a/deploy.sh b/deploy.sh
index 7bed751..18f5c2b 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -7,5 +7,5 @@ cd ~/workspace/home_api
 docker-compose down
 git pull
 docker-compose build
-docker-compose up -d
+docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
 EOS
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
new file mode 100644
index 0000000..de49e49
--- /dev/null
+++ b/docker-compose.prod.yml
@@ -0,0 +1,6 @@
+version: '3'
+services:
+  web:
+    environment:
+      - APP_ENV=production
+    restart: always
diff --git a/docker-compose.yml b/docker-compose.yml
index 90231e8..3d63903 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,4 +9,3 @@ services:
       - .:/app
     env_file:
       - .env
-    restart: always

mackerel-agentのアップデート

Tags: Mackerel

Amazon Linuxで使っているmackrel-agentのバージョンが古かったのでアップデートをした

$ /usr/bin/mackerel-agent version
mackerel-agent version 0.32.2 (rev b9a3ef3) [linux amd64 go1.6.2]

手順 🔗

アップデート 🔗

$ sudo yum update mackerel-agent

バージョン確認 🔗

$ /usr/bin/mackerel-agent version
mackerel-agent version 0.71.1 (rev de40822) [linux amd64 go1.14.13]

再起動 🔗

$ sudo service mackerel-agent restart
Stopping mackerel-agent:                                   [  OK  ]
Starting mackerel-agent:                                   [  OK  ]

参考 🔗

Amazon Linuxにmackerel-agentをインストールする - Mackerel ヘルプ

【Mac】Touch BarからEscキーが消えた時の解決方法

Tags: Mac

起きたこと 🔗

Vimを使っていて、インサートモードからノーマルモードに切り替えようとした時にTouch BarにEscキーが表示されていないことに気づいた。

解決方法 🔗

アクティビティモニタで「TouchBarServer」を終了させる。 アクティビティモニタ_TouchBar

他にもMac自体の再起動でも復活するみたい。

参考 🔗

【Mac】mkfileでダミーファイルを作成する

サンプルのために大きめのファイルが欲しかったので探したら、標準で入っているmkfileというコマンドでできるらしい。

手順 🔗

使い方の確認 🔗

$ mkfile --help
mkfile: illegal option -- -
usage: mkfile [-nv] size[b|k|m|g] filename ...

オプションの詳細は参考リンクを

1GBのテキストファイルを作成 🔗

$ mkfile -nv 1g 1g.txt
1g.txt 1073741824 bytes

1GBあるかの確認 🔗

$ ls -lh 1g.txt
-rw-------  1 TAKAGI  staff   1.0G  2  8 21:43 1g.txt

参考 🔗

mkfileコマンド - Qiita

【Pixela】グラフのバックアップを取るスクリプト

Tags: Pixela Ruby

以前ブログに書いとおり、Togglで記録した作業時間をPixelaで可視化している。
昨年の7月くらいから毎日続けることができて、可処分時間を無駄にしないで済んでいるのは間違いなくPixelaのおかげである。

今回は、うっかり操作ミスなどをしてグラフのデータがおかしくなってしまったら辛いので、そうなる前にバックアップを取ることにした。

※ちなみに今回の場合はTogglに元データはあるので復元は可能である。ただバックアップをやってみたかっただけ。

完成品 🔗

ytkg/pixela-backup - GitHub

実行 🔗

ちゃんとした実行方法は、README.md を参照

$ bundle exec ruby app.rb task-durations td-backup
195/195

結果 🔗

グラフID以外全く同じのグラフを作ることができた。

オリジナルのグラフ 🔗

Pixela オリジナルのグラフ

バックアップしてできたグラフ 🔗

Pixela バックアップのグラフ

振り返り 🔗

  • copy_graphメソッドで既にグラフがあった場合など、エラーになるがrescueで握りつぶした
    もちろん良くはないけど、使い捨て予定だったスクリプトにしては全体的にちゃんと書いた方だと思うので良しとする

  • 進捗がわかるように何ピクセル中何ピクセルまで終わったかプログレス?的なのを入れてみた

  • 1ピクセルごとAPIを叩くことになるので、1秒のスリープを入れた。(ドキュメントに制限のルールは見つからなかったけど念の為)
    1ピクセルに付き最低1秒はかかってしまうので一気に送れたら時間短縮できて良いのになと思った

  • バックアップのグラフIDを本当は「task-durations-backup」にしたかったけど、16文字の制限があったっぽい
    もう少し緩めにしてほしい。。。

    • Validation rule: ^[a-z][a-z0-9-]{1,16} (Document )
  • 最近追加されたエンドポイントを早速使う時がきた
    Gem(sue445/pixela )も既に対応してて良き

  • こういう使い方していいのかわからない

    • 怒られたら消します

【Ruby】minitestを実行する方法

Tags: Ruby Gem minitest

とあるGemにプルリクを出すためにminitestを実行したいのにやり方が分からず、ググっても意外とすぐに出てこなかったのでメモ。
どこのリポジトリか忘れたが、GitHub Actionsで実行している記述を見つけて無事解決した。

$ bundle exec rake test
Run options: --seed 31456

# Running:

....

Finished in 0.041567s, 96.2302 runs/s, 96.2302 assertions/s.

4 runs, 4 assertions, 0 failures, 0 errors, 0 skips

SwitchBotのスマート加湿器が止まっていたら起動させるスクリプト

Tags: SwitchBot API IoT

SwitchBotのスマート加湿器が振動によって安全装置が作動したのか、一定時間連続起動していると止まるのかわからないが、気がついたら止まっていることがある。
ということで、止まっていたら起動させるシェルスクリプトを書いて、Raspberry Piで動かすことにした。

さっそく実際にAPIを使って操作する事例ができた。 (SwitchBotのスマート加湿器をAPIで操作する

完成物 🔗

リポジトリ 🔗

ytkg/wake_up_smart_humidifier - GitHub

コード 🔗

シェルスクリプトはあまり書きなれていないからこれでいいのかわからん。

#!/bin/bash

set -eu

SCRIPT_DIR=$(cd $(dirname $0); pwd)

source "${SCRIPT_DIR}/.env"

power=$(
  curl -s https://api.switch-bot.com/v1.0/devices/${SWITCHBOT_DEVICE_ID}/status \
    -H "Authorization: ${SWITCHBOT_API_TOKEN}" \
    | jq .body.power -r
)

if [[ $power = off ]]; then
  curl -s -X POST https://api.switch-bot.com/v1.0/devices/${SWITCHBOT_DEVICE_ID}/commands \
    -H "Authorization: ${SWITCHBOT_API_TOKEN}" \
    -H 'Content-Type: application/json' \
    -d '{"command": "turnOn","parameter": "default","commandType": "command"}' \
    -o /dev/null
fi

使い方 🔗

リポジトリのREADMEを参照。

Upptimeでブログの死活監視をする

Upptime 」という無料でウェブサイトの死活監視ができるサービス(というかOSS)を見つけたので、
このブログとまだ1記事も書いていないけどエンジニア以外の記事を書く予定の「高木のノート 」を死活監視するようにしてみた。
GitHub ActionsやGitHub Pagesを使うから無料でできるっぽい。

完成物 🔗

ステータスページ 🔗

https://status.takagi.blog/

スクリーンショット 🔗

tatus.takagi.blog

リポジトリ 🔗

ytkg/status.takagi.blog - GitHub

参考 🔗

GitHubのサービスを駆使してウェブサイトの死活監視が無料で行える「Upptime」 - GIGAZINE

SwitchBotのスマート加湿器をAPIで操作する

Tags: SwitchBot API IoT

SwitchBotのスマート加湿器 を買ったので、ご多分に漏れずAPIで操作してみる。
ちなみに買ったメインの目的が加湿器をAPIを使って操作してみたいという理由だけだけど、加湿器の機能としても大変満足している。

手順 🔗

ドキュメント: SwitchBotAPI/README.md at main · OpenWonderLabs/SwitchBotAPI

APIトークンを取得する 🔗

公式アプリから取得できる。(iPhoneでしか確認していないが、Androidも変わらないはず)

  1. 「プロフィール」をタップ
  2. 「設定」をタップ
  3. 「アプリバージョン」を10回タップ(「開発者向けオプション」が表示される)
  4. 「開発者向けオプション」をタップ(トークンが生成される)

デバイスを取得する 🔗

リクエスト 🔗

curl -X GET -H 'Authorization: token' https://api.switch-bot.com/v1.0/devices

レスポンス 🔗

{
  "statusCode": 100,
  "body": {
    "deviceList": [
      {
        "deviceId": "AC0123456789",
        "deviceName": "リビング加湿器",
        "deviceType": "Humidifier",
        "enableCloudService": true,
        "hubDeviceId": "000000000000"
      },
      {
        "deviceId": "XX0123456789",
        "deviceName": "リビング温湿度計",
        "deviceType": "Meter",
        "enableCloudService": false,
        "hubDeviceId": "000000000000"
      }
    ],
    "infraredRemoteList": []
  },
  "message": "success"
}

デバイスのステータスを取得する 🔗

リクエスト 🔗

$ curl -H 'Authorization: token' https://api.switch-bot.com/v1.0/devices/AC0123456789/status

レスポンス 🔗

{
  "statusCode": 100,
  "body": {
    "deviceId": "AC0123456789",
    "deviceType": "Humidifier",
    "hubDeviceId": "000000000000",
    "nebulizationEfficiency": 1,
    "humidity": 43,
    "auto": true,
    "childLock": false,
    "sound": true,
    "power": "off",
    "temperature": "19.9"
  },
  "message": "success"
}

デバイスにコマンドを送信する 🔗

https://github.com/OpenWonderLabs/SwitchBotAPI/blob/main/README.md#command-set-for-physical-devices

Nature RemoのローカルAPIを叩いて家電を操作する

Nature Remoはアプリなどから操作した際、たとえ家に居たとしてもクラウドAPIを経由して実行される仕様になっている
この仕様のせいで、Nature Remoのクラウドで障害が起きたときに電気が消すことができなくて不便な思いをしたので、次に障害が起きても問題ないようにローカルAPI経由でも家電を操作できるようにしておく

手順 🔗

公式ドキュメント: https://local-swagger.nature.global/

Nature RemoのIPアドレスを確認 🔗

Instance Nameを特定する 🔗

$ dns-sd -B _remo._tcp
Browsing for _remo._tcp
DATE: ---Thu 14 Jan 2021---
21:00:16.807  ...STARTING...
Timestamp     A/R    Flags  if Domain               Service Type         Instance Name
21:00:21.766  Add        2   6 local.               _remo._tcp.          Remo-XXXXXX

IPアドレスを確認 🔗

Instance Nameに.localを追加

$ dns-sd -G v4 Remo-XXXXXX.local
DATE: ---Thu 14 Jan 2021---
21:00:47.557  ...STARTING...
Timestamp     A/R    Flags if Hostname                               Address                                      TTL
21:00:47.557  Add 40000002  6 Remo-XXXXXX.local.                     192.168.10.3                                 120

赤外線信号の受信 🔗

Nature Remo本体にリモコンで信号を送ったあとに以下を実行する

$ curl -X GET http://192.168.10.3/messages -H "X-Requested-With: curl" -H "Expect:"
{"format":"us","freq":36,"data":[3401,1821,353,516,358,1387,356,506,361,509,360,509,360,510,383,482,353,516,353,516,358,508,358,510,360,509,358,511,350,1391,358,513,363,517,347,524,359,515,358,516,358,516,357,517,361,511,354,519,361,1386,355,517,360,510,348,514,353,516,363,507,358,507,386,483,360,509,359,1384,352,516,380,1361,350,1391,360,1387,361,1384,448,419,358,510,357,1383,360,509,442,1302,394,1354,356,1380,354,1392,358,509,353,1390,392,65535,0,8885,3403,1821,360,506,354,1390,359,509,359,511,358,506,363,507,353,516,360,82,65535,427,356,508,361,510,357,511,454,410,365,504,363,1382,348,524,352,521,352,525,357,520,355,518,356,517,356,515,359,514,361,513,362,1385,351,518,359,512,349,514,360,510,359,510,357,511,454,414,360,507,358,1384,352,517,359,1379,361,1382,360,1387,361,1383,354,515,350,518,360,1383,351,513,353,1392,358,1386,361,1384,358,1385,356,510,382,1360,357,65535,0,8923,3403,1825,349,523,359,1391,356,506,361,511,413,456,358,509,357,510,359,508,352,517,358,507,351,518,360,510,383,486,357,1381,362,508,350,518,358,511,355,511,359,509,353,518,356,511,358,507,361,510,358,1384,359,510,357,509,358,87,65535,422,362,506,361,508,359,507,359,512,358,508,352,1393,356,515,405,1343,358,1385,358,1382,354,1388,360,515,358,516,359,1390,355,511,358,1384,351,1392,358,1385,350,1394,360,509,362,1380,351]}

赤外線信号の送信 🔗

受信時に受け取ったものをそのままPOSTで送ってあげればよい

$ curl -X POST http://192.168.10.3/messages -H "X-Requested-With: curl" -H "Expect:" -d '{"format":"us","freq":36,"data":[3401,1821,353,516,358,1387,356,506,361,509,360,509,360,510,383,482,353,516,353,516,358,508,358,510,360,509,358,511,350,1391,358,513,363,517,347,524,359,515,358,516,358,516,357,517,361,511,354,519,361,1386,355,517,360,510,348,514,353,516,363,507,358,507,386,483,360,509,359,1384,352,516,380,1361,350,1391,360,1387,361,1384,448,419,358,510,357,1383,360,509,442,1302,394,1354,356,1380,354,1392,358,509,353,1390,392,65535,0,8885,3403,1821,360,506,354,1390,359,509,359,511,358,506,363,507,353,516,360,82,65535,427,356,508,361,510,357,511,454,410,365,504,363,1382,348,524,352,521,352,525,357,520,355,518,356,517,356,515,359,514,361,513,362,1385,351,518,359,512,349,514,360,510,359,510,357,511,454,414,360,507,358,1384,352,517,359,1379,361,1382,360,1387,361,1383,354,515,350,518,360,1383,351,513,353,1392,358,1386,361,1384,358,1385,356,510,382,1360,357,65535,0,8923,3403,1825,349,523,359,1391,356,506,361,511,413,456,358,509,357,510,359,508,352,517,358,507,351,518,360,510,383,486,357,1381,362,508,350,518,358,511,355,511,359,509,353,518,356,511,358,507,361,510,358,1384,359,510,357,509,358,87,65535,422,362,506,361,508,359,507,359,512,358,508,352,1393,356,515,405,1343,358,1385,358,1382,354,1388,360,515,358,516,359,1390,355,511,358,1384,351,1392,358,1385,350,1394,360,509,362,1380,351]}'
{}

参考 🔗

Nature Remo E用のGem作った

Nature Remo E / E lite用のAPIクライアント用のGemを作った。E liteでしか動作確認していないけど、Eでも使えるはず。

ytkg/nature_remo_e - GitHub

作った理由 🔗

  • 単純にGemを作ってみたかった
  • Nature RemoのGemは既に存在しているが、スマートメーターの値だけを取るだけなのにコードが長くなってしまうので、それ専用にGemを作ってコードを短くしたかった。

コードを短くすることができた具体例 🔗

【Nature Remo E lite】瞬時電力計測値をAmbientでグラフ化する で書いたコード

Before 🔗

require 'dotenv/load'
require 'nature_remo'
require 'ruby-ambient'

client = NatureRemo::Client.new(ENV['NATURE_REMO_API_TOKEN'])
response = client.appliances
appliances = JSON.parse(response.body, symbolize_names: true)

el_smart_meter = appliances.find { |appliance| appliance[:type] == 'EL_SMART_METER' }
echonetlite_properties = el_smart_meter[:smart_meter][:echonetlite_properties].each_with_object({}) do |echonetlite_property, hash|
  hash[echonetlite_property[:name].to_sym] = echonetlite_property[:val].to_i
end

ambient = Ambient.new(ENV['AMBIENT_CHANNEL_ID'], write_key: ENV['AMBIENT_WRITE_KEY'])
ambient.send({ d1: echonetlite_properties[:measured_instantaneous] })

After 🔗

require 'dotenv/load'
require 'nature_remo_e'
require 'ruby-ambient'

client = NatureRemoE::Client.new(ENV['NATURE_REMO_API_TOKEN'])

ambient = Ambient.new(ENV['AMBIENT_CHANNEL_ID'], write_key: ENV['AMBIENT_WRITE_KEY'])
ambient.send({ d1: client.measured_instantaneous })

できること 🔗

  • ECHONETプロパティ全てをHashクラスで取得できる
  • ECHONETプロパティを個別に取得できる
名前 プロパティ
normal_direction_cumulative_electric_energy 積算電力量計測値(正方向)
reverse_direction_cumulative_electric_energy 積算電力量計測値(逆方向)
coefficient 係数
cumulative_electric_energy_unit 積算電力量単位
cumulative_electric_energy_effective_digits 積算電力量有効桁数
measured_instantaneous 瞬時電力計測値

この名前はNature社が独自に命名したもので、将来的に変わる可能性があるとのこと。

【Nature Remo E lite】瞬時電力計測値をAmbientでグラフ化する

半年くらい前にNature Remo E lite を買って放置していたが、せっかくなのでAmbient に値を投げてグラフ化してみることにした。
Nature Remoの公式アプリでグラフは見れるので、正直何も意味はない。
API でいろいろ値は取れるが、ばっと見、意味がわかるのは瞬時電力計測値くらいなのでそれを使う。

手順 🔗

コード 🔗

source 'https://rubygems.org'

git_source(:github) {|repo_name| 'https://github.com/#{repo_name}' }

gem 'dotenv'
gem 'nature_remo'
gem 'ruby-ambient'
NATURE_REMO_API_TOKEN=
AMBIENT_CHANNEL_ID=
AMBIENT_WRITE_KEY=
require 'dotenv/load'
require 'nature_remo'
require 'ruby-ambient'

client = NatureRemo::Client.new(ENV['NATURE_REMO_API_TOKEN'])
response = client.appliances
appliances = JSON.parse(response.body, symbolize_names: true)

el_smart_meter = appliances.find { |appliance| appliance[:type] == 'EL_SMART_METER' }
echonetlite_properties = el_smart_meter[:smart_meter][:echonetlite_properties].each_with_object({}) do |echonetlite_property, hash|
  hash[echonetlite_property[:name].to_sym] = echonetlite_property[:val].to_i
end

ambient = Ambient.new(ENV['AMBIENT_CHANNEL_ID'], write_key: ENV['AMBIENT_WRITE_KEY'])
ambient.send({ d1: echonetlite_properties[:measured_instantaneous] })

ytkg/nature_remo_e_lite_to_ambient - GitHub

Raspberry Piで動かす 🔗

GitHub Actionsで1分毎動かすのはちょっと気が引けたので、Raspberry Piで動かすことにした。

* * * * * cd /home/pi/workspace/nature_remo_e_lite_to_ambient/ && /home/pi/.rbenv/shims/bundle exec ruby app.rb

bundleだけだと、bundle: not foundになってしまったので、フルパスで呼び出した。
crontab何もわからん。

RubyでAmbientにデータを送信してグラフ化する

Tags: Ruby Ambient IoT

Ambient というIoTデータ向けの可視化サービスを見つけたので、RubyからAmbientにデータを送ってグラフを作るところまでをやってみた。

手順 🔗

1. ユーザー登録 🔗

Ambient 新規登録

2. チャネル作成 🔗

様々なグラフを表示するボードみたいなもの。(多分)
「チャネルを作る」ボタンを押すだけで生成される。

ambient_001

3. データ送信 🔗

ruby-ambientというGemがあったのでそれを使わせてもらう。 suruseas/ruby-ambient - GitHub

3.1 gemのインストール 🔗

$ gem install ruby-ambient

3.2 コード 🔗

require 'ruby-ambient'

am = Ambient.new('チャネルID', write_key: 'ライトキー')
am.send( { d1: 1, d2: 2.5 } )
# => #<Net::HTTPOK 200 OK readbody=true>

データはd1からd8まで8種類まで送ることができる。
最短の送信間隔は5秒。

4. グラフ作成 🔗

チャネル一覧画面のチャネル名をクリックするとボードが表示されるので、そこでグラフ追加のアイコンを押したらグラフを作れる。

ambient_002

データは最大1年分保持される。

参考 🔗

チュートリアル – Ambient

RustでHello, World!

Tags: Rust Docker

Rustも触っておきたいなと思ったので、取り急ぎのHello, World!をやってみた。

手順 🔗

事前準備 🔗

RustのDockerイメージを取得し、コンテナに入る 🔗

$ docker pull rust:1.48.0
$ docker run -it --rm rust:1.48.0 bash
root@6819088df44e:/#

Vimのインストール 🔗

root@6819088df44e:/# apt update
root@6819088df44e:/# apt install -y vim

作業用ディレクトリを作成し、移動 🔗

root@6819088df44e:/# mkdir hello_world
root@6819088df44e:/# cd hello_world
root@6819088df44e:/hello_world#

Rustファイル作成 🔗

root@6819088df44e:/hello_world# vim main.rs
fn main() {
    println!("Hello, World!");
}

コンパイル 🔗

root@6819088df44e:/hello_world# rustc main.rs
root@6819088df44e:/hello_world# ls
total 3136
-rwxr-xr-x 1 root root 3205040 Dec 30 12:41 main
-rw-r--r-- 1 root root      45 Dec 30 12:40 main.rs

mainという名前の実行可能ファイルができている。

実行 🔗

root@6819088df44e:/hello_world# ./main
Hello, world!

参考 🔗

Hello, World! - The Rust Programming Language

Raspberry Pi上にDocker ComposeでHomebridgeを起動する

Raspberry Pi上にDocker ComposeでHomebridgeを起動する手順。

手順 🔗

ディレクトリを作成し、移動 🔗

$ mkdir /path/to/homebridge
$ cd /path/to/homebridge

docker-compose.ymlを作成 🔗

$ vim docker-compose.yml
version: '3'
services:
  homebridge:
    image: oznu/homebridge:raspberry-pi
    restart: always
    network_mode: host
    volumes:
      - ./config:/homebridge
    environment:
      - PGID=1000
      - PUID=1000
      - HOMEBRIDGE_CONFIG_UI=1
      - HOMEBRIDGE_CONFIG_UI_PORT=8080

起動 🔗

$ docker-compose up -d

起動ログ 🔗

$ docker-compose logs -f

...

homebridge_1  | Enter this code with your HomeKit app on your iOS device to pair with Homebridge:
homebridge_1  |
homebridge_1  |     ┌────────────┐
homebridge_1  |     │ XXX-XX-XXX │
homebridge_1  |     └────────────┘
homebridge_1  |
homebridge_1  | [12/26/2020, 10:14:29 PM] Homebridge v1.2.4 is running on port 51393.
homebridge_1  | [12/26/2020, 10:14:31 PM] [Homebridge UI] Homebridge Config UI X v4.36.0 is listening on :: port 8080
homebridge_1  | [12/26/2020, 10:14:32 PM] [Homebridge UI] Added new user: admin
homebridge_1  | [12/26/2020, 10:14:32 PM] [Homebridge UI] Username and password have been set to default:
homebridge_1  | [12/26/2020, 10:14:32 PM] [Homebridge UI] Username: admin
homebridge_1  | [12/26/2020, 10:14:32 PM] [Homebridge UI] Password: admin

確認 🔗

http://192.168.10.2:8080にアクセスする。

RailsにTailwindCSSを導入する

Tags: Rails

Rails6.1にTailwind CSS v1.9.6を導入した手順。
Tailwind CSSのv2系はまだ対応していない部分が多いため、v1.9.6にした。
参考: https://tailwindcss.com/docs/installation#post-css-7-compatibility-build

環境 🔗

  • Ruby: 2.7.2
  • Rails: 6.1.0
  • Node.js: v12.20.0
  • Yarn: 1.22.5

手順 🔗

Tailwind CSSの導入 🔗

$ yarn add [email protected]
$ yarn tailwindcss init # tailwind.config.jsというファイルが生成される
$ mkdir app/javascript/css
$ vim app/javascript/css/tailwindcss.css
$ vim app/javascript/packs/application.js
$ vim app/views/layouts/application.html.erb
$ vim postcss.config.js
module.exports = {
  purge: [],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
  import Rails from "@rails/ujs"
  import Turbolinks from "turbolinks"
  import * as ActiveStorage from "@rails/activestorage"
  import "channels"
+ import "../css/tailwindcss.css";
  
  Rails.start()
  Turbolinks.start()
  ActiveStorage.start()
  <head>
    <title>App</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

-   <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
+   <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>
  module.exports = {
    plugins: [
+     require('tailwindcss'),
+     require('autoprefixer'),
      require('postcss-import'),
      require('postcss-flexbugs-fixes'),
      require('postcss-preset-env')({
        autoprefixer: {
          flexbox: 'no-2009'
        },
        stage: 3
      })
    ]
  }

動作確認 🔗

適当のページを作って確認する。

Raspberry PiにDockerとDocker Composeをインストールする

Raspberry PiにDockerとDocker Composeをインストールした。

$ cat /etc/debian_version
10.6

Docker 🔗

1. ダウンロードとインストール 🔗

$ curl -sSL https://get.docker.com | sh

2. 権限を追加 🔗

$ sudo usermod -aG docker pi

3. Dockerサービスの有効化 🔗

$ sudo systemctl enable docker

4. 再起動 🔗

$ sudo reboot

確認 🔗

$ docker -v
Docker version 20.10.1, build 831ebea

Docker Compose 🔗

1. GitHubからClone、ブランチ切り替え 🔗

$ git clone https://github.com/docker/compose.git
$ cd compose/
$ git checkout 1.27.4

現時点で最新版の1.27.4のブランチに切り替え。

2. ビルド 🔗

$ ./script/build/linux

約20分くらい掛かった。

distディレクトリが作られ、その中にバイナルができる。
この状態で一応動作確認。

$ ./dist/docker-compose-Linux-armv7l -v
docker-compose version 1.27.4, build 40524192

3. /usr/local/binにコピー 🔗

$ sudo cp dist/docker-compose-Linux-armv7l /usr/local/bin/docker-compose

4. 所有者をrootに変更 🔗

$ sudo chown root:root /usr/local/bin/docker-compose

5. 実行権限を追加 🔗

$ sudo chmod +x /usr/local/bin/docker-compose

確認 🔗

$ docker-compose -v
docker-compose version 1.27.4, build 40524192

参考 🔗

Raspberry Piを使ってSwitchBot 温湿度計のデータをMackerelで可視化する

この記事は、「Raspberry Pi Advent Calendar 2020 」16日目の記事です。

はじめに 🔗

最近、SwitchBot 温湿度計を購入しました。
いろいろ連携させるためには別でSwitchBotハブというものが必要みたいで、急遽代わりにRaspberry Piも購入しました。
(SwitchBotハブではなくRaspberry Piを購入した理由は、スマートリモコンはNature Remoを使っているのでいらなかったのとRaspberry Piなら他にも有効活用できると思ったので。)

今回はRaspberry Piを使って温度や湿度をMackerelで可視化してみたという記事です。

SwitchBot 温湿度計 🔗

SwitchBot(スイッチボット) 温湿度計,スマホで温湿度管理,スマート家電, Alexa, GoogleHome, IFTTT対応

手順 🔗

各種バージョン 🔗

  • Raspberry Pi OS: 10.6
  • Python: 3.7.3
  • pip: 18.1
  • mackerel-agent: 0.70.3

1. SwitchBot 温湿度計のデータを読み取るスクリプトを作成する 🔗

既に実装されている方がいらしたので、参考にさせていただきました。(参考にリンク貼ってあります。)
そのままmackerel-agentで使えるフォーマットで値を出力しています。

{metric name}\t{metric value}\t{epoch seconds}

1.1. 必要なパッケージをインストール 🔗

$ sudo apt-get install libglib2.0-dev

1.2. bluepyのインストール 🔗

$ pip3 install bluepy

1.3. bluepy-helperにsudo権限を付与する 🔗

$ sudo setcap 'cap_net_raw,cap_net_admin+eip' bluepy-helper

1.4. SwitchBot 温湿度計のMACアドレスを確認する 🔗

SwitchBot公式アプリで確認することができます。別記事で書いたのでそちらを参照してください。1

Categories


Tags