2021年8月22日日曜日

【SESAMEサイクル(SESAMEシリーズ)】 NFCタグをエミュレートするアプリをつくった (Android)


セサミのNFCタグ機能を実際のタグなしで自由に発動させたくて、アプリを作った話。

(ページ下部よりAPKダウンロードできます)



 【まえがき】

うちでは玄関のセサミ3は前回記事で書いたGASで運用していてNFCタグ機能は使っていないのだけど、最近セサミサイクル(ママチャリ)を導入したことでうちにもNFCタグの出番がやってきた。

さっそくハンドルラバーの側面にタグを貼りつけ(ちょうど円くてピッタリ)、快適さをテストしたところ、どうも本体との接続に時間がかかった場合に解錠が作動せずセサミアプリの起動のみで終わってしまうことがある模様。


ただでさえAndroidはNFC読み取りにロック解除が必要なため、これでは一連の動作が

  • 自転車の手前でスマホを取り出す
  • スマホロックを解除
  • NFCタグにかざす
  • スタンドを外す
  • 自転車にまたがってスマホを見ながら解錠を待機(ダメなら手動で開ける)
  • スマホをしまい、Go!

となってしまい、スマートさにかける印象だった。

普通の物理鍵なら、これで済む話。
  • (たまに鍵を探す)
  • 鍵を取り出す
  • 鍵を挿す
  • スタンドを外し、Go!

ウィジェット運用も考えたけれど、たまの自転車に乗る時のために常に存在感を放たれてもちょっと…邪魔かも…ということで却下となった。


【対策を思案】

当初、全く別のアプローチ(Termux + pysesameos2)で途中まで進めてしまったが、Bluetooth関連の制約(?)でAndroid上ではうまく実装できず断念。
次にsesame_bridgeを検討したものの出先では使えなさそうだったためこちらも断念した。(ローカルループバックアドレスを使えば可能と後で知る)

ただ収穫はあり、sesame_bridgeのソースコードを参考に(というか丸パクリ)すればNFCタグインテントをエミュレートするアプリの作成はさほど難しくないのでは?と思い至り、急遽開発環境を整えた。(Androidアプリ初挑戦)



そして出来上がったのがこちら。

【アプリ概要 - EmuNFCtag】

初期設定を済ませると、次回から起動するだけでセサミのNFCタグ動作をエミュレートする単機能アプリ。SESAME3等にも使用可能。





【使用方法】

使い方としては、Taskerなどのマクロアプリを用いての「好みのトリガー → アプリ新規起動」という運用を想定。


実際の使用例

1. 環境に合わせて、EmuNFCtagを適切に設定しておく。

(自分の環境では4秒間隔、2回リトライに落ち着いた。)


2. MacroDroidでマクロ作成。(一例)





【解説】

・トリガー

可能な限りシンプルかつ誤操作の恐れが少ないものを考えて設定。
画面操作は個人的にめんどうなので、画面を見る必要がないものにしてみた。

・アクション

なぜか稀にサイクルとの接続が BleLogining で止まってしまうことがあったため、安定性重視でセサミアプリも毎回新規起動させることにした。こうしてからは今のところ発生していない。

すぐさまポケットにしまうことを想定し、画面を消すようにしている。


【使用感】

自分の環境では、これでかなり快適になった。

動作としては、

  • 自転車へ向かう良きタイミングでスマホを取り出す
  • スマホロックを解除
  • ジェスチャートリガー
  • スマホをしまう
  • スタンドを外し、Go!


箇条書きだと動作数こそなお物理鍵に劣るものの、実際には快適感が比ではない。タイミングを掴んだら大抵は自転車の数歩手前で、遅くてもハンドルを握ったくらいで作動してくれるようになった。

【SESAME 3での使用】

このアプリはもちろんセサミ3(セサミ4)に使用することもできる。ただリトライするとトグルで逆戻りしかねないため、一発を遠くから実行するという使い方になると思う。

たとえば、Wi-Fiモジュール+玄関先に貼り付けたNFCタグで運用しているのであれば「玄関前での数秒の待機がストレス」という問題の解消には使えそうだ。(ウィジェットとどちらが便利かは個人差)


2021年6月16日水曜日

【SESAME】 GAS から Web API を利用するサンプル

2021/10/30 更新

複数台操作の不具合の指摘をうけ、修正がてらプロパティ周りの仕様を大幅変更。


 【まえがき:APIが刷新されていた】


SESAME用のAPI* がひっそりと上がっていた(正式発表はまだ?)ので、Google Apps Script からいじれるようにしてみた。

*追記:セサミ3/4/5などに対応。

従来のものとは仕様が変更され、導入のハードルがすこし上がった感じ。

今回は、以前書いたセサミミニ用のスクリプトから乗り換えやすいよう、使用方法を極力継承してセサミ3以降に対応した新しいスクリプトを書いてみた。



【事前準備1:必要な各種データを入手】


