Skip to main content

Downloads

Momonga

Python Route B Library: A Communicator for Low-voltage Smart Electric Energy Meters

Description

MomongaはBルートサービスを利用してスマートメーターと通信するPythonライブラリ。ターゲットデバイスはROHM社製Wi-SUNモジュールBP35C2または互換品。

Tested Devices

  • ラトックシステム RS-WSUHA-P
  • テセラ・テクノロジー RL7023 Stick-D/DSS
  • テセラ・テクノロジー RL7023 Stick-D/IPS

Note

  • MomongaはWOPT 01\rコマンドを実行して、Wi-SUNモジュールがUDPパケットのペイロードをASCIIフォーマットで出力するように設定する。注意: WOPTコマンドは実行回数に制限があるので初回のみ実行し、その設定はWi-SUNモジュールに保存される。
  • 一部のWi-SUNモジュールではROPTコマンドがFAIL ER04を返しサポートされない。その場合MomongaはASCII出力で動作していると仮定し、WOPTコマンドを実行せずに処理を継続する。
  • メソッドは物理量に即して命名しており、ECHONETの英語版ドキュメントの表記とは必ずしも一致しない。対応するEPCを調べる場合はメソッド名ではなくEPCのコード値で検索すること。
  • 送信ブロッキングなど諸条件により応答が遅延することがあるため、get_historical_cumulative_energy_1()は日を跨ぐタイミングで実行すべきではない。
  • プロパティの定義(EDTの長さ、乗率、データなしを表すコードなど)は、ECHONET ConsortiumのMachine Readable Appendix dataVersion 1.3.2 / release R(2026-06-12、配布物名 MRA_v1.4.0) の低圧スマート電力量メータクラス(0x0288)および機器オブジェクトスーパークラス(0x0000)に照合している。

Installation

$ pip install momonga

Simple Example

下記のコードはPANAセッションを確立し、瞬時電力計測値を取得して表示する。既定値ではPANのスキャンに最大で約2分、PANAセッションの確立に最大で約2分かかる(scan_retriesjoin_retriesで変わる)。 BルートID、パスワード、デバイスファイルへのパスは適宜変更すること。

import momonga
import time

rbid = 'SET YOUR ROUTE B ID'
pwd  = 'SET YOUR ROUTE B PASSWORD'
dev  = '/dev/ttyUSB0' # in a case of RaspberryPi OS

with momonga.Momonga(rbid, pwd, dev) as mo:
    while True:
        res = mo.get_instantaneous_power()
        print('no data' if res is None else '%0.1fW' % res)
        time.sleep(60)

Arguments

  • rbid: BルートID
  • pwd: Bルートパスワード
  • dev: Wi-SUNモジュールのデバイスファイルへのパス
  • baudrate: シリアル通信のボーレート(デフォルト: 115200)

Return Value

  • mo: Momongaクラスのインスタンス

Logging

Momongaには下記のロガーがある。

momonga.logger

ECHONET Liteスマートメータークラスを抽象化したレイヤのログ

momonga.session_manager_logger

PANAセッション管理レイヤのログ

momonga.sk_wrapper_logger

Wi-SUNモジュールとの通信ログ

Logging Example

import momonga
import time
import logging

log_fmt = logging.Formatter('%(asctime)s | %(levelname)s | %(name)s - %(message)s')
log_hnd = logging.StreamHandler()
log_hnd.setFormatter(log_fmt)
momonga.logger.addHandler(log_hnd)
momonga.logger.setLevel(logging.DEBUG)
momonga.session_manager_logger.addHandler(log_hnd)
momonga.session_manager_logger.setLevel(logging.DEBUG)
momonga.sk_wrapper_logger.addHandler(log_hnd)
momonga.sk_wrapper_logger.setLevel(logging.DEBUG)

rbid = 'SET YOUR ROUTE B ID'
pwd  = 'SET YOUR ROUTE B PASSWORD'
dev  = '/dev/ttyUSB0' # in a case of RaspberryPi OS

with momonga.Momonga(rbid, pwd, dev) as mo:
    while True:
        res = mo.get_instantaneous_power()
        print('no data' if res is None else '%0.1fW' % res)
        time.sleep(60)

Exception

主な例外は下記のとおり。

momonga.MomongaError

Momongaが送出する例外すべての基底クラス。原因を問わず「Momonga由来の失敗」をまとめて捕まえたい場合に使う。

momonga.MomongaRuntimeError

momonga.open()を呼ぶ前にリクエストを発行したなど、使い方が誤っているときに送出される。RuntimeErrorのサブクラスでもある。再接続では直らないので、捕捉して再試行するのではなく呼び出し方を直すこと。

momonga.MomongaValueError

引数の値が受け付けられる範囲を外れているときに送出される。scan_retriesに0を渡した、reopen_delaysに負の値が入っていた、dayに0〜99の外を渡した、といった場合。ValueErrorのサブクラスでもある。MomongaRuntimeErrorと同じく呼び出し方の誤りなので、再試行ではなく渡した値を直すこと。

momonga.MomongaConnectionFailure

セッションが乗っている無線通信やデバイスといったインフラに問題があるときに送出される例外の基底クラス。下記の5つがこれを継承する。原因は違うが、呼び出し側の対応は「待ってから再試行する」で共通なので、個別に列挙せずこれを捕捉すればよい。

  • MomongaSkScanFailure — PANが見つからない
  • MomongaSkJoinFailure — PANAセッションを確立できない
  • MomongaTimeoutError — Wi-SUNモジュールが応答しない
  • MomongaIOError — デバイスファイルやUSBドングルの失敗
  • MomongaSkResponseNotExpected — Wi-SUNモジュールの応答が読めない

