Quick Start

This page walks you through installing msunpv and reading your first data from an MSunPV solar router in a few lines of Python.

Prerequisites

  • Python 3.9 or newer.

  • Your MSunPV device reachable on the local network (know its IP address or hostname).

  • The device’s HTTP interface enabled (factory default).

Installation

Install from PyPI with pip:

pip install msunpv

Or, to install the latest development version directly from GitHub:

pip install git+https://github.com/thanatos-vf-2000/msunpv.git

Basic usage — read status once

The simplest way to query the device is through MSunPVRead, which manages the aiohttp session for you.

import asyncio
from msunpv import MSunPVRead, MSunPVConnectionException, MSunPVXMLDataException

DEVICE_IP = "192.168.1.123"   # ← replace with your router's IP

async def main():
    reader = MSunPVRead(DEVICE_IP)
    await reader.start()

    try:
        await reader.refresh_data()       # fetches status.xml (and index.xml on first call)

        status = reader.DataMSunPVDataStatus

        print(f"Grid power  : {status.power_reso:+.1f} W")
        print(f"PV power    : {status.power_pv_positive:.1f} W")
        print(f"Home load   : {status.power_home:.1f} W")
        print(f"Balloon temp: {status.temperature_balloon:.1f} °C")
        print(f"Daily prod. : {status.daily_production:.3f} kWh")

    except (MSunPVConnectionException, MSunPVXMLDataException) as exc:
        print(f"Error: {exc}")
    finally:
        await reader.stop()

asyncio.run(main())

Expected output (values depend on your installation):

Grid power  : -230.0 W
PV power    : 280.0 W
Home load   : 50.0 W
Balloon temp: 52.3 °C
Daily prod. : 1.243 kWh

Polling loop with wait_for

Use wait_for() to poll the device at a regular interval without drift — it subtracts the time already spent reading:

import asyncio
from msunpv import MSunPVRead

DEVICE_IP = "192.168.1.123"
POLL_INTERVAL = 10   # seconds

async def main():
    reader = MSunPVRead(DEVICE_IP)
    await reader.start()

    try:
        for _ in range(6):                         # 6 iterations → ~1 minute
            await reader.refresh_data()
            power = reader.DataMSunPVDataStatus.power_reso
            print(f"Grid: {power:+.1f} W")
            await reader.wait_for(POLL_INTERVAL)   # waits the remaining seconds
    finally:
        await reader.stop()

asyncio.run(main())

Reading the device index

The index (index.xml) contains sensor labels, unit suffixes, output names and command descriptions. It is fetched automatically on the first call to refresh_data(). Pass All=True to force a refresh:

await reader.refresh_data(All=True)   # forces re-fetch of both status and index

index = reader.DataMSunPVDataIndex
print(f"Model   : {index.modele}")
print(f"Version : {index.version}")

# Sensor type info for sensor 0 (usually grid power)
info = index.sensor_type_info(0)
print(f"Sensor 0: {info['name']} ({info['suffix']})")

# Command info for command slot 0
cmd = index.command_info(0)
print(f"Cmd 0: {cmd['cmdtxt']} — params: {cmd['param1']}, {cmd['param2']}")

Using the lower-level API (aiohttp session)

If you already manage your own aiohttp.ClientSession (e.g. inside Home Assistant or another async framework), use MSunPVWebConnect directly:

import asyncio
import aiohttp
from msunpv import MSunPVWebConnect

DEVICE_IP = "192.168.1.123"

async def main():
    async with aiohttp.ClientSession() as session:
        client = MSunPVWebConnect(session, DEVICE_IP)

        status = await client.get_status()
        print(f"PV power: {status.power_pv_positive:.1f} W")

        index = await client.get_index()
        print(f"Serial: {index.serial_number}")

asyncio.run(main())

Tip

refresh() is a convenience wrapper that dispatches to get_status() or get_index() based on the data_type argument ("status.xml" or "index.xml").

Error handling

All library errors inherit from MSunPVException:

Exception

When it is raised

MSunPVConnectionException

Network error, timeout, device unreachable, or empty IP

MSunPVXMLDataException

Malformed XML or unexpected payload from the device

from msunpv import MSunPVException, MSunPVConnectionException, MSunPVXMLDataException

try:
    await reader.refresh_data()
except MSunPVConnectionException as exc:
    print(f"Cannot reach device: {exc}")
except MSunPVXMLDataException as exc:
    print(f"Bad data from device: {exc}")
except MSunPVException as exc:
    print(f"Library error: {exc}")

Generic attribute access

Every data class exposes a get() helper that returns None instead of raising AttributeError for unknown names — useful for dynamic access:

value = status.get("power_reso")    # returns the float, or None if missing

Next steps

  • Browse the msunpv package API reference for the full list of attributes.

  • See example.py in the repository root for a complete command-line demo.

  • Check Changelog for recent changes.