2022年8月13日土曜日

温度・湿度の記録

高気密高断熱の家に住む前に、今の家の温熱環境を記録しておかないと、後でどれだけよくなったのか比較できないと思い、記録を始めました。

測定に使う道具

これまでRaspberry Piを買ったりセンサーを買ったりしてみていましたが、結局設定が面倒で手を付けられていなかったので、今回は手軽さ重視で以下のものを買いました。

  • Switchbot温湿度計
    • 外用、1階用、2階用の3つ
  • Switchbot ハブミニ
    • 温湿度計のデータをクラウドに送る用

設定

同梱マニュアルのとおり設定。

詰まった点は以下のとおりです。

  • スマートフォンのSwitchbotアプリに温湿度計が出て来ない場合は、スマートフォンのBluetoothをオフ→オンすると出てきた
  • 金属製のポストの中に温湿度計を設置するとハブミニと通信できなかった
  • ハブミニを2階に置いていると、屋外の温湿度計との通信が途切れたりしたので、ハブミニの場所を1階に変更した

これでスマートフォンから気温、湿度を確認できるようになりました。

グラフ化

外と中の温度・湿度を1つのグラフで見たいので、Google スプレッドシートにデータを保管して閲覧できるように設定しました。

以下の記事を参考にさせていただきました。

最初のうちはGoogle データポータルで表示させていました。

が、Google データポータルでは表示までに数秒待たされるのと、複数のデータソースを設定すると一気に重たくなったので、今はデータ収集用のスプレッドシートから、集約用のスプレッドシートにIMPORTRANGE関数を使って直近3日間のデータを送信して、それをグラフ化しています。

記録を取り始めてわかったのは以下のことです。

現宅の条件:築40年、木造2階建、1階と2階に各1台エアコンあり、温湿度計はエアコンのある部屋の隣の部屋に設置

  • 外は気温が上がると湿度が下がり、気温が下がると湿度が上がる
  • エアコンを付けていても1階より2階のほうが暑い
  • 2階は設定温度26度にしているが、昼間の気温よりやや低いぐらいで、全然効かない
  • ただ、それでも汗がうっすら浮く程度で収まっているのは、湿度が外よりもだいぶ低いからか
  • 日中、2階のエアコンを止めると、外より気温が高くなる

絶対湿度を記録する

温度と湿度をバラバラに見ていてもいまいちよくわからないので、住宅の温熱環境で重要とされているらしい絶対湿度も記録してみるようにしました。

以下の記事を参考にさせていただきました。

ハマったポイント

  • 上の記事ではべき乗計算に「^」を使っているが、Javascriptの場合はべき乗計算に「**」を使う。

こんな感じ。(重量絶対湿度[g/kg]の場合 小数点第二位四捨五入)

mr = Math.round(622*(6.1078*10**(7.5*t/(t+237.3))*rh/100)/(p-(6.1078*10**(7.5*t/(t+237.3))*rh/100))*10)/10;


グラフにしてみると、夏場の晴れている1日の範囲内では、気温と湿度は大きく変わるが、絶対湿度は1日を通してあまり変わらない、ということがわかります。

よく考えればそれはそうです。雨が降らないのであれば、空気中に溶けている水分が大きく変わることはないということになります。湿度が大きく変動していたのは、温度の変動により空気に溶け込める水分量が変わっていたから、なんですね。

ほかにもわかったこと:

  • エアコンで空気中の水分が外に排出されるため、外よりも中のほうが絶対湿度が低い
  • 1階と2階とでは、エアコンを付けていれば絶対湿度はそれほど変わらない = 2階はエアコンを付けていても暑いが、エアコンはきちんと稼働していて空気中の水分を外に排出してくれている
  • エアコンを付けたり、エアコンのある部屋のドアを開けたら絶対湿度は下がる

不快指数を記録する

絶対湿度という指標があることは、住宅の温熱環境に興味を持つまでは知りませんでしたが、不快指数はテレビの天気予報などで聞いたことがありました。
これも計算式さえ分かれば、同じ方法でグラフにできると思ったので、やってみました。

不快指数の計算式(小数点第二位四捨五入)

thi = Math.round((0.81*t+0.01*rh*(0.99*t-14.3)+46.3)*10)/10;


不快指数だと絶対湿度よりも気温の状況が反映されているので、外よりも2階のほうがマシで、2階より1階のほうが快適という、個人的な感覚と一致しています。

家族に見せるときも、絶対湿度より不快指数のほうがわかってもらいやすいかも。

他の指標としては暑さ指数=湿球黒球温度(WBGT)というものがあるのですが、これは温度と湿度だけでは計算できないという点と、冬場の活用ができないという点がデメリットです。

参考 GAEで稼働させているスクリプト全体