この基底クラス自身が直接送出されることはない。捕捉のためだけに存在する。

対になるのがMomongaNeedToReopenで、セッションに異常があるときに送出される。セッションを張り直せば直る可能性が高い。

なおreopen_delaysが働くのはリクエスト中だけで、momonga.open()は対象外。対象になるのはMomongaNeedToReopenMomongaIOErrorである。

momonga.MomongaSkScanFailure

PANをスキャンしたが見つからなかったときに送出される。スマートメーターと通信できるロケーションか、またBルートIDが正しく設定されているかを確認し、再試行すること。

momonga.MomongaSkJoinFailure

PANAセッションを確立できなかったときに送出される。BルートIDとパスワードを確認し、再試行すること。

momonga.MomongaNeedToReopen

スマートメーターに対してコマンドを送信できなかったなどの理由で、スマートメーターに再接続が必要なときに送出される例外の基底クラス。下記の4つがこれを継承する。原因は違うが、呼び出し側の対応は「セッションを張り直す」で共通なので、個別に列挙せずこれを捕捉すればよい。

  • MomongaXmitTimeout — 制限時間内に送信権を得られない
  • MomongaSkCommandBusy — 別のSKコマンドが実行中で開始できない
  • MomongaSkCommandCancelledmomonga.close()が実行中のSKコマンドを打ち切った
  • MomongaSkCommandDeadlineExceeded — SKコマンドに使える時間を使い切った

この基底クラス自身も、応答が得られない、パケット配信元が止まった、といった場合に直接送出される。

対になるのがMomongaConnectionFailureで、セッションが乗っているインフラに問題があるときに送出される。

momonga.MomongaXmitTimeout

momonga.xmit_timeoutで指定した秒数のあいだにパケットを送信できなかったときに送出される。MomongaNeedToReopenのサブクラス。

momonga.MomongaSkCommandBusy

別のSKコマンドが実行中で、制限時間内にコマンドを開始できなかったときに送出される。MomongaNeedToReopenのサブクラス。

momonga.MomongaSkCommandCancelled

momonga.close()がセッションを閉じるために、実行中のSKコマンドを打ち切ったときに送出される。MomongaNeedToReopenのサブクラス。

momonga.MomongaSkCommandDeadlineExceeded

SKコマンドを始める前に、そのコマンドに使える時間を使い切っていたときに送出される。Momongaはmomonga.close()のSKTERM、再JOINのSKJOIN、パケット送信のSKSENDTOに期限を設けており、その期限が過ぎている場合、コマンドを送らずにこの例外を送出する。MomongaNeedToReopenのサブクラス。

MomongaSkCommandBusyと紛らわしいが別物である。あちらは他のSKコマンドが実行中でロックを取れなかったという意味で、こちらはロックの状態とは無関係に時間が尽きたという意味である。

TimeoutErrorを継承していないので、asyncio.wait_for()で自分が指定した待ち時間と取り違えることはない。

momonga.MomongaTimeoutError

momonga.open()の実行中にWi-SUNモジュールが応答しなかったときに送出される。デバイスファイルのパスと、モジュールが正しく接続されているかを確認すること。

この例外はTimeoutErrorのサブクラスでもある。Python 3.11以降asyncio.TimeoutErrorTimeoutErrorと同じクラスなので、awaitasyncio.wait_for()で囲んでasyncio.TimeoutErrorを捕捉していると、自分が指定した待ち時間が尽きた場合と区別できない。区別が必要ならMomongaTimeoutErrorを先に捕捉すること。なおMomongaXmitTimeoutMomongaSkCommandBusyTimeoutErrorを継承していないので、この問題は起きない。

momonga.MomongaIOError

シリアルデバイスそのものが失敗したときに送出される。デバイスファイルが存在しない、権限がない、USBドングルが抜けた、といった場合。pyserialのSerialExceptionやOSのFileNotFoundErrorはこの例外に包まれるので、Momongaを使う側がpyserialをimportする必要はない。__cause__に元の例外が入っている。

この例外はOSErrorのサブクラスでもある。reopen_delaysを指定していれば自動再接続の対象になる。

momonga.MomongaSkResponseNotExpected

Wi-SUNモジュールの応答が読めなかったときに送出される。項目が欠けている、値が16進数として読めない、といった場合。シリアル回線のノイズや取りこぼしで起こる一過性の失敗なので、MomongaConnectionFailureのサブクラスであり、待ってから再試行すればよい。

スマートメーターの応答が読めない場合がMomongaResponseNotExpectedで、こちらはWi-SUNモジュールの応答が読めない場合にあたる。

なおスキャン中に見つかったPANの記述が読めなかった場合は、Momonga自身がスキャンをやり直すのでこの例外は送出されない。scan_retriesを使い切るとMomongaSkScanFailureになる。

momonga.MomongaKeyError

Wi-SUNモジュールの応答から必要な項目を取り出せなかったときに送出される。値が読めない場合と区別する必要がなければ、MomongaSkResponseNotExpectedを捕捉すればよい。KeyErrorのサブクラスでもある。

momonga.MomongaSkCommandExecutionFailure

Wi-SUNモジュールがコマンドを受け付けなかったとき(FAIL ERxx)に送出される例外の基底クラス。エラーコードごとにMomongaSkCommandUnknownErrorMomongaSkCommandUnsupportedMomongaSkCommandInvalidArgumentMomongaSkCommandInvalidSyntaxMomongaSkCommandSerialInputErrorMomongaSkCommandFailedToExecuteのいずれかになる。

