Ejemplo n.º 1
0
def log_exception(ex, domain, config, hass=None):
    """Generate log exception for config validation."""
    message = 'Invalid config for [{}]: '.format(domain)
    if hass is not None:
        _PERSISTENT_VALIDATION.add(domain)
        message = ('The following platforms contain invalid configuration:  ' +
                   ', '.join(list(_PERSISTENT_VALIDATION)) +
                   '  (please check your configuration)')
        persistent_notification.create(hass, message, 'Invalid config',
                                       'invalid_config')

    if 'extra keys not allowed' in ex.error_message:
        message += '[{}] is an invalid option for [{}]. Check: {}->{}.'\
                   .format(ex.path[-1], domain, domain,
                           '->'.join('%s' % m for m in ex.path))
    else:
        message += '{}.'.format(humanize_error(config, ex))

    if hasattr(config, '__line__'):
        message += " (See {}:{})".format(config.__config_file__,
                                         config.__line__ or '?')

    if domain != 'homeassistant':
        message += (' Please check the docs at '
                    'https://home-assistant.io/components/{}/'.format(domain))

    _LOGGER.error(message)
Ejemplo n.º 2
0
 def check_auth(self):
     rdt = self.request_miot_api('v2/device/blt_get_beaconkey', {
         'did': 'blt.0.14cj9o6b4eg00',
         'pdid': 1,
     }) or {}
     eno = rdt.get('code', 0)
     if eno == 3:
         # auth err
         persistent_notification.create(
             self.hass,
             f'Xiaomi cloud: {self.user_id} auth failed, '
             'Please update option for this integration to refresh token.\n'
             f'小米账号:{self.user_id} 登陆失效,请重新保存集成选项以更新登陆信息。',
             'Xiaomi Miot Warning',
             f'xiaomi-miot-auth-warning-{self.user_id}',
         )
         _LOGGER.error(
             'Xiaomi cloud: %s auth failed, Please update option for this integration to refresh token.',
             self.user_id,
         )
         self.user_id = None
         self.service_token = None
         self.ssecurity = None
         if self.login():
             return True
         _LOGGER.warning('Retry login xiaomi cloud failed: %s', self.username)
     return False
Ejemplo n.º 3
0
    def test_create_notification_id(self):
        """Ensure overwrites existing notification with same id."""
        notifications = self.hass.data[pn.DOMAIN]["notifications"]
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0
        assert len(notifications) == 0

        pn.create(self.hass, "test", notification_id="Beer 2")
        self.hass.block_till_done()

        assert len(self.hass.states.entity_ids()) == 1
        assert len(notifications) == 1

        entity_id = "persistent_notification.beer_2"
        state = self.hass.states.get(entity_id)
        assert state.attributes.get("message") == "test"

        notification = notifications.get(entity_id)
        assert notification["message"] == "test"
        assert notification["title"] is None

        pn.create(self.hass, "test 2", notification_id="Beer 2")
        self.hass.block_till_done()

        # We should have overwritten old one
        assert len(self.hass.states.entity_ids()) == 1
        state = self.hass.states.get(entity_id)
        assert state.attributes.get("message") == "test 2"

        notification = notifications.get(entity_id)
        assert notification["message"] == "test 2"
        notifications.clear()
Ejemplo n.º 4
0
 def _update_data(self) -> PowerwallData:
     """Fetch data from API endpoint."""
     _LOGGER.debug("Updating data")
     for attempt in range(2):
         try:
             if attempt == 1:
                 self._recreate_powerwall_login()
             data = _fetch_powerwall_data(self.power_wall)
         except PowerwallUnreachableError as err:
             raise UpdateFailed(
                 "Unable to fetch data from powerwall") from err
         except MissingAttributeError as err:
             _LOGGER.error("The powerwall api has changed: %s", str(err))
             # The error might include some important information about what exactly changed.
             persistent_notification.create(self.hass,
                                            API_CHANGED_ERROR_BODY,
                                            API_CHANGED_TITLE)
             self.runtime_data[POWERWALL_API_CHANGED] = True
             raise UpdateFailed("The powerwall api has changed") from err
         except AccessDeniedError as err:
             if attempt == 1:
                 # failed to authenticate => the credentials must be wrong
                 raise ConfigEntryAuthFailed from err
             if self.password is None:
                 raise ConfigEntryAuthFailed from err
             _LOGGER.debug("Access denied, trying to reauthenticate")
             # there is still an attempt left to authenticate, so we continue in the loop
         except APIError as err:
             raise UpdateFailed(
                 f"Updated failed due to {err}, will retry") from err
         else:
             return data
     raise RuntimeError("unreachable")
Ejemplo n.º 5
0
    def test_create(self):
        """Test creating notification without title or notification id."""
        notifications = self.hass.data[pn.DOMAIN]["notifications"]
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0
        assert len(notifications) == 0

        pn.create(self.hass,
                  "Hello World {{ 1 + 1 }}",
                  title="{{ 1 + 1 }} beers")
        self.hass.block_till_done()

        entity_ids = self.hass.states.entity_ids(pn.DOMAIN)
        assert len(entity_ids) == 1
        assert len(notifications) == 1

        state = self.hass.states.get(entity_ids[0])
        assert state.state == pn.STATE
        assert state.attributes.get("message") == "Hello World 2"
        assert state.attributes.get("title") == "2 beers"

        notification = notifications.get(entity_ids[0])
        assert notification["status"] == pn.STATUS_UNREAD
        assert notification["message"] == "Hello World 2"
        assert notification["title"] == "2 beers"
        assert notification["created_at"] is not None
        notifications.clear()
