Esempio n. 1
0
    async def async_login(cls,
                          email: str,
                          password: str,
                          set_env_var: bool = False,
                          creds_env_var_name: str = '__MEROSS_CREDS',
                          *args,
                          **kwargs) -> MerossCloudCreds:
        """
        Performs the login against the Meross HTTP endpoint.
        This api returns a MerossCloudCreds object, which contains a token.
        Be cautious when invoking this API: asking for too many tokens as the Meross HTTP API might refuse
        to issue more tokens. Instead, you should keep using the same issued token when possible, possibly
        storing it across sessions. When you are done using a specific token, be sure to invoke logout
        to invalidate it.

        :param email: Meross account email
        :param password: Meross account password

        :return: a `MerossCloudCreds` object
        """
        data = {"email": email, "password": password}
        response_data = await cls._async_authenticated_post(
            _LOGIN_URL, params_data=data, mask_params_in_log=True)
        creds = MerossCloudCreds(token=response_data["token"],
                                 key=response_data["key"],
                                 user_id=response_data["userid"],
                                 user_email=response_data["email"],
                                 issued_on=datetime.utcnow())
        if set_env_var:
            os.environ[creds_env_var_name] = base64.b64encode(
                creds.to_json().encode("utf8")).decode("utf8")
        return creds
Esempio n. 2
0
async def async_get_client() -> Tuple[MerossHttpClient, bool]:
    if CREDS is not None:
        _LOGGER.info("Found cached credentials. Using them.")
        jsoncreds = base64.b64decode(CREDS)
        creds = MerossCloudCreds.from_json(jsoncreds)
        return await MerossHttpClient.async_from_cloud_creds(creds), False
    else:
        _LOGGER.info("Using username-password credentials")
        return await MerossHttpClient.async_from_user_password(
            email=EMAIL, password=PASSWORD), True
Esempio n. 3
0
        async def execute(method, *args, **kwargs):
            b64creds = os.getenv("__MEROSS_CREDS", None)
            if b64creds is None:
                raise ValueError(
                    "This method requires __MEROSS_CREDS env variable set. "
                    "Please invoke auth login first.")

            jsoncreds = base64.b64decode(b64creds)
            creds = MerossCloudCreds.from_json(jsoncreds)
            client = await MerossHttpClient.async_from_cloud_creds(creds)
            m = getattr(client, method)
            return await m(*args, **kwargs)
Esempio n. 4
0
    async def async_login(cls, email: str, password: str) -> MerossCloudCreds:
        """
        Performs the login against the Meross HTTP endpoint.
        This api returns a MerossCloudCreds object, which contains a token.
        Be cautious when invoking this API: asking for too many tokens as the Meross HTTP API might refuse
        to issue more tokens. Instead, you should keep using the same issued token when possible, possibly
        storing it across sessions. When you are done using a specific token, be sure to invoke logout
        to invalidate it.

        :param email: Meross account email
        :param password: Meross account password

        :return:
        """
        data = {"email": email, "password": password}
        response_data = await cls._async_authenticated_post(_LOGIN_URL,
                                                            params_data=data)
        creds = MerossCloudCreds(token=response_data["token"],
                                 key=response_data["key"],
                                 user_id=response_data["userid"],
                                 user_email=response_data["email"],
                                 issued_on=datetime.utcnow())
        return creds
Esempio n. 5
0
    async def async_login(cls,
                          email: str,
                          password: str,
                          creds_env_var_name: str = '__MEROSS_CREDS',
                          api_base_url: str = DEFAULT_MEROSS_HTTP_API,
                          http_proxy: str = None,
                          ua_header: str = _DEFAULT_UA_HEADER,
                          app_type: str = _DEFAULT_APP_TYPE,
                          app_version: str = _MODULE_VERSION,
                          log_identifier: str = _DEFAULT_LOG_IDENTIFIER,
                          stats_counter: HttpStatsCounter = None,
                          *args,
                          **kwargs) -> MerossCloudCreds:
        """
        Performs the login against the Meross HTTP endpoint.
        This api returns a MerossCloudCreds object, which contains a token.
        Be cautious when invoking this API: asking for too many tokens as the Meross HTTP API might refuse
        to issue more tokens. Instead, you should keep using the same issued token when possible, possibly
        storing it across sessions. When you are done using a specific token, be sure to invoke logout
        to invalidate it.

        :param email: Meross account email
        :param password: Meross account password
        :param creds_env_var_name: If set, makes thi method store the obtained login-credentials in the specified env variable.
        indicate which env variables stores such credentials
        :param api_base_url: Meross API base url
        :param http_proxy: Optional http proxy to use when to performing the request
        :param ua_header: User Agent header to use when issuing the HTTP request
        :param stats_counter: Stats counter object
        :param app_type: App Type header parameter to use
        :param app_version:  App Version header parameter to use

        :return: a `MerossCloudCreds` object
        """
        data = {
            "email": email,
            "password": password,
            "mobileInfo": {
                "deviceModel": platform.machine(),
                "mobileOsVersion": platform.version(),
                "mobileOs": platform.system(),
                "uuid": log_identifier,
                "carrier": ""
            }
        }
        url = _LOGIN_URL % api_base_url
        response_data = await MerossHttpClient._async_authenticated_post(
            url=url,
            params_data=data,
            mask_params_in_log=True,
            http_proxy=http_proxy,
            ua_header=ua_header,
            app_type=app_type,
            app_version=app_version,
            stats_counter=stats_counter)
        creds = MerossCloudCreds(token=response_data["token"],
                                 key=response_data["key"],
                                 user_id=response_data["userid"],
                                 user_email=response_data["email"],
                                 issued_on=datetime.utcnow())
        if creds_env_var_name is not None:
            os.environ[creds_env_var_name] = base64.b64encode(
                creds.to_json().encode("utf8")).decode("utf8")
        return creds