パケット送信中に出たものはMomonga自身が再送で吸収し、使い切るとMomongaNeedToReopenになる。呼び出し側に届くのは主にmomonga.open()の途中で、BルートIDやパスワードの書式が誤っている場合などである。

MomongaConnectionFailureMomongaNeedToReopenのどちらにも属さない。再接続では直らないので、待って再試行するのではなく渡した値を確認すること。

momonga.MomongaResponseNotPossible

スマートメーターがリクエストしたEPC (ECHONET Property Code) をサポートしていなかったとき送出される。スマートメーターに対して複数のEPCを同時に発行したとき、ひとつでもサポートされていないEPCがあるとこのエクセプションが送出される。スマートメーターがサポートしているEPCはmomonga.get_properties_to_set_values()momonga.get_properties_to_get_values()で取得できる。

momonga.MomongaResponseNotExpected

スマートメーターの応答が読めなかったときに送出される。宣言された長さがプロパティに足りない、プロパティコードが要求と一致しない、といった場合。Wi-SUNモジュールの応答が読めない場合はMomongaSkResponseNotExpectedで、そちらは待って再試行する対象だが、こちらは違う。セッションが失われたわけではないので、次のリクエストは通ることが多い。通知(get_notification())ではこの例外は送出されず、読めなかったプロパティの値が生のバイト列のまま返り、警告がログに出る。

Exception Handling Example

import momonga
import time
import sys

rbid = 'SET YOUR ROUTE B ID'
pwd  = 'SET YOUR ROUTE B PASSWORD'
dev  = '/dev/ttyUSB0' # in a case of RaspberryPi OS

# a connect that failed has just spent minutes scanning or joining,
# so going straight round again spends radio time and changes nothing
CONNECT_RETRY_DELAY = 600.0

while True:
    try:
        with momonga.Momonga(rbid, pwd, dev) as mo:
            while True:
                try:
                    res = mo.get_instantaneous_power()
                except momonga.MomongaResponseNotExpected as e:
                    # one response that could not be read, not a lost session
                    print('%s: %s' % (type(e).__name__, e), file=sys.stderr)
                else:
                    print('no data' if res is None else '%0.1fW' % res)
                time.sleep(60)
    except momonga.MomongaNeedToReopen as e:
        # the module is answering; a new session is worth having at once
        print('%s: %s' % (type(e).__name__, e), file=sys.stderr)
    except momonga.MomongaConnectionFailure as e:
        # the module, the port or the radio is not answering
        print('%s: %s' % (type(e).__name__, e), file=sys.stderr)
        time.sleep(CONNECT_RETRY_DELAY)

同じ内容を含む3通りの書き方(自分で張り直す、reopen_delaysに任せる、何も捕捉しない)を並べた実行可能な例がtests/error_handling_example.pyにある。

MomongaResponseNotPossibleMomongaRuntimeErrorMomongaSkCommandExecutionFailureを捕捉していないのは意図的である。順に、スマートメーターがそのEPCをサポートしていない、使い方が誤っている、モジュールが渡された値を受け付けない、という意味で、どれも再接続では直らない。握りつぶすと原因が見えないまま無限ループになる。

No Handler At All

reopen_delaysで無限に再接続を試み、scan_retriesjoin_retriesを現実的な範囲で大きな値に設定したうえで、稀な例外は補足せずギブアップしてプロセスを終了するような選択も取り得る。

import momonga
import time

from itertools import chain, repeat

rbid = 'SET YOUR ROUTE B ID'
pwd  = 'SET YOUR ROUTE B PASSWORD'
dev  = '/dev/ttyUSB0'

def backoff():
    return chain([60.0, 120.0, 300.0], repeat(600.0))

with momonga.Momonga(rbid, pwd, dev, reopen_delays=backoff,
                     scan_retries=6, join_retries=15) as mo:
    while True:
        res = mo.get_instantaneous_power()
        print('no data' if res is None else '%0.1fW' % res)
        time.sleep(60)

Transmission Restriction

下記のイベントが発生したときMomongaはスマートメーターに対するコマンドの送信をブロッキングする。

  1. PANAセッションのライフタイムが近づきWi-SUNモジュールが自動再認証を試みているとき
  2. 送信データ量が規定値に達しWi-SUNモジュールが送信制限しているとき

したがって開発者はデータ設定または取得関数を呼び出したあと即座に応答が返ってこない可能性を考慮すること。

No Data

スマートメーターが値を持たないとき、ECHONETは数値のかわりに規定のコードを返す(積算電力量は0xFFFFFFFE、瞬時電力は0x7FFFFFFE、瞬時電流は相ごとに0x7FFE)。MomongaはこれらをNoneとして返す。

したがって計測値を返す関数はNoneを返しうる。値ごとにNoneを判定してから使うこと。履歴系のように複数の値を含む結果では、値ごとに独立してNoneになる。

Notification

スマートメーターは定時積算電力量(EPC: 0xEA/0xEB)を毎時0分・30分から5分以内に自動通知する(INF/INFC)。 Momongaはこれらの通知をget_notification()で受け取れる。AsyncMomongaではnotifications()も使える。

INFCを受信した場合、Momongaは自動的にINFC_Resを送信する。この送信はベストエフォートで、送信ブロッキング中などで15秒以内に送出できないときは送信を諦める。通知そのものの受け取りはtimeoutに指定した時間を超えない。

Notification Example

import momonga

rbid = 'SET YOUR ROUTE B ID'
pwd  = 'SET YOUR ROUTE B PASSWORD'
dev  = '/dev/ttyUSB0'

