スキップしてメイン コンテンツに移動

帰宅した時に、Sesami4 を自動解錠するよう IFTTT で操作する

Sesami4 を IFTTT で操作する

先日、スマートロックのSesami4 を購入した。 使い始めて 1 ヶ月ちょいになり、不満点が出てきた。

  • 手ぶらで解錠がロクに効かない
  • Android のウィジェットが常時表示はウザい
  • オートロックを有効にしてると締め出される(当たり前だよなぁ!?)

と言った感じ。 この中では特に手ぶらで解錠がロクに効かないのには相当不満があった。 原因はシンプルで、BLE(Bluetooth Low Energy)が上手く動作していなかった。 観察した結果だが、動作する時はちゃんと動作するが、ダメな時はとことんダメ。 Sesami の作り込みが甘いのか、純粋に Android の反応が悪いだけなのか、どちらなのか判断は付けられない。 その両方である可能性もあるが、いずれにせよこれでは快適な生活が送れない。

Sesami は API を公開しているので、それを使ってリモートコントロールする事にした。 あ、一応Sesami の WiFi モジュールは導入している前提。 これがないと今回の Tips は使えないので、気をつけるように。

今回はスマートロックのセサミをさらに便利にする(アレクサからパスコードレスで解錠&オートロック化)の記事をそのままパクってるだけなので、その記事を読めば概ね同じことが出来る。

Sesami API キー取得

公式ページにアクセスし、メールアドレスを入力する。 認証コードが送られてくるので、それを入力すると API 取得画面が表示される。

スマホアプリの「このセサミの鍵をシェア」から QR コードのスクリーンショットを撮って、API 取得画面にアップロードする。 そうする事で、デバイスの UUID と SecretKey が取得出来る。

  • API キー
  • デバイスの UUID
  • デバイスの SecretKey

GAS のエンドポイント認証用のハッシュ値を用意する

下記のコマンドを実行して、認証用ハッシュ値を取得する。 任意のパスワードは、IFTTT との連携に使用するので、メモしておく事

$ echo -n '任意のパスワード' | shasum -a 512

自分は WSL2 上で実行して取得した。 SHA512 Onlineで生成してもいい。

GAS のスクリプトプロパティを設定する

スプレッドシートで適当にファイルを 1 個作り、「拡張機能」→「App Script」から AppScript エディタを起動する。 左側の歯車アイコンからプロジェクトの設定を開き

  • API_KEY
  • DEVICES
  • PASSWORD_DIGEST

を設定する。 API_KEY には、Sesami の API キーを設定する。 DEVICES は、

[{"uuid" : "デバイスのUUID","secretKey" : "デバイスのSecretKey"}]

というフォーマットで設定する。複数台登録したい時は、ここの配列を追加すれば良い。 PASSWORD_DIGEST には、先程取得した認証用ハッシュ値を設定する。

ファイルを作成する。

4 つのファイルを用意する。 まず、秘匿情報用

const passwordDigest = PropertiesService.getScriptProperties().getProperty('PASSWORD_DIGEST');
const apiKey = PropertiesService.getScriptProperties().getProperty('API_KEY');
const devices = JSON.parse(PropertiesService.getScriptProperties().getProperty('DEVICES'));

次に、CryptJS 用