Ejemplo n.º 6
0
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Melnor RainCloud component."""
    conf = config[DOMAIN]
    username = conf.get(CONF_USERNAME)
    password = conf.get(CONF_PASSWORD)
    scan_interval = conf.get(CONF_SCAN_INTERVAL)

    try:
        raincloud = RainCloudy(username=username, password=password)
        if not raincloud.is_connected:
            raise HTTPError
        hass.data[DATA_RAINCLOUD] = RainCloudHub(raincloud)
    except (ConnectTimeout, HTTPError) as ex:
        _LOGGER.error("Unable to connect to Rain Cloud service: %s", str(ex))
        persistent_notification.create(
            hass,
            f"Error: {ex}<br />"
            "You will need to restart hass after fixing.",
            title=NOTIFICATION_TITLE,
            notification_id=NOTIFICATION_ID,
        )
        return False

    def hub_refresh(event_time):
        """Call Raincloud hub to refresh information."""
        _LOGGER.debug("Updating RainCloud Hub component")
        hass.data[DATA_RAINCLOUD].data.update()
        dispatcher_send(hass, SIGNAL_UPDATE_RAINCLOUD)

    # Call the Raincloud API to refresh updates
    track_time_interval(hass, hub_refresh, scan_interval)

    return True
Ejemplo n.º 7
0
 def device_need_refresh(**kwargs):
     device = kwargs['device']
     persistent_notification.create(hass,
                                    ('The ZiGate device {} needs some'
                                     ' refresh (missing important'
                                     ' information)').format(device.addr),
                                    title='ZiGate')
Ejemplo n.º 8
0
def taskStatus(hass, task, command):
    """Check status of running task."""
    from time import sleep
    from homeassistant.components import persistent_notification

    # wait while task is in progress
    state = vim.TaskInfo.State
    while task.info.state not in [state.success, state.error]:
        if task.info.progress is not None:
            _LOGGER.debug("Task %s progress %s", task.info.eventChainId,
                          task.info.progress)

        sleep(2)

    # output task status once complete
    if task.info.state == "success":
        _LOGGER.info("Sending command to '%s' complete", task.info.entityName)

        message = "Complete - " + command
        persistent_notification.create(hass, message, "ESXi Stats")
    if task.info.state == "error":
        _LOGGER.info("Sending command to '%s' failed", task.info.entityName)
        _LOGGER.info(task.info.error.msg)

        message = "Failed - " + command + "\n\n"
        message += task.info.error.msg
        persistent_notification.create(hass, message, "ESXi Stats")
Ejemplo n.º 9
0
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up Lupusec component."""
    conf = config[DOMAIN]
    username = conf[CONF_USERNAME]
    password = conf[CONF_PASSWORD]
    ip_address = conf[CONF_IP_ADDRESS]
    name = conf.get(CONF_NAME)

    try:
        hass.data[DOMAIN] = LupusecSystem(username, password, ip_address, name)
    except LupusecException as ex:
        _LOGGER.error(ex)

        persistent_notification.create(
            hass,
            f"Error: {ex}<br />You will need to restart hass after fixing.",
            title=NOTIFICATION_TITLE,
            notification_id=NOTIFICATION_ID,
        )
        return False

    for platform in LUPUSEC_PLATFORMS:
        discovery.load_platform(hass, platform, DOMAIN, {}, config)

    return True
Ejemplo n.º 10
0
 def _update_data(self) -> PowerwallData:
     """Fetch data from API endpoint."""
     _LOGGER.debug("Updating data")
     for attempt in range(2):
         try:
             if attempt == 1:
                 self._recreate_powerwall_login()
             data = _fetch_powerwall_data(self.power_wall)
         except PowerwallUnreachableError as err:
             raise UpdateFailed(
                 "Unable to fetch data from powerwall") from err
         except MissingAttributeError as err:
             _LOGGER.error("The powerwall api has changed: %s", str(err))
             # The error might include some important information about what exactly changed.
             persistent_notification.create(self.hass,
                                            API_CHANGED_ERROR_BODY,
                                            API_CHANGED_TITLE)
             self.runtime_data[POWERWALL_API_CHANGED] = True
             raise UpdateFailed("The powerwall api has changed") from err
         except AccessDeniedError as err:
             if attempt == 1:
                 self._increment_failed_logins()
                 raise ConfigEntryAuthFailed from err
             if self.password is None:
                 raise ConfigEntryAuthFailed from err
             raise UpdateFailed(
                 f"Login attempt {self.login_failed_count}/{MAX_LOGIN_FAILURES} failed, will retry: {err}"
             ) from err
         except APIError as err:
             raise UpdateFailed(
                 f"Updated failed due to {err}, will retry") from err
         else:
             self._clear_failed_logins()
             return data
     raise RuntimeError("unreachable")