with momonga.Momonga(rbid, pwd, dev) as mo:
    while True:
        notif = mo.get_notification(timeout=2400)
        if notif is None:
            continue  # timed out
        esv = notif['esv']
        for epc, value in notif['properties'].items():
            print(f'ESV: {esv.name}, EPC: {epc}, value: {value}')

Async Notification Example

import asyncio
import momonga

rbid = 'SET YOUR ROUTE B ID'
pwd  = 'SET YOUR ROUTE B PASSWORD'
dev  = '/dev/ttyUSB0'

async def main():
    async with momonga.AsyncMomonga(rbid, pwd, dev) as mo:
        async for notif in mo.notifications(timeout=2400):
            for epc, value in notif['properties'].items():
                print(f'EPC: {epc}, value: {value}')

asyncio.run(main())

API

momonga.Momonga(rbid: str, pwd: str, dev: str, baudrate: int = 115200, reset_dev: bool = True, reopen_delays: Iterable[float] | Callable[[], Iterable[float]] | None = None, scan_retries: int = 3, join_retries: int = 3)

Momongaクラスのインスタンス化。

Arguments

  • rbid: BルートID
  • pwd: Bルートパスワード
  • dev: デバイスファイルへのパス
  • baudrate: シリアル通信のボーレート
  • reset_dev: momonga.open()を実行するときSKRESETコマンドを実行するかどうか
  • reopen_delays: MomongaNeedToReopen 発生時に再接続を試みるまでの待機秒数の列。None の場合は自動再接続しない。再接続が必要になるたびに列の先頭から使われる。最初のmomonga.open()は対象外である。接続そのものの失敗(MomongaConnectionFailureとその4つのサブクラス)はここを通らず即座に送出されるので、無人運転では呼び出し側で待ってから再試行すること。待たずに繰り返すと電波を使うだけで状況は変わらない。
  • scan_retries: PANのスキャンを繰り返す回数。1以上。使い切るとMomongaSkScanFailureを送出する
  • join_retries: PANAセッションの確立を試みる回数。1以上。1回あたり最大約40秒。使い切るとMomongaSkJoinFailureを送出する

e.g.

from itertools import chain, repeat

# reconnect up to 3 times, 10 minutes apart
momonga.Momonga(rbid, pwd, dev, reopen_delays=[600.0, 600.0, 600.0])

# reconnect indefinitely, 10 minutes apart
momonga.Momonga(rbid, pwd, dev, reopen_delays=repeat(600.0))

# back off: a minute, then two, then five, then every ten indefinitely
def backoff():
    return chain([60.0, 120.0, 300.0], repeat(600.0))

momonga.Momonga(rbid, pwd, dev, reopen_delays=backoff)

# try harder to join before giving up (scanning is left at its default)
momonga.Momonga(rbid, pwd, dev, join_retries=15)

reopen_delaysは再接続のたびに列の先頭から使われる。リストでも、一度しか回せないイテレータでも同じで、後者は内部で再生される。ただしその再生はひとつのインスタンスの中だけで成り立つ。同じイテレータを別のインスタンスに渡すと前回の続きから始まるので、上のバックオフをchain(...)のまま渡すと最初のセッションだけバックオフが機能し、以降は10分固定になる。コーラブルを渡せば毎回新しい列が作られ、インスタンスをまたいでもバックオフが機能する。

momonga.xmit_retries

ひとつのリクエストを送り直す回数の上限。使い切るとMomongaNeedToReopenを送出する。既定値は12。

momonga.recv_timeout

1回の送信に対して応答を待つ秒数。超えると送り直す。既定値は12。

スマートメーターが応答しないとき、ひとつのリクエストを諦めるまでにかかる時間はおおむねmomonga.xmit_retriesmomonga.recv_timeoutの積になる。既定値では約144秒。ただしこれは応答を待つ時間だけで、送信ブロッキング中の待ち時間は含まない。そちらはmomonga.xmit_timeoutが上限になる。

momonga.xmit_timeout

ひとつのリクエストが送信権を得るまでに待つ秒数の上限。送信ブロッキングが続いてこの秒数を超えるとMomongaXmitTimeoutを送出する。MomongaNeedToReopenのサブクラスなので、reopen_delaysを指定していれば自動再接続の対象になる。既定値は300。

この上限はリクエスト全体に対して1回分で、momonga.xmit_retriesの回数だけ繰り返されることはない。0以上の数値を指定すること。0は「待たずに諦める」を意味する。Noneは指定できない。ゲートが走る最長の日程が要る場合は3600を指定すること。

momonga.open()が内部で発行するリクエスト(積算電力量の単位と係数の取得)にも同じ上限が適用される。PANのスキャンとPANAセッションの確立は対象外なので、影響を受けるのはこの2リクエストだけである。

momonga.internal_xmit_interval

momongaが続けて送信するときに空ける秒数。既定値は5。

使われるのは次の2箇所。

  • momonga.open()のなか。PANAセッション確立の直後と、積算電力量の単位・係数を取得したそれぞれの後(既定値では合計15秒)
  • Wi-SUNモジュールがパケットを送信できなかったと通知したときの、送り直しの前(送信完了通知EVENT 21の結果が失敗だった場合)

スマートメーターから応答が返らずmomonga.recv_timeoutで打ち切ったときの送り直しには使われない。この場合は待たずに送り直す。

これらのパラメータはインスタンス化したあとに変更できる。

e.g.

mo = momonga.Momonga(rbid, pwd, dev)
mo.recv_timeout = 30  # longer, for a meter that answers slowly
mo.xmit_retries = 3   # give up sooner and let reopen_delays rebuild the session
mo.xmit_timeout = 300 # give up after five minutes of blocked transmission

