📝 はじめに
この記事では、Tapo Matterデバイス(特にP110Mスマートプラグ)の初期設定からPythonでの電力データ取得までを解説します。
Switchbot と並んで手に入りやすい、TapoMatterデバイスについて、
SwitchBotプラグミニ 消費電力定期取得 (準備編) #Python - Qiita
と同等にまとめてみました。
この記事の対象読者
- Matter対応デバイスの活用に興味がある方
- スマートホームシステムを構築したい方
- 電力監視・省エネシステムを導入したい方
- PythonでのIoT連携を学びたい方
前提条件
- Tapo P110Mスマートプラグを所有していること
- 基本的なプログラミング知識(Python)
🎯 背景・動機
なぜTapo Matterなのか?
- 業界標準プロトコル: MatterはAmazon、Apple、Google、Samsungなどが共同開発
- 相互運用性: 異なるメーカーのデバイスが連携可能
- ローカル制御: クラウドに依存しない高速応答
- セキュリティ: 最新の暗号化技術を採用
Tapo P110Mの特徴
- 電力監視機能: リアルタイムで消費電力を測定
- Matter対応: 業界標準プロトコルで動作
- 音声制御: Alexa、Google Home、Siriに対応
- 予算管理: 月間の電気代を自動計算
🔧 技術的基礎知識
Matterプロトコルの仕組み
Matterは、OSI参照モデルの上位3層(アプリケーション層、セッション層、ネットワーク層)を統一したプロトコルです:
Matterの主要コンポーネント
| コンポーネント | 役割 | 説明 |
|---|---|---|
| コントローラー | 制御元 | Home Assistant、Apple Home、Google Home等 |
| エンドデバイス | 制御対象 | スマートプラグ、照明、センサー等 |
| クラスター | 機能単位 | On/Off、電力測定、温度等の機能グループ |
| エンドポイント | アクセスポイント | デバイス内の論理的な接続点 |
通信方式の比較
| プロトコル | リアルタイム性 | 相互運用性 | セキュリティ | 設定難易度 |
|---|---|---|---|---|
| Matter | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| Wi-Fi | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| Zigbee | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Bluetooth | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
電力データの構造
Tapo P110Mから取得できる電力データ:
{
"power": 45.2,
"voltage": 119.8,
"current": 377,
"energy_consumption": 0.324,
"timestamp": "2025-01-07T12:30:00Z"
}
電力測定クラスター(0x0B04)の属性
| 属性ID | 属性名 | 単位 | 説明 |
|---|---|---|---|
| 0x050B | ActivePower | デシワット | 有効電力(÷10でW) |
| 0x0505 | RMSVoltage | デシボルト | 実効電圧(÷10でV) |
| 0x0508 | RMSCurrent | ミリアンペア | 実効電流(÷1000でA) |
| 0x0500 | CurrentSummationDelivered | ワット時 | 累積消費電力量 |
🛠️ 実装手順
Step 1: Tapo P110Mの初期設定
1. ハードウェア接続
# デバイスの準備手順
1. Tapo P110Mを電源コンセントに接続
2. 制御したい家電をP110Mに接続
3. LEDインジケータがオレンジ点滅することを確認
2. Tapoアプリでの設定
# セットアップ手順
1. Tapoアプリをダウンロード・インストール
- iOS: App Store
- Android: Google Play Store
2. アカウントを作成・ログイン
- TP-Link IDを作成(メールアドレス必要)
- 既存アカウントがあればログイン
3. [+追加] → [スマートプラグ]を選択
- デバイスカテゴリから「スマートプラグ」を選択
- P110Mを選択
4. P110MのQRコードをスキャン
- デバイス本体のQRコードをカメラでスキャン
- または手動でシリアル番号を入力
5. Wi-Fi(2.4GHz)ネットワークを選択
- 注意: 5GHz帯は非対応
- パスワードを入力して接続
6. デバイス名を設定(例:Living Room Plug)
- わかりやすい名前を付ける
- 部屋名+用途がおすすめ
3. Matter対応の有効化
# Matterセットアップ
1. Tapoアプリでデバイスを選択
2. 右上の歯車アイコン → 「Matterセットアップ」を選択
3. Matterペアリングコード(11桁の数字)をメモ
4. オプション:ホーム画面に追加コードを保存
セットアップ時の注意点
| 項目 | 推奨設定 | 注意事項 |
|---|---|---|
| Wi-Fi帯域 | 2.4GHz | 5GHzは非対応 |
| ルーター設定 | IPv6有効 | Matterに必要 |
| ファイアウォール | ポート5540開放 | Matter通信用 |
| mDNS | 有効 | デバイス発見に必要 |
Step 2: Home Assistantでの統合
前提条件の確認
# Home Assistantの要件
- Home Assistant 2023.1以降
- Matter Server アドオン
- Python Matter Server対応
# 確認コマンド
ha --version
Home Assistant Matter統合の設定
# configuration.yaml
matter:
# Matterサーバーアドオンが自動的に有効化されます
# デバイス追加後の自動検出
sensor:
- platform: matter
name: "Living Room Power"
device_id: "your_device_id"
entity_id: "sensor.living_room_power"
手動でのデバイス追加手順
- Home Assistant Companionアプリを開く
- [設定] > [デバイスとサービス] に移動
- [デバイスを追加] → [Matterデバイスを追加] を選択
- 「いいえ、新しいデバイスです」を選択
- QRコードをスキャンまたはセットアップコードを手動入力
- [Home Assistantに追加] を選択
追加後の確認
# デバイスが正常に追加されると以下のエンティティが作成される
- switch.tapo_p110m_power # ON/OFF制御
- sensor.tapo_p110m_power # 消費電力(W)
- sensor.tapo_p110m_voltage # 電圧(V)
- sensor.tapo_p110m_current # 電流(A)
- sensor.tapo_p110m_energy_today # 本日の消費電力量(kWh)
Home Assistant Matter統合公式ドキュメント
Step 3: Pythonでの電力データ取得
Matterコントローラーライブラリのインストール
# 必要なライブラリをインストール
pip install matter-server-client
pip install python-matter-server
# オプション:非同期処理用
pip install asyncio
# 開発時の追加ライブラリ
pip install python-dateutil # 日時処理
pip install typing-extensions # 型ヒント拡張
環境構成
プロジェクト構成/
├── tapo_monitor/
│ ├── __init__.py
│ ├── monitor.py # メイン監視クラス
│ ├── config.py # 設定管理
│ └── utils.py # ユーティリティ
├── requirements.txt
└── main.py
Python実装例
import asyncio
from datetime import datetime
from typing import Dict, List, Optional
from matter_server.client import MatterClient
from matter_server.client.model.node_device import MatterNodeDevice
class TapoMatterMonitor:
"""
Tapo MatterデバイスからPythonで電力データを取得するクラス
Attributes:
client: Matterサーバークライアント
devices: 発見されたデバイスの辞書
"""
def __init__(self, server_url: str = "ws://localhost:5580/ws"):
"""
初期化
Args:
server_url: Matterサーバーのwebsocket URL
"""
self.server_url = server_url
self.client = MatterClient(server_url)
self.devices: Dict[int, MatterNodeDevice] = {}
async def connect(self) -> bool:
"""
Matterサーバーに接続
Returns:
接続成功時True、失敗時False
"""
try:
await self.client.connect()
print(f"✅ Matterサーバーに接続しました: {self.server_url}")
return True
except Exception as e:
print(f"❌ 接続エラー: {e}")
return False
async def discover_devices(self) -> int:
"""
デバイスを発見
Returns:
発見されたプラグデバイスの数
"""
try:
devices = await self.client.get_nodes()
for device in devices:
if device.is_online and self._is_plug_device(device):
self.devices[device.node_id] = device
print(f"🔌 デバイス発見: {device.name} (ID: {device.node_id})")
return len(self.devices)
except Exception as e:
print(f"❌ デバイス発見エラー: {e}")
return 0
def _is_plug_device(self, device: MatterNodeDevice) -> bool:
"""
プラグデバイスかどうか判定
Args:
device: Matterデバイス
Returns:
プラグデバイスの場合True
"""
# デバイスタイプIDで判定
# 0x010A: On/Off Plug-in Unit
# 0x010B: Dimmable Plug-in Unit
plug_device_types = [0x010A, 0x010B]
return device.device_type in plug_device_types
async def get_power_data(self, device_id: int) -> Optional[Dict]:
"""
電力データ取得
Args:
device_id: デバイスのノードID
Returns:
電力データの辞書、取得失敗時None
"""
if device_id not in self.devices:
print(f"⚠️ デバイスID {device_id} が見つかりません")
return None
device = self.devices[device_id]
try:
# 電力データエンドポイントを取得 (一般的にエンドポイント1)
power_endpoint = device.get_endpoint(1)
# 電力測定クラスター (0x0B04)
power_cluster = power_endpoint.get_cluster(0x0B04)
# 電力データ読み取り
power = await power_cluster.get_attribute_value(0x050B) # ActivePower
voltage = await power_cluster.get_attribute_value(0x0505) # RMSVoltage
current = await power_cluster.get_attribute_value(0x0508) # RMSCurrent
energy = await power_cluster.get_attribute_value(0x0500) # CurrentSummationDelivered
return {
"power": power / 10.0 if power else 0, # デシワット→ワット
"voltage": voltage / 10.0 if voltage else 0, # デシボルト→ボルト
"current": current / 1000.0 if current else 0, # ミリアンペア→アンペア
"energy": energy / 1000.0 if energy else 0, # ワット時→キロワット時
"timestamp": datetime.now().isoformat(),
"device_id": device_id,
"device_name": device.name
}
except Exception as e:
print(f"❌ 電力データ取得エラー: {e}")
return None
async def get_all_power_data(self) -> List[Dict]:
"""
全デバイスの電力データ取得
Returns:
全デバイスの電力データリスト
"""
results = []
for device_id in self.devices:
data = await self.get_power_data(device_id)
if data:
results.append(data)
return results
def get_device_list(self) -> List[Dict]:
"""
登録済みデバイス一覧を取得
Returns:
デバイス情報のリスト
"""
return [
{
"device_id": device_id,
"name": device.name,
"is_online": device.is_online,
"device_type": hex(device.device_type)
}
for device_id, device in self.devices.items()
]
async def close(self) -> None:
"""接続終了"""
if self.client:
await self.client.disconnect()
print("🔌 接続を終了しました")
# 使用例
async def main():
"""メイン関数"""
# 監視インスタンス作成
monitor = TapoMatterMonitor()
# Matterサーバーに接続
if not await monitor.connect():
return
# デバイス発見
device_count = await monitor.discover_devices()
print(f"📊 {device_count}台のデバイスを発見しました")
if device_count == 0:
print("デバイスが見つかりませんでした")
await monitor.close()
return
# デバイス一覧表示
print("\n--- 登録デバイス一覧 ---")
for device in monitor.get_device_list():
print(f" - {device['name']} (ID: {device['device_id']}, Type: {device['device_type']})")
# 全デバイスの電力データ取得
print("\n--- 電力データ ---")
power_data = await monitor.get_all_power_data()
for data in power_data:
print(f"📍 {data['device_name']}")
print(f" 電力: {data['power']:.1f} W")
print(f" 電圧: {data['voltage']:.1f} V")
print(f" 電流: {data['current']:.3f} A")
print(f" 累積: {data['energy']:.3f} kWh")
print(f" 時刻: {data['timestamp']}")
print()
# 接続終了
await monitor.close()
if __name__ == "__main__":
asyncio.run(main())
実行結果例
✅ Matterサーバーに接続しました: ws://localhost:5580/ws
🔌 デバイス発見: リビングルーム (ID: 12345)
🔌 デバイス発見: 書斎デスク (ID: 12346)
📊 2台のデバイスを発見しました
--- 登録デバイス一覧 ---
- リビングルーム (ID: 12345, Type: 0x10a)
- 書斎デスク (ID: 12346, Type: 0x10a)
--- 電力データ ---
📍 リビングルーム
電力: 45.2 W
電圧: 119.8 V
電流: 0.377 A
累積: 12.345 kWh
時刻: 2025-01-08T10:30:00.123456
📍 書斎デスク
電力: 85.0 W
電圧: 120.1 V
電流: 0.708 A
累積: 8.765 kWh
時刻: 2025-01-08T10:30:00.234567
🔌 接続を終了しました
🚨 トラブルシューティング
よくある問題と解決策
| 問題 | 原因 | 解決方法 |
|---|---|---|
| Matterデバイスが見つからない | ネットワーク接続不備 | Wi-Fi 2.4GHz帯域を確認 |
| ペアリングに失敗する | コントローラーの互換性 | Home Assistantを最新版に更新 |
| 電力データが取得できない | エンドポイント指定ミス | エンドポイント1を確認 |
| 通信が不安定 | Threadネットワーク問題 | Threadボーダールーターを追加 |
| 認証エラー | 署名生成ミス | タイムスタンプとnonceを確認 |
| 接続タイムアウト | サーバー未起動 | Matterサーバーの状態を確認 |
デバッグ用コマンド
# Matterサーバーの状態確認
curl -X GET http://localhost:5580/api/nodes
# 特定デバイスの詳細情報取得
curl -X GET http://localhost:5580/api/nodes/12345
# ログの確認
docker logs matter-server
# Home Assistantログ
tail -f /config/home-assistant.log | grep -i matter
# ネットワーク確認
ping <デバイスIP>
nmap -sn 192.168.1.0/24 # ネットワークスキャン
Pythonデバッグ用コード
import asyncio
import logging
# デバッグログ有効化
logging.basicConfig(level=logging.DEBUG)
async def debug_connection():
"""接続デバッグ"""
monitor = TapoMatterMonitor()
print("=== 接続テスト ===")
result = await monitor.connect()
print(f"接続結果: {result}")
if result:
print("\n=== デバイス発見テスト ===")
count = await monitor.discover_devices()
print(f"発見数: {count}")
print("\n=== デバイス詳細 ===")
for device in monitor.get_device_list():
print(f" {device}")
await monitor.close()
asyncio.run(debug_connection())
📝 まとめ
この記事では、Tapo P110Mの初期設定からPythonでの電力データ取得までを解説しました。
実装した内容
- ✅ Tapo P110Mの初期設定(Tapoアプリ)
- ✅ Matter対応の有効化
- ✅ Home Assistantでの統合
- ✅ Pythonでの電力データ取得クラス
以上です。