Esempio n. 6
0
async def async_setup_entry(hass: HomeAssistantType, config_entry):
    """
    This class is called by the HomeAssistant framework when a configuration entry is provided.
    For us, the configuration entry is the username-password credentials that the user
    needs to access the Meross cloud.
    """

    # Retrieve the stored credentials from config-flow
    email = config_entry.data.get(CONF_USERNAME)
    password = config_entry.data.get(CONF_PASSWORD)
    str_creds = config_entry.data.get(CONF_STORED_CREDS)
    rate_limit_per_second = config_entry.data.get(CONF_RATE_LIMIT_PER_SECOND,
                                                  2)
    rate_limit_max_tokens = config_entry.data.get(CONF_RATE_LIMIT_MAX_TOKENS,
                                                  10)

    creds = None
    if str_creds is not None:
        issued_on = datetime.fromisoformat(str_creds.get('issued_on'))
        creds = MerossCloudCreds(token=str_creds.get('token'),
                                 key=str_creds.get('key'),
                                 user_id=str_creds.get('user_id'),
                                 user_email=str_creds.get('user_email'),
                                 issued_on=issued_on)
        _LOGGER.info(
            f"Found application token issued on {creds.issued_on} to {creds.user_email}. Using it."
        )

    try:
        client, http_devices, creds_renewed = await get_or_renew_creds(
            email=email, password=password, stored_creds=creds)
        if creds_renewed:
            creds = client.cloud_credentials
            hass.config_entries.async_update_entry(
                entry=config_entry,
                data={
                    CONF_USERNAME: email,
                    CONF_PASSWORD: password,
                    CONF_STORED_CREDS: {
                        'token': creds.token,
                        'key': creds.key,
                        'user_id': creds.user_id,
                        'user_email': creds.user_email,
                        'issued_on': creds.issued_on.isoformat()
                    }
                })

        manager = MerossManager(http_client=client, auto_reconnect=True)
        # Setup the rate limiter
        limiter = RateLimiter(rate=rate_limit_per_second,
                              max_tokens=rate_limit_max_tokens)

        hass.data[PLATFORM] = {}
        hass.data[PLATFORM][MANAGER] = manager
        hass.data[PLATFORM]["ADDED_ENTITIES_IDS"] = set()
        hass.data[PLATFORM][LIMITER] = limiter

        # Keep a registry of added sensors
        # TODO: Do the same for other platforms?
        hass.data[PLATFORM][HA_SENSOR] = dict()

        print_startup_message(http_devices=http_devices)
        _LOGGER.info("Starting meross manager")
        await manager.async_init()

        # Perform the first discovery
        _LOGGER.info("Discovering Meross devices...")
        await manager.async_device_discovery()

        for platform in MEROSS_COMPONENTS:
            hass.async_create_task(
                hass.config_entries.async_forward_entry_setup(
                    config_entry, platform))
        """
        async def trigger_discovery(time):
            await run_discovery(manager=manager)

        async_track_time_interval(hass=hass, action=trigger_discovery, interval=timedelta(seconds=30))
        """
        return True

    except TooManyTokensException:
        msg = "Too many tokens have been issued to this account. " \
              "The Remote API refused to issue a new one."
        notify_error(hass, "http_connection", "Meross Cloud", msg)
        log_exception(msg, logger=_LOGGER)
        raise ConfigEntryNotReady()

    except UnauthorizedException:
        msg = "Your Meross login credentials are invalid or the network could not be reached at the moment."
        notify_error(
            hass, "http_connection", "Meross Cloud",
            "Could not connect to the Meross cloud. Please check"
            " your internet connection and your Meross credentials")
        log_exception(msg, logger=_LOGGER)
        return False

    except Exception as e:
        log_exception(
            "An exception occurred while setting up the meross manager. Setup will be retried...",
            logger=_LOGGER)
        raise ConfigEntryNotReady()