momonga.open()

PANをスキャンし、PANAセッションの確立を行う。自身を返すのでwithのほかmo = Momonga(...).open()とも書ける。 

所要時間はPANのスキャンとPANAセッションの確立が支配的で、電波状況によって数十秒から数分かかる。確立できなかった場合も、MomongaSkScanFailureまたはMomongaSkJoinFailureを送出するまでに同程度の時間がかかる。既定値での上限はスキャンが約2分(17秒+35秒+69秒)、確立が約2分(40秒×3回)で、scan_retriesjoin_retriesを増やせばそのぶん延びる。reopen_delaysで再接続の間隔を決めるときは、1回の再接続にこの時間が加わることを見込むこと。

Arguments

  • Void

Return Value

  • Momonga: 自身

momonga.close()

PANAセッションを終了する。

Arguments

  • Void

Return Value

  • None

momonga.reopen()

PANAセッションを一度終了し、張り直す。MomongaNeedToReopenを受け取ったあとに手動で再接続するときに使う。reopen_delaysを指定している場合は自動で呼ばれるので、通常は直接呼ぶ必要はない。

再接続のあいだmomonga.is_openはFalseになる。この間に他のスレッドから発行されたリクエストはMomongaNeedToReopenとなり、reopen_delaysを指定していれば再接続の完了後に自動で再試行される。momonga.get_notification()は再接続の完了を待ってから新しいセッションの通知を返す。

Arguments

  • Void

Return Value

  • None

momonga.lqi / momonga.rssi

最後にスマートメーターから届いたパケットの受信品質。読み取り専用。

  • momonga.lqi: 受信品質を表す0-255の値(int
  • momonga.rssi: 受信電力(float、dBm)。0.275 × lqi - 104.27で算出

momonga.open()を実行した直後や、まだ一度もパケットが届いていないあいだは両方ともNone。BP35A1系のWi-SUNモジュールはERXUDPに受信品質を含めないため、その場合もNoneのままになる。

セッションを張り直すとNoneに戻る。

e.g.

with momonga.Momonga(rbid, pwd, dev) as mo:
    while True:
        res = mo.get_instantaneous_power()
        reading = 'no data' if res is None else '%0.1fW' % res
        print('%s (rssi: %s dBm)' % (reading, mo.rssi))
        time.sleep(60)

momonga.get_operation_status()

スマートメーターの状態を取得する。

Arguments

  • Void

Return Value

  • bool | None: スマートメーターの状態 (True: オン, False: オフ, None: 不明)

momonga.get_installation_location()

Arguments

  • Void

Return Value

  • str: スマートメーターの設置場所

e.g.

'garden/perimeter 1'

momonga.get_standard_version()

Arguments

  • Void

Return Value

  • str: 規格バージョン

e.g.

'F.0'

momonga.get_fault_status()

Arguments

  • Void

Return Value

  • bool | None: スマートメーターの異常発生状態 (True: 異常有, False: 異常無, None: 不明)

momonga.get_manufacturer_code()

Arguments

  • Void

Return Value

  • bytes: 3バイトのメーカーコード

momonga.get_serial_number()

Arguments

  • Void

Return Value

  • str: 製造番号

momonga.get_current_time_setting()

Arguments

  • Void

Return Value

  • datetime.time: 現在時刻設定

momonga.get_current_date_setting()

Arguments

  • Void

Return Value

  • datetime.date: 現在年月日設定

momonga.get_properties_for_status_notification()

Arguments

  • Void

Return Value

  • set: 状変アナウンスプロパティマップ。このセットに含まれるEPCの値変化をスマートメーターが自動通知する。通知の受け取りにはget_notification()AsyncMomongaではnotifications()も)を使用する
{<EchonetPropertyCode.operation_status: 128>,
 <EchonetPropertyCode.installation_location: 129>,
 <EchonetPropertyCode.fault_status: 136>}

momonga.get_properties_to_set_values()

Arguments

  • Void

Return Value

  • set: Setプロパティマップ
{<EchonetPropertyCode.installation_location: 129>,
 <EchonetPropertyCode.day_for_historical_data_1: 229>,
 <EchonetPropertyCode.time_for_historical_data_2: 237>}

momonga.get_properties_to_get_values()

Arguments

  • Void

Return Value

  • set: Getプロパティマップ
{<EchonetPropertyCode.operation_status: 128>, <EchonetPropertyCode.installation_location: 129>,
 <EchonetPropertyCode.standard_version_information: 130>, <EchonetPropertyCode.fault_status: 136>,
 <EchonetPropertyCode.manufacturer_code: 138>, <EchonetPropertyCode.serial_number: 141>,
 <EchonetPropertyCode.current_time_setting: 151>, <EchonetPropertyCode.current_date_setting: 152>,
 <EchonetPropertyCode.properties_for_status_notification: 157>, <EchonetPropertyCode.properties_to_set_values: 158>,
 <EchonetPropertyCode.properties_to_get_values: 159>, <EchonetPropertyCode.coefficient_for_cumulative_energy: 211>,
 <EchonetPropertyCode.number_of_effective_digits_for_cumulative_energy: 215>,
 <EchonetPropertyCode.measured_cumulative_energy: 224>, <EchonetPropertyCode.unit_for_cumulative_energy: 225>,
 <EchonetPropertyCode.historical_cumulative_energy_1: 226>, <EchonetPropertyCode.measured_cumulative_energy_reversed: 227>,
 <EchonetPropertyCode.historical_cumulative_energy_1_reversed: 228>, <EchonetPropertyCode.day_for_historical_data_1: 229>,
 <EchonetPropertyCode.instantaneous_power: 231>, <EchonetPropertyCode.instantaneous_current: 232>,
 <EchonetPropertyCode.cumulative_energy_measured_at_fixed_time: 234>,
 <EchonetPropertyCode.cumulative_energy_measured_at_fixed_time_reversed: 235>,
 <EchonetPropertyCode.historical_cumulative_energy_2: 236>, <EchonetPropertyCode.time_for_historical_data_2: 237>}

