Skip to content

Reference

Top-level module for importing the Ecos class.

AsyncEcos(datacenter=None, url=None, access_token=None, refresh_token=None)

Asynchronous ECOS API client class.

This class provides methods for interacting with the ECOS API, including authentication, retrieving user information, and managing homes. It uses the aiohttp library to make asynchonous HTTP requests to the API.

Initialize a session with ECOS API.

Parameters:

Name Type Description Default
datacenter Optional[str]

The location of the ECOS API datacenter. Can be one of CN, EU, or AU. If not specified and url is not provided, a ValueError is raised.

None
url Optional[str]

The URL of the ECOS API. If specified, datacenter is ignored.

None
access_token Optional[str]

The access token for authentication with the ECOS API.

None
refresh_token Optional[str]

The refresh token for authentication with the ECOS API.

None

Raises:

Type Description
ValueError

If datacenter is not one of CN, EU, or AU and url is not provided.

Source code in src/ecactus/_base.py
def __init__(
    self,
    datacenter: str | None = None,
    url: str | None = None,
    access_token: str | None = None,
    refresh_token: str | None = None,
) -> None:
    """Initialize a session with ECOS API.

    Args:
        datacenter (Optional[str]): The location of the ECOS API datacenter.
            Can be one of `CN`, `EU`, or `AU`. If not specified and `url` is not provided,
            a `ValueError` is raised.
        url (Optional[str]): The URL of the ECOS API. If specified, `datacenter` is ignored.
        access_token (Optional[str]): The access token for authentication with the ECOS API.
        refresh_token (Optional[str]): The refresh token for authentication with the ECOS API.

    Raises:
        ValueError: If `datacenter` is not one of `CN`, `EU`, or `AU` and `url` is not provided.

    """
    logger.info("Initializing session")
    self.access_token = access_token
    self.refresh_token = refresh_token
    # TODO: get datacenters from https://dcdn-config.weiheng-tech.com/prod/config.json
    datacenters = {
        "CN": "https://api-ecos-hu.weiheng-tech.com",
        "EU": "https://api-ecos-eu.weiheng-tech.com",
        "AU": "https://api-ecos-au.weiheng-tech.com",
    }
    if url is None:
        if datacenter is None:
            raise ValueError("url or datacenter not specified")
        if datacenter not in datacenters:
            raise ValueError(
                "datacenter must be one of {}".format(", ".join(datacenters.keys()))
            )
        self.url = datacenters[datacenter]
    else:  # url specified, ignore datacenter
        self.url = url.rstrip("/")  # remove trailing / from url

login(email, password) async

Authenticate with the ECOS API using a provided email and password.

Parameters:

Name Type Description Default
email str

The user's email to use for authentication.

required
password str

The user's password to use for authentication.

required
Source code in src/ecactus/_async_client.py
async def login(self, email: str, password: str) -> None:
    """Authenticate with the ECOS API using a provided email and password.

    Args:
        email (str): The user's email to use for authentication.
        password (str): The user's password to use for authentication.

    """
    logger.info("Login")
    payload = {
        "_t": int(time.time()),
        "clientType": "BROWSER",
        "clientVersion": "1.0",
        "email": email,
        "password": password,
    }
    data = await self._post("/api/client/guide/login", payload=payload)
    self.access_token = data["accessToken"]
    self.refresh_token = data["refreshToken"]

get_user_info() async

Get user details.

Returns:

Type Description
JSON

Details concerning the user. Example:

{
    "username": "john.doe@acme.com",
    "nickname": "JohnD",
    "email": "john.doe@acme.com",
    "phone": "",
    "timeZoneId": "209",
    "timeZone": "GMT-05:00",
    "timezoneName": "America/Toronto",
    "datacenterPhoneCode": 49,
    "datacenter": "EU",
    "datacenterHost": "https://api-ecos-eu.weiheng-tech.com"
}

Source code in src/ecactus/_async_client.py
async def get_user_info(self) -> JSON:
    """Get user details.

    Returns:
        Details concerning the user. Example:
            ``` py
            {
                "username": "john.doe@acme.com",
                "nickname": "JohnD",
                "email": "john.doe@acme.com",
                "phone": "",
                "timeZoneId": "209",
                "timeZone": "GMT-05:00",
                "timezoneName": "America/Toronto",
                "datacenterPhoneCode": 49,
                "datacenter": "EU",
                "datacenterHost": "https://api-ecos-eu.weiheng-tech.com"
            }
            ```

    """
    logger.info("Get user info")
    return await self._get("/api/client/settings/user/info")

get_homes() async

Get a list of homes.

Returns:

Type Description
JSON

A list of homes. Example:

[
    {
        "homeId": "1234567890123456789",
        "homeName": "SHARED_DEVICES",
        "homeType": 0,
        "longitude": None,
        "latitude": None,
        "homeDeviceNumber": 1,
        "relationType": 1,
        "createTime": 946684800000,
        "updateTime": 946684800000,
    },
    {
        "homeId": "9876543210987654321",
        "homeName": "My Home",
        "homeType": 1,
        "longitude": None,
        "latitude": None,
        "homeDeviceNumber": 0,
        "relationType": 1,
        "createTime": 946684800000,
        "updateTime": 946684800000,
    },
]

