hypweb.net
XOOPSマニア  最新情報のRSS(サイト全体)
[ 自宅サーバーWebRing |ID=54 前後5表示乱移動サイト一覧 ]

TOP » UsersWiki » Calendar view nao-pon​/blog :
 
RSS of nao-pon/blog

Panasonic (ナショナル) エアコンの 2008 年モデルも WiFi 化 anchor.png

先日の Mitsubishi エアコンに引き続き、古めの Panasonic (ナショナル) エアコン CS-X288A2 も WiFi 化して、HomeAssistant でコントロールできるようになった。

あまりに古くて、Panasonic のサイトにも、据付工事説明書がなかったので、メイン基板までアクセスできるか不安だったが、なんとか到達。

フロントパネルを外すと、右側のカバーが外すことができ、すぐにアクセスできた。

DSC_3623.jpg

こんな感じに制作。ソケットは ESP8266 のモジュール用

DSC_3636.jpg

そして、インストール。圧電ブザーの上が HA 端子。

DSC_3641.jpg

赤外線レシーバーとトランスミッターは、こんな感じに配置。

DSC_3639.jpg

無事に稼働しました。

DSC_3643.jpg

高校の時に取った電気工事士(現在の第二種電気工事士)の免許。ずっとペーパーだったが、取っててよかった。

Page Top

Tasmota は独自ビルド・Tasmota-IRHVAC もカスタム anchor.png


 
RSS of nao-pon/blog

Tasmota で HA 端子(JEM-A) 連携 anchor.png

Tasmota で HA 端子と連携するには、すこし設定が必要なので、未来の自分のための健忘録。

ESPHome は、YAML で設定していくので、割と直感的に設定できるし設定ファイルを見れば後で再利用も簡単。

しかし、Tasmota はコマンド発行で設定を変えていく。設定一覧が見たいけど現状で、その方法が分からない。

今回は、HA 端子との連携なので、スイッチとバイナリーセンサーの組み合わせ。

ブリッジは自作したもの。フォトカプラー2個と1kΩ抵抗3個、200Ω抵抗1個のだけの簡単回路。200Ω抵抗は、ESP側のLEDをドライブするところ。

Tasumota では、スイッチを Relay、バイナリーセンサーを Switch として構成。

Page Top

コンソールでコマンド発行 anchor.png

そして、オプション設定。Relay 1, Switch 2, Switch_n 3 (PIRセンサー用) の前提。

  • SwitchMode1 13
    • プッシュオンモード、回路が閉じると ON になり、回路が開いた時は何もしない。
  • PulseTime1 3
    • HA 端子は、ON/OFF をする時に 250ms 以上のパルスを送るので、3 = 300ms だけ ON にしてから OFF にする。
  • SwitchMode2 2 (HA端子からの運転/停止センサー入力)
    • 反転フォローモード、回路を閉じると ON、開くと OFF。
  • SwitchMode3 1 (人感センサー[SR602]入力)
    • フォローモード、回路を開くと ON、閉じると OFF。
  • SetOption114 1
    • Relay と Switch を切り離す。これで HomeAssistan 側に、バイナリーセンサーが現れる。
  • PowerOnState 0
    • パワーオン時は OFF 固定。
  • TelePeriod 60
    • センサー更新間隔を60秒にセット。
  • TempOffset -2.8
    • 温度センサーの校正。
  • HumOffset 3.0
    • 湿度センサーの校正。
  • HumRes 0
    • 湿度センサーの小数点桁数の指定。
  • SetOption56 1*1
    • 再起動時にネットワークスキャンを行い、最も強い信号のSSIDを選択。
  • Timezone +9
    • タイムゾーンを日本時間にする。

こんなとこかな。

Backlog で一括設定する場合は

Backlog SwitchMode1 13; PulseTime1 3; SwitchMode2 2; SwitchMode3 1; SetOption114 1; PowerOnState 0; TelePeriod 60; Timezone +9;

 
RSS of nao-pon/blog

Tasmota で三菱エアコンを完全コントロール anchor.png

三菱エアコンは、esphome-mitsubishiheatpump により CN105 ポートを利用して、シリアル通信によるコントロールが可能で、私も2種類の三菱エアコン MSZ-GV2519 と MSZ-GE2520 に取り付けて、問題無くコントロールしいる。