Ejemplo n.º 11
0
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Establish connection to MAX! Cube."""

    if DATA_KEY not in hass.data:
        hass.data[DATA_KEY] = {}

    connection_failed = 0
    gateways = config[DOMAIN][CONF_GATEWAYS]
    for gateway in gateways:
        host = gateway[CONF_HOST]
        port = gateway[CONF_PORT]
        scan_interval = gateway[CONF_SCAN_INTERVAL].total_seconds()

        try:
            cube = MaxCube(host, port, now=now)
            hass.data[DATA_KEY][host] = MaxCubeHandle(cube, scan_interval)
        except timeout as ex:
            _LOGGER.error("Unable to connect to Max!Cube gateway: %s", str(ex))
            persistent_notification.create(
                hass,
                f"Error: {ex}<br />You will need to restart Home Assistant after fixing.",
                title=NOTIFICATION_TITLE,
                notification_id=NOTIFICATION_ID,
            )
            connection_failed += 1

    if connection_failed >= len(gateways):
        return False

    load_platform(hass, Platform.CLIMATE, DOMAIN, {}, config)
    load_platform(hass, Platform.BINARY_SENSOR, DOMAIN, {}, config)

    return True
Ejemplo n.º 12
0
async def check_xiaomi_account(hass, user_input, errors, renew_devices=False):
    dvs = []
    mic = None
    try:
        mic = await MiotCloud.from_token(hass, user_input, login=False)
        await mic.async_login()
        if not await mic.async_check_auth(False):
            raise MiCloudException('Login failed')
        await mic.async_stored_auth(mic.user_id, save=True)
        user_input['xiaomi_cloud'] = mic
        dvs = await mic.async_get_devices(renew=renew_devices) or []
        if renew_devices:
            await MiotSpec.async_get_model_type(hass,
                                                'xiaomi.miot.auto',
                                                use_remote=True)
    except (MiCloudException, MiCloudAccessDenied, Exception) as exc:
        err = f'{exc}'
        errors['base'] = 'cannot_login'
        if isinstance(exc, MiCloudAccessDenied) and mic:
            if url := mic.attrs.pop('notificationUrl'):
                err = f'The login of Xiaomi account needs security verification. [Click here]({url}) to continue!\n' \
                      f'本次登陆小米账号需要安全验证,[点击这里]({url})继续!'
                persistent_notification.create(
                    hass,
                    err,
                    f'Login to Xiaomi: {mic.username}',
                    f'{DOMAIN}-login',
                )
        if isinstance(exc, requests.exceptions.ConnectionError):
            errors['base'] = 'cannot_reach'
        elif 'ZoneInfoNotFoundError' in err:
            errors['base'] = 'tzinfo_error'
        hass.data[DOMAIN]['placeholders'] = {'tip': f'⚠️ {err}'}
        _LOGGER.error('Setup xiaomi cloud for user: %s failed: %s',
                      mic.username, exc)
Ejemplo n.º 13
0
    def _reconnect(self):
        """Reconnect on a failure."""

        self._fails += 1
        if self._fails > MAX_FAILS:
            _LOGGER.error(
                "Failed to refresh login credentials. Thread stopped")
            persistent_notification.create(
                self.hass,
                "Error:<br/>Connection to waterfurnace website failed "
                "the maximum number of times. Thread has stopped",
                title=NOTIFICATION_TITLE,
                notification_id=NOTIFICATION_ID,
            )

            self._shutdown = True
            return

        # sleep first before the reconnect attempt
        _LOGGER.debug("Sleeping for fail # %s", self._fails)
        time.sleep(self._fails * ERROR_INTERVAL.total_seconds())

        try:
            self.client.login()
            self.data = self.client.read()
        except WFException:
            _LOGGER.exception("Failed to reconnect attempt %s", self._fails)
        else:
            _LOGGER.debug("Reconnected to furnace")
            self._fails = 0
Ejemplo n.º 14
0
    def test_create(self):
        """Test creating notification without title or notification id."""
        notifications = self.hass.data[pn.DOMAIN]['notifications']
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0
        assert len(notifications) == 0

        pn.create(self.hass, 'Hello World {{ 1 + 1 }}',
                  title='{{ 1 + 1 }} beers')
        self.hass.block_till_done()

        entity_ids = self.hass.states.entity_ids(pn.DOMAIN)
        assert len(entity_ids) == 1
        assert len(notifications) == 1

        state = self.hass.states.get(entity_ids[0])
        assert state.state == pn.STATE
        assert state.attributes.get('message') == 'Hello World 2'
        assert state.attributes.get('title') == '2 beers'

        notification = notifications.get(entity_ids[0])
        assert notification['status'] == pn.STATUS_UNREAD
        assert notification['message'] == 'Hello World 2'
        assert notification['title'] == '2 beers'
        assert notification['created_at'] is not None
        notifications.clear()
Ejemplo n.º 15
0
async def async_setup_platform(hass, config, async_add_entities,
                               discovery_info=None):
    """Create the IntesisHome climate devices."""
    from pyintesishome import IntesisHome

    ihuser = config[CONF_USERNAME]
    ihpass = config[CONF_PASSWORD]

    controller = IntesisHome(ihuser, ihpass, hass.loop)

    await hass.async_add_executor_job(controller.connect)
    while not controller.is_connected and not controller.error_message:
        await asyncio.sleep(0.1)

    if controller.is_connected:
        intesis_devices = controller.get_devices().items()
        async_add_entities([IntesisAC(deviceid, device, controller)
                            for deviceid, device in intesis_devices], True)
    elif controller.error_message == "WRONG_USERNAME_PASSWORD":
        persistent_notification.create(
            hass, "Wrong username/password.", "IntesisHome", 'intesishome')
    else:
        persistent_notification.create(
            hass, controller.error_message, "IntesisHome Error", 'intesishome')

        controller.stop()
        raise PlatformNotReady()
Ejemplo n.º 16
0
 def check_auth(self, notify=False):
     rdt = self.get_user_device_data('1', 'power') or {}
     nid = f'xiaomi-miot-auth-warning-{self.user_id}'
     eno = rdt.get('code', 0)
     if eno == 3:
         # auth err
         if notify:
             persistent_notification.create(
                 self.hass,
                 f'Xiaomi cloud: {self.user_id} auth failed, '
                 'Please update option for this integration to refresh token.\n'
                 f'小米账号:{self.user_id} 登陆失效,请重新保存集成选项以更新登陆信息。',
                 'Xiaomi Miot Warning',
                 nid,
             )
             _LOGGER.error(
                 'Xiaomi cloud: %s auth failed, Please update option for this integration to refresh token.\n%s',
                 self.user_id,
                 rdt,
             )
         self.user_id = None
         self.service_token = None
         self.ssecurity = None
         if self.login():
             persistent_notification.dismiss(self.hass, nid)
             return True
         _LOGGER.warning('Retry login xiaomi cloud failed: %s', self.username)
         return False
     return True
Ejemplo n.º 17
0
async def check_xiaomi_account(hass, user_input, errors, renew_devices=False):
    dvs = []
    mic = None
    try:
        mic = await MiotCloud.from_token(hass, user_input, login=False)
        mic.login_times = 0
        await mic.async_login(captcha=user_input.get('captcha'))
        if not await mic.async_check_auth(False):
            raise MiCloudException('Login failed')
        user_input['xiaomi_cloud'] = mic
        dvs = await mic.async_get_devices(renew=renew_devices) or []
        if renew_devices:
            await MiotSpec.async_get_model_type(hass,
                                                'xiaomi.miot.auto',
                                                use_remote=True)
    except (MiCloudException, MiCloudAccessDenied, Exception) as exc:
        err = f'{exc}'
        errors['base'] = 'cannot_login'
        if isinstance(exc, MiCloudAccessDenied) and mic:
            if url := mic.attrs.pop('notificationUrl', None):
                err = f'The login of Xiaomi account needs security verification. [Click here]({url}) to continue!\n' \
                      f'本次登陆小米账号需要安全验证,[点击这里]({url})继续!'
                persistent_notification.create(
                    hass,
                    err,
                    f'Login to Xiaomi: {mic.username}',
                    f'{DOMAIN}-login',
                )
            elif url := mic.attrs.pop('captchaImg', None):
                err = f'Captcha:\n![captcha](data:image/jpeg;base64,{url})'
                user_input['xiaomi_cloud'] = mic
                user_input['captchaIck'] = mic.attrs.get('captchaIck')
Ejemplo n.º 18
0
Archivo: core.py Proyecto: jbouwh/core
    def run(self) -> None:
        """Start processing events to save."""
        current_version = self._setup_recorder()

        if current_version is None:
            self.hass.add_job(self.async_connection_failed)
            return

        self.schema_version = current_version

        schema_is_current = migration.schema_is_current(current_version)
        if schema_is_current:
            self._setup_run()
        else:
            self.migration_in_progress = True
            self.migration_is_live = migration.live_migration(current_version)

        self.hass.add_job(self.async_connection_success)

        if self.migration_is_live or schema_is_current:
            # If the migrate is live or the schema is current, we need to
            # wait for startup to complete. If its not live, we need to continue
            # on.
            self.hass.add_job(self.async_set_db_ready)
            # If shutdown happened before Home Assistant finished starting
            if self._wait_startup_or_shutdown() is SHUTDOWN_TASK:
                self.migration_in_progress = False
                # Make sure we cleanly close the run if
                # we restart before startup finishes
                self._shutdown()
                self.hass.add_job(self.async_set_db_ready)
                return

        # We wait to start the migration until startup has finished
        # since it can be cpu intensive and we do not want it to compete
        # with startup which is also cpu intensive
        if not schema_is_current:
            if self._migrate_schema_and_setup_run(current_version):
                self.schema_version = SCHEMA_VERSION
                if not self._event_listener:
                    # If the schema migration takes so long that the end
                    # queue watcher safety kicks in because MAX_QUEUE_BACKLOG
                    # is reached, we need to reinitialize the listener.
                    self.hass.add_job(self.async_initialize)
            else:
                persistent_notification.create(
                    self.hass,
                    "The database migration failed, check [the logs](/config/logs)."
                    "Database Migration Failed",
                    "recorder_database_migration",
                )
                self.hass.add_job(self.async_set_db_ready)
                self._shutdown()
                return

        self.hass.add_job(self.async_set_db_ready)

        _LOGGER.debug("Recorder processing the queue")
        self.hass.add_job(self._async_set_recorder_ready_migration_done)
        self._run_event_loop()
Ejemplo n.º 19
0
Archivo: core.py Proyecto: jbouwh/core
    def _migrate_schema_and_setup_run(self, current_version: int) -> bool:
        """Migrate schema to the latest version."""
        persistent_notification.create(
            self.hass,
            "System performance will temporarily degrade during the database upgrade. Do not power down or restart the system until the upgrade completes. Integrations that read the database, such as logbook and history, may return inconsistent results until the upgrade completes.",
            "Database upgrade in progress",
            "recorder_database_migration",
        )
        self.hass.add_job(self._async_migration_started)

        try:
            migration.migrate_schema(self, self.hass, self.engine,
                                     self.get_session, current_version)
        except exc.DatabaseError as err:
            if self._handle_database_error(err):
                return True
            _LOGGER.exception("Database error during schema migration")
            return False
        except Exception:  # pylint: disable=broad-except
            _LOGGER.exception("Error during schema migration")
            return False
        else:
            self._setup_run()
            return True
        finally:
            self.migration_in_progress = False
            persistent_notification.dismiss(self.hass,
                                            "recorder_database_migration")
Ejemplo n.º 20
0
    async def check_api_token(now):
        """Check if the current API token has expired and renew if so."""
        next_check_interval = TOKEN_CHECK_INTERVAL

        result = await noonlight_integration.check_api_token()

        if not result:
            _LOGGER.error("API token failed renewal, retrying in 3 min")
            check_api_token.fail_count += 1
            persistent_notification.create(
                hass, "Noonlight API token failed to renew {} time{}!\n"
                "Home Assistant will automatically attempt to renew the "
                "API token in 3 minutes.".format(
                    check_api_token.fail_count,
                    's' if check_api_token.fail_count > 1 else ''),
                "Noonlight Token Renewal Failure",
                NOTIFICATION_TOKEN_UPDATE_FAILURE)
            next_check_interval = timedelta(minutes=3)
        else:
            if check_api_token.fail_count > 0:
                persistent_notification.create(
                    hass, "Noonlight API token has now been "
                    "renewed successfully.", "Noonlight Token Renewal Success",
                    NOTIFICATION_TOKEN_UPDATE_SUCCESS)
            check_api_token.fail_count = 0

        async_track_point_in_utc_time(hass, check_api_token,
                                      dt_util.utcnow() + next_check_interval)
Ejemplo n.º 21
0
async def async_setup_platform(hass,
                               config,
                               async_add_entities,
                               discovery_info=None):
    hass.data.setdefault(DATA_KEY, {})
    hass.data[DOMAIN]['add_entities'][ENTITY_DOMAIN] = async_add_entities
    model = str(config.get(CONF_MODEL) or '')
    entities = []
    miot = config.get('miot_type')
    if miot:
        spec = await MiotSpec.async_from_type(hass, miot)
        for srv in spec.get_services(ENTITY_DOMAIN, 'camera_control',
                                     'video_doorbell'):
            if not spec.get_service('camera_stream_for_google_home',
                                    'camera_stream_for_amazon_alexa'):
                persistent_notification.create(
                    hass,
                    f'Your camera [**{model}**](https://miot-spec.org/miot-spec-v2/instance?type={miot}) '
                    'doesn\'t support streaming services.\n'
                    f'你的摄像机不支持流服务。\n'
                    'https://github.com/al-one/hass-xiaomi-miot/issues/60#issuecomment-819435571',
                    'Xiaomi Miot Warning',
                    f'{DATA_KEY}-warning-{model}',
                )
                continue
            cfg = {**config, 'name': f"{config.get('name')} {srv.description}"}
            entities.append(MiotCameraEntity(hass, cfg, srv))
    for entity in entities:
        hass.data[DOMAIN]['entities'][entity.unique_id] = entity
    async_add_entities(entities)
    bind_services_to_entries(hass, SERVICE_TO_METHOD)
Ejemplo n.º 22
0
    def test_create_notification_id(self):
        """Ensure overwrites existing notification with same id."""
        notifications = self.hass.data[pn.DOMAIN]['notifications']
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0
        assert len(notifications) == 0

        pn.create(self.hass, 'test', notification_id='Beer 2')
        self.hass.block_till_done()

        assert len(self.hass.states.entity_ids()) == 1
        assert len(notifications) == 1

        entity_id = 'persistent_notification.beer_2'
        state = self.hass.states.get(entity_id)
        assert state.attributes.get('message') == 'test'

        notification = notifications.get(entity_id)
        assert notification['message'] == 'test'
        assert notification['title'] is None

        pn.create(self.hass, 'test 2', notification_id='Beer 2')
        self.hass.block_till_done()

        # We should have overwritten old one
        assert len(self.hass.states.entity_ids()) == 1
        state = self.hass.states.get(entity_id)
        assert state.attributes.get('message') == 'test 2'

        notification = notifications.get(entity_id)
        assert notification['message'] == 'test 2'
        notifications.clear()
Ejemplo n.º 23
0
def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Aladdin Connect platform."""

    username: str = config[CONF_USERNAME]
    password: str = config[CONF_PASSWORD]
    acc = AladdinConnectClient(username, password)

    try:
        if not acc.login():
            raise ValueError("Username or Password is incorrect")
        add_entities(
            (AladdinDevice(acc, door) for door in acc.get_doors()),
            update_before_add=True,
        )
    except (TypeError, KeyError, NameError, ValueError) as ex:
        _LOGGER.error("%s", ex)
        persistent_notification.create(
            hass,
            "Error: {ex}<br />You will need to restart hass after fixing.",
            title=NOTIFICATION_TITLE,
            notification_id=NOTIFICATION_ID,
        )