Source code in src/ecactus/_async_client.py
async def get_homes(self) -> JSON:
    """Get a list of homes.

    Returns:
        A list of homes. Example:
            ``` py
            [
                {
                    "homeId": "1234567890123456789",
                    "homeName": "SHARED_DEVICES",
                    "homeType": 0,
                    "longitude": None,
                    "latitude": None,
                    "homeDeviceNumber": 1,
                    "relationType": 1,
                    "createTime": 946684800000,
                    "updateTime": 946684800000,
                },
                {
                    "homeId": "9876543210987654321",
                    "homeName": "My Home",
                    "homeType": 1,
                    "longitude": None,
                    "latitude": None,
                    "homeDeviceNumber": 0,
                    "relationType": 1,
                    "createTime": 946684800000,
                    "updateTime": 946684800000,
                },
            ]
            ```

    """
    logger.info("Get home list")
    home_list: list[Any] = await self._get("/api/client/v2/home/family/query")
    for (
        home
    ) in home_list:  # force the name of the home for shared devices (homeType=0)
        if int(home["homeType"]) == 0:
            home["homeName"] = "SHARED_DEVICES"
    return home_list

get_devices(home_id) async

Get a list of devices for a home.

Parameters:

Name Type Description Default
home_id str

The home ID to get devices for.

required

Returns:

Type Description
JSON

A list of devices. Example:

[
    {
        "deviceId": "1234567890123456789",
        "deviceAliasName": "My Device",
        "state": 0,
        "batterySoc": 0.0,
        "batteryPower": 0,
        "socketSwitch": None,
        "chargeStationMode": None,
        "vpp": False,
        "type": 1,
        "deviceSn": "SHC000000000000001",
        "agentId": "9876543210987654321",
        "lon": 0.0,
        "lat": 0.0,
        "deviceType": "XX-XXX123       ",
        "resourceSeriesId": 101,
        "resourceTypeId": 7,
        "master": 0,
        "emsSoftwareVersion": "000-00000-00",
        "dsp1SoftwareVersion": "111-11111-11",
    },
]

Source code in src/ecactus/_async_client.py
async def get_devices(self, home_id: str) -> JSON:
    """Get a list of devices for a home.

    Args:
        home_id (str): The home ID to get devices for.

    Returns:
        A list of devices. Example:
            ``` py
            [
                {
                    "deviceId": "1234567890123456789",
                    "deviceAliasName": "My Device",
                    "state": 0,
                    "batterySoc": 0.0,
                    "batteryPower": 0,
                    "socketSwitch": None,
                    "chargeStationMode": None,
                    "vpp": False,
                    "type": 1,
                    "deviceSn": "SHC000000000000001",
                    "agentId": "9876543210987654321",
                    "lon": 0.0,
                    "lat": 0.0,
                    "deviceType": "XX-XXX123       ",
                    "resourceSeriesId": 101,
                    "resourceTypeId": 7,
                    "master": 0,
                    "emsSoftwareVersion": "000-00000-00",
                    "dsp1SoftwareVersion": "111-11111-11",
                },
            ]
            ```

    """
    logger.info("Get devices for home %s", home_id)
    return await self._get(
        "/api/client/v2/home/device/query", payload={"homeId": home_id}
    )

get_all_devices() async

Get a list of all the devices.

Returns:

Type Description
JSON

A list of devices. Example:

[
    {
        "deviceId": "1234567890123456789",
        "deviceAliasName": "My Device",
        "wifiSn": "azerty123456789azertyu",
        "state": 0,
        "weight": 0,
        "temp": None,
        "icon": None,
        "vpp": False,
        "master": 0,
        "type": 1,
        "deviceSn": "SHC000000000000001",
        "agentId": "",
        "lon": 0.0,
        "lat": 0.0,
        "category": None,
        "model": None,
        "deviceType": None,
    },
]

Source code in src/ecactus/_async_client.py
async def get_all_devices(self) -> JSON:
    """Get a list of all the devices.

    Returns:
        A list of devices. Example:
            ``` py
            [
                {
                    "deviceId": "1234567890123456789",
                    "deviceAliasName": "My Device",
                    "wifiSn": "azerty123456789azertyu",
                    "state": 0,
                    "weight": 0,
                    "temp": None,
                    "icon": None,
                    "vpp": False,
                    "master": 0,
                    "type": 1,
                    "deviceSn": "SHC000000000000001",
                    "agentId": "",
                    "lon": 0.0,
                    "lat": 0.0,
                    "category": None,
                    "model": None,
                    "deviceType": None,
                },
            ]
            ```

    """
    logger.info("Get devices for every homes")
    return await self._get("/api/client/home/device/list")

get_today_device_data(device_id) async

Get power metrics of the current day until now.

Parameters:

Name Type Description Default
device_id str

The device ID to get power metrics for.

required

Returns:

Type Description
JSON

Multiple metrics of the current day. Example:

{
    "solarPowerDps": {
        "946685100": 0.0,
        "946685400": 0.0,
        ...
        "946733700": 0.0,
    },
    "batteryPowerDps": {...},
    "gridPowerDps": {...},
    "meterPowerDps": {...},
    "homePowerDps": {...},
    "epsPowerDps": {...},
}

Source code in src/ecactus/_async_client.py
async def get_today_device_data(self, device_id: str) -> JSON:
    """Get power metrics of the current day until now.

    Args:
        device_id (str): The device ID to get power metrics for.

    Returns:
        Multiple metrics of the current day. Example:
            ``` py
            {
                "solarPowerDps": {
                    "946685100": 0.0,
                    "946685400": 0.0,
                    ...
                    "946733700": 0.0,
                },
                "batteryPowerDps": {...},
                "gridPowerDps": {...},
                "meterPowerDps": {...},
                "homePowerDps": {...},
                "epsPowerDps": {...},
            }
            ```

    """
    logger.info("Get current day data for device %s", device_id)
    return await self._post(
        "/api/client/home/now/device/realtime", payload={"deviceId": device_id}
    )

get_realtime_home_data(home_id) async

Get current power for the home.

Parameters:

Name Type Description Default
home_id str

The home ID to get current power for.

required

Returns:

Type Description
JSON

Power data. Example:

{
    "batteryPower": 0,
    "epsPower": 0,
    "gridPower": 23,
    "homePower": 1118,
    "meterPower": 1118,
    "solarPower": 0,
    "chargePower": 0,
    "batterySocList": [
        {
            "deviceSn": "SHC000000000000001",
            "batterySoc": 0.0,
            "sysRunMode": 1,
            "isExistSolar": True,
            "sysPowerConfig": 3,
        }
    ],
}

Source code in src/ecactus/_async_client.py
async def get_realtime_home_data(self, home_id: str) -> JSON:
    """Get current power for the home.

    Args:
        home_id (str): The home ID to get current power for.

    Returns:
        Power data. Example:
            ``` py
            {
                "batteryPower": 0,
                "epsPower": 0,
                "gridPower": 23,
                "homePower": 1118,
                "meterPower": 1118,
                "solarPower": 0,
                "chargePower": 0,
                "batterySocList": [
                    {
                        "deviceSn": "SHC000000000000001",
                        "batterySoc": 0.0,
                        "sysRunMode": 1,
                        "isExistSolar": True,
                        "sysPowerConfig": 3,
                    }
                ],
            }
            ```

    """
    logger.info("Get realtime data for home %s", home_id)
    return await self._get(
        "/api/client/v2/home/device/runData", payload={"homeId": home_id}
    )

get_realtime_device_data(device_id) async

Get current power for a device.

Parameters:

Name Type Description Default
device_id str

The device ID to get current power for.

required

Returns:

Type Description
JSON

Power data. Example (without solar production):

{
    "batterySoc": 0,
    "batteryPower": 0,
    "epsPower": 0,
    "gridPower": 0,
    "homePower": 3581,
    "meterPower": 3581,
    "solarPower": 0,
    "sysRunMode": 0,
    "isExistSolar": true,
    "sysPowerConfig": 3,
}
Example when all solar production is used by home:
{'batterySoc': 0.0, 'batteryPower': 0, 'epsPower': 0, 'gridPower': 2479, 'homePower': 3674, 'meterPower': 1102, 'solarPower': 2572, 'sysRunMode': 1, 'isExistSolar': True, 'sysPowerConfig': 3}
# Home 3674 (homePower)
# PV -> Home 2572 (solarPower)
# Grid -> Home 1102 (meterPower)
# gridPower (could be related to operating mode with % reserved SOC for grid connection)
Example when solar over production is injected to the grid:
{'batterySoc': 0.0, 'batteryPower': 0, 'epsPower': 0, 'gridPower': 4194, 'homePower': 3798, 'meterPower': -650, 'solarPower': 4448, 'sysRunMode': 1, 'isExistSolar': True, 'sysPowerConfig': 3}
# Home 3798
# PV -> Home 4448
# Home -> Grid 650
# gridPower (could be related to operating mode with % reserved SOC for grid connection)

Source code in src/ecactus/_async_client.py
async def get_realtime_device_data(self, device_id: str) -> JSON:
    """Get current power for a device.

    Args:
        device_id (str): The device ID to get current power for.

    Returns:
        Power data. Example (without solar production):
            ``` py
            {
                "batterySoc": 0,
                "batteryPower": 0,
                "epsPower": 0,
                "gridPower": 0,
                "homePower": 3581,
                "meterPower": 3581,
                "solarPower": 0,
                "sysRunMode": 0,
                "isExistSolar": true,
                "sysPowerConfig": 3,
            }
            ```
            Example when all solar production is used by home:
            ``` py
            {'batterySoc': 0.0, 'batteryPower': 0, 'epsPower': 0, 'gridPower': 2479, 'homePower': 3674, 'meterPower': 1102, 'solarPower': 2572, 'sysRunMode': 1, 'isExistSolar': True, 'sysPowerConfig': 3}
            # Home 3674 (homePower)
            # PV -> Home 2572 (solarPower)
            # Grid -> Home 1102 (meterPower)
            # gridPower (could be related to operating mode with % reserved SOC for grid connection)
            ```
            Example when solar over production is injected to the grid:
            ``` py
            {'batterySoc': 0.0, 'batteryPower': 0, 'epsPower': 0, 'gridPower': 4194, 'homePower': 3798, 'meterPower': -650, 'solarPower': 4448, 'sysRunMode': 1, 'isExistSolar': True, 'sysPowerConfig': 3}
            # Home 3798
            # PV -> Home 4448
            # Home -> Grid 650
            # gridPower (could be related to operating mode with % reserved SOC for grid connection)
            ```

    """
    logger.info("Get realtime data for device %s", device_id)
    return await self._post(
        "/api/client/home/now/device/runData", payload={"deviceId": device_id}
    )

get_history(device_id, start_date, period_type) async

Get aggregated energy for a period.

Parameters:

Name Type Description Default
device_id str

The device ID to get history for.

required
start_date datetime

The start date.

required
period_type int

Possible value:

  • 0: daily values of the calendar month corresponding to start_date
  • 1: today daily values (start_date is ignored) (?)
  • 2: daily values of the current month (start_date is ignored)
  • 3: same than 2 ?
  • 4: total for the current month (start_date is ignored)
required

Returns:

Type Description
JSON

Data and metrics corresponding to the defined period. Example:

{
    "energyConsumption": 1221.2,
    "solarPercent": 47.0,
    "homeEnergyDps": {
        "1733112000": 39.6,
        "1733198400": 68.1,
        "1733284800": 75.3,
        ...
        "1735707599": 41.3,
    },
}