momonga.get_route_b_id()

Bルート識別番号を取得する。

Arguments

  • Void

Return Value

  • dict: {'manufacturer code': manufacturer_code, 'authentication id': authentication_id}

momonga.get_one_minute_measured_cumulative_energy()

1分毎の積算電力量計測値を取得する。

Arguments

  • Void

Return Value

  • dict: 収集日時と正方向および逆方向の積算電力量(kWh)

e.g.

{'timestamp': datetime.datetime,
 'cumulative energy': {'normal direction': int | float | None,
                       'reverse direction': int | float | None}}

momonga.get_coefficient_for_cumulative_energy()

積算電力量計測値、履歴を実使用量に換算する係数を取得する。Momongaが出力する結果には適宜この値が乗じられている。

Arguments

  • Void

Return Value

  • int: 係数

momonga.get_number_of_effective_digits_for_cumulative_energy()

積算電力量計測値の有効桁数を取得する。

Arguments

  • Void

Return Value

  • int: 有効桁数

momonga.get_measured_cumulative_energy(reverse: bool = False)

積算電力量計測値を取得する。

Arguments

  • reverse: Trueのとき逆方向の積算電力量を取得する

Return Value

  • int | float | None: 積算電力量(kWh)。スマートメーターが値を持たないときはNone

momonga.get_unit_for_cumulative_energy()

積算電力量計測値、履歴の乗率を取得する。Momongaが出力する結果には適宜この値が乗じられている。

Arguments

  • Void

Return Value

  • int | float: 積算電力量の乗率

momonga.get_historical_cumulative_energy_1(day: int = 0, reverse: bool = False)

積算電力量計測値履歴1を取得する。

Arguments

  • day: 積算履歴収集日(0:当日、1~:前日の日数)
  • reverse: Trueのとき逆方向の積算電力量を取得する

Return Value

  • list: 収集日時と積算電力量(kWh)

e.g.

[{'timestamp': datetime.datetime,
  'cumulative energy': int | float | None}]

注意: 収集日時はスマートメーター側で設定されたものではなくMomonga自身が設定しているため、実行中に日を跨ぐと収集日時に齟齬が生じる可能性がある。

momonga.set_day_for_historical_data_1(day: int = 0)

積算履歴収集日1を設定する。

Arguments

  • day: 積算履歴収集日(0:当日、1~:前日の日数)

Return Value

  • None

momonga.get_day_for_historical_data_1()

積算履歴収集日1を取得する。

Arguments

  • Void

Return Value

  • int: 積算履歴収集日1

momonga.get_instantaneous_power()

瞬時電力計測値を取得する。

Arguments

  • Void

Return Value

  • int | None: 瞬時電力測定値(W)。スマートメーターが値を持たないときはNone

momonga.get_instantaneous_current()

瞬時電流計測値を取得する。

Arguments

  • Void

Return Value

  • dict: R相瞬時電流(A)とT相瞬時電流(A)。相ごとに、値を持たないときはNone

e.g.

{'r phase current': float | None,
 't phase current': float | None}

momonga.get_cumulative_energy_measured_at_fixed_time(reverse: bool = False)

定時積算電力量計測値を取得する。

Arguments

  • reverse: Trueのとき逆方向の積算電力量を取得する

Return Value

  • dict: 収集日時と積算電力量(kWh)。積算電力量は、値を持たないときはNone

e.g.

{'timestamp': datetime.datetime,
 'cumulative energy': int | float | None}

momonga.get_historical_cumulative_energy_2(timestamp: datetime.datetime | None = None, num_of_data_points: int = 12)

積算履歴収集日時、収集コマ数ならびに積算電力量の計測結果履歴を、正・逆 30 分毎のデータで過去最大6時間分取得する。

Arguments

  • timestamp: 収集日時 (Noneのときは現時刻)
  • num_of_data_points: 収集コマ数 1~12

Return Value

  • list: 収集日時と正方向および逆方向の積算電力量(kWh)

e.g.

[{'timestamp': datetime.datetime,
  'cumulative energy': {'normal direction': int | float | None,
                        'reverse direction': int | float | None}}]

momonga.set_time_for_historical_data_2(timestamp: datetime.datetime, num_of_data_points: int = 12)

積算履歴収集日時ならびに収集コマ数を設定する。

Arguments

  • timestamp: 収集日時
  • num_of_data_points: 収集コマ数

Return Value

  • None

momonga.get_time_for_historical_data_2()

積算履歴収集日時ならびに収集コマ数を取得する。

Arguments

  • Void

Return Value

  • dict: 収集日時と収集コマ数

e.g.

{'timestamp': datetime.datetime | None,
 'number of data points': int}

momonga.get_historical_cumulative_energy_3(timestamp: datetime.datetime | None = None, num_of_data_points: int = 10)

積算履歴収集日時、収集コマ数ならびに積算電力量の計測結果履歴を、正・逆 1 分毎のデータで過去最大10分間ぶん取得する。

Arguments

  • timestamp: 収集日時 (Noneのときは現時刻)
  • num_of_data_points: 収集コマ数 1~10