Ejemplo n.º 24
0
    def step2_exchange(now):
        """Keep trying to validate the user_code until it expires."""

        # For some reason, oauth.step1_get_device_and_user_codes() returns a datetime
        # object without tzinfo. For the comparison below to work, it needs one.
        user_code_expiry = dev_flow.user_code_expiry.replace(
            tzinfo=timezone.utc)

        if now >= user_code_expiry:
            persistent_notification.create(
                hass,
                "Authentication code expired, please restart "
                "Home-Assistant and try again",
                title=NOTIFICATION_TITLE,
                notification_id=NOTIFICATION_ID,
            )
            listener()

        try:
            credentials = oauth.step2_exchange(device_flow_info=dev_flow)
        except FlowExchangeError:
            # not ready yet, call again
            return

        storage = Storage(hass.config.path(TOKEN_FILE))
        storage.put(credentials)
        do_setup(hass, hass_config, config)
        listener()
        persistent_notification.create(
            hass,
            (f"We are all setup now. Check {YAML_DEVICES} for calendars that have "
             f"been found"),
            title=NOTIFICATION_TITLE,
            notification_id=NOTIFICATION_ID,
        )
Ejemplo n.º 25
0
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Hunter Hydrawise component."""
    conf = config[DOMAIN]
    access_token = conf[CONF_ACCESS_TOKEN]
    scan_interval = conf.get(CONF_SCAN_INTERVAL)

    try:
        hydrawise = Hydrawiser(user_token=access_token)
        hass.data[DATA_HYDRAWISE] = HydrawiseHub(hydrawise)
    except (ConnectTimeout, HTTPError) as ex:
        _LOGGER.error("Unable to connect to Hydrawise cloud service: %s",
                      str(ex))
        persistent_notification.create(
            hass,
            f"Error: {ex}<br />You will need to restart hass after fixing.",
            title=NOTIFICATION_TITLE,
            notification_id=NOTIFICATION_ID,
        )
        return False

    def hub_refresh(event_time):
        """Call Hydrawise hub to refresh information."""
        _LOGGER.debug("Updating Hydrawise Hub component")
        hass.data[DATA_HYDRAWISE].data.update_controller_info()
        dispatcher_send(hass, SIGNAL_UPDATE_HYDRAWISE)

    # Call the Hydrawise API to refresh updates
    track_time_interval(hass, hub_refresh, scan_interval)

    return True
Ejemplo n.º 26
0
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Wireless Sensor Tag component."""
    conf = config[DOMAIN]
    username = conf.get(CONF_USERNAME)
    password = conf.get(CONF_PASSWORD)

    try:
        wirelesstags = WirelessTags(username=username, password=password)

        platform = WirelessTagPlatform(hass, wirelesstags)
        platform.load_tags()
        platform.start_monitoring()
        hass.data[DOMAIN] = platform
    except (ConnectTimeout, HTTPError, WirelessTagsException) as ex:
        _LOGGER.error("Unable to connect to wirelesstag.net service: %s",
                      str(ex))
        persistent_notification.create(
            hass,
            f"Error: {ex}<br />Please restart hass after fixing this.",
            title=NOTIFICATION_TITLE,
            notification_id=NOTIFICATION_ID,
        )
        return False

    return True