// Compiled using ts2gas 4.1.0 (TypeScript 4.3.5)
// Based on [屋内外の温度と湿度を計測してダッシュボードで表示する(WebAPI利用編) - Qiita](https://qiita.com/kunikada/items/28981ecdeee6e47a229f)
// Modified by mokemoke
var Config = /** @class */ (function () {
    function Config() {
    }
    Config.SPREADSHEET_ID = 'https://docs.google.com/spreadsheets/d/****/'; // ここにスプレッドシートのURLを記載
    Config.SWITCHBOT_API_TOKEN = '****'; // APIのトークンを記載
    Config.DEVICE_IDS = ['c1c0********', 'd8ea********', 'e3d3********']; // 記録対象のMACアドレスを記載
    Config.DEVICE_NAME = { 'c1c0********': 'outside', 'd8ea********': '1f', 'e3d3********': '2f' }; // MACアドレスと表示名のリスト
    return Config;
}());
var API = /** @class */ (function () {
    function API() {
        this.HOST = 'https://api.switch-bot.com';
    }
    API.prototype.setStatus = function (bot) {
        var url = this.HOST + ("/v1.0/devices/" + bot.deviceId + "/status");
        var response = UrlFetchApp.fetch(url, {
            method: 'get',
            headers: {
                Authorization: Config.SWITCHBOT_API_TOKEN
            }
        });
        var responseData = JSON.parse(response.getContentText());
        if (responseData.statusCode !== 100) {
            console.error('status code is not 100.');
        }
        bot.humidity = responseData.body.humidity;
        bot.temperature = responseData.body.temperature;
    };
    return API;
}());
var SwitchBot = /** @class */ (function () {
    function SwitchBot(_deviceId) {
        this._deviceId = _deviceId;
    }
    Object.defineProperty(SwitchBot.prototype, "deviceId", {
        get: function () {
            return this._deviceId;
        },
        enumerable: false,
        configurable: true
    });
    return SwitchBot;
}());
var MySpreadsheet = /** @class */ (function () {
    function MySpreadsheet(id, sheetId) {
        if (sheetId === void 0) { sheetId = 0; }
        if (id.match(/^https:\/\//)) {
            this.ss = SpreadsheetApp.openByUrl(id);
        }
        else {
            this.ss = SpreadsheetApp.openById(id);
        }
        this.sheet = this.ss.getSheets()[sheetId];
    }
    MySpreadsheet.prototype.headers = function () {
        var range = this.sheet.getRange(1, 1, 1, this.sheet.getMaxColumns());
        return range.getValues()[0];
    };
    MySpreadsheet.prototype.appendRow = function (row) {
        var insertRow = [];
        this.headers().forEach(function (name, index) {
            insertRow.push((name && name in row) ? row[name] : "");
        });
        this.ss.appendRow(insertRow);
    };
    MySpreadsheet.prototype.unshiftRow = function (row) {
        this.sheet.insertRowAfter(1);
        var range = this.sheet.getRange(2, 1, 1, this.sheet.getMaxColumns());
        this.headers().forEach(function (name, index) {
            if (name) {
                range.getCell(1, index + 1).setValue(row[name]);
            }
        });
    };
    return MySpreadsheet;
}());
var Message = /** @class */ (function () {
    function Message() {
        this._bots = [];
        this._message = {};
    }
    Message.prototype.addBot = function (bot) {
        this._bots.push(bot);
    };
    Message.prototype.post = function () {
        var key;
        var name;
        var t; // 気温
        var rh; // 相対湿度
        var p = 1013.25; // 気圧 1気圧で仮置き
        var mr; // 重量絶対湿度 g/kg
        var vh; // 容積絶対湿度 g/m3
        var thi; // 不快指数
        if (this._bots.length === 0) {
            console.error('no bot.');
        }
        for (var i in this._bots) {
            name = Config.DEVICE_NAME[this._bots[i].deviceId.toLowerCase()];
            t = this._bots[i].temperature;
            rh = this._bots[i].humidity;
            mr = Math.round(622*(6.1078*10**(7.5*t/(t+237.3))*rh/100)/(p-(6.1078*10**(7.5*t/(t+237.3))*rh/100))*10)/10;
            vh = Math.round(217*(6.1078*10**(7.5*t/(t+237.3)))/(t+273.15)*rh/100*10)/10;
            thi = Math.round((0.81*t+0.01*rh*(0.99*t-14.3)+46.3)*10)/10;
            key = name + "_t";
            this._message[key] = t;
            key = name + "_rh";
            this._message[key] = rh;
            key = name + "_mr";
            this._message[key] = mr;
            key = name + "_vh";
            this._message[key] = vh;                key = name + "_thi";
            this._message[key] = thi;
        }
        this._message['created_at'] = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyyy-MM-dd HH:mm:ss");
        var ss = new MySpreadsheet(Config.SPREADSHEET_ID);
        ss.unshiftRow(this._message);
    };
    return Message;
}());
function myFunction() {
    var api = new API();
    var message = new Message();
    for (var i in Config.DEVICE_IDS) {
        var bot = new SwitchBot(Config.DEVICE_IDS[i].toUpperCase());
        api.setStatus(bot);
        message.addBot(bot);
    }
    message.post();
}