【Nature Remo】ブラウザから家電リモコンを操作する方法|Cloud API


赤外線リモコンデバイスのNature Remoでは、赤外線送信などの操作ができるCloud APIとLocal APIが公開されています。
今回は、そのNature RemoのCloud APIを利用して、ブラウザから家電操作を行う例となります。
Nature Remoをブラウザから操作する準備
今回の例では、ローカルに配置したHTMLファイルにブラウザでアクセスし、そのページからNature Remoアプリから登録したリモコンを、Cloud APIを利用して操作をできるようにします。
Cloud APIを利用する準備
Nature RemoのCloud APIでは、登録したリモコン情報や現在の気温などを取得でき、利用するにはアクセストークンを発行する必要があります。
APIのアクセストークンの発行方法については、以下のリンク先を参考にしてみてください。
APIのリクエスト制限について
5分以内に30回 以上のAPIリクエストを送信すると、その後はステータスコード 429 を返し、リクエストエラーとなります。
アプリからリモコンを登録
今回の例では、Nature Remoアプリから以下の方法でリモコンを登録しています。
- エアコンのリモコンは「ON」ボタンで一括登録
- その他のリモコンはボタン毎に一つづつ登録
Nature Remoを操作するページの作成
作成するページは、家電ごとに分かれたレイアウトとなり、それぞれのボタン・プルダウンメニューを選択することでリモコン操作を行います。
また、リモコン機能のない照明や家電などに、リモコン機能を簡単に取り付ける機器もあります。詳しくは以下のリンク先を参考にしてみてください。
以下、Nature Remoをブラウザから操作するページのHTML・CSS・JavaScriptのコードとなります。
HTML・CSSのコード
HTML・CSSのコードは以下となります。また、CSSではリセットCSSを利用しています。
See the Pen Nature Remo Browser by yic666kr (@yic666kr) on CodePen.
上記コードではリモコン送信が成功した際に、ページ左上にある固定された箇所をCSSで点滅させています。
CSSで要素を点滅させる方法については以下のリンク先を参考にしてみてください。
HTMLのコードについて
以下、HTMLのポイントとなる箇所の説明となります。
ボタン毎に登録したリモコンについて
例のHTMLでは、アプリからボタン毎に一つづつ登録したリモコンの場合、指定したbutton要素をクリックすることで操作をします。
また、リモコン操作時にJavaScriptで利用するためbutton要素のvalueの値には、アプリでボタン登録時に設定する「名前」を指定します。
一括登録したエアコンのリモコンについて
例のHTMLでは、アプリから一括登録したエアコンのリモコンの場合、指定したbutton要素のクリックとoption要素の選択により操作します。
また、リモコン操作時にJavaScriptで利用するため、button要素とoption要素のvalueの値には、エアコンのリモコンを登録した際に設定される値を指定します。
それらの値は、APIで取得できるエアコンのリモコン情報から確認できます。詳しくは、以下のリンク先を参考にしてみてください。
JavaScriptのコード
以下、作成するページのJavaScriptとなり、上記のHTMLから読み込みます。
また、取得したAPIのアクセストークンは、以下のソースコード6行目のXXXXXXXXXXの箇所に記述します。
document.addEventListener('DOMContentLoaded', function(){
// APIのリクエストヘッダーをセット
const headers = new Headers({
'Accept': 'application/json',
'Authorization': 'Bearer XXXXXXXXXX'
});
// APIのPOSTリクエスト時に利用する関数
function postSend(url, params = []){
return fetch(url, {
method: 'Post',
body: params,
headers: headers
})
.then(function(response) {
if(response.ok) {
document.getElementsByClassName('light_box')[0].classList.add('flash');
setTimeout(function() {
document.getElementsByClassName('light_box')[0].classList.remove('flash');
}, 1000);
return;
}
throw new Error('Network response was not ok.');
})
.catch(function(error) {
console.error('Error:', error);
});
};
// APIのGETリクエスト時に利用する関数
function getJson(url){
return fetch(url, {
headers: headers
})
.then(function(response) {
if(response.ok) {
return response.json();
}
throw new Error('Network response was not ok.');
})
.catch(function(error) {
console.error('Error:', error);
});
};
// エアコンの状態を扱う際に利用する変数
let acOpe; // ON・OFF
let acMode; // モード
let acTemp; // 温度
let acVol; // 風量
// 現在のエアコンの状態を取得して、ページ上の表示を変更する関数
function acNow() {
const opeElem = document.getElementById('now_ope');
opeElem.classList.remove('now_cool', 'now_warm', 'now_dry');
if(acOpe == 'power-off'){
document.getElementById('now_onoff').textContent = '停止中';
}else{
document.getElementById('now_onoff').textContent = '運転中';
['cool', 'warm', 'dry'].forEach(function(mode){
if(acMode == mode){
opeElem.classList.add('now_' + mode);
return;
}
});
}
}
// APIからリモコンのデータを取得して、利用する値をセットする関数
function dataSet() {
url = 'https://api.nature.global/1/appliances';
getJson(url)
.then(function(dataJson) {
// APIのレスポンスから利用するデータを取得
sessionStorage.setItem('signals', JSON.stringify(dataJson[0]['signals']));
sessionStorage.setItem('appliance', JSON.stringify(dataJson[1]['id']));
settings = dataJson[1]['settings'];
// ページ上のセレクトボックスで選択中の項目を現在のデータに変更する関数
function selectSet(id, settings){
let select = document.getElementById(id);
let options = select.options ;
for (let i = 0; i < options.length; i++){
if(options[i].value == settings){
options[i].selected = true;
return;
}
}
}
// 上記関数をそれぞれのプルダウンメニューに適用
acMode = settings['mode'];
selectSet('mode_select', acMode);
acTemp = settings['temp'];
selectSet('temp_select', acTemp);
acVol = settings['vol'];
selectSet('vol_select', acVol);
// 現在のON・OFFの状態をセット
acOpe = settings['button'];
// ページ上のエアコンの状態を変更
acNow();
});
}
dataSet();
// APIから気温を取得して表示する関数
function tempSet(){
url = 'https://api.nature.global/1/devices';
getJson(url)
.then(function(devicesJson) {
let now_temp = devicesJson[0]['newest_events']['te']['val'];
now_temp = now_temp.toFixed(1);
document.getElementById('now_temp').textContent = now_temp + '°' ;
});
}
tempSet();
let startTime = Date.now();
let intervalId;
// ウィンドウがアクティブになった際に実行する関数
function play() {
intervalId = setInterval(function() {
// 変数startTimeの時刻から30分経過した場合
if (Date.now() > (startTime + (30 * 60 * 1000))) {
// データ更新
dataSet();
tempSet();
// 変数startTimeの時刻を更新
startTime = Date.now();
}
}, 1000);
}
// ウィンドウがアクティブでなくなった際に実行する関数
function pause() {
clearInterval(intervalId);
}
window.addEventListener('focus', play);
window.addEventListener('blur', pause);
// ボタン毎に登録をしたリモコンを送信する関数
function irSend(value){
let signals = sessionStorage.getItem('signals');
signals = JSON.parse(signals);
function signalGet(item) {
if (item.name == value) {
return true;
}
}
let signal = signals.filter(signalGet);
let signalID = signal[0]['id'];
url = 'https://api.nature.global/1/signals/' + signalID + '/send';
postSend(url);
}
// エアコンのリモコンを送信する関数
function acSend(){
// プルダウンメニューで選択中のvalueを取得
acMode = document.getElementById('mode_select').value;
acTemp = document.getElementById('temp_select').value;
acVol = document.getElementById('vol_select').value;
// ページ上のエアコンの状態を変更
acNow();
// POSTリクエストのbodyの内容をセット
let params = new URLSearchParams({
'operation_mode': acMode,
'temperature': acTemp,
'air_volume': acVol
});
if(acOpe == 'power-off'){
params.append('button', acOpe);
}
let appliances = sessionStorage.getItem('appliance');
appliances = JSON.parse(appliances);
url = 'https://api.nature.global/1/appliances/' + appliances + '/aircon_settings';
postSend(url, params);
}
// リモコンボタンのクリック時に関数を実行(ボタン毎に登録をしたリモコン)
const irButton = document.getElementsByName('ir');
for(let i = 0; i < irButton.length; i++) {
irButton[i].addEventListener('click', function(){
let irValue = irButton[i].value;
irSend(irValue);
});
}
// エアコンリモコンのボタンクリック時・操作時に関数を実行
const acButton = document.getElementsByClassName('ac');
for(let i = 0; i < acButton.length; i++) {
let acTag = acButton[i].tagName;
if(acTag == 'BUTTON'){
acButton[i].addEventListener('click', function(){
// button要素のvalue値を取得しエアコンのオン・オフ設定にセット
let acValue = acButton[i].value;
acOpe = acValue;
acSend();
});
}else{
acButton[i].addEventListener('change', acSend);
}
}
});
JavaScriptのコードについて
以下、JavaScriptのポイントとなる箇所の説明となります。
APIリクエスト時に利用する関数
ソースコード3行目からの以下の箇所では、APIのリクエスト時に利用する関数を、POSTとGETリクエストでそれぞれ作成しています。
また、APIリクエストにはFetchを利用しています。
// APIのリクエストヘッダーをセット
const headers = new Headers({
'Accept': 'application/json',
'Authorization': 'Bearer XXXXXXXXXX'
});
// APIのPOSTリクエスト時に利用する関数
function postSend(url, params = []){
return fetch(url, {
method: 'Post',
body: params,
headers: headers
})
.then(function(response) {
if(response.ok) {
document.getElementsByClassName('light_box')[0].classList.add('flash');
setTimeout(function() {
document.getElementsByClassName('light_box')[0].classList.remove('flash');
}, 1000);
return;
}
throw new Error('Network response was not ok.');
})
.catch(function(error) {
console.error('Error:', error);
});
};
// APIのGETリクエスト時に利用する関数
function getJson(url){
return fetch(url, {
headers: headers
})
.then(function(response) {
if(response.ok) {
return response.json();
}
throw new Error('Network response was not ok.');
})
.catch(function(error) {
console.error('Error:', error);
});
};
エアコンの状態をセットする変数
ソースコード48行目からの以下の箇所では、エアコンの状態を扱う際に利用する変数を宣言しています。
// エアコンの状態を扱う際に利用する変数
let acOpe; // ON・OFF
let acMode; // モード
let acTemp; // 温度
let acVol; // 風量
現在のエアコンの状態をページ上に表示する関数
ソースコード55行目からの以下の箇所は、現在のエアコンの状態を取得して、ページ上の表示を設定する関数となります。
// 現在のエアコンの状態を取得して、ページ上の表示を変更する関数
function acNow() {
const opeElem = document.getElementById('now_ope');
opeElem.classList.remove('now_cool', 'now_warm', 'now_dry');
if(acOpe == 'power-off'){
document.getElementById('now_onoff').textContent = '停止中';
}else{
document.getElementById('now_onoff').textContent = '運転中';
['cool', 'warm', 'dry'].forEach(function(mode){
if(acMode == mode){
opeElem.classList.add('now_' + mode);
return;
}
});
}
}
上記の関数内では、現在のエアコンの運転状況(ON-OFF)、モード(暖房・冷房・DRY)を取得して、ページ上に表示しています。
また、上記関数内ではclassの値を変更していますが、詳しくは以下のリンク先を参考にしてみてください。
APIからリモコンのデータを取得して利用する値をセットする関数
ソースコード74行目からの以下の箇所では、APIからリモコンのデータを取得して、利用する値をセットする関数を作成し、その関数を呼び出しています。
// APIからリモコンのデータを取得して、利用する値をセットする関数
function dataSet() {
url = 'https://api.nature.global/1/appliances';
getJson(url)
.then(function(dataJson) {
// APIのレスポンスから利用するデータを取得
sessionStorage.setItem('signals', JSON.stringify(dataJson[0]['signals']));
sessionStorage.setItem('appliance', JSON.stringify(dataJson[1]['id']));
settings = dataJson[1]['settings'];
// ページ上のセレクトボックスで選択中の項目を現在のデータに変更する関数
function selectSet(id, settings){
let select = document.getElementById(id);
let options = select.options ;
for (let i = 0; i < options.length; i++){
if(options[i].value == settings){
options[i].selected = true;
return;
}
}
}
// 上記関数をそれぞれのプルダウンメニューに適用
acMode = settings['mode'];
selectSet('mode_select', acMode);
acTemp = settings['temp'];
selectSet('temp_select', acTemp);
acVol = settings['vol'];
selectSet('vol_select', acVol);
// 現在のON・OFFの状態をセット
acOpe = settings['button'];
// ページ上のエアコンの状態を変更
acNow();
});
}
dataSet();
上記の関数内では、まず、Nature Remoに登録したリモコン情報などを取得できるAPIのエンドポイントから、リモコン操作やエアコンの現在の状態を表示する際に利用する値を取得しています。
そして、ページ上に表示する現在のエアコンの状態を設定しています。
また、上記関数内ではセレクトボックスの値を変更していますが、詳しくは以下のリンク先を参考にしてみてください。
APIから気温を取得して表示する関数
ソースコード114行目からの以下の箇所では、APIから気温を取得して表示する関数を作成し、その関数を呼び出しています。
// APIから気温を取得して表示する関数
function tempSet(){
url = 'https://api.nature.global/1/devices';
getJson(url)
.then(function(devicesJson) {
let now_temp = devicesJson[0]['newest_events']['te']['val'];
now_temp = now_temp.toFixed(1);
document.getElementById('now_temp').textContent = now_temp + '°' ;
});
}
tempSet();
上記関数内では、Nature Remoのデバイスの情報を取得できるAPIのエンドポイントから、現在の温度のデータを取得しています。
一定時間経過ごとに処理を実行
ソースコード127行目からの以下の箇所は、スリープ復帰時など一定時間が経過した際に、ページ上に表示する気温とエアコンの設定を更新したいため、30分経過ごとに指定した関数を実行しています。
let startTime = Date.now();
let intervalId;
// ウィンドウがアクティブになった際に実行する関数
function play() {
intervalId = setInterval(function() {
// 変数startTimeの時刻から30分経過した場合
if (Date.now() > (startTime + (30 * 60 * 1000))) {
// データ更新
dataSet();
tempSet();
// 変数startTimeの時刻を更新
startTime = Date.now();
}
}, 1000);
}
// ウィンドウがアクティブでなくなった際に実行する関数
function pause() {
clearInterval(intervalId);
}
window.addEventListener('focus', play);
window.addEventListener('blur', pause);
上記では、ウィンドウがアクティブになった際に、setInterval()を利用し、30分経過ごと(スリープ時の経過時間も含む)を判断して処理を実行させています。
そして、ウィンドウがアクティブでなくなった際に、setInterval()の繰り返し処理を解除しています。
setInterval()について
setInterval()に関するコードについては、以下のリンク先を参考にしてみてください。
ウィンドウのアクティブ・非アクティブを判断
ウィンドウがアクティブになった際・アクティブでなくなった際に関数を実行する方法については、以下のリンク先を参考にしてみてください。
リモコン操作をする関数
ソースコード153行目からの以下の箇所は、APIを利用しボタン毎に登録をしたリモコンと、エアコンのリモコンを操作する、それぞれの関数となります。
また、ボタン毎に登録をしたリモコンと、エアコンのリモコンを送信するAPIではエンドポイントが異なります。
// ボタン毎に登録をしたリモコンを送信する関数
function irSend(value){
let signals = sessionStorage.getItem('signals');
signals = JSON.parse(signals);
function signalGet(item) {
if (item.name == value) {
return true;
}
}
let signal = signals.filter(signalGet);
let signalID = signal[0]['id'];
url = 'https://api.nature.global/1/signals/' + signalID + '/send';
postSend(url);
}
// エアコンのリモコンを送信する関数
function acSend(){
// プルダウンメニューで選択中のvalueを取得
acMode = document.getElementById('mode_select').value;
acTemp = document.getElementById('temp_select').value;
acVol = document.getElementById('vol_select').value;
// ページ上のエアコンの状態を変更
acNow();
// POSTリクエストのbodyの内容をセット
let params = new URLSearchParams({
'operation_mode': acMode,
'temperature': acTemp,
'air_volume': acVol
});
if(acOpe == 'power-off'){
params.append('button', acOpe);
}
let appliances = sessionStorage.getItem('appliance');
appliances = JSON.parse(appliances);
url = 'https://api.nature.global/1/appliances/' + appliances + '/aircon_settings';
postSend(url, params);
}
リモコン操作をする要素を指定
ソースコード197行目からの以下の箇所は、ボタン毎に登録をしたリモコンと、エアコンのリモコンを操作する要素に対して、それぞれaddEventListener()でイベントリスナーを指定しています。
それにより、それぞれのイベント発生時に、リモコンを操作する関数を実行しています。
// リモコンボタンのクリック時に関数を実行(ボタン毎に登録をしたリモコン)
const irButton = document.getElementsByName('ir');
for(let i = 0; i < irButton.length; i++) {
irButton[i].addEventListener('click', function(){
let irValue = irButton[i].value;
irSend(irValue);
});
}
// エアコンリモコンのボタンクリック時・操作時に関数を実行
const acButton = document.getElementsByClassName('ac');
for(let i = 0; i < acButton.length; i++) {
let acTag = acButton[i].tagName;
if(acTag == 'BUTTON'){
acButton[i].addEventListener('click', function(){
// button要素のvalue値を取得しエアコンのオン・オフ設定にセット
let acValue = acButton[i].value;
acOpe = acValue;
acSend();
});
}else{
acButton[i].addEventListener('change', acSend);
}
}
上記のように、指定したすべての要素に対してクリックイベントを適用させる方法については、以下のリンク先を参考にしてみてください。