Ejemplo n.º 27
0
def log_exception(ex, domain, config, hass=None):
    """Generate log exception for config validation."""
    message = "Invalid config for [{}]: ".format(domain)
    if hass is not None:
        _PERSISTENT_VALIDATION.add(domain)
        message = (
            "The following platforms contain invalid configuration:  "
            + ", ".join(list(_PERSISTENT_VALIDATION))
            + "  (please check your configuration)"
        )
        persistent_notification.create(hass, message, "Invalid config", "invalid_config")

    if "extra keys not allowed" in ex.error_message:
        message += "[{}] is an invalid option for [{}]. Check: {}->{}.".format(
            ex.path[-1], domain, domain, "->".join("%s" % m for m in ex.path)
        )
    else:
        message += "{}.".format(humanize_error(config, ex))

    if hasattr(config, "__line__"):
        message += " (See {}:{})".format(config.__config_file__, config.__line__ or "?")

    if domain != "homeassistant":
        message += " Please check the docs at " "https://home-assistant.io/components/{}/".format(domain)

    _LOGGER.error(message)
Ejemplo n.º 28
0
    def handle_request(self, request, **values):
        """Handle request to url."""
        from werkzeug.exceptions import MethodNotAllowed, Unauthorized

        if request.method == "OPTIONS":
            # For CORS preflight requests.
            return self.options(request)

        try:
            handler = getattr(self, request.method.lower())
        except AttributeError:
            raise MethodNotAllowed

        remote_addr = HomeAssistantWSGI.get_real_ip(request)

        # Auth code verbose on purpose
        authenticated = False

        if self.hass.wsgi.api_password is None:
            authenticated = True

        elif self.hass.wsgi.is_trusted_ip(remote_addr):
            authenticated = True

        elif hmac.compare_digest(request.headers.get(HTTP_HEADER_HA_AUTH, ''),
                                 self.hass.wsgi.api_password):
            # A valid auth header has been set
            authenticated = True

        elif hmac.compare_digest(request.args.get(DATA_API_PASSWORD, ''),
                                 self.hass.wsgi.api_password):
            authenticated = True

        if self.requires_auth and not authenticated:
            _LOGGER.warning(
                'Login attempt or request with an invalid '
                'password from %s', remote_addr)
            persistent_notification.create(
                self.hass, 'Invalid password used from {}'.format(remote_addr),
                'Login attempt failed', NOTIFICATION_ID_LOGIN)
            raise Unauthorized()

        request.authenticated = authenticated

        _LOGGER.info('Serving %s to %s (auth: %s)', request.path, remote_addr,
                     authenticated)

        result = handler(request, **values)

        if isinstance(result, self.Response):
            # The method handler returned a ready-made Response, how nice of it
            return result

        status_code = 200

        if isinstance(result, tuple):
            result, status_code = result

        return self.Response(result, status=status_code)