Source code in src/ecactus/_async_client.py
async def get_history(
    self, device_id: str, start_date: datetime, period_type: int
) -> JSON:
    """Get aggregated energy for a period.

    Args:
        device_id (str): The device ID to get history for.
        start_date (datetime): The start date.
        period_type (int): Possible value:

            - `0`: daily values of the calendar month corresponding to `start_date`
            - `1`: today daily values (`start_date` is ignored) (?)
            - `2`: daily values of the current month (`start_date` is ignored)
            - `3`: same than 2 ?
            - `4`: total for the current month (`start_date` is ignored)

    Returns:
        Data and metrics corresponding to the defined period. Example:
            ``` py
            {
                "energyConsumption": 1221.2,
                "solarPercent": 47.0,
                "homeEnergyDps": {
                    "1733112000": 39.6,
                    "1733198400": 68.1,
                    "1733284800": 75.3,
                    ...
                    "1735707599": 41.3,
                },
            }
            ```

    """
    logger.info("Get history for device %s", device_id)
    start_ts = int(start_date.timestamp())
    return await self._post(
        "/api/client/home/history/home",
        payload={
            "deviceId": device_id,
            "timestamp": start_ts,
            "periodType": period_type,
        },
    )

get_insight(device_id, start_date, period_type) async

Get energy metrics and statistics of a device for a period.

Parameters:

Name Type Description Default
device_id str

The device ID to get data for.

required
start_date datetime

The start date.

required
period_type int

Possible value:

  • 0: 5-minute power measurement for the calendar day corresponding to start_date (insightConsumptionDataDto is None)
  • 1: (not implemented)
  • 2: daily energy for the calendar month corresponding to start_date (deviceRealtimeDto is None)
  • 3: (not implemented)
  • 4: monthly energy for the calendar year corresponding to start_date (deviceRealtimeDto is None)
  • 5: yearly energy, start_date is ignored (?) (deviceRealtimeDto is None)
required

Returns:

Type Description
JSON

Statistics and metrics corresponding to the defined period. Example:

{
    "selfPowered": 0,
    "deviceRealtimeDto": {
        "solarPowerDps": {
            "1732129500": 0.0,
            "1732129800": 0.0,
            ...
            "1732132800": 0.0,
        },
        "batteryPowerDps": {...},
        "gridPowerDps": {...},
        "meterPowerDps": {...},
        "homePowerDps": {...},
        "epsPowerDps": {...},
    },
    "deviceStatisticsDto": {
        "consumptionEnergy": 0.0,
        "fromBattery": 0.0,
        "toBattery": 0.0,
        "fromGrid": 0.0,
        "toGrid": 0.0,
        "fromSolar": 0.0,
        "eps": 0.0,
    },
    "insightConsumptionDataDto": {
        "fromBatteryDps": {
            "1733976000": 0.0,
            "1733889600": 0.0,
            ...
            "1734062400": 0.0,
        },
        "toBatteryDps": {...},
        "fromGridDps": {...},
        "toGridDps": {...},
        "fromSolarDps": {...},
        "homeEnergyDps": {...},
        "epsDps": {...},
        "selfPoweredDps": {...},
    },
}

Source code in src/ecactus/_async_client.py
async def get_insight(
    self, device_id: str, start_date: datetime, period_type: int
) -> JSON:
    """Get energy metrics and statistics of a device for a period.

    Args:
        device_id (str): The device ID to get data for.
        start_date (datetime): The start date.
        period_type (int): Possible value:

            - `0`: 5-minute power measurement for the calendar day corresponding to `start_date` (`insightConsumptionDataDto` is `None`)
            - `1`: (not implemented)
            - `2`: daily energy for the calendar month corresponding to `start_date` (`deviceRealtimeDto` is `None`)
            - `3`: (not implemented)
            - `4`: monthly energy for the calendar year corresponding to `start_date` (`deviceRealtimeDto` is `None`)
            - `5`: yearly energy, `start_date` is ignored (?) (`deviceRealtimeDto` is `None`)

    Returns:
        Statistics and metrics corresponding to the defined period. Example:
            ``` py
            {
                "selfPowered": 0,
                "deviceRealtimeDto": {
                    "solarPowerDps": {
                        "1732129500": 0.0,
                        "1732129800": 0.0,
                        ...
                        "1732132800": 0.0,
                    },
                    "batteryPowerDps": {...},
                    "gridPowerDps": {...},
                    "meterPowerDps": {...},
                    "homePowerDps": {...},
                    "epsPowerDps": {...},
                },
                "deviceStatisticsDto": {
                    "consumptionEnergy": 0.0,
                    "fromBattery": 0.0,
                    "toBattery": 0.0,
                    "fromGrid": 0.0,
                    "toGrid": 0.0,
                    "fromSolar": 0.0,
                    "eps": 0.0,
                },
                "insightConsumptionDataDto": {
                    "fromBatteryDps": {
                        "1733976000": 0.0,
                        "1733889600": 0.0,
                        ...
                        "1734062400": 0.0,
                    },
                    "toBatteryDps": {...},
                    "fromGridDps": {...},
                    "toGridDps": {...},
                    "fromSolarDps": {...},
                    "homeEnergyDps": {...},
                    "epsDps": {...},
                    "selfPoweredDps": {...},
                },
            }
            ```

    """
    logger.info("Get insight for device %s", device_id)
    start_ts = int(start_date.timestamp() * 1000)  # timestamp in milliseconds
    return await self._post(
        "/api/client/v2/device/three/device/insight",
        payload={
            "deviceId": device_id,
            "timestamp": start_ts,
            "periodType": period_type,
        },
    )

Ecos(datacenter=None, url=None, access_token=None, refresh_token=None)

Synchronous ECOS API client class.

This class provides methods for interacting with the ECOS API, including authentication, retrieving user information, and managing homes. It uses the requests library to make HTTP requests to the API.