/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k<a;k++)c[j+k>>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535<e.length)for(k=0;k<a;k+=4)c[j+k>>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e<a;e+=4)c.push(4294967296*u.random()|0);return new r.init(c,a)}}),w=d.enc={},v=w.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++){var k=c[j>>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j+=2)e[j>>>3]|=parseInt(a.substr(j,
2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++)e.push(String.fromCharCode(c[j>>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j++)e[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}},
q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q<a;q+=k)this._doProcessBlock(e,q);q=e.splice(0,a);c.sigBytes-=j}return new r.init(q,j)},clone:function(){var a=t.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:t.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,e){return(new a.init(e)).finalize(b)}},_createHmacHelper:function(a){return function(b,e){return(new n.HMAC.init(a,
e)).finalize(b)}}});var n=d.algo={};return d}(Math);
(function(){var u=CryptoJS,p=u.lib.WordArray;u.enc.Base64={stringify:function(d){var l=d.words,p=d.sigBytes,t=this._map;d.clamp();d=[];for(var r=0;r<p;r+=3)for(var w=(l[r>>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v<p;v++)d.push(t.charAt(w>>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w<
l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<<j|b>>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<<j|b>>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<<j|b>>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<<j|b>>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])},
_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]),
f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f,
m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m,
E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/
4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math);
(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length<q;){n&&s.update(n);var n=s.update(d).finalize(r);s.reset();for(var a=1;a<p;a++)n=s.finalize(n),s.reset();b.concat(n)}b.sigBytes=4*q;return b}});u.EvpKDF=function(d,l,p){return s.create(p).compute(d,
l)}})();
CryptoJS.lib.Cipher||function(u){var p=CryptoJS,d=p.lib,l=d.Base,s=d.WordArray,t=d.BufferedBlockAlgorithm,r=p.enc.Base64,w=p.algo.EvpKDF,v=d.Cipher=t.extend({cfg:l.extend(),createEncryptor:function(e,a){return this.create(this._ENC_XFORM_MODE,e,a)},createDecryptor:function(e,a){return this.create(this._DEC_XFORM_MODE,e,a)},init:function(e,a,b){this.cfg=this.cfg.extend(b);this._xformMode=e;this._key=a;this.reset()},reset:function(){t.reset.call(this);this._doReset()},process:function(e){this._append(e);return this._process()},
finalize:function(e){e&&this._append(e);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(e){return{encrypt:function(b,k,d){return("string"==typeof k?c:a).encrypt(e,b,k,d)},decrypt:function(b,k,d){return("string"==typeof k?c:a).decrypt(e,b,k,d)}}}});d.StreamCipher=v.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var b=p.mode={},x=function(e,a,b){var c=this._iv;c?this._iv=u:c=this._prevBlock;for(var d=0;d<b;d++)e[a+d]^=
c[d]},q=(d.BlockCipherMode=l.extend({createEncryptor:function(e,a){return this.Encryptor.create(e,a)},createDecryptor:function(e,a){return this.Decryptor.create(e,a)},init:function(e,a){this._cipher=e;this._iv=a}})).extend();q.Encryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize;x.call(this,e,a,c);b.encryptBlock(e,a);this._prevBlock=e.slice(a,a+c)}});q.Decryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize,d=e.slice(a,a+c);b.decryptBlock(e,a);x.call(this,
e,a,c);this._prevBlock=d}});b=b.CBC=q;q=(p.pad={}).Pkcs7={pad:function(a,b){for(var c=4*b,c=c-a.sigBytes%c,d=c<<24|c<<16|c<<8|c,l=[],n=0;n<c;n+=4)l.push(d);c=s.create(l,c);a.concat(c)},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a,
this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684,
1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})},
decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d,
b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}();
(function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8,
16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j<a;j++)if(j<d)e[j]=c[j];else{var k=e[j-1];j%d?6<d&&4==j%d&&(k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;d<a;d++)j=a-d,k=d%4?e[j]:e[j-4],c[d]=4>d||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>>
8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r<m;r++)var q=d[g>>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t=
d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})();

次に、cmac 用

/*
 * The MIT License
 *
 * (MIT)Copyright (c) 2015 artjomb
 */