昨年の11月頃に新たに、MLZ-RX5017 を設置したので、また同じように組み込みましたが、通信規格が違うのか全く動かすことができませんでした。

そこで、SmartIR を利用して、赤外線リモコン RS-WFIREX4 経由でコントロールしていた。しかし、少し問題が・・・。

運転自体の ON/OFF は、HA端子から信号を取って反映できていたが、付属のリモコンで温度を変えたり運転モードを変えたりした時に、HomeAssistant 側でその変更を感知することができない問題があった。

そこで、今度は Tasmota の IRremoteESP8266 を利用して、付属のリモコンで操作したときのIR信号を受け取り、HomeAssistant にフィードバックするすることにした。

HomeAssistant とのブリッジは、Tasmota-IRHVAC を今回のこともあり色々拡張した nao-pon/Tasmota-IRHVAC を使用。

Page Top

紆余曲折 anchor.png

実際に使ってみると、概ねうまく行ったものの、少し不備な点が・・・

  • 0.5℃刻みの温度に対応していない。IR信号を調べてみると、32℃以上にマッピングされているようだ。
  • スイングモードの自動とスイングが同じに扱われるのでコントロールすることができない。
  • MLZ-RX5017 をオフにすることができない。
    • 早速、 IRremoteESP8266 へ Issue を送ってみた。
    • これは、私の早とちりで、問題は Tasmota 側にあった。crankyoldgit には悪いことをした。
    • そこで、Tasumata に PullRequest を送ってみたら、速攻で受理された!素晴らしくありがたい!
  • 週間タイマー機能があってそれを使っているのだが、HomeAssistant でコントロールすると、その週間タイマーが効かなくなってしまう。
  • そこで IRコードを調べたところ、週間タイマーをON/OFFする信号も送られていることがわかった。
    • 早速、 IRremoteESP8266 へ Issue を送ってみた。
      • 速攻で対応してくれた!素晴らしい! (ただし、週間タイマーの ON/OFF を動的にコントロールすることはできない)

ここまで、順調に進んできたがちょっと大きめの問題が・・・。

Page Top

そして、独自ビルド・・・ anchor.png

週間タイマーを利用するには、IR データ送信時に、現在時刻と曜日の情報を送信する必要があるようだ。

現在時刻は Clock として、IRremoteESP8266 側では実装されているが、Tasmota 側では利用していない。これは大きな問題ではないが、曜日の情報は全く定義されていないので、新たに定義する必要があり、他のメーカーのエアコンには必要ないかも知れないので、取り入れるにはハードルが高そうだ。

ということで、独自ビルドでお茶を濁すことにした。一度ファームが固定されれば、そうそう書き換えることもないだろう。

それがこれ。

ついでに、三菱エアコンは Beep が使われていないので、それに WeeklyTimer を割り当てることで、動的にコントロールできるようにした。

これで、付属リモコンと全く同じ IR 信号を送られることを確認したので、多分問題ないなかろう。

また、自作したシステムには CCS811 を使った二酸化炭素濃度センサーもつけてあるので、fileuser_config_override.h でビルドオプションを変更し I2C の CCS811 コンポーネントを有効にして、サイズ削減のために他の I2C コンポーネントは無効にした。

Page Top

結果・・・ anchor.png

楽しかった! :-D
living_ac - Main Menu.pngliving_ac - Configure Module.png


 
RSS of nao-pon/blog

ラトック RS-WFIREX4 用の HomeAssistant カスタムコンポーネントを作った anchor.png

ラトックシステムのスマート家電リモコン RS-WFIREX4 を以前は使っていたけど、最近は HomeAssistant ですべてコントロールするようになり、それに対応していない RS-WFIREX4 は、ほとんど利用していなかった。

そんな折、ネットでとあるブログを見つけた。素晴らしい! 8-)

これはいい!ということで、早速カスタムコンポーネントにしてみた。 :-D