Ejemplo n.º 29
0
    def turn_on(self):
        if self.gw.lock_firmware(enable=True):
            self._state = True
            self.schedule_update_ha_state()

            persistent_notification.create(
                self.hass, "Firmware update is locked. You can sleep well.",
                "Xiaomi Gateway 3")
Ejemplo n.º 30
0
    def handle_request(self, request, **values):
        """Handle request to url."""
        from werkzeug.exceptions import MethodNotAllowed, Unauthorized

        if request.method == "OPTIONS":
            # For CORS preflight requests.
            return self.options(request)

        try:
            handler = getattr(self, request.method.lower())
        except AttributeError:
            raise MethodNotAllowed

        # Auth code verbose on purpose
        authenticated = False

        if self.hass.wsgi.api_password is None:
            authenticated = True

        elif request.remote_addr in self.hass.wsgi.approved_ips:
            authenticated = True

        elif hmac.compare_digest(request.headers.get(HTTP_HEADER_HA_AUTH, ''),
                                 self.hass.wsgi.api_password):
            # A valid auth header has been set
            authenticated = True

        elif hmac.compare_digest(request.args.get(DATA_API_PASSWORD, ''),
                                 self.hass.wsgi.api_password):
            authenticated = True

        if self.requires_auth and not authenticated:
            _LOGGER.warning('Login attempt or request with an invalid '
                            'password from %s', request.remote_addr)
            persistent_notification.create(
                self.hass,
                'Invalid password used from {}'.format(request.remote_addr),
                'Login attempt failed', NOTIFICATION_ID_LOGIN)
            raise Unauthorized()

        request.authenticated = authenticated

        _LOGGER.info('Serving %s to %s (auth: %s)',
                     request.path, request.remote_addr, authenticated)

        result = handler(request, **values)

        if isinstance(result, self.Response):
            # The method handler returned a ready-made Response, how nice of it
            return result

        status_code = 200

        if isinstance(result, tuple):
            result, status_code = result

        return self.Response(result, status=status_code)