!function(t){var r;r=t.hasOwnProperty("ext")?t.ext:t.ext={},r.bitshift=function(t,r){var e,s,n=0,o=t.words,i=0;if(r>0){for(;r>31;)o.splice(0,1),o.push(0),r-=32,i++;if(0==r)return n;for(var a=o.length-i-1;a>=0;a--)e=o[a],o[a]<<=r,o[a]|=n,n=e>>>32-r}else if(0>r){for(;-31>r;)o.splice(0,0,0),o.length--,r+=32,i++;if(0==r)return n;r=-r,s=(1<<r)-1;for(var a=i;a<o.length;a++)e=o[a]&s,o[a]>>>=r,o[a]|=n,n=e<<32-r}return n},r.neg=function(t){for(var r=t.words,e=0;e<r.length;e++)r[e]=~r[e];return t},r.xor=function(t,r){for(var e=0;e<t.words.length;e++)t.words[e]^=r.words[e];return t},r.bitand=function(t,r){for(var e=t.clone(),s=e.words,n=r.words,o=0;o<s.length;o++)s[o]&=n[o];return e}}(CryptoJS),function(t){var r;r=t.hasOwnProperty("ext")?t.ext:t.ext={};{var e=(t.lib.Base,t.lib.WordArray);t.algo.AES}r.const_Zero=e.create([0,0,0,0]),r.const_One=e.create([0,0,0,1]),r.const_Rb=e.create([0,0,0,135]),r.const_Rb_Shifted=e.create([2147483648,0,0,67]),r.const_nonMSB=e.create([4294967295,4294967295,2147483647,2147483647]),r.isWordArray=function(t){return t&&"function"==typeof t.clamp&&"function"==typeof t.concat&&"array"==typeof t.words},t.pad.OneZeroPadding={pad:function(t,r){for(var s=4*r,n=s-t.sigBytes%s,o=[],i=0;n>i;i+=4){var a=0;0===i&&(a=2147483648),o.push(a)}var c=e.create(o,n);t.concat(c)},unpad:function(){}},t.pad.NoPadding={pad:function(){},unpad:function(){}},r.leftmostBytes=function(t,r){var e=t.clone();return e.sigBytes=r,e.clamp(),e},r.rightmostBytes=function(t,e){t.clamp();var s=32,n=t.clone(),o=8*(n.sigBytes-e);if(o>=s){var i=Math.floor(o/s);o-=i*s,n.words.splice(0,i),n.sigBytes-=i*s/8}return o>0&&(r.bitshift(n,o),n.sigBytes-=o/8),n},r.popWords=function(t,e){var s=r.leftmostBytes(t,4*e);return t.words=t.words.slice(e),t.sigBytes-=4*e,s},r.shiftBytes=function(t,s){s=s||16;var n=s%4;s-=n;for(var o=e.create(),i=0;s>i;i+=4)o.words.push(t.words.shift()),t.sigBytes-=4,o.sigBytes+=4;return n>0&&(o.words.push(t.words[0]),o.sigBytes+=n,r.bitshift(t,8*n),t.sigBytes-=n),o},r.xorendBytes=function(t,e){return r.leftmostBytes(t,t.sigBytes-e.sigBytes).concat(r.xor(r.rightmostBytes(t,e.sigBytes),e))},r.dbl=function(t){var e=r.msb(t);return r.bitshift(t,1),r.xor(t,1===e?r.const_Rb:r.const_Zero),t},r.inv=function(t){var e=1&t.words[4];return r.bitshift(t,-1),r.xor(t,1===e?r.const_Rb_Shifted:r.const_Zero),t},r.equals=function(t,r){if(!r||!r.words||t.sigBytes!==r.sigBytes)return!1;t.clamp(),r.clamp();for(var e=0,s=0;s<t.words.length;s++)e|=t.words[s]^r.words[s];return 0===e},r.msb=function(t){return t.words[0]>>>31}}(CryptoJS),function(t){function r(r,e){var o=n.createEncryptor(r,{iv:s.create(),padding:t.pad.NoPadding}),i=o.finalize(e);return i}var e=t.lib.Base,s=t.lib.WordArray,n=t.algo.AES,o=t.ext,i=t.pad.OneZeroPadding,a=t.algo.CMAC=e.extend({init:function(t){var e=r(t,o.const_Zero),s=e.clone();if(o.dbl(s),this._isTwo){var n=e.clone();o.inv(n)}else{var n=s.clone();o.dbl(n)}this._K1=s,this._K2=n,this._K=t,this._const_Bsize=16,this.reset()},reset:function(){this._x=o.const_Zero.clone(),this._counter=0,this._buffer=s.create()},update:function(e){if(!e)return this;var s=this._buffer,n=this._const_Bsize;for("string"==typeof e&&(e=t.enc.Utf8.parse(e)),s.concat(e);s.sigBytes>n;){var i=o.shiftBytes(s,n);o.xor(this._x,i),this._x.clamp(),this._x=r(this._K,this._x),this._counter++}return this},finalize:function(t){this.update(t);var e=this._buffer,s=this._const_Bsize,n=e.clone();return e.sigBytes===s?o.xor(n,this._K1):(i.pad(n,s/4),o.xor(n,this._K2)),o.xor(n,this._x),this.reset(),r(this._K,n)},_isTwo:!1});t.CMAC=function(t,r){return a.create(t).finalize(r)},t.algo.OMAC1=a,t.algo.OMAC2=a.extend({_isTwo:!0})}(CryptoJS);