Initialize a session with ECOS API.

Parameters:

Name Type Description Default
datacenter Optional[str]

The location of the ECOS API datacenter. Can be one of CN, EU, or AU. If not specified and url is not provided, a ValueError is raised.

None
url Optional[str]

The URL of the ECOS API. If specified, datacenter is ignored.

None
access_token Optional[str]

The access token for authentication with the ECOS API.

None
refresh_token Optional[str]

The refresh token for authentication with the ECOS API.

None

Raises:

Type Description
ValueError

If datacenter is not one of CN, EU, or AU and url is not provided.

Source code in src/ecactus/_base.py
def __init__(
    self,
    datacenter: str | None = None,
    url: str | None = None,
    access_token: str | None = None,
    refresh_token: str | None = None,
) -> None:
    """Initialize a session with ECOS API.

    Args:
        datacenter (Optional[str]): The location of the ECOS API datacenter.
            Can be one of `CN`, `EU`, or `AU`. If not specified and `url` is not provided,
            a `ValueError` is raised.
        url (Optional[str]): The URL of the ECOS API. If specified, `datacenter` is ignored.
        access_token (Optional[str]): The access token for authentication with the ECOS API.
        refresh_token (Optional[str]): The refresh token for authentication with the ECOS API.

    Raises:
        ValueError: If `datacenter` is not one of `CN`, `EU`, or `AU` and `url` is not provided.

    """
    logger.info("Initializing session")
    self.access_token = access_token
    self.refresh_token = refresh_token
    # TODO: get datacenters from https://dcdn-config.weiheng-tech.com/prod/config.json
    datacenters = {
        "CN": "https://api-ecos-hu.weiheng-tech.com",
        "EU": "https://api-ecos-eu.weiheng-tech.com",
        "AU": "https://api-ecos-au.weiheng-tech.com",
    }
    if url is None:
        if datacenter is None:
            raise ValueError("url or datacenter not specified")
        if datacenter not in datacenters:
            raise ValueError(
                "datacenter must be one of {}".format(", ".join(datacenters.keys()))
            )
        self.url = datacenters[datacenter]
    else:  # url specified, ignore datacenter
        self.url = url.rstrip("/")  # remove trailing / from url

login(email, password)

Authenticate with the ECOS API using a provided email and password.

Parameters:

Name Type Description Default
email str

The user's email to use for authentication.

required
password str

The user's password to use for authentication.

required
Source code in src/ecactus/_client.py
def login(self, email: str, password: str) -> None:
    """Authenticate with the ECOS API using a provided email and password.

    Args:
        email (str): The user's email to use for authentication.
        password (str): The user's password to use for authentication.

    """
    logger.info("Login")
    payload = {
        "_t": int(time.time()),
        "clientType": "BROWSER",
        "clientVersion": "1.0",
        "email": email,
        "password": password,
    }
    data = self._post("/api/client/guide/login", payload=payload)
    self.access_token = data["accessToken"]
    self.refresh_token = data["refreshToken"]

get_user_info()

Get user details.

Returns:

Type Description
JSON

Details concerning the user. Example:

{
    "username": "john.doe@acme.com",
    "nickname": "JohnD",
    "email": "john.doe@acme.com",
    "phone": "",
    "timeZoneId": "209",
    "timeZone": "GMT-05:00",
    "timezoneName": "America/Toronto",
    "datacenterPhoneCode": 49,
    "datacenter": "EU",
    "datacenterHost": "https://api-ecos-eu.weiheng-tech.com"
}

Source code in src/ecactus/_client.py
def get_user_info(self) -> JSON:
    """Get user details.

    Returns:
        Details concerning the user. Example:
            ``` py
            {
                "username": "john.doe@acme.com",
                "nickname": "JohnD",
                "email": "john.doe@acme.com",
                "phone": "",
                "timeZoneId": "209",
                "timeZone": "GMT-05:00",
                "timezoneName": "America/Toronto",
                "datacenterPhoneCode": 49,
                "datacenter": "EU",
                "datacenterHost": "https://api-ecos-eu.weiheng-tech.com"
            }
            ```

    """
    logger.info("Get user info")
    return self._get("/api/client/settings/user/info")

get_homes()

Get a list of homes.

Returns:

Type Description
JSON

A list of homes. Example:

[
    {
        "homeId": "1234567890123456789",
        "homeName": "SHARED_DEVICES",
        "homeType": 0,
        "longitude": None,
        "latitude": None,
        "homeDeviceNumber": 1,
        "relationType": 1,
        "createTime": 946684800000,
        "updateTime": 946684800000,
    },
    {
        "homeId": "9876543210987654321",
        "homeName": "My Home",
        "homeType": 1,
        "longitude": None,
        "latitude": None,
        "homeDeviceNumber": 0,
        "relationType": 1,
        "createTime": 946684800000,
        "updateTime": 946684800000,
    },
]

Source code in src/ecactus/_client.py
def get_homes(self) -> JSON:
    """Get a list of homes.

    Returns:
        A list of homes. Example:
            ``` py
            [
                {
                    "homeId": "1234567890123456789",
                    "homeName": "SHARED_DEVICES",
                    "homeType": 0,
                    "longitude": None,
                    "latitude": None,
                    "homeDeviceNumber": 1,
                    "relationType": 1,
                    "createTime": 946684800000,
                    "updateTime": 946684800000,
                },
                {
                    "homeId": "9876543210987654321",
                    "homeName": "My Home",
                    "homeType": 1,
                    "longitude": None,
                    "latitude": None,
                    "homeDeviceNumber": 0,
                    "relationType": 1,
                    "createTime": 946684800000,
                    "updateTime": 946684800000,
                },
            ]
            ```

    """
    logger.info("Get home list")
    home_list: list[Any] = self._get("/api/client/v2/home/family/query")
    for (
        home
    ) in home_list:  # force the name of the home for shared devices (homeType=0)
        if int(home["homeType"]) == 0:
            home["homeName"] = "SHARED_DEVICES"
    return home_list