使い方は簡単。

  1. HACS でインストール。(マニュアルでもできるけど)
    1. カスタムリポジトリhttps://github.com/nao-pon/hass_rs_wfirex4をIntegrationで追加
    2. RS-WFIREX4 を検索してインストール
  2. configuration.yaml に次のような感じで設定
    1
    2
    3
    4
    5
    6
    7
    8
    
    rs_wfirex4:
      - host: "xxx.xxx.xxx.xxx" # IP address of your first RS-WFIREX4
        name: "リビング"        # Optional entity name
        scan_interval: 30       # Optional seconds of scan interval (Default 60)
     
      - host: "xxx.xxx.xxx.xxx" # IP address of your second RS-WFIREX4
        name: "寝室"            # Optional entity name
        scan_interval: 30       # Optional seconds of scan interval (Default 60)
  3. HomeAssistant を再起動

これで、こんな感じ。

_20210127_164741.JPG

リモートコマンドの学習もできるようにした。これは、Broadlink 統合の Remote と同じように、サービスから呼び出して学習すると .storage/rs_wfirex4_codes に JSON で保存される。

保存したコマンドは、オートメーションやスクリプトから登録したコマンド名で呼び出せる。

あとボーナスとして、SmartIR カスタムコンポーネントの元データとしても使える。

 "supportedController": "Broadlink",
 "commandsEncoding": "Hex",

でデータ作ればOK。これでエアコンのコントロールも可能になる。

風量・風向自動のみだけだけど、とりあえず作ったデータがこれ。
除湿は、16〜27℃までが強除湿、27.5〜28.5℃が標準、29℃以上が弱除湿にしてみた。

  • file922.json (三菱霧ヶ峰ビルトイン用)

 
RSS of nao-pon/blog

ESPHome の CCS811 コンポーネントで Baseline 値センサーを追加 anchor.png

二酸化炭素濃度を簡易的に測れるセンサー CCS811 は、起動時出来るだけ早く適切な値が得られるようにするには Baseline 値が結構重要。なので、Baseline値をセンサーとして観察したい。

それには、ESPHome のソースコードを変更する必要があるけど、custom_components ディレクトリを使用することで、簡単にハックできる。

早速、いじってみた。

fileccs811.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#pragma once
 
#include "esphome/core/component.h"
#include "esphome/core/preferences.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
 
namespace esphome {
namespace ccs811 {
 
class CCS811Component : public PollingComponent, public i2c::I2CDevice {
 public:
  void set_co2(sensor::Sensor *co2) { co2_ = co2; }
  void set_tvoc(sensor::Sensor *tvoc) { tvoc_ = tvoc; }
  void set_baseline(uint16_t baseline) { baseline_ = baseline; }
  void set_cur_baseline(sensor::Sensor *cur_baseline) { cur_baseline_ = cur_baseline; }
  void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
  void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
 
  /// Setup the sensor and test for a connection.
  void setup() override;
  /// Schedule temperature+pressure readings.
  void update() override;
 
  void dump_config() override;
 
  float get_setup_priority() const override { return setup_priority::DATA; }
 
 protected:
  optional<uint8_t> read_status_() { return this->read_byte(0x00); }
  bool status_has_error_() { return this->read_status_().value_or(1) & 1; }
  bool status_app_is_valid_() { return this->read_status_().value_or(0) & (1 << 4); }
  bool status_has_data_() { return this->read_status_().value_or(0) & (1 << 3); }
  void send_env_data_();
 
  enum ErrorCode {
    UNKNOWN,
    COMMUNICAITON_FAILED,
    INVALID_ID,
    SENSOR_REPORTED_ERROR,
    APP_INVALID,
    APP_START_FAILED,
  } error_code_{UNKNOWN};
 
  sensor::Sensor *co2_{nullptr};
  sensor::Sensor *tvoc_{nullptr};
  sensor::Sensor *cur_baseline_{nullptr};
  optional<uint16_t> baseline_{};
  /// Input sensor for humidity reading.
  sensor::Sensor *humidity_{nullptr};
  /// Input sensor for temperature reading.
  sensor::Sensor *temperature_{nullptr};
};
 
}  // namespace ccs811
}  // namespace esphome
fileccs811.cpp
Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
 
 
 
-
-
|
|
|
-
|
!
|
-
|
|
|
!
|
|
|
-
-
!
|
|
|
-
!
|
|
-
!
|
|
-
!
|
-
!
|
|
|
|
|
|
|
|
|
|
-
-
!
!
!
-
|
|
|
-
!
-
|
|
|
!
|
|
|
|
-
!
|
-
|
!
|
|
|
|
|
|
|
|
|
|
|
|
|
!
-
|
|
|
|
|
|
|
|
|
|
|
|
|
-
!
|
-
!
|
|
!
-
|
|
|
|
|
|
-
|
-
|
!
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
!
!
|
!
!
#include "ccs811.h"
#include "esphome/core/log.h"
 