Ejemplo n.º 31
0
def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Decora WiFi platform."""

    email = config[CONF_USERNAME]
    password = config[CONF_PASSWORD]
    session = DecoraWiFiSession()

    try:
        success = session.login(email, password)

        # If login failed, notify user.
        if success is None:
            msg = "Failed to log into myLeviton Services. Check credentials."
            _LOGGER.error(msg)
            persistent_notification.create(hass,
                                           msg,
                                           title=NOTIFICATION_TITLE,
                                           notification_id=NOTIFICATION_ID)
            return

        # Gather all the available devices...
        perms = session.user.get_residential_permissions()
        all_switches = []
        for permission in perms:
            if permission.residentialAccountId is not None:
                acct = ResidentialAccount(session,
                                          permission.residentialAccountId)
                for residence in acct.get_residences():
                    for switch in residence.get_iot_switches():
                        all_switches.append(switch)
            elif permission.residenceId is not None:
                residence = Residence(session, permission.residenceId)
                for switch in residence.get_iot_switches():
                    all_switches.append(switch)

        add_entities(DecoraWifiLight(sw) for sw in all_switches)
    except ValueError:
        _LOGGER.error("Failed to communicate with myLeviton Service")

    # Listen for the stop event and log out.
    def logout(event):
        """Log out..."""
        try:
            if session is not None:
                Person.logout(session)
        except ValueError:
            _LOGGER.error("Failed to log out of myLeviton Service")

    hass.bus.listen(EVENT_HOMEASSISTANT_STOP, logout)
    def test_dismiss_notification(self):
        """Ensure removal of specific notification."""
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0

        pn.create(self.hass, 'test', notification_id='Beer 2')
        self.hass.block_till_done()

        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 1
        pn.dismiss(self.hass, notification_id='Beer 2')
        self.hass.block_till_done()

        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0
Ejemplo n.º 33
0
    def test_dismiss_notification(self):
        """Ensure removal of specific notification."""
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0

        pn.create(self.hass, 'test', notification_id='Beer 2')
        self.hass.block_till_done()

        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 1
        pn.dismiss(self.hass, notification_id='Beer 2')
        self.hass.block_till_done()

        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0
    def test_create_template_error(self):
        """Ensure we output templates if contain error."""
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0

        pn.create(self.hass, '{{ message + 1 }}', '{{ title + 1 }}')
        self.hass.block_till_done()

        entity_ids = self.hass.states.entity_ids(pn.DOMAIN)
        assert len(entity_ids) == 1

        state = self.hass.states.get(entity_ids[0])
        assert state.state == '{{ message + 1 }}'
        assert state.attributes.get('title') == '{{ title + 1 }}'
    def test_create(self):
        """Test creating notification without title or notification id."""
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0

        pn.create(self.hass, 'Hello World {{ 1 + 1 }}',
                  title='{{ 1 + 1 }} beers')
        self.hass.block_till_done()

        entity_ids = self.hass.states.entity_ids(pn.DOMAIN)
        assert len(entity_ids) == 1

        state = self.hass.states.get(entity_ids[0])
        assert state.state == 'Hello World 2'
        assert state.attributes.get('title') == '2 beers'
Ejemplo n.º 36
0
    def test_dismiss_notification(self):
        """Ensure removal of specific notification."""
        notifications = self.hass.data[pn.DOMAIN]['notifications']
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0
        assert len(notifications) == 0

        pn.create(self.hass, 'test', notification_id='Beer 2')
        self.hass.block_till_done()

        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 1
        assert len(notifications) == 1
        pn.dismiss(self.hass, notification_id='Beer 2')
        self.hass.block_till_done()

        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0
        assert len(notifications) == 0
        notifications.clear()
    def test_create_notification_id(self):
        """Ensure overwrites existing notification with same id."""
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0

        pn.create(self.hass, 'test', notification_id='Beer 2')
        self.hass.block_till_done()

        assert len(self.hass.states.entity_ids()) == 1
        state = self.hass.states.get('persistent_notification.beer_2')
        assert state.state == 'test'

        pn.create(self.hass, 'test 2', notification_id='Beer 2')
        self.hass.block_till_done()

        # We should have overwritten old one
        assert len(self.hass.states.entity_ids()) == 1
        state = self.hass.states.get('persistent_notification.beer_2')
        assert state.state == 'test 2'
Ejemplo n.º 38
0
    def test_create_template_error(self):
        """Ensure we output templates if contain error."""
        notifications = self.hass.data[pn.DOMAIN]['notifications']
        assert len(self.hass.states.entity_ids(pn.DOMAIN)) == 0
        assert len(notifications) == 0

        pn.create(self.hass, '{{ message + 1 }}', '{{ title + 1 }}')
        self.hass.block_till_done()

        entity_ids = self.hass.states.entity_ids(pn.DOMAIN)
        assert len(entity_ids) == 1
        assert len(notifications) == 1

        state = self.hass.states.get(entity_ids[0])
        assert state.attributes.get('message') == '{{ message + 1 }}'
        assert state.attributes.get('title') == '{{ title + 1 }}'

        notification = notifications.get(entity_ids[0])
        assert notification['message'] == '{{ message + 1 }}'
        assert notification['title'] == '{{ title + 1 }}'
        notifications.clear()
Ejemplo n.º 39
0
def prepare_setup_platform(hass: core.HomeAssistant, config, domain: str, platform_name: str) -> Optional[ModuleType]:
    """Load a platform and makes sure dependencies are setup."""
    _ensure_loader_prepared(hass)

    platform_path = PLATFORM_FORMAT.format(domain, platform_name)

    platform = loader.get_platform(domain, platform_name)

    # Not found
    if platform is None:
        _LOGGER.error("Unable to find platform %s", platform_path)

        _PERSISTENT_PLATFORMS.add(platform_path)
        message = (
            "Unable to find the following platforms: "
            + ", ".join(list(_PERSISTENT_PLATFORMS))
            + "(please check your configuration)"
        )
        persistent_notification.create(hass, message, "Invalid platforms", "platform_errors")
        return None

    # Already loaded
    elif platform_path in hass.config.components:
        return platform

    # Load dependencies
    for component in getattr(platform, "DEPENDENCIES", []):
        if not setup_component(hass, component, config):
            _LOGGER.error(
                "Unable to prepare setup for platform %s because " "dependency %s could not be initialized",
                platform_path,
                component,
            )
            return None

    if not _handle_requirements(hass, platform, platform_path):
        return None

    return platform
Ejemplo n.º 40
0
    def test_mark_read(self):
        """Ensure notification is marked as Read."""
        notifications = self.hass.data[pn.DOMAIN]['notifications']
        assert len(notifications) == 0

        pn.create(self.hass, 'test', notification_id='Beer 2')
        self.hass.block_till_done()

        entity_id = 'persistent_notification.beer_2'
        assert len(notifications) == 1
        notification = notifications.get(entity_id)
        assert notification['status'] == pn.STATUS_UNREAD

        self.hass.services.call(pn.DOMAIN, pn.SERVICE_MARK_READ, {
            'notification_id': 'Beer 2'
        })
        self.hass.block_till_done()

        assert len(notifications) == 1
        notification = notifications.get(entity_id)
        assert notification['status'] == pn.STATUS_READ
        notifications.clear()