get_devices(home_id)

Get a list of devices for a home.

Parameters:

Name Type Description Default
home_id str

The home ID to get devices for.

required

Returns:

Type Description
JSON

A list of devices. Example:

[
    {
        "deviceId": "1234567890123456789",
        "deviceAliasName": "My Device",
        "state": 0,
        "batterySoc": 0.0,
        "batteryPower": 0,
        "socketSwitch": None,
        "chargeStationMode": None,
        "vpp": False,
        "type": 1,
        "deviceSn": "SHC000000000000001",
        "agentId": "9876543210987654321",
        "lon": 0.0,
        "lat": 0.0,
        "deviceType": "XX-XXX123       ",
        "resourceSeriesId": 101,
        "resourceTypeId": 7,
        "master": 0,
        "emsSoftwareVersion": "000-00000-00",
        "dsp1SoftwareVersion": "111-11111-11",
    },
]

Source code in src/ecactus/_client.py
def get_devices(self, home_id: str) -> JSON:
    """Get a list of devices for a home.

    Args:
        home_id (str): The home ID to get devices for.

    Returns:
        A list of devices. Example:
            ``` py
            [
                {
                    "deviceId": "1234567890123456789",
                    "deviceAliasName": "My Device",
                    "state": 0,
                    "batterySoc": 0.0,
                    "batteryPower": 0,
                    "socketSwitch": None,
                    "chargeStationMode": None,
                    "vpp": False,
                    "type": 1,
                    "deviceSn": "SHC000000000000001",
                    "agentId": "9876543210987654321",
                    "lon": 0.0,
                    "lat": 0.0,
                    "deviceType": "XX-XXX123       ",
                    "resourceSeriesId": 101,
                    "resourceTypeId": 7,
                    "master": 0,
                    "emsSoftwareVersion": "000-00000-00",
                    "dsp1SoftwareVersion": "111-11111-11",
                },
            ]
            ```

    """
    logger.info("Get devices for home %s", home_id)
    return self._get(
        "/api/client/v2/home/device/query", payload={"homeId": home_id}
    )

get_all_devices()

Get a list of all the devices.

Returns:

Type Description
JSON

A list of devices. Example:

[
    {
        "deviceId": "1234567890123456789",
        "deviceAliasName": "My Device",
        "wifiSn": "azerty123456789azertyu",
        "state": 0,
        "weight": 0,
        "temp": None,
        "icon": None,
        "vpp": False,
        "master": 0,
        "type": 1,
        "deviceSn": "SHC000000000000001",
        "agentId": "",
        "lon": 0.0,
        "lat": 0.0,
        "category": None,
        "model": None,
        "deviceType": None,
    },
]

Source code in src/ecactus/_client.py
def get_all_devices(self) -> JSON:
    """Get a list of all the devices.

    Returns:
        A list of devices. Example:
            ``` py
            [
                {
                    "deviceId": "1234567890123456789",
                    "deviceAliasName": "My Device",
                    "wifiSn": "azerty123456789azertyu",
                    "state": 0,
                    "weight": 0,
                    "temp": None,
                    "icon": None,
                    "vpp": False,
                    "master": 0,
                    "type": 1,
                    "deviceSn": "SHC000000000000001",
                    "agentId": "",
                    "lon": 0.0,
                    "lat": 0.0,
                    "category": None,
                    "model": None,
                    "deviceType": None,
                },
            ]
            ```

    """
    logger.info("Get devices for every homes")
    return self._get("/api/client/home/device/list")

get_today_device_data(device_id)

Get power metrics of the current day until now.

Parameters:

Name Type Description Default
device_id str

The device ID to get power metrics for.

required

Returns:

Type Description
JSON

Multiple metrics of the current day. Example:

{
    "solarPowerDps": {
        "946685100": 0.0,
        "946685400": 0.0,
        ...
        "946733700": 0.0,
    },
    "batteryPowerDps": {...},
    "gridPowerDps": {...},
    "meterPowerDps": {...},
    "homePowerDps": {...},
    "epsPowerDps": {...},
}

Source code in src/ecactus/_client.py
def get_today_device_data(self, device_id: str) -> JSON:
    """Get power metrics of the current day until now.

    Args:
        device_id (str): The device ID to get power metrics for.

    Returns:
        Multiple metrics of the current day. Example:
            ``` py
            {
                "solarPowerDps": {
                    "946685100": 0.0,
                    "946685400": 0.0,
                    ...
                    "946733700": 0.0,
                },
                "batteryPowerDps": {...},
                "gridPowerDps": {...},
                "meterPowerDps": {...},
                "homePowerDps": {...},
                "epsPowerDps": {...},
            }
            ```

    """
    logger.info("Get current day data for device %s", device_id)
    return self._post(
        "/api/client/home/now/device/realtime", payload={"deviceId": device_id}
    )

get_realtime_home_data(home_id)

Get current power for the home.

Parameters:

Name Type Description Default
home_id str

The home ID to get current power for.

required

Returns:

Type Description
JSON

Power data. Example:

{
    "batteryPower": 0,
    "epsPower": 0,
    "gridPower": 23,
    "homePower": 1118,
    "meterPower": 1118,
    "solarPower": 0,
    "chargePower": 0,
    "batterySocList": [
        {
            "deviceSn": "SHC000000000000001",
            "batterySoc": 0.0,
            "sysRunMode": 1,
            "isExistSolar": True,
            "sysPowerConfig": 3,
        }
    ],
}