namespace esphome {
namespace ccs811 {
 
static const char *TAG = "ccs811";
 
// based on
//  - https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf
 
#define CHECK_TRUE(f, error_code) \
  if (!(f)) { \
    this->mark_failed(); \
    this->error_code_ = (error_code); \
    return; \
  }
 
#define CHECKED_IO(f) CHECK_TRUE(f, COMMUNICAITON_FAILED)
 
void CCS811Component::setup() {
  // page 9 programming guide - hwid is always 0x81
  uint8_t hw_id;
  CHECKED_IO(this->read_byte(0x20, &hw_id))
  CHECK_TRUE(hw_id == 0x81, INVALID_ID)
 
  // software reset, page 3 - allowed to fail
  this->write_bytes(0xFF, {0x11, 0xE5, 0x72, 0x8A});
  delay(5);
 
  // page 10, APP_START
  CHECK_TRUE(!this->status_has_error_(), SENSOR_REPORTED_ERROR)
  CHECK_TRUE(this->status_app_is_valid_(), APP_INVALID)
  CHECK_TRUE(this->write_bytes(0xF4, {}), APP_START_FAILED)
  // App setup, wait for it to load
  delay(1);
 
  // set MEAS_MODE (page 5)
  uint8_t meas_mode = 0;
  uint32_t interval = this->get_update_interval();
  if (interval <= 1000)
    meas_mode = 1 << 4;
  else if (interval <= 10000)
    meas_mode = 2 << 4;
  else
    meas_mode = 3 << 4;
 
  CHECKED_IO(this->write_byte(0x01, meas_mode))
 
  if (this->baseline_.has_value()) {
    // baseline available, write to sensor
    this->write_bytes(0x11, decode_uint16(*this->baseline_));
  }
}
void CCS811Component::update() {
  if (!this->status_has_data_())
    this->status_set_warning();
 
  // page 12 - alg result data
  auto alg_data = this->read_bytes<4>(0x02);
  if (!alg_data.has_value()) {
    ESP_LOGW(TAG, "Reading CCS811 data failed!");
    this->status_set_warning();
    return;
  }
  auto res = *alg_data;
  uint16_t co2 = encode_uint16(res[0], res[1]);
  uint16_t tvoc = encode_uint16(res[2], res[3]);
 
  // also print baseline
  auto baseline_data = this->read_bytes<2>(0x11);
  uint16_t baseline = 0;
  if (baseline_data.has_value()) {
    baseline = encode_uint16((*baseline_data)[0], (*baseline_data)[1]);
  }
 
  ESP_LOGD(TAG, "Got co2=%u ppm, tvoc=%u ppb, baseline=0x%04X", co2, tvoc, baseline);
 
  if (this->co2_ != nullptr)
    this->co2_->publish_state(co2);
  if (this->tvoc_ != nullptr)
    this->tvoc_->publish_state(tvoc);
  if (this->cur_baseline_ != nullptr)
    this->cur_baseline_->publish_state(baseline);
 
  this->status_clear_warning();
 
  this->send_env_data_();
}
void CCS811Component::send_env_data_() {
  if (this->humidity_ == nullptr && this->temperature_ == nullptr)
    return;
 
  float humidity = NAN;
  if (this->humidity_ != nullptr)
    humidity = this->humidity_->state;
  if (isnan(humidity) || humidity < 0 || humidity > 100)
    humidity = 50;
  float temperature = NAN;
  if (this->temperature_ != nullptr)
    temperature = this->temperature_->state;
  if (isnan(temperature) || temperature < -25 || temperature > 50)
    temperature = 25;
  // temperature has a 25° offset to allow negative temperatures
  temperature += 25;
 
  // only 0.5 fractions are supported (application note)
  auto hum_value = static_cast<uint8_t>(roundf(humidity * 2));
  auto temp_value = static_cast<uint8_t>(roundf(temperature * 2));
  this->write_bytes(0x05, {hum_value, 0x00, temp_value, 0x00});
}
void CCS811Component::dump_config() {
  ESP_LOGCONFIG(TAG, "CCS811");
  LOG_I2C_DEVICE(this)
  LOG_UPDATE_INTERVAL(this)
  LOG_SENSOR("  ", "CO2 Sensor", this->co2_)
  LOG_SENSOR("  ", "TVOC Sensor", this->tvoc_)
  LOG_SENSOR("  ", "Current Baseline", this->cur_baseline_)
  if (this->baseline_) {
    ESP_LOGCONFIG(TAG, "  Baseline: %04X", *this->baseline_);
  } else {
    ESP_LOGCONFIG(TAG, "  Baseline: NOT SET");
  }
  if (this->is_failed()) {
    switch (this->error_code_) {
      case COMMUNICAITON_FAILED:
        ESP_LOGW(TAG, "Communication failed! Is the sensor connected?");
        break;
      case INVALID_ID:
        ESP_LOGW(TAG, "Sensor reported an invalid ID. Is this a CCS811?");
        break;
      case SENSOR_REPORTED_ERROR:
        ESP_LOGW(TAG, "Sensor reported internal error");
        break;
      case APP_INVALID:
        ESP_LOGW(TAG, "Sensor reported invalid APP installed.");
        break;
      case APP_START_FAILED:
        ESP_LOGW(TAG, "Sensor reported APP start failed.");
        break;
      case UNKNOWN:
      default:
        ESP_LOGW(TAG, "Unknown setup error!");
        break;
    }
  }
}
 
}  // namespace ccs811
}  // namespace esphome
filesensor.py
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_ID, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \
    UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY, ICON_MOLECULE_CO2, ICON_SCALE
 