Return Value

  • list: 収集日時と正方向および逆方向の積算電力量(kWh)

e.g.

[{'timestamp': datetime.datetime,
  'cumulative energy': {'normal direction': int | float | None,
                        'reverse direction': int | float | None}}]

momonga.set_time_for_historical_data_3(timestamp: datetime.datetime, num_of_data_points: int = 10)

積算履歴収集日時ならびに収集コマ数を設定する。

Arguments

  • timestamp: 収集日時
  • num_of_data_points: 収集コマ数

Return Value

  • None

momonga.get_time_for_historical_data_3()

積算履歴収集日時ならびに収集コマ数を取得する。

Arguments

  • Void

Return Value

  • dict: 収集日時と収集コマ数

e.g.

{'timestamp': datetime.datetime | None,
 'number of data points': int}

momonga.request_to_set(day_for_historical_data_1: dict | None = None, time_for_historical_data_2: dict | None = None, time_for_historical_data_3: dict | None = None)

複数のEchonetプロパティを一括設定するためのインタフェース。指定した引数だけが1回のリクエストにまとめられる。すべてNoneのときは何も送信しない。

Arguments

  • day_for_historical_data_1: momonga.set_day_for_historical_data_1()に渡す引数
  • time_for_historical_data_2: momonga.set_time_for_historical_data_2()に渡す引数
  • time_for_historical_data_3: momonga.set_time_for_historical_data_3()に渡す引数

Return Value

  • None

e.g.

import datetime

with momonga.Momonga(rbid, pwd, dev) as mo:
    mo.request_to_set(
        day_for_historical_data_1={'day': 1},
        time_for_historical_data_2={'timestamp': datetime.datetime.now(),
                                    'num_of_data_points': 12},
    )

momonga.request_to_get(properties: set[EchonetPropertyCode])

複数のEchonetプロパティを一括取得するためのインタフェース。1回のリクエストにまとめて送信する。

Arguments

  • properties: EchonetPropertyCodeの集合

Return Value

  • dict: EchonetPropertyCodeと結果

e.g.

import time

from momonga import EchonetPropertyCode as EPC

with momonga.Momonga(rbid, pwd, dev) as mo:
    while True:
        res = mo.request_to_get({
            EPC.instantaneous_power,
            EPC.instantaneous_current,
            EPC.measured_cumulative_energy,
        })

        for epc, r in res.items():
            print(f'epc: {epc.name}, result: {r}')

        time.sleep(60)

momonga.get_notification(timeout: int | float | None = None)

スマートメーターからの通知(INF/INFC)を受け取る。INFCを受信した場合はINFC_Resを自動送信する。

Arguments

  • timeout: 待機秒数。Noneのとき通知が届くまでブロッキングする

Return Value

  • Notification | None: 通知データ。タイムアウト時はNone

NotificationTypedDictで、キーは'esv'EchonetServiceCode)と'properties'(EPCと値の辞書)の2つ。実体はdictなので、そのまま添字で読める。

{'esv': momonga.EchonetServiceCode.inf,
 'properties': {momonga.EchonetPropertyCode.cumulative_energy_measured_at_fixed_time: ...}}

Note: timeout=None is not recommended

PANAセッションが切断された場合でも、スマートメーターやWi-SUNモジュールが状態変化の通知(EVENT)を送出せずに沈黙する状態(電波途絶、スマートメーター電源断など)では、MomongaはPANAセッションの切断を検知できない。その場合get_notification(timeout=None)は無期限にブロッキングする。

有限のtimeoutを設定し、Noneが一定回数連続した場合はスマートメーターにコマンドを送信してセッションの疎通を確認することを推奨する。セッションが切断されていればMomongaNeedToReopenが送出される。

with momonga.Momonga(rbid, pwd, dev) as mo:
    consecutive_timeouts = 0
    while True:
        notif = mo.get_notification(timeout=2400)  # 40 minutes
        if notif is None:
            consecutive_timeouts += 1
            if consecutive_timeouts >= 3:
                mo.get_operation_status()  # raises MomongaNeedToReopen if session is lost
            continue
        consecutive_timeouts = 0
        # process notification

AsyncMomonga

AsyncMomongaMomongaの全メソッドをasyncioで利用できるラッパークラスである。Momongaのブロッキング処理はインスタンスごとに持つスレッドプールで実行されるため、イベントループをブロックしない。プロセスで共有されるデフォルトのexecutorは使わない。

スレッドプールは3つに分かれている。get_notification()notifications()は専用プール、open()/close()/reopen()はもう1つの専用プール、それ以外のメソッドは汎用プールを使う。リクエストが何本詰まっていても、通知のtimeoutが守られ、セッションの開閉が待たされないようにするためである。専用プールはそれぞれワーカーを2本持つ。1本は実行中の呼び出し用、もう1本は、キャンセルされて誰も待っていない呼び出しが次の呼び出しを塞がないための予備。

スレッドプールを停止するのはasync with文を抜けるときだけである。抜けたあとのインスタンスは再利用できない。

AsyncMomonga.close()はスマートメーターとのセッションを閉じるだけで、スレッドプールはそのまま残る。close()のあとにopen()を呼んで使い続けられるのはこのためである。async withを使わずにopen()close()だけで使う場合、スレッドプールはプロセスが終わるまで残るが、ワーカーはアイドル状態なのでプロセスの終了を妨げない。

Note: awaitをキャンセルしても処理は止まらない

asyncio.wait_for()やタスクのキャンセルで待つのをやめても、スレッドプールで動いているMomongaの処理は最後まで走り続ける。実行中のSKコマンドを途中で捨てるとWi-SUNモジュールの応答とコマンド列が同期を失うため、途中で止める手段は用意していない。