最後に、本体のコード

function doPost(e) {
  const params = JSON.parse(e.postData.getDataAsString());

  const encodedUserName = Utilities.base64Encode(params.userName, Utilities.Charset.UTF_8);
  const action = { toggle: 88, lock: 82, unlock: 83 }[params.action];

  if (isAuthed(params.password)) {
    UrlFetchApp.fetchAll(
      devices.map(device => ({
        url: `https://app.candyhouse.co/api/sesame2/${device.uuid}/cmd`,
        method : "post",
        payload: JSON.stringify({ cmd: action, history: encodedUserName, sign: generateCmacSign(device.secretKey) }),
        headers: { 'x-api-key': apiKey },
      }))
    );
  }

  return ContentService.createTextOutput(JSON.stringify({ result: 'ok' }));
}

function isAuthed(inputPassword) {
  raw = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_512, inputPassword, Utilities.Charset.UTF_8);

  // https://qiita.com/tatataichi/items/7e3a27e1b69200d3fd86#%E3%82%B3%E3%83%BC%E3%83%89%E5%86%85%E5%AE%B9
  let inputPasswordDigest = ''; //ハッシュ値の文字列
  let numHash = 0;  //ハッシュ値の数値

  for (i = 0; i < raw.length; i++) {
    numHash = raw[i];
    if (numHash < 0) { //ゼロ未満だったら
      numHash += 256;  //マイナスの除去
    }
    if (numHash.toString(16).length == 1) {
      inputPasswordDigest += '0'; //1桁なら0をくっつける
    }
    inputPasswordDigest += numHash.toString(16); //16進数表記の文字列に変換
  }

  return passwordDigest === inputPasswordDigest;
}

// https://zmzlz.blogspot.com/2021/06/sesame3-sesame-os2-gas-web-api.html
function generateCmacSign(secKey) {
  const date = Math.floor(Date.now() / 1000);
  const dateDate = new DataView(new ArrayBuffer(4));
  dateDate.setUint32(0, date, true);
  const msg = dateDate.getUint32(0).toString(16).slice(2, 8);
  const hex = CryptoJS.enc.Hex.parse;
  return CryptoJS.CMAC(hex(secKey), hex(msg)).toString();
}

これらを 4 つのファイルに保存する。

外部に API を公開する

デプロイボタンから、ウェブアプリとして公開する。 この辺は、色々な記事が出ているので、その記事を参考に公開すること。 デプロイすると、https://script.google.com/macros/s/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/execのような URL が出来るので、保存しておく。

IFTTT と連携する

IFTTT と連携する。アプレットを作成する。If This には、location を Then That には Webhooks を指定。

location では、"You enter an area"を選ぶと、帰宅時。"You exit an area" だと出発時の動作になる。 無料アカウントだと、IFTTT のアプレットは 2 個までしか登録出来ないので、注意。