Source code in src/ecactus/_client.py
def get_realtime_home_data(self, home_id: str) -> JSON:
    """Get current power for the home.

    Args:
        home_id (str): The home ID to get current power for.

    Returns:
        Power data. Example:
            ``` py
            {
                "batteryPower": 0,
                "epsPower": 0,
                "gridPower": 23,
                "homePower": 1118,
                "meterPower": 1118,
                "solarPower": 0,
                "chargePower": 0,
                "batterySocList": [
                    {
                        "deviceSn": "SHC000000000000001",
                        "batterySoc": 0.0,
                        "sysRunMode": 1,
                        "isExistSolar": True,
                        "sysPowerConfig": 3,
                    }
                ],
            }
            ```

    """
    logger.info("Get realtime data for home %s", home_id)
    return self._get(
        "/api/client/v2/home/device/runData", payload={"homeId": home_id}
    )

get_realtime_device_data(device_id)

Get current power for a device.

Parameters:

Name Type Description Default
device_id str

The device ID to get current power for.

required

Returns:

Type Description
JSON

Power data. Example (without solar production):

{
    "batterySoc": 0,
    "batteryPower": 0,
    "epsPower": 0,
    "gridPower": 0,
    "homePower": 3581,
    "meterPower": 3581,
    "solarPower": 0,
    "sysRunMode": 0,
    "isExistSolar": true,
    "sysPowerConfig": 3,
}
Example when all solar production is used by home:
{'batterySoc': 0.0, 'batteryPower': 0, 'epsPower': 0, 'gridPower': 2479, 'homePower': 3674, 'meterPower': 1102, 'solarPower': 2572, 'sysRunMode': 1, 'isExistSolar': True, 'sysPowerConfig': 3}
# Home 3674 (homePower)
# PV -> Home 2572 (solarPower)
# Grid -> Home 1102 (meterPower)
# gridPower (could be related to operating mode with % reserved SOC for grid connection)
Example when solar over production is injected to the grid:
{'batterySoc': 0.0, 'batteryPower': 0, 'epsPower': 0, 'gridPower': 4194, 'homePower': 3798, 'meterPower': -650, 'solarPower': 4448, 'sysRunMode': 1, 'isExistSolar': True, 'sysPowerConfig': 3}
# Home 3798
# PV -> Home 4448
# Home -> Grid 650
# gridPower (could be related to operating mode with % reserved SOC for grid connection)

Source code in src/ecactus/_client.py
def get_realtime_device_data(self, device_id: str) -> JSON:
    """Get current power for a device.

    Args:
        device_id (str): The device ID to get current power for.

    Returns:
        Power data. Example (without solar production):
            ``` py
            {
                "batterySoc": 0,
                "batteryPower": 0,
                "epsPower": 0,
                "gridPower": 0,
                "homePower": 3581,
                "meterPower": 3581,
                "solarPower": 0,
                "sysRunMode": 0,
                "isExistSolar": true,
                "sysPowerConfig": 3,
            }
            ```
            Example when all solar production is used by home:
            ``` py
            {'batterySoc': 0.0, 'batteryPower': 0, 'epsPower': 0, 'gridPower': 2479, 'homePower': 3674, 'meterPower': 1102, 'solarPower': 2572, 'sysRunMode': 1, 'isExistSolar': True, 'sysPowerConfig': 3}
            # Home 3674 (homePower)
            # PV -> Home 2572 (solarPower)
            # Grid -> Home 1102 (meterPower)
            # gridPower (could be related to operating mode with % reserved SOC for grid connection)
            ```
            Example when solar over production is injected to the grid:
            ``` py
            {'batterySoc': 0.0, 'batteryPower': 0, 'epsPower': 0, 'gridPower': 4194, 'homePower': 3798, 'meterPower': -650, 'solarPower': 4448, 'sysRunMode': 1, 'isExistSolar': True, 'sysPowerConfig': 3}
            # Home 3798
            # PV -> Home 4448
            # Home -> Grid 650
            # gridPower (could be related to operating mode with % reserved SOC for grid connection)
            ```

    """
    logger.info("Get realtime data for device %s", device_id)
    return self._post(
        "/api/client/home/now/device/runData", payload={"deviceId": device_id}
    )

get_history(device_id, start_date, period_type)

Get aggregated energy for a period.

Parameters:

Name Type Description Default
device_id str

The device ID to get history for.

required
start_date datetime

The start date.

required
period_type int

Possible value:

  • 0: daily values of the calendar month corresponding to start_date
  • 1: today daily values (start_date is ignored) (?)
  • 2: daily values of the current month (start_date is ignored)
  • 3: same than 2 ?
  • 4: total for the current month (start_date is ignored)
required

Returns:

Type Description
JSON

Data and metrics corresponding to the defined period. Example:

{
    "energyConsumption": 1221.2,
    "solarPercent": 47.0,
    "homeEnergyDps": {
        "1733112000": 39.6,
        "1733198400": 68.1,
        "1733284800": 75.3,
        ...
        "1735707599": 41.3,
    },
}

Source code in src/ecactus/_client.py
def get_history(
    self, device_id: str, start_date: datetime, period_type: int
) -> JSON:
    """Get aggregated energy for a period.

    Args:
        device_id (str): The device ID to get history for.
        start_date (datetime): The start date.
        period_type (int): Possible value:

            - `0`: daily values of the calendar month corresponding to `start_date`
            - `1`: today daily values (`start_date` is ignored) (?)
            - `2`: daily values of the current month (`start_date` is ignored)
            - `3`: same than 2 ?
            - `4`: total for the current month (`start_date` is ignored)

    Returns:
        Data and metrics corresponding to the defined period. Example:
            ``` py
            {
                "energyConsumption": 1221.2,
                "solarPercent": 47.0,
                "homeEnergyDps": {
                    "1733112000": 39.6,
                    "1733198400": 68.1,
                    "1733284800": 75.3,
                    ...
                    "1735707599": 41.3,
                },
            }
            ```

    """
    logger.info("Get history for device %s", device_id)
    start_ts = int(start_date.timestamp())
    return self._post(
        "/api/client/home/history/home",
        payload={
            "deviceId": device_id,
            "timestamp": start_ts,
            "periodType": period_type,
        },
    )