放棄された処理はそのリクエストが終わるまで汎用プールの枠を占有する。占有時間はMomonga側の設定で決まる。

  • reopen_delaysを指定していない場合の上限はおおむねxmit_timeoutxmit_retries × recv_timeoutの和。既定値では約7分。xmit_timeoutを大きくすればそのぶん延びる
  • reopen_delaysrepeat()など終わりのない列を渡している場合、放棄されたリクエストは再接続を繰り返していつまでも終わらない

占有時間を短くしたい場合はxmit_timeoutを下げ、max_workersには余裕を持たせること。終わりのないreopen_delaysを避ければ、占有時間の上限も有限になる。汎用プールが放棄されたリクエストで埋まっても、async with文からの退出は専用スレッドで実行されるので待たされない。

e.g.

async with momonga.AsyncMomonga(rbid, pwd, dev,
                                reopen_delays=[600.0, 600.0, 600.0]) as mo:
    mo.xmit_timeout = 60  # give up after a minute of blocked transmission

get_notification()notifications()はこの制限を受けない。1秒以下の単位で読み取りを区切っているため、キャンセルしても次の読み取りはすぐ始められる。読み取り済みの通知は次の呼び出しに引き継がれる。読み取りを同時に走らせた場合など、引き継がれる通知が複数になることもある。誰も読み出さないまま32件を超えると古いものから捨てられ、警告がログに出る。ただしキャンセルした読み取りがINFC_Resの送出中だった場合、そのワーカーは最大15秒残る。予備のワーカーがあるので次の読み取りは待たされない。

momonga.AsyncMomonga(rbid: str, pwd: str, dev: str, baudrate: int = 115200, reset_dev: bool = True, reopen_delays: Iterable[float] | Callable[[], Iterable[float]] | None = None, max_workers: int = 4, scan_retries: int = 3, join_retries: int = 3)

AsyncMomongaクラスのインスタンス化。max_workers以外の引数はMomongaと同じ。

Arguments

  • max_workers: 汎用プールのワーカー数。通知の読み取りとセッションの開閉は専用スレッドで動くのでこの数には含まれない。リクエストは内部で直列化されるため増やしても速くはならない。既定値は4(実行中のリクエスト1本と、放棄されたリクエストのための予備3本)

momonga.xmit_retriesmomonga.recv_timeoutmomonga.xmit_timeoutmomonga.internal_xmit_intervalAsyncMomongaのインスタンスにもそのまま設定できる。momonga.is_openmomonga.energy_unitmomonga.energy_coefficientmomonga.lqimomonga.rssiは読み取りのみ。

async with文による使用を推奨する。

import asyncio
import momonga

async def main():
    async with momonga.AsyncMomonga(rbid, pwd, dev) as mo:
        power = await mo.get_instantaneous_power()
        print('no data' if power is None else f'{power}W')

asyncio.run(main())

async AsyncMomonga.notifications(timeout: int | float = 60)

通知を非同期ジェネレータとして受け取る。timeout秒待って通知がない場合は次の待機に入る(Noneは返さない)。

async def main():
    async with momonga.AsyncMomonga(rbid, pwd, dev) as mo:
        async for notif in mo.notifications(timeout=2400):
            print(notif)

async AsyncMomonga.get_notification(timeout: int | float | None = None)

同期版get_notification()と同じ動作。タイムアウト時はNoneを返す。

Other Methods

Momongaの全メソッドに対応するasync版が定義されている。await mo.メソッド名()の形式で呼び出せる。

Feedback

イシュー報告、プルリクエスト、コメント等、なんでもよいのでフィードバックを歓迎する。星をもらうと開発が活発になる。
Questions, suggestions, and comments are welcome! Please feel free to write in English.

Credits

This project was originally developed during my time at BitMeister Inc., with support and resources generously provided by the company. I am really thankful for the people and the environment that helped make it happen. It is now maintained independently.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

momonga-0.7.0.tar.gz (175.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

momonga-0.7.0-py3-none-any.whl (52.2 kB view details)

Uploaded Python 3

File details

Details for the file momonga-0.7.0.tar.gz.

File metadata

  • Download URL: momonga-0.7.0.tar.gz
  • Upload date:
  • Size: 175.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for momonga-0.7.0.tar.gz
Algorithm Hash digest
SHA256 52591bbeabd9ea5c33cf422190b467cc9bdf4c06763dfd651d8ea78bfaa1556b
MD5 268da721a29c9ea801f1405923bd7a6f
BLAKE2b-256 a98008561afa7e8b3b16365201d4c2fca33366687650f0636436bbf11b25f261

See more details on using hashes here.

Provenance

The following attestation bundles were made for momonga-0.7.0.tar.gz:

Publisher: release.yml on nbtk/momonga

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file momonga-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: momonga-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 52.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for momonga-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2448d5f1468f1a8dabcaa51a4e5c573c03d56d1f791d24d94c39668c144ace6c
MD5 6014a754e9e25e44fde54bd78de35b68
BLAKE2b-256 6f871d1b6964b5568c0aa7f3269985268d2a69a68ae975dbb672b825008c56cf

See more details on using hashes here.

Provenance

The following attestation bundles were made for momonga-0.7.0-py3-none-any.whl:

Publisher: release.yml on nbtk/momonga

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

1 file

0.1.2

1 file

0.1.1

1 file

0.1.0

1 file

0.0.8

1 file

0.0.6

1 file

0.0.5

1 file

0.0.1

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page