それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

bash で三項演算子っぽく処理を書くには

bash でも三項演算子を使いたい

かんたんな、初期値nullチェックとかで三項演算子的なことをよくやります。

bash でもやりたいなと思って調べててみたけど、なかった(あったので追記書きます。)。

条件分岐を使って原始的な手法がsh/bash で使えるのでご紹介。

三項演算子の変わりの原始的手法

[[  $var == abc ]] && echo OK || echo NG

サンプル

#!/bin/bash

## 変数
a=1

echo TRUE になる場合
[[ $a == 1 ]] && echo TRUE || echo FALSE

echo FALSE が実行される場合
[[ $a == 0 ]] && echo TRUE || echo FALSE

三項演算子bash に存在しない

将来のアップデートでbash には搭載されるかもしれないけど、sh には永遠に搭載されないだろし

条件判定を組み合わせて三項演算子っぽく記述できる方法は便利だし採用していきたい。

三項演算子の書き方(追記

三項演算子はsh にはなくbash 独自の機能 (( ))を使えば、書くことが出来ました。

a=1
b=$(( a !=0 ? -1 : 0  ))

ちなみに、(( ))のArithmetic expressions を書くときは、変数に$を付けない。

実際に動かしてみた例はこちら。

takuya@~$ unset a b
takuya@~$ a=1
takuya@~$ b=$(( a !=0 ? -1 : 0  ))
takuya@~$ echo $b
-1
takuya@~$
takuya@~$ unset a b
takuya@~$ a=0
takuya@~$ b=$(( a !=0 ? -1 : 0  ))
takuya@~$ echo $b
0

2019-08-29 訂正

三項演算子ありました。 (( )) を使えば、C言語スタイルの演算ができるので、もしやと思って調べたら・・・

参考資料

https://wiki.bash-hackers.org/syntax/arith_expr

systemdのジョブの起動前、起動後に処理を記述する preExec / PostExec

systemd で自動起動するファイルを記述したが pip のアップデートで苦労する

systemd でdaemon を起動しているのですが、pip 関連でアップデートが頻繁にあるモジュールだと、前もって更新してあげないとエラーになることがる。

systemd で事前処理・事後処理を書けないか調べてみたらあった

ExecStartPre を使うと、起動前に実行してくれるようだ。

ExecStartPre=, ExecStartPost= Additional commands that are executed before or after the command in ExecStart=, respectively. Syntax is the same as for ExecStart=, except that multiple command lines are allowed and the commands are executed one after the other, serially.

複数行の処理は?

ExecStartPreを複数書けばいいらしい

書いてみた。

pipenv に入れた pip や requirements.txt をDaemonの起動前に、更新をするように書きました。

[Unit]
Description=Splattoon splatnet2ink

[Service]
WorkingDirectory=/PATH/TO/project
ExecStartPre=/PATH/bin/python /PATH/TO/pipenv/bin/pip install --upgrade pip 
ExecStartPre=/PATH/TO/pipenv/bin/python /PATH/TO/pipenv/bin/pip install --upgrade -r  requirements.txt
ExecStart=/PATH/TO/pipenv//bin/python /PATH/TO/pipenv/daemon.py -M

[Install]
WantedBy=default.target

これで更新処理を別途書く必要がなくなる。

これまでは、ライブラリ更新処理を、別途 timer にしてたり、更新後再起動をタイマーにしてたのですが、不便でした。

これで、PCを起動時、ユーザー systemd 空間なら、ログイン時に更新されるはず。だから timer 関連のsystemd ファイルが減って管理が楽になりそうです。

参考資料

https://www.freedesktop.org/software/systemd/man/systemd.service.html

ついに牙を向いたPublic DNS

Cloudflare の 1.1.1.1 が dns 応答を拒否し始める。

巨大になった組織の宿命というか、世間のレベルより「踏み込んだ」対応をして自粛をしないとダメなんだろうけど、管理は、インターネットな文化と相容れないと思ってた私の脳みそは時代遅れで腐っているのでしょうかと。

www.itmedia.co.jp

DoH も危ない

DNS over HTTPSFirefox のも Cloudflare が請負っているので、あちらも同じことになると思う。

自前で dns が必要

public dns が使えないなら、昔のように、自前で public dns を建てるしか無い。

unbound をインストールするしか。

しかし、自宅ならともかく、4G/5Gのモバイルネットワークでどうするんだよこれ。 DNSを支配される悪夢だ・・・

ユーザー毎の systemd を使ってシステム全体設定と個人用設定を分ける。

systemd のユーザーごとの設定というのがあります。

systemd は sudo 権限がないと動かないと思ってませんか。 タイマーや自動起動インスタンスやサービスはもう、ユーザー空間でできるのです。

ユーザーモードでの systemd というのがあります。

引数 --user でユーザーモード

systemd 関連のコマンドに引数の ---user をつけると、ユーザー空間での、ユーザーに固有の systemd 空間を扱えます。

自分自身でログインしているときに、自分のsystemd 関連サービスのステータスを表示するには次のようにします。

takuya@:~$ systemctl  --user status 

f:id:takuya_1st:20190809001329p:plain

systemd サービスの作成

サービスの作成は簡単です。通常の systemd となんら違いはありませんでした。

systemd のサービスは、 ~/.config/systemd/user/ に設置する。記述方法は、通常のsystemd となんらかわりがありません。

簡単に言えば、作るファイルの場所がいつもと異なるだけ、root権限がいらない。自分権限で完結している。

ユーザー毎のsystemd のサービスを作ってみる。

それでは、試しに、自分だけのサービスを作ってみたいと思います。

最初に、ディレクトリを準備します。

mkdir -p ~/.config/systemd/user/

つぎに、service ファイルを作成したいと思います。

touch  ~/.config/systemd/user/my-loop.service

ファイルの中身を記述します。

takuya@:~$ cat   - >  ~/.config/systemd/user/my-loop.service  
[Unit]
Description=サービスを作成する練習。

[Service]
ExecStart=bash -c 'while : ; do echo 1 ; sleep 1 ;done '

[Install]
WantedBy=default.target

有効化します。

takuya@:~$ systemctl  --user enable my-loop.service 
Created symlink /home/takuya/.config/systemd/user/default.target.wants/my-loop.service → /home/takuya/.config/systemd/user/my-loop.service.

起動します。

起動方法は、通常作業で sudo 作業と同じです。

f:id:takuya_1st:20190809002950p:plain

ね?簡単でしょ。もう sudo 権限なんてもらわなくても簡単に自分でDaemon起動ができるんですよ。

LXDやLXCがり、snap があり、ユーザー空間を切り離している今の時代のLinuxには、非常に心地よいsystemd ではないでしょうか。

いつ実行されるのか

ユーザーがログインしたときです。最初のログインで実行されます。複数ログインしてログインする際ではなく、システムが起動してから最初のログインです。

ログアウトしたら終了される?

されません。ログアウトしても動作してます。 user のログイン session とは切り離されているので。環境変数も渡されないっっていうのと同じでしょうね。

起動時にならないの?

起動時に、指定ユーザーの systemd を全部ロードするには

sudo loginctl enable-linger username

これで、起動時に指定したユーザーのsysd が実行できますが、起動時に実行するのであれば、もうsudo で /etc/systemd に書くよね。

使いようによっては便利だと思います。

ubuntu デスクトップなどを利用しているときのログイン時に実行などするものを、ある程度こちらに持ってこれます。

参考資料

systemd/ユーザー - ArchWiki

ubuntu でもCtrl-Shift-ESC でタスクマネージャーを起動したい

Ctrl −Shift - Esc でタスクマネージャーを起動したい

強制終了をしたいときとか pkill で構わないんだけど、ついつい、癖で押しちゃうので、ショートカットにする

ショートカットの設定画面

f:id:takuya_1st:20190807021807p:plain

コマンドを指定する。

Gnome Desktop でWindowsのタスクマネージャーに相当するのは、デスクトップアプリの gnome-system-monitor が相当するので、それを利用することに。

f:id:takuya_1st:20190807021740p:plain

忘れそうなのでメモ

自分で設定したことを忘れそうなので、メモを取る

任意のフォルダを隠しフォルダにして非表示にする ~.hidden を使って snap フォルダを隠す

Gnomeで指定したファイルを不可視にしたい。

通常であれば、ファイル名の先頭にドットをつけt,えdotfiles にして非表示にすることができます。

dotfilesにできないフォルダやファイルを非表示にしたい。特にSnapフォルダ

Ubuntuを使っていると、最近は snap フォルダができます。 snap フォルダは名前の変更がちょっとめんどくさいので、他の方法を探した。 環境変数を使わずに、手早く非表示にする方法があればいいなとおもったら、ありました。

snap フォルダを隠しフォルダにする。

これだけ。

echo snap > ~/.hidden

非表示になりました。

f:id:takuya_1st:20190807015626p:plain

.hidden を使って、好きなフォルダを非表示に

.hidden というファイルを使うことで、ドットファイルにできないフォルダでも非表示にすることができることがわかりました。

ArchWikiを読んでいて見つけました。

隠しファイル 他のファイルマネージャと同じように GNOME Files もデフォルトでファイルの名前がドットから始まるファイルを非表示にします。

さらに、GNOME Files ではファイルの名前が同一ディレクトリの .hidden ファイルに記載されていた場合、ファイルを表示しません (1行ごとにファイル名を記述します)。

任意のフォルダに .hidden が設置可能

.hidden ファイルは、任意のフォルダに設置して、任意のファイルをNautilusで不可視にすることができる。

これで、フォルダに普段使わないファイルやフォルダを保存していたとしても、よく使うものだけの表示にすることができる。これってすごく便利じゃないですかね。

こんかいは、 ~/snap フォルダに適用したけど、プロジェクトフォルダ、node_modules とかにも使えそうですね。

参考資料

GNOME Files - ArchWiki

0001docomo に PC/Mac から接続する- WAP2エンタープライズ

0001docomo の ドコモWifiMacから接続する。

docomoWiFiはめっちゃ優秀です。ガンガン使っていいレベルのインフラだと思います。 とくに、0001docomo は WAP2 エンタープライズなので、めっちゃ安全です。しかも通信速度も快適です。

接続方法

WPA2で接続するので、自動接続は使えない。

Macの場合は、「他のネットワークに接続」を明示的に選んで

f:id:takuya_1st:20190805184159p:plain

WAP2のユーザー名とパスワードを入れます。

f:id:takuya_1st:20190805184233p:plain

このID/PWは、マイドコモにログインして、接続情報から、WiFiパスワードを閲覧すれば、情報を得られます。

契約者であれば誰でも使えます。同じAPに同時に複数ログインはできない

iPhone/ mac の場合の問題点

WPA2のパスワードをMacが保存してKeychainに保存します。そのキーチェーンをiOSに同期してしまいます。 その結果、iPhoneEAP_SIM で動作せずにWAP2になってしまうので、iPhoneでドコモSIMかつキーチェーンを使っている場合は、パスワードを共有するApple仕様のために不便です。

接続情報はKeychainsに保存される。

次のように、キーチェーンに保存されます。このキーチェーンがMaciPhoneに同期されて、めんどくさいに事になります。

  • SIM認証が失敗する
  • Mac/ iPhone / iPad で接続の奪い合いになる。

f:id:takuya_1st:20190807182903p:plain

キーチェーンに保存しないでコマンドから接続するのが無難だと思います。

公衆無線LANは安全?危険

WPA2エンタープライズの0001docomo はユーザーごとに鍵交換を行うので、前提が違う。

「公衆無線LANは危険」とか区別もつかず暗黙的にお題目を言ってる人たちと一線を画している。

参考資料

docomo Wi-Fi:0001docomo - Windows PCを無線LAN接続

gas(google app script)でデザインや数式の入ったシートから値だけのシートにしてダウンロード

Google App Scriptを書いてみた

初めてのAppScriptだけど、すごく便利だったので、メモ

スプレッドシートから値だけを取り出してCSV的にする

スプレッド・シートは結構活用するのですが、スプレッドシートに数式や参照が含まれていて そのままコピペして送るのがちょっとあれだなと思ったので、スプレッドシートをコピーして数式を外した

Excelならコピーと貼り付けオプションでできるし、Gsuiteでももちろんできるんだけど

シートの枚数が多いのでめんどくさくなってきた。なのでGAS書いてみた。

gas であれこれやるサンプルになる

GASであれこれやるのに、参考資料として使えそうなので、資料を残すことに。

  • シートの作成
  • シートの選択
  • シートのコピー
  • ファイル形式変換して取得
  • メール送信

この辺のサンプルになるなと。

gas.js

function MailSpreadSheet(){
  
  // created_at : 2018-12-25 
  // auther     : takuya_1st 
  // description: 指定したファイルから数式とデザインを除外して、値だけのファイルにして、メールにして送る
  
  var file_id = "13OAD2yER60___GoogleDriveのファイルID____UMNw5MrSVA";
  var mail_to = "takuya@example.com";
  var subject = "実験-"+ (new Date()).toISOString();
  var body = "これは実験です。";
  var attachment_files =  [];
  
  var fetchOpt = {
    "headers" : { Authorization: "Bearer " + ScriptApp.getOAuthToken() },
    "muteHttpExceptions" : true
  };
  
  /// 新規スプレッドシートを作る。
  const createNewSpreadSheetFile = function (){
    // https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app
    const name = (new Date()).toISOString()
    const app = SpreadsheetApp.create(name);
    // Logger.log(app.getUrl());
    return app
  }

  const CopyFileToNewFile = function ( src_file_id ){
    // 元ファイルを取得
    const src_doc = SpreadsheetApp.openById(src_file_id)

    // 新規ファイルを作成:シートのコピー先ファイルを追加。
    const dest = createNewSpreadSheetFile();  
    const dest_file = SpreadsheetApp.openByUrl(dest.getUrl());

    // すべてのシートについて
    //    作業用一時シートに値で貼り付けて
    //    新規ファイルへシートをコピー
    for ( idx in src_doc.getSheets() ){
      const sheet = src_doc.getSheets()[idx];
      const range = src_doc.getDataRange();
      
      // 新規シートを作って選択範囲をコピペ。
      const temp_sheet_name = (new Date()).toISOString();
      const temp_sheet = src_doc.insertSheet(temp_sheet_name);
      const temp_range = temp_sheet.getDataRange();
      range.copyTo(temp_range,{contentsOnly:true});
      // 新規ファイルへシートをコピー
      const dest_sheet =temp_sheet.copyTo(dest_file);
      dest_sheet.setName( sheet.getName() );//名前を元シートに合わせる。
      //一時的なシートを削除
      src_doc.deleteSheet(temp_sheet);
    }
    
    // 新規ファイルからファイル作成時の空白シート(先頭)を削除
    dest_file.deleteSheet( dest_file.getSheets()[0] );
    
    //コピーされたファイルを返す。
    return dest_file;
  }
  const fetchFileAsExcel = function( file_id ){
    const file = DriveApp.getFileById(file_id);
    const fileName = file.getName();
    xlsxName = fileName + "-変換-" + (new Date()).toLocaleString() + ".xlsx";
    //const blob = file.getAs( MimeType.MICROSOFT_EXCEL )
    //blob.setName(xlsxName);
    //return blob
    // もし blob が取れないときはURL経由
    fetchUrl = "https://docs.google.com/feeds/download/spreadsheets/Export?key=" + file.getId() + "&exportFormat=xlsx&format=xlsx";
    const blob = UrlFetchApp.fetch(fetchUrl, fetchOpt).getBlob().setName(xlsxName);
    return blob
  }
  
  const SendMail = function(){    
    
    MailApp.sendEmail(mail_to, subject, body, {attachments:attachment_files});
  }
 
  
  
  const main = function() {
    const file = CopyFileToNewFile(file_id);
    const blob = fetchFileAsExcel( file.getId() );
    
    DriveApp.removeFile(DriveApp.getFileById(file.getId()));
    attachment_files.push(blob);
    body = body + blob.getName();
    
    SendMail();
  }
  
  
  main();
}

サイト内検索を duckduckgo に切り替え

google のサイト内検索がどうもおかしい

記事はあるはずなのに、記事が出て来ない。

これが一定期間をすぎると検索結果から外されるというものでしょうか。

自分の過去記事をアーカイブとして利用しているとどうも、記事が見つからないことが増えてきた。

duckgo に試験的に変えてみる。

duckduck go でサイト内検索を設置するときは次のようにする

<form action="https://duckduckgo.com/">
  <input type="text" name="q">
  <input type="hidden" name="sites" value="example.com/sites">
  <button type="submit">Search</button>
</form>

しばらく様子を見てみたい。

参考資料

https://coderwall.com/p/0euadw/site-search-form-with-duckduckgo

gitでブランチ名を名前変更したい / ローカル branchのrename

git で ローカルブランチをtypo したとき、名前を変えたい。

isssue とか ちょっとした名前のミスであれば、名前の変更をしたい。

また、masterを直接更新 から git checkout -b name でブランチを後付で更新したいときに、名前をミスると、名称の変更が速い時がある。

ローカルのブランチの名前変更(1)

カレントブランチ(今いるブランチ)を変更

git branch -m  新しい名前

ローカルのブランチの名前変更(2)

変更前のブランチと変更後のブランチを指定して変更

git branch -m '古い名前'  '新しい名前'

branch -m / -M

ちなみに -m の m は移動の m です。rename というより コマンドのmv だと思ったほうがいいでしょう。move だと覚えておけば覚えやすいし忘れないです。

       -m, --move
           Move/rename a branch and the corresponding reflog.

       -M
           Shortcut for --move --force.

branch -m がおぼえられないひとむけ

何度やっても - m が覚えれない人は、ショートカットに登録しましょう

~/.gitconfig

[alias]
  rename-branch = branch -m

参考資料

man git-branch

git ブランチ作成と削除(ローカル)

git の使い方

今回は、git branch の操作方法。

ブランチは、もう説明が不要なくらい浸透してて嬉しい。

ブランチの作成と削除という基本概念をメモします。

ブランチ作成

今いるブランチから作成する

git checkout -b ブランチ名

いろいろあるけど、まずこれを覚える。

ブランチ削除

git branch -d ブランチ名

名前を指定して消します。

ブランチの一覧

git branch 

これでブランチ名がわかるので、ここから必要なものを消します。

でもめんどくさいの一括して消したいですよね・・・。

マージ済みのブランチをすべて消す。

git branch --merged | \grep -Ev  '\*|develop|master' | xargs git branch -d

git flow でよく使う maste /dev と、 いまのブランチ名* name だけを除外して全部消す。

alias に登録する。

これらを覚えたら。、一括して削除するコマンドをショートカットに登録する。

[alias]
   delete-merged = "!func () { git branch --merged| egrep -v  '\\*|master|develop' |xargs -I@  -t git branch -d @; }; func;"
git delete-merged

これで瞬殺できるから、お掃除できて楽ちん。

参考

man git branch

gitサーバのgogs を使うことに

github だけがgit webじゃない。

git をWEBで閲覧するのには、github 以外にも git serverとして gitweb でもなんでもある。

gitlab は要求水準が高いし、prometheus がCPU食いまくる、またgitlab は複数のドメインで運用が面倒だ。*1

gitlab を複数インスタンス入れるのも面倒な話だし。マルチドメインをできないのなら、別のgit web を探すことにして。 gogs に目をつけた次第です

gogs インストール

gogs は go で書かれたgit サーバー

packager を使うのが早い

https://packager.io/gh/pkgr/gogs/builds/689/install/ubuntu-16.04

パッケージでインストールする。

wget -qO- https://dl.packager.io/srv/pkgr/gogs/key | sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/gogs.list \
  https://dl.packager.io/srv/pkgr/gogs/pkgr/installer/ubuntu/16.04.repo
sudo apt-get update
sudo apt-get install gogs

設定ファイルが作られる

インストール完了したら、次の設定が作られる。

/etc/gogs/conf/app.ini

適当に設定ファイルを編集。

再起動

systemctl status gogs
systemctl restart gogs
systemctl status gogs

また、すでに起動しているので、インストーラーにアクセスしたいところがだが、MySQLを要求するので、先に作っておく

DB 作成

apt インストールが完了したら、あとは、DB設定とユーザー作成して登録する。

Gogs はMYSQLのDBを要求するので、MYSQLにテーブル作成ようにユーザーとDBスキームを作っておく

takuya@sakura:~$ mycli -u root
mysql root@(none):(none)> create database gogs
mysql root@(none):(none)> create user "gogs-user" identified
mysql root@(none):(none)> create user "gogs-user" identified by "**************";
mysql root@(none):(none)> grant all on gogs.* to 'gogs-user'@'%';
mysql root@(none):(none)>

接続テスト

ローカルホストと通信して、接続を確認する。

curl locahost:6000/install 

リモートなら、SSH の ポートフォワーディングでいいわ

ssh gogs-server -L 8080:localhost:6000
open localhost:8080

インストールして初期ユーザを作ったら準備完了

初回登録ユーザーが「管理者」

「1番目」のユーザーが自動的に管理者になり、2番目以降は通常ユーザーになる。

これは最初「アレレ、管理者設定どこ?」って思って勘違いしてググりまくって苦労した。2番目以降の通常ユーザーに管理者フラグを立てると管理者になれる。

メールの設定とポートとSSL

メール送信の設定とかを入れて、再起動する

あとは他に、ポート指定したり、nginx にSSLのポートなどを設定。起動ウイザードで設定する

最後に、pull / push の sshhttps のNATなしのポートをなどを設定して準備完了。

PAM / LDAP が使える

Gitlab で認証連携のコレらをやろうと思うと有料版など面倒なのだが、ユーザーの管理に PAM(/etc/passwdなど PAM対応してる認証機構ならなんでも) や LDAP(DN) とSMTPが使える。PAM対応してるのが熱い。

PAM対応があると、sshMySQL と共通のID/PWでユーザーを管理できるので、保守性が高くて複数インストールしても苦なく使える。

軽快

とても軽快で使いやすい。もっさり感もなく、要求スペックは高くない。

git の認知に関する愚痴など

「git は使いたいけどgithub はちょっと。」とか「github が導入できないから git 導入に苦労する」とかいう言説はまやかしである。SFTPでもSMB/Cifsやでも使えるのであるから。git は「分散」レポジトリであることを忘れて中央サーバーが必須だと思いこんでる人の多いことといったら。

github にアクセスできなくてもgit は使えるしソースコードの管理もできる。なんなら今回のようなgogs など git server をオンプレで立ち上げてもいい。

github イコール git と思いこんでる言説が多くて困る。

*1: X-Original-Host を見てくれればいいのにな。

Nature Remo mini を買ったのでローカルAPI叩いてリモコンのデータを取得してみる

未来の家に。

我が家も、スマート・ホームへの進化をします。スマートリモコン(学習型IR)で音声操作や外出先から操作するのは、小さい頃からの夢でした。でも一朝一夕で実現するわけでもないので、少しずつ準備をしていました。

Remoがやってきた

買いました。

早速APIを叩いてみます。

API を叩く前に、一通りの初期設定は終わっているものとします。 1. 開封する 2. 接続する 3. 登録する 4 .とりあえずなんかOn/Offは出来てる。

ローカルAPIでできること

  • GET / 最後に受信した、赤外線リモコンの波長(信号)の記憶を取得します。
  • Post / 任意の赤外線信号を指定して、それを発光して送信します。

つまり、これさえあれば、仮にNature Remo の運営会社が今すぐ倒産したとしても、Raspiなどから直接Curlでリモコン信号を叩けるわけです。 iOSのアプリが提供されなくなっても、WebUIからプロセス起動すれば動くわけです、やったね。

購入後、自己責任において、自由に使えるソフトウェアとハードウェアは本当に嬉しい。

ローカルのAPIへアクセスします。

curl  'http://Remo-1XXXXXX/messages' -H 'X-Requested-With: curl  ' -v

まだなにも出てきません。404 になると思います。

Remoに赤外線リモコンを受光させます。

Remoへ向かって赤外線リモコンを発光します、Remoが青色に反応したらOKです。

再度ローカルAPIを取得します。

curl  'http://Remo-1XXXXXX/messages' -H 'X-Requested-With: curl  ' -v

JSONが返ってきます。

返ってきたJSONの構造

次のようなデータが返ってくる。

{
  "format": "us",
  "freq": 37,
  "data": [
    8991,
    4505,
    550,
    1689,.... 
    525
  ]
}

ここで注目するべきは、 data 属性の値。

この値は、毎回同じにならない。たとえ同じリモコンで同じボタンを使って送信しても。同じにならないのだ。これは時間分解能の限界なので仕方がない。

ただ、比率は毎回ほぼおなじになっている。先頭2つは16T/8T ないし、8T/4T の整数倍に近い値になっている。

先頭2つは、その信号の補正でベースとなる波長の時間間隔Tを求めることができる。

リモコンデータをリピートする

取得したリモコンのデータをそのままオウム返ししてあげれば、まぁそのまま使える。

 cat power-on.json | curl-norc -X POST 'http://192.168.2.216/messages' -H 'X-Requested-With: curl' -d @- -vp://

リモコンのデータを解析

日本の家電の多くは、次のような仕様に基づいて、0/1 のバイナリデータ信号のオンオフ時間によって制御している。

先頭2つの時間合計を12で割ってみて、24で割ってみて、誤差を丸めてやり、時間T(変調単位)をもとめて、残りのペアをTの整数倍としてみてみる。

より1:1、1:3の整数値に近い方のTをみつければ、12/24のいずれかででNECとAEHAがわかるはずである。

f:id:takuya_1st:20190722225015p:plain

赤外線リモコンの通信フォーマット

今回、アイリスのLEDリモコンで試してみた。

仕様によれば、先頭の2つから、Tを求めて、あとはその整数倍のペアが出現するはずである。(赤外線の照射時間Tと整数比を測定するわけで、受光器の精度はmsである。機器は所詮整数倍しか見れない精度であろう。とすれば有効桁数は1桁の計測器である。2桁まで除算で計算し、それを整数値に丸めてやる)

import sys
import json

obj_txt = sys.stdin.read()
obj = json.loads(obj_txt)

code = obj['data']
data = ""
t = int((code[0]+code[1])/24)

data = [ [ code[i], code[i+1] ] for i in range( 2, len(code)-1,2) ]
data = [ [ round(code[i]/t), round(code[i+1]/t) ] for i in range( 2, len(code)-1,2) ]

print(data)

実行結果は次のようになった。

[[1, 3], [1, 1], [1, 1], [1, 1], [1, 3], [1, 1], [1, 3], [1, 1], [1, 1], [1, 3], [1, 3], [1, 1],...]

ここで、仕様を見ると、 [1,1] (すなわち [1T,1T] ) は 0 を示し、[1,3] は 1 を示す。 これを使って0/1 にマッピングしてやり、2進数表記を得たら、4ビットずつ16進数表記にしてみた。

import sys
import json

obj_txt = sys.stdin.read()
obj = json.loads(obj_txt)

code = obj['data']
data = ""
t = int((code[0]+code[1])/24)

data = [ [ code[i], code[i+1] ] for i in range( 2, len(code)-1,2) ]
data = [ [ round(code[i]/t), round(code[i+1]/t) ] for i in range( 2, len(code)-1,2) ]
data = [ "1" if e == [1,3] else "0" for e in data ]
data = [ "".join(data[i:i+4]) for i in range(0,len(data)-3,4)  ]
data = [ int(e,2) for e in data  ]
data = [ format(e , 'x') for e in data  ]
data = "".join(data)

print(data)
cat led-on.json | python decode-LED.py
8a6e0800800036

あとは、これをボタンごとに調べてやると、ボタンごとの差異が明確になるはずである。

さらに、ボタンごとの差異から、リモコンでは実装されてないOn/Offが見つかる可能性もある。

さらに、この整数値を時間間隔に書き直してRemo経由で発光してあげれば、照明のリモコンとして動作する。

といってもアイリスのLEDはプリセットに含まれているので、ここまで計算する必要も無い。これ以上は時間の都合上やる意味もないが、もし非対応のリモコンが出てきたときにどうやってデータを取得するかだけメモを残しておく

残念なことに

ローカルAPIでは、リモコンの送受信、つまりGET/POSTしかできないようです。

関連商品

Nature Japan Nature Remo REMO-1W2

Nature Japan Nature Remo REMO-1W2

参考資料

Nature Remoに存在しないボタンを登録する - 暇な女子高専生のブログ

curlコマンドで ~/.curlrc の設定を有効無効を切り替える

curlrc でよく使うオプションを入れておくと便利

curl でよく使う設定をまとめる ~/.curlrc - それマグで!

便利なのですが、ついつい入れすぎてしまって、いざというときに不要なファイルを消せない。

どういうことかというと、~/.curlrc が存在してると最優先でそれを使うので。

解決策 Aliasなどと組み合わせる

alias curl-with-json=curl -q -K ~/.curl-json.conf
alias curl-with-my=curl -q -K ~/.curl-my.conf

その他の解決策

必要のないときだけ、デフォルト値を明示して使う。

alias curl-ddefault=curl -q -K ~/.curl-my.conf

ポイント

curl で curlrc を「使わない」ときは -q を先頭にかいて --config/-K を その次に書く

curl -q --config /dev/null

参考

man curl

すぐわかる標準エラー出力と標準出力の捨て方( /dev/null へリダイレクト

だれですか ' &1' なんてファイルを作ったのは。

サーバーにログインしてみてたら'1' っていうファイルがあるんですね。1という名前のファイルがあるんですよ。

どう考えても、リダイレクトに失敗してますよね。

標準エラー出力を捨てる速攻で覚えるの書き方

command  &> /dev/null

bash ならもうこれだけ覚えてほしい。

個別に指定する場合

個別に指定する方法が一番基本なのでこれをまず覚えてほしい。

commdn  2>/dev/null  >/dev/ null

アンパサンド(&) の指定でミスるなら

もう、アンド使うなといいたい。