Webhooks には下記の指定をする

  • URL には GAS をデプロイした時に発行された URL (https://script.google.com/macros/s/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/exec)を指定
  • Method は POST
  • Content Type は application/json
  • Additinal Headers には何も指定しない
  • Body は { "password": "任意のパスワード", "userName": "任意の名前", "action": "unlock" }
    • 任意のパスワードは、エンドポイント認証用ダイジェストの時に指定したパスワード。
    • userName の名前は sesami に表示される名前なので、適当に
    • action には unlock、lock、toggle のどれかを指定する

これで設定は終わり。 IFTTT の連携は省略するが、Android でも Web と同じメールアドレスとパスワードを使えば連携出来る。

最後に

ちなみに、この連携作業は調査を開始して、1 時間ほどで実装出来た。 めちゃくちゃ簡単に連携出来たのは、先人のおかげである。 この記録は備忘録として書いている。 このブログが消えるまでは、少なくとも誰かの役に立つだろう。

コメント

このブログの人気の投稿

EFIブートローダを移動した話

EFIブートローダを移動した HX90に環境を整え終わってから、アホな事をしたので、その記録を残す。 SSD: Cドライブ SSD: Dドライブ(データストレージ用) + ESP※ SSD: Eドライブ(データストレージ用) ※ESP(EFI System Partition) インストールした時、こんな構成だった。 ESPがDドライブにあるのが気持ち悪かったので、これを削除した。 そしたら、BIOS画面が出るだけになり、Windowsが起動しなくなった。 移動手順 この時の自分はMBRをふっ飛ばした時と同じ現象だと思ったので、MBRというキーワードで検索したが、今はEFIブートローダーと呼んでいるらしい。 【Win10】任意のディスクにEFIブートローダをインストールする 色々検索した結果この記事が参考になった。 Diskpartを使って、パーティションを新たに分割し、bcdbootを実行して、無事に事なきを得た。 パーティションの分割はこんな感じ Diskpart Select volume 0 shrink desired = 200 Select disk 0 Create partition EFI size=200 Format quick fs=fat32 label="ESP" Assign letter=P exit EFIブートローダーのインストールはこんな感じ bcdboot C:\Windows /s P: /f UEFI ちなみに、自分の環境だけの問題なのだが、コマンドラインで、「\」を入力するのができなかった。我が家のキーボードはHHKBだけなので、日本語配列を無理やり適用されると、バックスラッシュが入力できないという不具合が生じる。 結局、コマンドプロンプトからマウスで範囲選択してコピーして貼り付けるという荒業でクリアした。 普通の人は、何も考えずに、\を入力すれば良い。 最終的に SSD: Cドライブ + ESP※ SSD: Dドライブ(データストレージ用) SSD: Eドライブ(データストレージ用) ※ESP(EFI System Partition) という構成に切り替えることができた。

PlemolJP導入

PlemolJP を導入した 思い立つことがあり、 PlemolJP をインストールする事にした。 自分は、PowerLine を使っているので、PlemolJP Nerd Fonts 版である、PlemolJP_NF をダウンロードしてインストールした PlemolJP の releases から、PlemolJP_NF_vx.x.x.zip をダウンロードする。 zip を展開し、PlemolJPConsoleNF-Regular.ttf をインストールする set guifont=PlemolJP_Console_NF:h18 を gvimrc に書く(フォントサイズはお好きに) ちなみに、これは Windows 限定の設定なので、Mac や Linux などで導入する際には、別の方法をオススメする。 余談 gvimrc に指定するフォント名が微妙にわかりづらい。 普段は、 set guioptions-=m してメニューバーは出していないが、今回は :set guioptions+=m を実行して、編集 → フォント設定からフォントを選び、フォント名に入力されている文字列をコピペして、スペース部分をアンダースコアで置き換えた。 この辺のルールとかに詳しい人にぜひとも教えて欲しい所。 導入した感想 今までの Powerline フォントはいまいちなのが多かったが、PlemolJP の表示は非常に良い。 今後はこちらをメインで使って行きたい。 あと、僕はヱビス派です。