get_insight(device_id, start_date, period_type)

Get energy metrics and statistics of a device for a period.

Parameters:

Name Type Description Default
device_id str

The device ID to get data for.

required
start_date datetime

The start date.

required
period_type int

Possible value:

  • 0: 5-minute power measurement for the calendar day corresponding to start_date (insightConsumptionDataDto is None)
  • 1: (not implemented)
  • 2: daily energy for the calendar month corresponding to start_date (deviceRealtimeDto is None)
  • 3: (not implemented)
  • 4: monthly energy for the calendar year corresponding to start_date (deviceRealtimeDto is None)
  • 5: yearly energy, start_date is ignored (?) (deviceRealtimeDto is None)
required

Returns:

Type Description
JSON

Statistics and metrics corresponding to the defined period. Example:

{
    "selfPowered": 0,
    "deviceRealtimeDto": {
        "solarPowerDps": {
            "1732129500": 0.0,
            "1732129800": 0.0,
            ...
            "1732132800": 0.0,
        },
        "batteryPowerDps": {...},
        "gridPowerDps": {...},
        "meterPowerDps": {...},
        "homePowerDps": {...},
        "epsPowerDps": {...},
    },
    "deviceStatisticsDto": {
        "consumptionEnergy": 0.0,
        "fromBattery": 0.0,
        "toBattery": 0.0,
        "fromGrid": 0.0,
        "toGrid": 0.0,
        "fromSolar": 0.0,
        "eps": 0.0,
    },
    "insightConsumptionDataDto": {
        "fromBatteryDps": {
            "1733976000": 0.0,
            "1733889600": 0.0,
            ...
            "1734062400": 0.0,
        },
        "toBatteryDps": {...},
        "fromGridDps": {...},
        "toGridDps": {...},
        "fromSolarDps": {...},
        "homeEnergyDps": {...},
        "epsDps": {...},
        "selfPoweredDps": {...},
    },
}

Source code in src/ecactus/_client.py
def get_insight(
    self, device_id: str, start_date: datetime, period_type: int
) -> JSON:
    """Get energy metrics and statistics of a device for a period.

    Args:
        device_id (str): The device ID to get data for.
        start_date (datetime): The start date.
        period_type (int): Possible value:

            - `0`: 5-minute power measurement for the calendar day corresponding to `start_date` (`insightConsumptionDataDto` is `None`)
            - `1`: (not implemented)
            - `2`: daily energy for the calendar month corresponding to `start_date` (`deviceRealtimeDto` is `None`)
            - `3`: (not implemented)
            - `4`: monthly energy for the calendar year corresponding to `start_date` (`deviceRealtimeDto` is `None`)
            - `5`: yearly energy, `start_date` is ignored (?) (`deviceRealtimeDto` is `None`)

    Returns:
        Statistics and metrics corresponding to the defined period. Example:
            ``` py
            {
                "selfPowered": 0,
                "deviceRealtimeDto": {
                    "solarPowerDps": {
                        "1732129500": 0.0,
                        "1732129800": 0.0,
                        ...
                        "1732132800": 0.0,
                    },
                    "batteryPowerDps": {...},
                    "gridPowerDps": {...},
                    "meterPowerDps": {...},
                    "homePowerDps": {...},
                    "epsPowerDps": {...},
                },
                "deviceStatisticsDto": {
                    "consumptionEnergy": 0.0,
                    "fromBattery": 0.0,
                    "toBattery": 0.0,
                    "fromGrid": 0.0,
                    "toGrid": 0.0,
                    "fromSolar": 0.0,
                    "eps": 0.0,
                },
                "insightConsumptionDataDto": {
                    "fromBatteryDps": {
                        "1733976000": 0.0,
                        "1733889600": 0.0,
                        ...
                        "1734062400": 0.0,
                    },
                    "toBatteryDps": {...},
                    "fromGridDps": {...},
                    "toGridDps": {...},
                    "fromSolarDps": {...},
                    "homeEnergyDps": {...},
                    "epsDps": {...},
                    "selfPoweredDps": {...},
                },
            }
            ```

    """
    logger.info("Get insight for device %s", device_id)
    start_ts = int(start_date.timestamp() * 1000)  # timestamp in milliseconds
    return self._post(
        "/api/client/v2/device/three/device/insight",
        payload={
            "deviceId": device_id,
            "timestamp": start_ts,
            "periodType": period_type,
        },
    )

ApiResponseError(code, message)

Raised when the API returns a non-successful response.

Source code in src/ecactus/_exceptions.py
def __init__(self, code: int, message: str) -> None:
    self.code = code
    self.message = message
    super().__init__(f"API call failed: {code} {message}")

HttpError(status_code, message)

Raised when an HTTP error occurs while making an API request.

Source code in src/ecactus/_exceptions.py
def __init__(self, status_code: int, message: str) -> None:
    self.status_code = status_code
    self.message = message
    super().__init__(f"HTTP error: {status_code} {message}")

InvalidJsonError()

Raised when the API returns invalid JSON.

Source code in src/ecactus/_exceptions.py
def __init__(self) -> None:
    super().__init__("Invalid JSON")