DEPENDENCIES = ['i2c']
 
ccs811_ns = cg.esphome_ns.namespace('ccs811')
CCS811Component = ccs811_ns.class_('CCS811Component', cg.PollingComponent, i2c.I2CDevice)
 
CONF_ECO2 = 'eco2'
CONF_TVOC = 'tvoc'
CONF_BASELINE = 'baseline'
CONF_CUR_BASELINE = 'cur_baseline'
 
CONFIG_SCHEMA = cv.Schema({
    cv.GenerateID(): cv.declare_id(CCS811Component),
    cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2,
                                                 0),
    cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0),
    cv.Required(CONF_CUR_BASELINE): sensor.sensor_schema('', ICON_SCALE, 0),
 
    cv.Optional(CONF_BASELINE): cv.hex_uint16_t,
    cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
    cv.Optional(CONF_HUMIDITY): cv.use_id(sensor.Sensor),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5A))
 
 
def to_code(config):
    var = cg.new_Pvariable(config[CONF_ID])
    yield cg.register_component(var, config)
    yield i2c.register_i2c_device(var, config)
 
    sens = yield sensor.new_sensor(config[CONF_ECO2])
    cg.add(var.set_co2(sens))
    sens = yield sensor.new_sensor(config[CONF_TVOC])
    cg.add(var.set_tvoc(sens))
    sens = yield sensor.new_sensor(config[CONF_CUR_BASELINE])
    cg.add(var.set_cur_baseline(sens))
 
    if CONF_BASELINE in config:
        cg.add(var.set_baseline(config[CONF_BASELINE]))
 
    if CONF_TEMPERATURE in config:
        sens = yield cg.get_variable(config[CONF_TEMPERATURE])
        cg.add(var.set_temperature(sens))
    if CONF_HUMIDITY in config:
        sens = yield cg.get_variable(config[CONF_HUMIDITY])
        cg.add(var.set_humidity(sens))


*1 メッシュWiFiに移行したのでSSIDは一つになったので不要

トップ 印刷に適した表示   ページ新規作成 全ページ一覧 単語検索 最新ページの一覧   ヘルプ   最新ページのRSS 1.0 最新ページのRSS 2.0 最新ページのRSS Atom Powered by xpWiki
このページのTopへ
メインメニュー
ログイン

ユーザー名:


パスワード:





パスワード紛失  |新規登録
最近の更新
#recent(): You called me too much
オンライン状況
700 人のユーザが現在オンラインです。 (8 人のユーザが UsersWiki を参照しています。)

登録ユーザ: 0
ゲスト: 700

もっと...
サイト情報