SAIC-iSmart-API / saic-python-client-ng

A Python package for interacting with the MG iSMART API.
MIT License
6 stars 2 forks source link

[HELP REQ] Home Assistant Custom Integration #6

Closed ad-ha closed 3 months ago

ad-ha commented 3 months ago

Hi,

I appreciate a lot the work that you guys are doing and everything that you have been able to achieve with this client.

I bought a Marvel R a couple of weeks ago and wanted to have integrated into my HA, as I had my previous EV6. Looking into the web I found your repository and the MQTT addon. As I am not really into MQTT, I figured that I could use your client as access to the API and create a custom integration (see more here: https://github.com/ad-ha/mg-saic-ha/), which I believe would be easier for most HA users (at least it would be for me)

As I'm progressing with the integration and the code, I made several changes to the initial release and am trying to add new sensors and info.

I've been able, so far, to get all the below working, through vehicle_list and get_vehicle_status, from here saic_ismart_client_ng/api/vehicle/__init__.py:

image

The issue comes when I start trying to add Charging details, including get_vehicle_charging_status from your API. No matter what I try, I always end up with the same response from the server:

ERROR (MainThread) [custom_components.mg_saic] Error retrieving charging information: 'SaicApi' object has no attribute 'sms_delivery_delay'
WARNING (MainThread) [custom_components.mg_saic] Charging information is not available at setup.

This is the code that I currently have on api.py:

# api.py

from saic_ismart_client_ng import SaicApi
from saic_ismart_client_ng.model import SaicApiConfiguration
from saic_ismart_client_ng.api.vehicle_charging import SaicVehicleChargingApi
from .const import LOGGER

class SAICMGAPIClient:
    def __init__(self, username, password, vin=None, region="eu", sms_delay=2):
        self.username = username
        self.password = password
        self.vin = vin
        self.region = region
        self.sms_delay = sms_delay
        self.saic_api = None
        self.charging_api = None

    async def login(self):
        """Authenticate with the API."""
        config = SaicApiConfiguration(
            username=self.username,
            password=self.password,
            region=self.region,
            sms_delivery_delay=self.sms_delay,  # Ensure it is initialized here
        )
        self.saic_api = SaicApi(config)
        await self.saic_api.login()
        LOGGER.info("Successfully logged in to MG SAIC API")
        self.charging_api = SaicVehicleChargingApi(self.saic_api)

    async def get_vehicle_info(self):
        """Retrieve vehicle information."""
        if not self.saic_api:
            await self.login()
        vehicle_list_resp = await self.saic_api.vehicle_list()
        vehicles = vehicle_list_resp.vinList
        self.vin = vehicles[0].vin if vehicles else None
        return vehicles

    async def get_vehicle_status(self):
        """Retrieve vehicle status."""
        if not self.saic_api or not self.vin:
            await self.login()
            await self.get_vehicle_info()
        try:
            vehicle_status = await self.saic_api.get_vehicle_status(self.vin)
            return vehicle_status
        except Exception as e:
            LOGGER.error("Error retrieving vehicle status: %s", e)
            return None

    async def get_charging_info(self):
        """Retrieve charging information."""
        if not self.saic_api or not self.vin:
            await self.login()
            await self.get_vehicle_info()

        try:
            charging_info = await self.charging_api.get_vehicle_charging_status(
                self.vin
            )
            LOGGER.debug("Charging information retrieved: %s", charging_info)
            return charging_info
        except Exception as e:
            LOGGER.error("Error retrieving charging information: %s", e)
            return None

    async def control_charging(self, action):
        """Control vehicle charging."""
        if not self.saic_api or not self.vin:
            await self.login()
            await self.get_vehicle_info()

        try:
            await self.charging_api.send_vehicle_charging_control(self.vin, action)
        except Exception as e:
            LOGGER.error("Error controlling charging: %s", e)

    async def close(self):
        """Close the client session."""
        await self.saic_api.close()

And BEFORE all changes and tries to solve the issue with the sms_delivert_delay, the code looked like this:

# api.py

from saic_ismart_client_ng import SaicApi
from saic_ismart_client_ng.model import SaicApiConfiguration
from saic_ismart_client_ng.api.vehicle_charging import SaicVehicleChargingApi
from .const import LOGGER

class SAICMGAPIClient:
    def __init__(self, username, password, vin=None):
        self.username = username
        self.password = password
        self.vin = vin
        self.saic_api = None
        self.charging_api = None

    async def login(self):
        """Authenticate with the API."""
        config = SaicApiConfiguration(username=self.username, password=self.password)
        self.saic_api = SaicApi(config)
        await self.saic_api.login()
        LOGGER.info("Successfully logged in to MG SAIC API")
        self.charging_api = SaicVehicleChargingApi(self.saic_api)

    async def get_vehicle_info(self):
        """Retrieve vehicle information."""
        if not self.saic_api:
            await self.login()
        vehicle_list_resp = await self.saic_api.vehicle_list()
        vehicles = vehicle_list_resp.vinList
        self.vin = vehicles[0].vin if vehicles else None
        return vehicles

    async def get_vehicle_status(self):
        """Retrieve vehicle status."""
        if not self.saic_api or not self.vin:
            await self.login()
            await self.get_vehicle_info()
        try:
            vehicle_status = await self.saic_api.get_vehicle_status(self.vin)
            return vehicle_status
        except Exception as e:
            LOGGER.error("Error retrieving vehicle status: %s", e)
            return None

    async def get_charging_info(self):
        """Retrieve charging information."""
        if not self.saic_api or not self.vin:
            await self.login()
            await self.get_vehicle_info()

        try:
            charging_info = await self.charging_api.get_vehicle_charging_status(
                self.vin
            )
            LOGGER.debug("Charging information retrieved: %s", charging_info)
            return charging_info
        except Exception as e:
            LOGGER.error("Error retrieving charging information: %s", e)
            return None

    async def control_charging(self, action):
        """Control vehicle charging."""
        if not self.saic_api or not self.vin:
            await self.login()
            await self.get_vehicle_info()

        try:
            await self.charging_api.send_vehicle_charging_control(self.vin, action)
        except Exception as e:
            LOGGER.error("Error controlling charging: %s", e)

    async def close(self):
        """Close the client session."""
        await self.saic_api.close()

Can you guys be so kind and point what I might be missing and how may I address the issue?

Let me know if you may also need some more info or bits of updated code.

Thanks a lot for your time and for being working on this.

Cheers

ad-ha commented 3 months ago

Adding into this, my idea is to somehow replicate the MQTT Gateway and be able to add most sensors, plus service controls (HVAC, Charging, etc..).

I also have issues when trying to get the config_flow to access the account via phone number. Always get an error mentioning that the account is not registered, so the only way I got it working so far is by logging in with email.

More info also in here: https://community.home-assistant.io/t/mg-saic-custom-integration/754590

ad-ha commented 3 months ago

Hi everyone,

So I've been able to sort this issue out and get it all working. Thanks anyway.

Now I'm working on implementing more sensors and features, but I believe that I got the mechanics behind the API and will figure it out along the way.