1. 公式のユーザーページ(https://biz.candyhouse.co)にログインし、「開発者向け」ページでAPIキーを取得。


2. 以下のいずれかを行う。(追記:現在はB推奨)

  A. セサミアプリでマネジャー権限のQRコードを発行し、好みのQRコードリーダーで読み取り、中身のテキスト(ssm://UI? から始まる長い文字列)をコピーしておく。
(以前はSecret Keyなどを簡単に取得できず、このようにするしかなかった)


  B. ユーザーページの 「個人で登録済みのデバイス」ボタンから任意のデバイスを選択し、UUIDとSecret Keyを取得。





 


【事前準備2:外部JavaScriptを追加】


施解錠操作用の署名生成がややこしくて自力では困難なため、artjombさんの cryptojs-extension を拝借する。

1. まずはスクリプトファイルを新規作成
(Googleドライブ 新規→その他→Google Apps Script)

  • lib/cryptojs-aes.min.js
  • build/cmac.min.js
の内容をそれぞれ丸ごとスクリプトファイルに追加(コピペ)する。





ファイル + からスクリプトを追加できる。
※初期状態で表示されるテキストは不要なので、テキストエリアを空白にしてから貼り付け

名前はなんでも良いけれどわかりやすくそのまま。

【重要】3件のファイルは必ず上記画像の順で配置する。




【汎用サンプルコード】

ようやくメインのコード。

はじめに、「プロジェクトの設定」でChrome V8 ランタイムが有効になっていることを確認。(多分デフォルトでなっている。)

コード.gsに戻り、テキストエリアを空白にしてから以下をコピペ。

const prop = PropertiesService.getScriptProperties();
const myKey = prop.getProperty('myKey');
const apiKey = prop.getProperty('apiKey');

// プロパティ登録 (*****部分を書き換えて初回のみ実行する。実行後は消してもOK)
function prepare(){
  prop.deleteAllProperties();
  prop.setProperty('myKey', '*****'); // 任意のキー。自由に設定する
  prop.setProperty('apiKey', '*****'); // ダッシュボードで取得したAPIキー
  
  // 事前準備A組は↓に、QRから取得したテキスト全文を入れる
  analyzeQR('*****');
  //analyzeQR('*****'); // デバイスが複数ある場合、適宜追加

  // 事前準備B組は↓に、セサミに付けた名前, UUID, SecretKey の順で入れる
  analyzeQR('*****', '*****', '*****');
  //analyzeQR('*****', '*****', '*****'); // デバイスが複数ある場合、適宜追加
  
  showProps();
}

//プロパティ確認
function showProps(){
   console.log(prop.getProperties());
}

// 動作テスト。成功したら200が返り、セサミが動く
function test() {
  main(myKey, "セサミの名前", 2, "GAS");
  /*
  説明:
  "セサミの名前" を実際のセサミの名前に変更する
  "GAS" はアプリの通知と履歴に表示される名前。変更可
  
  セサミの名前について補足説明:
  将来もしアプリ上でセサミの名前を変更したとしても、このスクリプトの運用に影響はありません 
  設定を更新する必要はなく、逆に言えば、prepare()実行時点の名前を使い続ける必要があります
  新しい名前をGASでも使いたい場合は、新しい情報で再度prepare()を実行します
  */
}

function doPost(e) {
  const p = JSON.parse(e.postData.contents);
  main(p.myKey, p.deviceName, p.command, p.user);
}

function doGet(e) {
  const p = e.parameter;
  main(p.myKey, p.deviceName, p.command, p.user);
}

// 施解錠操作
function main(key, device, command, user='ウェブアプリ') {
  if(key != myKey) return;
  const c = [83, 82, 88][parseInt(command)]; // lock:82,unlock:83,toggle:88
  const h = Utilities.base64Encode(user, Utilities.Charset.UTF_8);
  const devices = device.split(',');
  devices.forEach(function(name) {
    const data = JSON.parse(prop.getProperty(name));
    const body = {
      'cmd': c,
      'history': h,
      'sign': generateCmacSign(data.secKey)
    }
    const options = {
      headers: {'x-api-key': apiKey},
      method: 'POST',
      muteHttpExceptions: true,
      payload: JSON.stringify(body)
    }
    const url = `https://app.candyhouse.co/api/sesame2/${data.uuid}/cmd`;
    const response = UrlFetchApp.fetch(url, options).getResponseCode();
    console.log(response);
  });
}

// CMAC認証
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();
}

// QR情報デコード
function analyzeQR(p1, p2, p3){
  if(p1 == '' || p1.indexOf('*') == 0) return;
  let name;
  let data = {};
  if(p2){ // デコード済みデータが来てる
    name = p1;
    data = {'uuid':p2, 'secKey':p3};
  }
  else{ // 生データが来てる
    const ssm = decodeURIComponent(p1)
    const params = ssm.slice(ssm.indexOf('?') + 1).split('&');
    params.forEach(function(p) {
      if (p.indexOf('sk=') == 0){
        const sk = p.slice(3);
        const uuid = `${hx(sk,83,86)}-${hx(sk,87,88)}-${hx(sk,89,90)}-${hx(sk,91,92)}-${hx(sk,93,98)}`;
        data.uuid = uuid.toUpperCase();
        data.secKey = hx(sk, 1, 16);
      }
      else if(p.indexOf('n=') == 0){
        name = p.slice(2);
      }
    });
  }
  prop.setProperty(name, JSON.stringify(data));
}

// QR情報デコード Core
let hx = (data, start, end) => {
  return Utilities.base64Decode(data).slice(start, end + 1)
  .map(function(chr){return (chr+256).toString(16).slice(-2)}).join('');
}



【使用方法】


【1. プロパティを登録】

まず初回のみプロパティを登録する。
  1. function prepare() 内の *****部分を自前の情報に書き換え、保存ボタンを押す。
  2. 関数メニューで「prepare」を選択し、実行ボタンを押す。

【2. 動作チェック】

  1. function test() 内の情報を適宜書き換える。
  2.  関数メニューで「test」を選択し、実行ボタンを押す。

成功したら200が返り、セサミが動く。

うまく行かない場合は…
主な返り値と原因の可能性
404:サーバーダウン OR ルーターやWi-Fiモジュール等の問題かも。
403:APIキーが間違っているかも。
502:UUIDが間違っているかも。
200なのに動作しない:SecretKeyが間違っているかも。


【3. ウェブアプリ化】

  1. 「デプロイ」 → 「新しいデプロイ」
  2. 「種類の選択」で「ウェブアプリ」を選択
  3. 「次のユーザーとして実行」を自分、「アクセスできるユーザー」を全員にする。
  4. デプロイ実行






















途中で「承認が必要」と出た場合は流れに従って承認する。
(ログイン→詳細→安全ではないページに移動→許可)

最後に表示されたウェブアプリURLをコピーしておく。


【4. 運用】

あとは従来同様、このURLに適切にリクエストを送れば良い。
より汎用的にするためPOST、GET両方用意しているけれど、基本的にはPOSTでOK。

パラメータは従来からひとつ増え、myKey、deviceName、command、userの4つ。
  • myKey:prepareで準備した任意のキー。一致しないとはじかれる。

  • deviceName:セサミの名前。複数台同時に動かしたい場合は、カンマ区切りで「玄関,玄関2,倉庫」のように。

  • command:数字で、0~2のいずれか
    0=解錠 1=施錠 2=トグル

  • user:通知や履歴に表示される名前。省略可。省略した場合「ウェブアプリ」となる。
    ※userに特定の文字列を指定すると一部が文字化けする事象を確認済み。特定の文字というわけでなく、規則性をつかめず。現時点で詳細不明。



【最後に:スクリプトをあとから編集した場合】

スクリプトの編集をウェブアプリに反映させるには再デプロイが必要。

1. 編集を保存
2. 「デプロイ」→「デプロイを管理」
3. 編集ボタン(右上のペンアイコン)を押して、「バージョン」から新バージョンを選択
4. デプロイ実行


以上。

(注意・免責)リスクを管理し、あくまで自己責任で使用してください。

【余談】

今回は施解錠のみのスクリプトのため以上となるけれど、新APIはGETも刷新されていて、履歴やバッテリ電圧、サムターン角度なども取得できるようになっていた。アイデア次第で便利に使えそうだ。


2020年8月2日日曜日

【言語】【日本語アクセント】単語の一拍目の平板化(高音化)

日常の実際の発話において一拍目が高くなる単語とそのルールについての研究メモ

 

   ・・・ ・・・・

  ・

例:おんせん→おんせん



変化が起こりうる語の種類

  • 一拍目が低く二拍目が高い型(つまり1型以外)
  • おそらく品詞は問わない。
  • 複合語や活用変化も対象(ルール3の例を参照)

ルール1.二拍目が撥音の場合

例:安全 林檎 運転手 延長 温泉

ルール2.二拍目が長音の場合

例:黄色 新潟 言い分 小さい

  英語 携帯 性格 映画館

  教室 証言 風船 ニューヨーク

ルール3.二拍目が「い」の場合

例:階段 大会 来客 買いたい

  水田 追放 初陣 縫い目

  恋文 お暇 追いかける

  (「黄色」などは2と重複)


考察

  • 必ずしも変化するわけではなく個人差がある。変化の具合にも個人差があり、二拍目と同じ高さまで上げる人もいれば、二拍目の若干下でとどめる人もいる。
  • おそらく無意識の人が多く、単語単位で質問すると、平板化しない(教科書的に正しい)アクセントを教えてくれる日本人もいて、音感の良い外国人(日本語学習者)は体感との差に混乱してしまう可能性がある。

 

追記:辞書の凡例に同様の解説を見つけた(つまりあってた)

 

 

"コーリ0 、トーリ3 、ハンシャ0 、カイシャ0 など、二拍目がのばす音、はねる音、アイ/オイなどの後半のイという独立性の弱い拍の場合は、(i)のように普通は一拍目から高く発音する。一方、さらに弱いつまる音が二拍目にくるバッタ0 などでは、(j)のように三拍目から高くするのが一般的である。これらの弱い拍は、その直後で下げることも通常ない。○ー○、○ッ○などに2 型は欠けている。"

 画像元および引用元  <http://www.sanseido.biz/main/Dictionary/Hanrei/meikoku_accent.aspx>

 

 

もうひとつルールがあった。

文中の場合

 

(a) ス ミ レ 
(b) コ ノ ス ミ レ ガ 
(c) キ ノ ー サ イ タ コ ノ ス ミ レ オ ア ゲ ル 
( い す れ も 一 ま と ま り に 発 音 し た も の )

"スミレのスからミへの上げは、それを単独で発音したため、つまりスミレが最初に位置しているために現われたもので、スミレという単語にいつも決まって出てくる性質ではない。"

 

画像元および引用元  <http://www.sanseido.biz/main/Dictionary/Hanrei/meikoku_accent.aspx>


あとがき

複数の友人から同じ質問をされたため、ちょっと研究してみたのだけれど、結局、辞典にしっかり解説が出ていたことが判明。ならばなぜ知られていないのか…。国語研究者にとっての常識であって、外国人向け日本語教材にあまり反映されていない情報なのかもしれない。


2019年10月3日木曜日

Googleカレンダーで中国の祝日を中国語表示にする方法

【経緯】
日本語設定のGoogleカレンダーに中国の祝日を追加すると、通常の方法*では英語表示になってしまうらしい。

*設定 → カレンダーを追加 → 関心のあるカレンダーを探す → 地域限定の祝日 → 中国の祝日

【中国語表示にしてしまおう】
おそらく日本語表示は無理なので、中国語表示にする。

設定 → カレンダーを追加 → カレンダーに登録 →
以下を入力
zh.china#holiday@group.v.calendar.google.com

以上。

最初のzhが言語設定。jaだと英語になってしまう。

2019年9月20日金曜日

【スマートリモコン】テレビの電源 トグル問題

【まえがき】

テレビのリモコンはたいてい電源オンオフが同じ信号のため、部屋の外から確実にテレビを消したい、またはつけたい場合、スマートリモコンを普通に使うのでは不可能である。

これに関して、Nature Remoの公式記事に対策のひとつが紹介されていて、そういえばと思い、うちで使用中のその他の方法を書いておくことにした。


【1.ひとつのボタンに2倍学習させる】


原理は上の記事と同じで、適切なタイミングで電源の信号を二回送信してやる。
記事ではシーンを利用しているが、うちでは学習機能の待機時間が長めであることを利用し、ひとつのボタンに連打の信号を登録している。

手順はまずリモコンで電源ボタンを連打し、テレビがオフの時はついて消えない、かつオンの時は消えて再びつく間隔を調べる。それをそのままスマートリモコンのボタンに登録する。(一つのボタンに2連打を収める)

※パナソニックVIERAとシャープAQUOSで検証済み

ちなみにこの方法を使えば、音量などは長押し信号を一つのボタンに学習させることで、一度押すだけで「n上げる」「n下げる」なども可能になる。


【2.オンオフ独立信号】


SHARPアクオス(全機種かは不明)にはオン、オフ独立したコードが密かに存在しており、それを登録してやればいい。他メーカーもあるかも? あった(追記済み)

オフ AA5A8F124B62
オン AA5A8F124B72
このコードは以前ネットで見つけたもので、実際そのとおりだったので驚いた。

使用手順

【USBリモコン編】
PCなどと接続できるUSB赤外線リモコンキットを利用すれば、この信号を直接他のスマートリモコンに学習させることができる。
送信コードは013000を付加し、
オフ 013000AA5A8F124B62
オン 013000AA5A8F124A72
となる。

ただ、なかなかこれを持っている人は少ないと思う。

【Nature Remo編】
Nature RemoであればローカルAPIを叩いて、少々二度手間だが、別の学習リモコン(スマートリモコンである必要はない)経由で登録できる。

うちで取得したRemo独自のローカルAPI用コードはこちら

オフ
{"format":"us","freq":38,"data":[3425,1828,372,544,377,1348,376,537,378,1348,378,539,375,1352,374,542,375,1351,374,541,375,1351,366,548,377,1350,374,1351,376,541,375,1357,362,549,380,1345,377,1350,376,1350,376,1353,371,536,377,534,379,535,377,1349,375,544,371,1349,375,543,375,539,374,1349,368,552,370,539,374,538,373,1349,376,1353,374,537,375,1350,376,537,378,534,378,1351,372,541,371,538,375,1352,373,539,381,532,375,537,376,1351,367,1364,373,544,399]}

オン
{"format":"us","freq":36,"data":[3457,1794,399,513,422,1311,392,519,424,1303,398,515,421,1311,392,514,406,1327,379,532,426,1305,400,512,424,1310,404,1328,399,518,423,1308,396,516,423,1309,421,1316,424,1304,426,1314,396,520,378,531,397,511,421,1310,399,515,422,1303,400,517,399,514,421,1309,381,532,401,507,398,514,397,511,423,1309,377,537,422,1307,399,517,393,516,425,1304,399,512,398,514,422,1309,399,510,401,511,409,1322,424,1311,423,1305,398,514,424]}

これをまずRemoで送信し、学習リモコンに登録する。次に学習リモコンの信号を逆にRemoアプリで学習。これでグローバルに使えるようになる。

【ラトックシステム(RS-WFIREX3 / RS-WFIREX4)編】
ラトックシステムの場合はIFTTT連携の「赤外線コマンド送信」というアクションが活用できる。

うちで取得したラトックシステム独自のコードはこちら

オフ 23120405040d0405040d0405040e0405040d0405040d0405040e040d0405040d0405040e040d040d040d040504050406030e0405040d04050405040d040604050405040d040d0405040d04060405040d04050405040d040504060306040d040d040504

オン 23120405040d0405040d0405050d0405040d0405040d0405050d040d0405040d0406040d040d040d050d040504050405040d0405040d04060405040d0405040504050405040d0406040d04050405040d04050405050d04050405040d040d050d040504

IFTTTのアクションで、「赤外線データ」にこのコードを入力。
「赤外線データフォーマット」はデフォルト(0) 。

ただし、このアクションは将来予告なしに削除される予定とのこと。早めにNature Remoと同様の方法で学習リモコン経由でアプリに登録したほうが良さそう。

もしくは家電リモコンアプリのインポート・エクスポート機能を使っても登録可能と思われる。適当にボタンを2つ追加しておき、エクスポートしたデータを書き換えてインポートすれば良いと思う。(未検証)


【追記】

ほかのメーカーのコードも見つけた。ディスクリートコードと呼ばれているようだ。
残念ながらラトックシステムは手放してしまったため、NatureRemo版のみ記載しておく。
(ビエラ以外未検証)

パナソニック(VIERA) オフ
{"format":"us","freq":38,"data":[3387,1709,428,427,427,1282,427,427,397,427,428,427,427,427,428,427,427,427,428,427,427,397,427,427,428,427,427,427,428,1281,428,427,427,397,427,427,427,428,427,427,427,428,427,427,427,428,427,397,427,1282,427,427,427,428,427,427,427,428,396,428,427,427,427,428,427,427,427,1282,427,1252,427,1282,427,1281,428,1251,427,1282,427,427,428,427,427,1282,427,1251,428,1281,428,1281,428,1251,427,1282,427,427,428,1281,428,65535,0,36180]}

パナソニック(VIERA) オン
{"format":"us","freq":38,"data":[3387,1709,428,427,427,1282,427,427,397,427,428,427,427,427,428,427,427,427,428,427,427,397,427,427,428,427,427,427,428,1281,428,427,427,397,427,427,427,428,427,427,427,428,427,427,427,428,427,397,427,1282,427,427,427,428,427,427,427,428,396,428,427,427,427,428,427,427,427,428,427,1282,396,1282,427,1282,427,1282,427,1251,428,427,427,427,428,427,427,1282,427,1251,428,1281,428,1281,428,1251,427,427,428,1281,428,65535,0,36180]}

ソニー(BRAVIA) オフ
{"format":"us","freq":38,"data":[2411,580,1220,580,1221,580,1221,579,1221,580,610,611,1190,610,580,610,1190,611,610,580,610,580,611,610,580,24627,2381,610,1190,611,1190,610,1190,611,1190,610,580,610,1191,610,610,580,1221,580,610,610,580,611,610,580,610,24597,2411,580,1221,580,1220,580,1221,580,1220,580,611,610,1190,610,580,611,1190,610,611,579,611,610,580,610,611,65535,0,36180]}

ソニー(BRAVIA) オン
{"format":"us","freq":38,"data":[2411,580,610,610,1191,610,1190,611,1190,610,580,610,1190,611,610,580,1221,579,611,610,580,610,580,611,610,25207,2381,610,611,579,1221,580,1190,610,1191,610,610,580,1221,580,610,610,1191,610,580,610,610,580,611,610,580,25207,2411,580,610,611,1190,610,1190,611,1190,610,580,611,1190,610,610,580,1221,580,610,611,579,611,610,580,610,65535,0,36180]}

LG オフ
{"format":"us","freq":38,"data":[8942,4577,550,549,580,549,580,1678,611,549,549,550,580,579,580,549,611,519,549,1709,549,1709,550,579,580,1679,610,1648,549,1679,580,1678,610,1648,611,1617,580,580,580,1648,579,550,580,518,580,550,579,1679,580,1678,580,549,580,1679,579,519,580,1709,580,1678,580,1648,580,549,580,549,550,40771,9003,2258,611,65535,0,31785,9003,2228,579,65535,0,31817,1251,65535,0,36180]}

LG オン
{"format":"us","freq":38,"data":[8972,4547,550,579,580,519,580,1709,580,549,610,519,550,579,611,518,580,550,579,1679,580,1709,549,549,580,1679,580,1647,611,1648,610,1648,580,1678,611,519,610,519,549,1739,550,580,549,580,610,519,580,1678,580,1679,579,1648,611,1648,610,519,549,1709,580,1709,549,1679,580,518,580,549,580,40741,8972,2289,580,65535,0,31816,2869,65535,0,36180]}


【3.HDMI経由】


Chromecastを挿してあれば、テレビの設定次第でGoogleアシスタントから「(テレビの名前)を消して/つけて」が可能。スマートリモコンは関係ない。

2019年3月28日木曜日

【Alexa】Blueprintsや定型アクションで、喋らせる文章を区切る方法

【症状】
定型アクションのカスタムおしゃべりや、Blueprintsのスキルで設定した返答で、句読点が読み方に反映されない。2019.3現在、日本語の句読点は無視され、流れるように読んでしまう。
例外として、「は」のあとに読点を入れた場合は少し間を開けてくれる(?詳細不明)

【対策】
半角ピリオドを入れてやればいい。
全角ピリオドやスペース、!、?等はすべて区切りとしては機能しない。

【裏技】
ちなみに、もっと長い待機時間を設定したい場合、半角ピリオドと小さい「っ」を連続で入力すると、無音をキープすることができる。

.っ.っ.っ.っ.っ.っ.っ.っ.っ.っ.っ.っ.っ.っ.

ピリオドの連続では無音時間は増えない。「っ」の連続ではちょっと増えるけどコスパが悪い。

【補足】
がっつり自分でスキルを作るなら、
<break time='5s'/>
のような挿入が可能。Blueprintでは無理みたい。

2019年3月14日木曜日

【SESAME・SESAME mini】 GAS から Web API を利用する。(サンプル)

【作成日:2019/3/14】
【更新日:2020/12/20】Google Apps Scriptの新エディタに関する補足を追加。
【更新日:2020/6/16】SESAME3(Sesame OS2)のAPIに対応したバージョンへの案内を追加。

【追記】
※GASがV8対応する前に作ったものですが、動くので更新はしてません。
※SESAME3ではAPIが刷新されたので、この方法は使用できません。新しい方法はこちら

2018年2月24日土曜日

【言語】 日本語の自動詞・他動詞 形におけるルールの研究

【経緯】
外国人の友人が、自動詞と他動詞についてそれぞれの型は覚えているんだけど使う際にどっちがどっちだったかよく忘れてしまうので、法則を探ってみた。(自動詞・他動詞の概念は理解している前提)

一般にはよく、「~を」をつけられる方が他動詞という説明があるが、それを判断するにはどちらが自然かわかっている必要があり、ネイティブ以外にはピンとこないことも多い。そこで、かたち上のルールを探してみるも、少なくともネット上には見つけられなかった上に、「ない」との回答まで。なので、見た目および音声的に分かる規則を独自に研究してみることにした。

その結果わかったこと。(仮説)

【仮説1】

まず、自動詞と他動詞、両方を持っている動詞は、例外を除いて、
  1. 「語幹+aる」、「語幹+oる」は、自動詞
  2. 語幹の後ろにサ行を含むもの(「~す」「~せる」「~する」・サ行型と名付ける)は、他動詞
  3. 「語幹+eる」、「語幹+u」は、中間の性質を持つ

と考えればOK。


※ ここでの語幹とは、自動詞と他動詞で変化しない部分のこととする。

表にしてみると、


より左側にあるほうが自動詞、より右側にある方が他動詞となる。

※ 便宜上、サ行型の「~」は語幹プラスアルファも意味できるものとする。
例:出る(d-eru)/出す(d-asu)の場合、「d-a」までを表す。


例を挙げて確認してみよう。

自動詞/他動詞
集まる/集める:~aる/~eる
変わる/変える:~aる/~eる
出る/出す:~eる/サ行
溶ける/溶かす:~eる/サ行
増える/増やす:~eる/サ行

伸びる/伸ばす:~iる/サ行
落ちる/落とす:~iる/サ行
尽きる/尽くす:~iる/サ行
※ 表を簡易にするため、「~iる」は表には書いていないが、サ行型すなわち他動詞と考えれば良い。

隠れる/隠す:~reる(~eる と同類)/サ行
括弧 … 便宜上同類とみなす意。以下同じ。

乗る/乗せる:~ru(~u)/サ行
消える/消す:~ieる(~eる)/サ行
揺れる/揺する OR 揺らす:~eる/サ行
移る/移す:~ru(~u)/サ行
こもる/こめる:~oる/~eる
動く/動かす:~u/サ行
掴まる/掴む :~aる/~u

刺さる/刺す:~aる/~u
合わさる/合わせる:~aる/~eる
※このような語幹にサ行を含む動詞は注意する。(サ行型ではない。)

滅ぶ/滅ぼす:~u/サ行
潤う/潤す:~u/サ行
なくなる/なくす:~naる(~aる)/サ行
積もる/積む:~oる/~u


ただ、実は困ったことがある。

進む/進める:~u/~eる
立つ/立てる:~u/~eる
叶う/叶える:~u/~eる
傾く/傾ける:~u/~eる
付く/付ける:~u/~eる


折れる/折る:~eる/~u
とれる/とる:~eる/~u
割れる/割る:~eる/~u
砕ける/砕く:~eる/~u
焼ける/焼く:~eる/~u
そげる/そぐ:~eる/~u

「~eる」と「~u」は表の同じ位置にあるから、このような動詞は、どちらが自動詞でどちらが他動詞か区別がつかない・・・。

だけど、よく見てみると手がかりが。

折れる/折る:~eる/~u
とれる/とる:~eる/~u
割れる/割る:~eる/~u
破れる/破る:~eる/~u
切れる/切る:~eる/~u
生まれる/生む:~areる(~eる)/~u

「~eる」が「~れる」になる動詞は、「~u」が他動詞になっている。
例外もあるかもしれないけど。

逆に、「~eる」が「~れる」以外になる動詞は 、大体「~u」が自動詞。

沈む/沈める:~u/~eる
育つ/育てる:~u/~eる
揃う/揃える:~u/~eる
浮かぶ/浮かべる:~u/~eる
捕まる/捕まえる:~ru(~u)/~eる

でもでも、まだ問題がある。

「~く(ぐ)」と
「~ける(げる)」の動詞はどうやって区別する?

傾(かたむ)く/傾ける:~u/~eる
付く/付ける:~u/~eる
退く/退ける:~u/~eる
届く/届ける:~u/~eる
傾(かた)ぐ/傾げる:~u/~eる

砕ける/砕く:~eる/~u
焼ける/焼く:~eる/~u
そげる/そぐ:~eる/~u
抜ける/抜く:~eる/~u
剥(は)げる/剥ぐ:~eる/~u

これは結論から言うと、ひとつひとつ覚えるしかないと思う。
その証拠に

向く/向ける
剥(む)ける/剥く

むく、むける、むける、むく……。
活用も発音もアクセントも、まったく同じ。なのに自動詞・他動詞は逆転してしまうのだ。
仕方がないので、このパターンは地道に覚えてもらおう。
(ただ、すごく感覚的な話で言えば、「~ける(げる)/~く(ぐ)」のタイプは、結構大幅に物の状態が変わってしまうものが多い気がする。ある程度の目安にはなるかも(?))


最後に代表的な例外を載せて終わりとする。

分かれる/分ける:~areる/~eる
聞こえる/聞く:~oeる/~u
見える/見る:~eる/~ru
開(ひら)く/開(ひら)く


↓以下追記。更新版(仮説2)あり↓



【友人から指摘があったので、それに対する回答】

1.例にある「捕まる/捕まえる:~ru(~u)/~eる」って「語幹+aる」じゃない?
→ ごめん、「語幹」の定義について説明がなかったから混乱させた。(追記済み)
ここで言う語幹とは、自動詞と他動詞で変化しない部分のこと。
つまり、「集まる/集める」なら、「atsumaru/atsumeru」。語幹は「atsum」で、「語幹+aru/語幹+eru」となる。
「つかまる/つかまえる」の場合、「tsukamaru/tsukamaeru」。「tsukama」まで同じなので、「語幹+ru/語幹+eru」となる。

2.開くの自動詞は開けるじゃない?
→ 「開(ひら)ける」もたしかにある。「開(ひら)く」も自動詞として使える。特殊。

3.「~れる」は必ず自動詞だったら、最初の表の左側に「~れる」を入れた方がよくない?
→ 「~れる」が必ず自動詞という意味ではなくて、「~eる/~u」の組み合わせの時、「~eる」が「~れる」になる動詞は「~u」が他動詞ということ。
でも言われてみたら、もしかすると「~れる」はいつでも、ほぼ自動詞なのかもしれない。例外として、「入(はい)る/入(い)れる」がある。これを例外とすれば、「~れる」は自動詞として良いかも。

4.乗る/乗せるの「乗る」は「語幹+oる」のパターンだね?
→ これも1.と同じで、「noru/noseruで「no」まで同じだから、「語幹+ru/語幹+seru」。

5.「〜する」は大体自他動詞だね
→ ここでのルールは、冒頭に書いたとおり、自動詞と他動詞それぞれある動詞について、どちらかわからなくなった時のためのものだから、両方同じなら気にしない。
片方が「~する」で、もう片方が違う場合は、「~する」が他動詞と考えて良い。
自他動詞って用語はよく知らないんだけど、たぶん「熟語+する」(勉強する 運転する 等)の場合じゃないかな?


以上をふまえて更新すると、下の表のような仮説がたてられるかも。

【仮説2】


※ 「語幹+seる」(乗せる 等)は、サ行(~せる)としているが、別の解釈として「~eる」の同類とみなし「その他の ~eる」としても支障はない。

※ 例外の「分かれる/分ける:~areる/~eる」について、
「~areる」を「~eる」の同類とみなすと、「~れる」/「~ける」となり、うまく表に適合し、例外ではなくなる。

最後の最後に
改めて言うけれど、これはあくまで自動詞と他動詞が別の型として存在する動詞において有効と思われる相対的な位置であって、たとえば、自動詞のない「忘れる」は問答無用に他動詞である。


以上

2016年10月7日金曜日

【kobo】 cbzをうまくepub化する

環境:Windows10 64bit, kobo glo HD ver.3.19.5761

今まで自炊はcbzを使っていたが、CFWを使わないと著者不明になったり、ページめくりが逆だったり、慣れればどうでもいいことだが、スマートではない運用だった。

何か方法はないかと一念発起して調べてみた結果、問題のないepub化に成功した。
(epub化に際して主に出現しがちな問題は、全画面表示にならないことと、1ページ毎にページリフレッシュされてしまうこと、である)

最も手っ取り早いのは、RcKepubを使う方法だった。目次無しにでき、軽い運用ができそう。

設定を
表示方法:SVG/テキスト同列表示
ファーム:3.19以降
右綴じ
にして、cbzをD&Dで一括で処理できる。

ただ、koboに入れてみると、目次がないことはいいのだが、シークバーも無効になってしまったので、却下。(設定で変えれるのだろうか。よくわからない。割付の設定も意味がなかった。)


【最終型】
AozoraEpub3と、RcKepubと同じ作者の方のAozoraMaxPlusを併用することにした。

バージョンは、AozoraEpub3-1.1.0b46、AozoraMaxPlus ver1.09

※AozoraMaxPlus ver1.10では全画面表示がうまくいかなかった。
AozoraMaxPlusは32bit環境用ということだが、今回の用途では64bit環境でも問題なかった。


【方法】
まず、AozoraEpub3の設定を、
[変換] 
拡張子:kepub.epub
※この時点で固定レイアウトのfxl.kepub.epubを選ぶと、AozoraMaxPlusでうまくいかない
縦書き 
ページ出力:表紙画像OFF
[画像2] 
SVGタグ出力OFF
※この時点で全画面表示用にSVG化すると、AozoraMaxPlusでうまくいかない 
[詳細設定] 
栞用ID 各行に出力ON 

その他任意

として、cbzをD&D。kepub.epubが出力される。


次に、AozoraMaxPlusの設定を、
画像単ページを全画面化ON
画像単ページをテキストモードで表示ON
その他OFF
として、先に出力したkepub.epubをD&D。
ファイルが同名で保存され、もとのファイルはorgとしてバックアップされる。

【注意点】
もとのファイルに","(カンマ)が含まれているとエラーになるため、リネームしておくこと。

複数一括でD&Dした場合、この変換には結構時間がかかる。そのうえ、途中でTEMPファイル削除のウィンドウが勝手に何度も表示されるため、裏で動かしてほかごと(特に文字入力作業)をしていると鬱陶しい。(先にWIndowsのゴミ箱の設定を「削除と同時にファイルを消去する」にしておくと無駄なファイルがゴミ箱内に残らずに済む)

また、RcKepub、AozoraMaxPlusともに、起動するとなぜかWindowsの言語設定が勝手に変更され、英語IMEが有効になる。閉じると直る。日本語、英語以外のIMEを入れていて普段よく切り替えを行う場合、挙動が変わり混乱する。
処理中はPC放置がよい。




【結果】
全画面表示OK
シークバーOK
ページリフレッシュ間隔OK

AozoraEpub3の仕様上、目次が5ページごとについてしまった。
個人的にはなくていいのだが、気にしないことにする。cbzの1ページごとよりはいい。


【蛇足】
ついでに、.kobo\Kobo\Kobo eReader.confの[Reading]に
numPartialUpdatePageTurns=60
と追加してページリフレッシュ間隔を変更した。1~99を設定できるそう。

【Android 6.0】 arrows M03 でmicroSDカードを内部ストレージ化する

【結果】
うまく行った。ネット記事に散見されるおかしな容量表示にもならなかった。




【過程】
方法はすでに多くあがっているので、
「android6.0 sdカード 内部ストレージ化 adb.exe」
などで検索したら出てくる。

流れを要約すると、

  1. PCにAndroid SDKをインストール(使うのはplatform-toolsフォルダのadb.exe)し、スマホを「開発者オプション」から「デバッグモード」にして、PCと接続。(開発者オプションが出てない場合はビルド番号連打)
  2. PATHを通すとかは面倒なので、adb.exeのあるフォルダからコマンドプロンプトを開く。(エクスプローラーでadb.exeのあるフォルダを開き、パスの部分(アドレスバー)を空白にして、「cmd」と入力後、Enter)
  3. 以下のコマンドを入力
    adb shell
    sm list-disks
    sm partition disk:[※list-disksコマンドで示された数値] private
    ※当環境では179,64だった。(sm partition disk:179,64 private)
  4. 少し待って、最後が$マークの行が出たら完了。デバッグモードをオフにする。
容量表示が変になるという記事ばかりだったから恐る恐るだったが、拍子抜けするほど簡単に成功した。早くやればよかった。

以降、インストールするアプリはデフォルトでSDカードに保存される。(SDに保存できないアプリを除く)

既存のアプリを移動させるには、設定の「端末管理」→「アプリケーション」から、任意のアプリを選択し、「SDカードと保存領域」をタップ。
移動可能なアプリなら、「使用されているストレージ」という項目があり、変更が可能。

※ただ、Evernoteが、移動可能にもかかわらず移動するとデータベースエラーが出て、内部メモリに戻しても直らなかったため、再インストールした。他のアプリは分からないが、はじめから再インストールのほうが安全かもしれない。


【注意点】
このほどシステムのアップデート通知が来たので行ったら、なんとSDに保存していたアプリが消えてしまった。
少々パニックになったが、ランチャーのアプリ一覧には無いけれど、設定の「アプリケーション」一覧にはあることを確認。
そこから一つのアプリを選択し、内部メモリに移動させると無事復活した。

その後、同じ方法ですべてのアプリを復活させようと思ったら、なぜかもう復活していた。
もしかすると、アップデート後、SD上のアプリの読み込みに時間がかかるだけかもしれない。
いずれにせよ詳細は不明のため、システムアップデート時は注意が必要だ。



2014年10月8日水曜日

Windowsの システムの復元 と Dropbox の相性が悪い件

うちの環境 (Windows8.1) では、システムの復元を実行すると、
Dropbox内の一部のファイルのバージョンが、復元先(過去)のバージョンに戻ってしまう。

そして、Dropboxは厄介なことに、この復元されたローカルファイルを最新のバージョンとみなし、
ストレージを上書きしてしまう。

幸い、事後でもDropboxのバージョン管理で、1つずつチェックして復元はできるが、
該当ファイルの特定が面倒だ。


どうやら、ユーザー・プロファイル・フォルダ(C:\Users\名前\)
の直下に、任意で新しく作られたフォルダは、
システムの復元の対象となる(ユーザーデータとして扱われない)らしい。

とすると、Dropboxが変なところにフォルダを作るからいけない。
Dropboxフォルダの場所をマイドキュメントの中などに変更すると、
ユーザーデータとして扱われて、バージョンが戻ることはなくなった。

これは知っていないと困ることがあると思うのだが、
ネットにはこの件が問題になっている様子はない。

INIファイルやVBSファイルなど、システムの復元の対象になる拡張子を
Dropbox内に保存しているユーザーが少ないからだろうか。


参考URL(XPの情報):http://menushowdelay.blog.fc2.com/blog-entry-185.html

追記:
2016年8月現在、Windows10環境でシステムの復元をすると、ドキュメントフォルダ内のスクリプトファイルも復元対象になるようだ。(はっきりとは未確認)

システムの復元をした際には、再起動後ネットワーク接続を切った状態でログインして、事前にDropboxフォルダの中身を確認したほうが良いだろう。サイズの大きいファイルがなければ、同期前にローカルファイルを全削除してしまうのも手だ。



2014年10月6日月曜日

AHK MouseGestureL ウィンドウをマルチモニタ間で移動(トグル)

最近使い始めたMouseGesutureL。
見よう見まねで、やっとできた。
普通にアクションに入れるだけでいい。
マルチモニタといっても、デュアル限定。

ジェスチャーの認識終了後に実行 は気分的なもので、なくてもいいかもしれない。



;モニタ移動
;ジェスチャーの認識終了後に実行
if (MG_Defer()) {
;アクティブウィンドウをターゲットにする
MG_SetActiveAsTarget()
;モニタ数確認
sysget, cnt, MonitorCount
if cnt > 1
{
sysget, Pr, Monitor,1 ;座標取得
sysget, Se, Monitor,2
if (PrLeft > 0) ;左モニタを基準にする
{
sysget, Pr, Monitor,2
sysget, Se, Monitor,1
} 
;最大化時は一旦戻す
WinGet,MinMax,MinMax,A
Winget,Tgt,ID,A
If MinMax
   WinRestore,ahk_id %tgt% 
;ウィンドウ座標取得
WingetPos,Win_x,Win_y,,,
;数字 Integer にするには、"=%"。対象は%%で括らない。
if (Win_x < PrRight and PrTop <= Win_y and Win_y < PrBottom)
{
Go_X =% SeLeft + 80
Go_Y =% SeTop + 50
}
else
{
Go_X =% PrLeft + 80
Go_Y =% PrTop + 50
}
WinMove,Ahk_id %tgt%,,Go_X,Go_Y 
;再最大化
If MinMax
   WinMaximize,Ahk_id %tgt%
}
}



参考サイト
http://blog.livedoor.jp/irvcidre/archives/1146866.html

2014年2月14日金曜日

勝手にGmailアドレスが使われて、GoogleからWelcomeメールが届いた。

オランダの人が、勝手にメールアドレスを使ったらしい。
オランダ語でメールが届いた。


From: Gmail Team mail-noreply@google.com
To: 知らない人の名前 <自分のID@gmail.com>
日付: 2014年2月13日 23:44
件名: Je Gmail-adres (知らないID@gmail.com) is aangemaakt
送信元: google.com
署名元:
 google.com


原文
Welkom bij Gmail! Je kunt op je account inloggen via http://mail.google.com/.
Hier volgen wat tips om aan de slag te gaan met je nieuwe account:
  • Gebruik de importhulpprogramma's van Gmail om je e-mail en contactpersonen over te zetten van je andere e-mailaccounts naar je nieuwe Gmail-adres.
  • Download de mobiele app voor Android of iPhone en iPadom in contact te blijven, ook als je onderweg bent.
Als je problemen hebt met je account of je wachtwoord bent vergeten, nemen we op dit e-mailadres contact met je op.
Veel plezier!
Team Gmail


Als je dit Gmail-adres niet hebt aangemaakt en deze e-mail niet herkent, kun je via https://accounts.google.com/AccountDisavow?adt=AOX8kirOyn9inJXXdZrxFsw4mVidcDJhRM5FWcidV1kkjAoqVIg6oRfVTBw dit account opheffen.




自動翻訳(英語)

件名:
Your Gmail address (知らないID@gmail.com) is created
Welcome to Gmail! You can login to your account via http://mail.google.com/ .

Here are some tips to deal with your new account to get started:

  • Use the import utilities Gmail to you to transfer e-mail and contacts from your other email accounts to your new Gmail address.
  • Download the mobile app for Android or iPhone to stay in touch even when you're on the go.

If you have problems with your account or have forgotten your password, we take on this email address to contact you.

Have fun!

Gmail Team



If you did not create this Gmail address and do not recognize this email, you can via https://accounts.google.com/AccountDisavow?adt=AOX8kirOyn9inJXXdZrxFsw4mVidcDJhRM5FWcidV1kkjAoqVIg6oRfVTBw dit Lifting account.



自動翻訳(日本語)

件名:Gmailのアドレスは(知らないID@gmail.com)が作成されます

Gmailにようこそ!あなたが経由でアカウントにログインすることができますhttp://mail.google.com/ 。
ここで開始するには、新しいアカウントに対処するためのいくつかのヒントがあります:
  • あなたにインポート·ユーティリティのGmailを使用し 、電子メールや連絡先を転送するために 、新しいGmailアドレスに他のメールアカウントから。
  • モバイルアプリをダウンロード Androidのか、 iPhoneに外出場合でも連絡を取り合うこと。
アカウントに問題があるか、パスワードを忘れた場合は、私たちはあなたに連絡する場合には、この電子メールアドレスを取る。
楽しんできてね!
Gmailチーム


このGmailアドレスを作成していないと、この電子メールを認識しない場合は、次のことが可能via https://accounts.google.com/AccountDisavow?adt=AOX8kirOyn9inJXXdZrxFsw4mVidcDJhRM5FWcidV1kkjAoqVIg6oRfVTBw ditアカウントを持ち上げる。




最後の「opheffen」は、Lifting、持ち上げる、と翻訳されてはじめ意味がわからなかったが、Lift の第二義、「撤廃する」という意味だと思う。

同様のメールが送られてきた例を調べてみたところ、要するに、この「知らない人」が、本人のGoogleアカウントに予備のメールアドレスを追加しようとした際、私のメールアドレスを入力したらしい。

(Welcomeメールであることから考えると、今回のケースでは、この人は新しくGmailアカウントを作成しようとして、作成画面の予備のメールアドレス入力欄に入力したのだろうか。)


肝心の危険性については、単純にタイプミスであれば問題ないよう。
相手はパスワードを知らないはずだし、万が一でも二段階認証がある。

※ただし、もしIDが、タイプミスされるようなものでなく、かなり個性的である場合は、そのIDが漏れでている可能性は否定できない。


このまま放っておけば、メール本文にもあるように、相手先で予備のメールアドレスが必要になったとき、こちらへその知らせ(たとえばパスワードの再設定など)が届くものと思われる。
それではかえって結果的に、こちらが相手のIDをのっとることができてしまう可能性がある?ので(というかそもそも気持ちが悪いし)、メールの最後にあるURLからアドレスを削除(Lifting)しておいた。

日本語ではこの件の情報は少なく、質問サイトでは、メール自体がスパムではないか、アカウントが消えそうでクリックするのが怖い、など言われているが、少なくとも今回のケースでは、リンク先はテキスト通りのURLで、accounts.google.com に接続された。 差出人 Gmail Team は偽装ではなかった。
「削除(Lifting)」というのも、自分のIDが抹消されるというような意味ではない。自分のアドレスが相手先で予備のアドレスとして使えなくなるという意味だろう。

今回のメールに付いていたURLは、今アクセスすると、
「このリンクが無効であるか、このリンクに対応する Google アカウントとメール アドレスとの関連付けが既に解除されています。」
というページになり、操作が済んでいることが確認できる。



【SESAMEサイクル(SESAMEシリーズ)】 NFCタグをエミュレートするアプリをつくった (Android)

セサミのNFCタグ機能を実際のタグなしで自由に発動させたくて、アプリを作った話。 ( ページ下部より APKダウンロードできます )  【まえがき】 うちでは玄関のセサミ3は前回記事で書いたGASで運用していてNFCタグ機能は使っていないのだけど、最近セサミサイクル(ママチャリ)...