Ejemplo n.º 1
0
class AlexaConfigStore:
    """A configuration store for Alexa."""

    _STORAGE_VERSION = 1
    _STORAGE_KEY = DOMAIN

    def __init__(self, hass):
        """Initialize a configuration store."""
        self._data = None
        self._hass = hass
        self._store = Store(hass, self._STORAGE_VERSION, self._STORAGE_KEY)

    @property
    def authorized(self):
        """Return authorization status."""
        return self._data[STORE_AUTHORIZED]

    @callback
    def set_authorized(self, authorized):
        """Set authorization status."""
        if authorized != self._data[STORE_AUTHORIZED]:
            self._data[STORE_AUTHORIZED] = authorized
            self._store.async_delay_save(lambda: self._data, 1.0)

    async def async_load(self):
        """Load saved configuration from disk."""
        if data := await self._store.async_load():
            self._data = data
        else:
Ejemplo n.º 2
0
class GoogleConfigStore:
    """A configuration store for google assistant."""

    _STORAGE_VERSION = 1
    _STORAGE_KEY = DOMAIN

    def __init__(self, hass):
        """Initialize a configuration store."""
        self._hass = hass
        self._store = Store(hass, self._STORAGE_VERSION, self._STORAGE_KEY)
        self._data = {STORE_AGENT_USER_IDS: {}}

    @property
    def agent_user_ids(self):
        """Return a list of connected agent user_ids."""
        return self._data[STORE_AGENT_USER_IDS]

    @callback
    def add_agent_user_id(self, agent_user_id):
        """Add an agent user id to store."""
        if agent_user_id not in self._data[STORE_AGENT_USER_IDS]:
            self._data[STORE_AGENT_USER_IDS][agent_user_id] = {}
            self._store.async_delay_save(lambda: self._data, 1.0)

    @callback
    def pop_agent_user_id(self, agent_user_id):
        """Remove agent user id from store."""
        if agent_user_id in self._data[STORE_AGENT_USER_IDS]:
            self._data[STORE_AGENT_USER_IDS].pop(agent_user_id, None)
            self._store.async_delay_save(lambda: self._data, 1.0)

    async def async_load(self):
        """Store current configuration to disk."""
        if data := await self._store.async_load():
            self._data = data
Ejemplo n.º 3
0
class CacheStore:
    _STORAGE_VERSION = 1
    _STORAGE_KEY = f'{DOMAIN}.cache'

    def __init__(self, hass):
        self._hass = hass
        self._store = Store(hass, self._STORAGE_VERSION, self._STORAGE_KEY)
        self._data = {STORE_CACHE_ATTRS: {}}

    def get_attr_value(self, entity_id: str, attr: str) -> Any | None:
        """Return a cached value of attribute for entity."""
        if entity_id not in self._data[STORE_CACHE_ATTRS]:
            return None

        return self._data[STORE_CACHE_ATTRS][entity_id].get(attr)

    @callback
    def save_attr_value(self, entity_id: str, attr: str, value: Any):
        """Cache entity's attribute value to disk."""
        if entity_id not in self._data[STORE_CACHE_ATTRS]:
            self._data[STORE_CACHE_ATTRS][entity_id] = {}
            has_changed = True
        else:
            has_changed = self._data[STORE_CACHE_ATTRS][entity_id][attr] != value

        self._data[STORE_CACHE_ATTRS][entity_id][attr] = value

        if has_changed:
            self._store.async_delay_save(lambda: self._data, 5.0)

    async def async_load(self):
        data = await self._store.async_load()
        if data:
            self._data = data
Ejemplo n.º 4
0
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
    """Set up the Broadlink remote."""
    host = config[CONF_HOST]
    mac_addr = config[CONF_MAC]
    timeout = config[CONF_TIMEOUT]
    name = config[CONF_NAME]
    unique_id = f"remote_{hexlify(mac_addr).decode('utf-8')}"

    if unique_id in hass.data.setdefault(DOMAIN, {}).setdefault(COMPONENT, []):
        _LOGGER.error("Duplicate: %s", unique_id)
        return
    hass.data[DOMAIN][COMPONENT].append(unique_id)

    api = broadlink.rm((host, DEFAULT_PORT), mac_addr, None)
    api.timeout = timeout
    code_storage = Store(hass, CODE_STORAGE_VERSION, CODE_STORAGE_KEY.format(unique_id))
    flag_storage = Store(hass, FLAG_STORAGE_VERSION, FLAG_STORAGE_KEY.format(unique_id))
    remote = BroadlinkRemote(name, unique_id, api, code_storage, flag_storage)

    connected, loaded = (False, False)
    try:
        connected, loaded = await asyncio.gather(
            hass.async_add_executor_job(api.auth), remote.async_load_storage_files()
        )
    except socket.error:
        pass
    if not connected:
        hass.data[DOMAIN][COMPONENT].remove(unique_id)
        raise PlatformNotReady
    if not loaded:
        _LOGGER.error("Failed to set up %s", unique_id)
        hass.data[DOMAIN][COMPONENT].remove(unique_id)
        return
    async_add_entities([remote], False)
Ejemplo n.º 5
0
class EntityMapStorage:
    """
    Holds a cache of entity structure data from a paired HomeKit device.

    HomeKit has a cacheable entity map that describes how an IP or BLE
    endpoint is structured. This object holds the latest copy of that data.

    An endpoint is made of accessories, services and characteristics. It is
    safe to cache this data until the c# discovery data changes.

    Caching this data means we can add HomeKit devices to HA immediately at
    start even if discovery hasn't seen them yet or they are out of range. It
    is also important for BLE devices - accessing the entity structure is
    very slow for these devices.
    """

    def __init__(self, hass):
        """Create a new entity map store."""
        self.hass = hass
        self.store = Store(hass, ENTITY_MAP_STORAGE_VERSION, ENTITY_MAP_STORAGE_KEY)
        self.storage_data = {}

    async def async_initialize(self):
        """Get the pairing cache data."""
        raw_storage = await self.store.async_load()
        if not raw_storage:
            # There is no cached data about HomeKit devices yet
            return

        self.storage_data = raw_storage.get("pairings", {})

    def get_map(self, homekit_id):
        """Get a pairing cache item."""
        return self.storage_data.get(homekit_id)

    def async_create_or_update_map(self, homekit_id, config_num, accessories):
        """Create a new pairing cache."""
        data = {"config_num": config_num, "accessories": accessories}
        self.storage_data[homekit_id] = data
        self._async_schedule_save()
        return data

    def async_delete_map(self, homekit_id):
        """Delete pairing cache."""
        if homekit_id not in self.storage_data:
            return

        self.storage_data.pop(homekit_id)
        self._async_schedule_save()

    @callback
    def _async_schedule_save(self):
        """Schedule saving the entity map cache."""
        self.store.async_delay_save(self._data_to_save, ENTITY_MAP_SAVE_DELAY)

    @callback
    def _data_to_save(self):
        """Return data of entity map to store in a file."""
        return {"pairings": self.storage_data}
Ejemplo n.º 6
0
 def __init__(self, hass, config_entry, mqtt_prefix):
     self.hass = hass
     self.config_entry = config_entry
     self.mqtt_prefix = mqtt_prefix
     self.async_add_devices = {}
     self.devices = {}
     self.store = Store(hass, STORAGE_VERSION, STORAGE_KEY)
     self._history = []
Ejemplo n.º 7
0
async def async_setup_entry(hass, config_entry, async_add_entities):
    """Set up a Broadlink remote."""
    device = hass.data[DOMAIN].devices[config_entry.entry_id]
    remote = BroadlinkRemote(
        device,
        Store(hass, CODE_STORAGE_VERSION, f"broadlink_remote_{device.unique_id}_codes"),
        Store(hass, FLAG_STORAGE_VERSION, f"broadlink_remote_{device.unique_id}_flags"),
    )
    async_add_entities([remote], False)
Ejemplo n.º 8
0
    def __init__(self, hass: HomeAssistant):
        """Create a new entity map store."""
        self.hass = hass
        self.store = Store(hass, AID_MANAGER_STORAGE_VERSION,
                           AID_MANAGER_STORAGE_KEY)
        self.allocations = {}
        self.allocated_aids = set()

        self._entity_registry = None
Ejemplo n.º 9
0
 def __init__(self, hass: HomeAssistantType, component: EntityComponent,
              lock_node_id, config_tags, config_ozw_log):
     """Initialize lock history storage."""
     self.hass = hass
     self.component = component
     self.node_id = lock_node_id
     self.config_tags = config_tags
     self.store = Store(hass, STORAGE_VERSION, STORAGE_KEY)
     self.ozw_log = config_ozw_log
     self.ozw_file_offset = 0
     self.ozw_file_inode = None
     self._used_tags = {}  # indexed by tag index number
     self._history = []
Ejemplo n.º 10
0
    async def async_initialize(self):
        """Load the latest AID data."""
        self._entity_registry = (
            await self.hass.helpers.entity_registry.async_get_registry())
        aidstore = get_aid_storage_filename_for_entry_id(self._entry)
        self.store = Store(self.hass, AID_MANAGER_STORAGE_VERSION, aidstore)

        raw_storage = await self.store.async_load()
        if not raw_storage:
            # There is no data about aid allocations yet
            return

        self.allocations = raw_storage.get(ALLOCATIONS_KEY, {})
        self.allocated_aids = set(self.allocations.values())
Ejemplo n.º 11
0
async def async_setup_entry(hass, config_entry, async_add_entities):
    """Set up a Broadlink remote."""
    device = hass.data[DOMAIN].devices[config_entry.entry_id]
    remote = BroadlinkRemote(
        device,
        Store(hass, CODE_STORAGE_VERSION, f"broadlink_remote_{device.unique_id}_codes"),
        Store(hass, FLAG_STORAGE_VERSION, f"broadlink_remote_{device.unique_id}_flags"),
    )

    loaded = await remote.async_load_storage_files()
    if not loaded:
        _LOGGER.error("Failed to create '%s Remote' entity: Storage error", device.name)
        return

    async_add_entities([remote], False)
Ejemplo n.º 12
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Initialize the trace integration."""
    hass.data[DATA_TRACE] = {}
    websocket_api.async_setup(hass)
    store = Store(hass,
                  STORAGE_VERSION,
                  STORAGE_KEY,
                  encoder=ExtendedJSONEncoder)
    hass.data[DATA_TRACE_STORE] = store

    async def _async_store_traces_at_stop(*_) -> None:
        """Save traces to storage."""
        _LOGGER.debug("Storing traces")
        try:
            await store.async_save({
                key: list(traces.values())
                for key, traces in hass.data[DATA_TRACE].items()
            })
        except HomeAssistantError as exc:
            _LOGGER.error("Error storing traces", exc_info=exc)

    # Store traces when stopping hass
    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                               _async_store_traces_at_stop)

    return True
Ejemplo n.º 13
0
async def _update_person(call: ServiceCall) -> bool:
    """Update the settings for a person."""

    hass = get_base().hass
    store = Store(hass, 1, f"{DOMAIN}.{CONF_PERSONS}")
    data: Optional[PersonSettingsRegistry] = await store.async_load()

    if data is None:
        data = {}
    data[CONF_UPDATED] = False

    person = EnhancedPerson(call.data.get(CONF_ID))

    await _update_key_value(data, call, person.id, ATTR_NAME,
                            person.original_name)
    await _update_key_value(data, call, person.id, CONF_SORT_ORDER,
                            DEFAULT_SORT_ORDER)
    await _update_key_value(data, call, person.id, CONF_VISIBLE, True)

    if await _store_data(store, data, person.id):
        hass.bus.fire(
            EVENT_PERSON_SETTINGS_CHANGED,
            {
                CONF_ACTION: CONF_UPDATE,
                CONF_ID: person.id
            },
        )
        return True

    return False
Ejemplo n.º 14
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up an input boolean."""
    component = EntityComponent(_LOGGER, DOMAIN, hass)

    # Process integration platforms right away since
    # we will create entities before firing EVENT_COMPONENT_LOADED
    await async_process_integration_platform_for_component(hass, DOMAIN)

    id_manager = collection.IDManager()

    yaml_collection = collection.YamlCollection(
        logging.getLogger(f"{__name__}.yaml_collection"), id_manager)
    collection.sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component,
                                     yaml_collection, InputBoolean.from_yaml)

    storage_collection = InputBooleanStorageCollection(
        Store(hass, STORAGE_VERSION, STORAGE_KEY),
        logging.getLogger(f"{__name__}.storage_collection"),
        id_manager,
    )
    collection.sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component,
                                     storage_collection, InputBoolean)

    await yaml_collection.async_load([{
        CONF_ID: id_,
        **(conf or {})
    } for id_, conf in config.get(DOMAIN, {}).items()])
    await storage_collection.async_load()

    collection.StorageCollectionWebsocket(storage_collection, DOMAIN, DOMAIN,
                                          CREATE_FIELDS,
                                          UPDATE_FIELDS).async_setup(hass)

    async def reload_service_handler(service_call: ServiceCall) -> None:
        """Remove all input booleans and load new ones from config."""
        conf = await component.async_prepare_reload(skip_reset=True)
        if conf is None:
            return
        await yaml_collection.async_load([{
            CONF_ID: id_,
            **(conf or {})
        } for id_, conf in conf.get(DOMAIN, {}).items()])

    homeassistant.helpers.service.async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_RELOAD,
        reload_service_handler,
        schema=RELOAD_SERVICE_SCHEMA,
    )

    component.async_register_entity_service(SERVICE_TURN_ON, {},
                                            "async_turn_on")

    component.async_register_entity_service(SERVICE_TURN_OFF, {},
                                            "async_turn_off")

    component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")

    return True
Ejemplo n.º 15
0
Archivo: prefs.py Proyecto: jbouwh/core
 def __init__(self, hass):
     """Initialize cloud prefs."""
     self._hass = hass
     self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY)
     self._prefs = None
     self._listeners = []
     self.last_updated: set[str] = set()
Ejemplo n.º 16
0
async def async_load_from_store(hass, key):
    """Load the retained data from store and return de-serialized data."""
    store = Store(hass, STORAGE_VERSION, f"hacs.{key}", encoder=JSONEncoder)
    restored = await store.async_load()
    if restored is None:
        return {}
    return restored
Ejemplo n.º 17
0
async def _update_entity(call: ServiceCall) -> bool:
    """Update the settings for an entity."""

    hass = get_base().hass
    store = Store(hass, 1, f"{DOMAIN}.{CONF_ENTITIES}")
    data: Optional[EntitySettingsRegistry] = await store.async_load()
    if data is None:
        data = {}
    data[CONF_UPDATED] = False

    entity_id: str = call.data.get(CONF_ENTITY_ID)
    entity = _get_entity_by_id(entity_id)

    await _update_key_value(data, call, entity_id, CONF_AREA_NAME,
                            entity[CONF_ORIGINAL_AREA_ID])
    await _update_key_value(data, call, entity_id, CONF_SORT_ORDER,
                            DEFAULT_SORT_ORDER)
    await _update_key_value(data, call, entity_id, CONF_TYPE,
                            [entity[CONF_ORIGINAL_TYPE], None])
    await _update_key_value(data, call, entity_id, CONF_VISIBLE, True)

    if await _store_data(store, data, entity_id):
        hass.bus.fire(
            EVENT_ENTITY_SETTINGS_CHANGED,
            {
                CONF_ACTION: CONF_UPDATE,
                ATTR_AREA_ID: entity_id
            },
        )
        return True

    return False
Ejemplo n.º 18
0
async def async_setup_entry(hass: HomeAssistant,
                            config_entry: ConfigEntry) -> bool:
    if config_entry.data:
        hass.config_entries.async_update_entry(config_entry,
                                               data={},
                                               options=config_entry.data)

    store = Store(hass,
                  STORAGE_VERSION,
                  "{}.{}".format(DOMAIN, config_entry.entry_id),
                  encoder=JSONEncoder)

    devices = await store.async_load()
    if devices is None:
        devices = {}

    client = LuciData(hass, config_entry, store, devices)

    hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = client

    async def async_close(event):
        await client.save_to_store()
        await client.api.logout()

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_close)

    await _init_services(hass, store)

    if not await client.async_setup():
        return False

    return True
Ejemplo n.º 19
0
async def internal_cache_setup(
        hass: HomeAssistant, entry: ConfigEntry, devices: list = None
):
    await asyncio.gather(*[
        hass.config_entries.async_forward_entry_setup(entry, domain)
        for domain in PLATFORMS
    ])

    if devices is None:
        store = Store(hass, 1, f"{DOMAIN}/{entry.data['username']}.json")
        devices = await store.async_load()
        if devices:
            # 16 devices loaded from the Cloud Server
            _LOGGER.debug(f"{len(devices)} devices loaded from Cache")

    registry: XRegistry = hass.data[DOMAIN][entry.entry_id]
    if devices:
        devices = internal_unique_devices(entry.entry_id, devices)
        registry.setup_devices(devices)

    mode = entry.options.get(CONF_MODE, "auto")
    if mode != "local" and registry.cloud.auth:
        registry.cloud.start()
    if mode != "cloud":
        zc = await zeroconf.async_get_instance(hass)
        registry.local.start(zc)

    _LOGGER.debug(f"{mode.upper()} mode start")

    if not entry.update_listeners:
        entry.add_update_listener(async_update_options)

    entry.async_on_unload(
        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, registry.stop)
    )
Ejemplo n.º 20
0
async def hass_areas() -> List[AreaSettings]:
    """A dictionary list for the HA area registry used for this integrations domain data."""

    hass = get_base().hass

    areas: List[AreaSettings] = []  # make as an array so it can be sorted

    store = Store(hass, 1, f"{DOMAIN}.{CONF_AREAS}")
    data: Optional[AreaSettingsRegistry] = await store.async_load()
    if data is None:
        data = {}

    # Sorted by original name because this is what is needed for the picker
    area_registry: AreaRegistry = hass.data["area_registry"]
    areas_sorted: Iterable[AreaEntry] = sorted(
        area_registry.async_list_areas(), key=lambda entry: entry.name)

    for area in areas_sorted:
        area_data = data.get(area.id, {})
        area_item: AreaSettings = {
            ATTR_ID: area.id,
            ATTR_NAME: area_data.get(CONF_NAME, area.name),
            CONF_ICON: area_data.get(CONF_ICON, DEFAULT_ROOM_ICON),
            CONF_ORIGINAL_NAME: area.name,
            CONF_SORT_ORDER: area_data.get(CONF_SORT_ORDER,
                                           DEFAULT_SORT_ORDER),
            CONF_VISIBLE: area_data.get(CONF_VISIBLE, True),
        }
        areas.append(area_item)

    return areas
Ejemplo n.º 21
0
 async def _save_to_store(self, data):
     """Generate dynamic data to store and save it to the filesystem."""
     store = Store(self.hass,
                   STORAGE_VERSION,
                   f"smartthings.{self._name}",
                   encoder=JSONEncoder)
     await store.async_save(data)
Ejemplo n.º 22
0
async def _update_area(call: ServiceCall) -> bool:
    """Update the settings for an area."""

    hass = get_base().hass
    store = Store(hass, 1, f"{DOMAIN}.{CONF_AREAS}")
    data: Optional[AreaSettingsRegistry] = await store.async_load()

    if data is None:
        data = {}
    data[CONF_UPDATED] = False

    area_name: str = call.data.get(CONF_AREA_NAME)
    area_id = await _get_area_id_by_name(area_name)

    await _update_key_value(data, call, area_id, ATTR_NAME, area_name)
    await _update_key_value(data, call, area_id, CONF_ICON, DEFAULT_ROOM_ICON)
    await _update_key_value(data, call, area_id, CONF_SORT_ORDER,
                            DEFAULT_SORT_ORDER)
    await _update_key_value(data, call, area_id, CONF_VISIBLE, True)

    if await _store_data(store, data, area_id):
        hass.bus.fire(
            EVENT_AREA_SETTINGS_CHANGED,
            {
                CONF_ACTION: CONF_UPDATE,
                ATTR_AREA_ID: area_id
            },
        )
        return True

    return False
Ejemplo n.º 23
0
async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
    """Set up an input boolean."""
    component = EntityComponent(_LOGGER, DOMAIN, hass)
    id_manager = collection.IDManager()

    yaml_collection = collection.YamlCollection(
        logging.getLogger(f"{__name__}.yaml_collection"), id_manager
    )
    collection.attach_entity_component_collection(
        component, yaml_collection, lambda conf: InputBoolean(conf, from_yaml=True)
    )

    storage_collection = InputBooleanStorageCollection(
        Store(hass, STORAGE_VERSION, STORAGE_KEY),
        logging.getLogger(f"{__name__}_storage_collection"),
        id_manager,
    )
    collection.attach_entity_component_collection(
        component, storage_collection, InputBoolean
    )

    await yaml_collection.async_load(
        [{CONF_ID: id_, **(conf or {})} for id_, conf in config.get(DOMAIN, {}).items()]
    )
    await storage_collection.async_load()

    collection.StorageCollectionWebsocket(
        storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS
    ).async_setup(hass)

    collection.attach_entity_registry_cleaner(hass, DOMAIN, DOMAIN, yaml_collection)
    collection.attach_entity_registry_cleaner(hass, DOMAIN, DOMAIN, storage_collection)

    async def reload_service_handler(service_call: ServiceCallType) -> None:
        """Remove all input booleans and load new ones from config."""
        conf = await component.async_prepare_reload(skip_reset=True)
        if conf is None:
            return
        await yaml_collection.async_load(
            [
                {CONF_ID: id_, **(conf or {})}
                for id_, conf in conf.get(DOMAIN, {}).items()
            ]
        )

    homeassistant.helpers.service.async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_RELOAD,
        reload_service_handler,
        schema=RELOAD_SERVICE_SCHEMA,
    )

    component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on")

    component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")

    component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")

    return True
Ejemplo n.º 24
0
async def async_save_to_store(hass, key, data):
    """Generate dynamic data to store and save it to the filesystem."""
    from homeassistant.helpers.storage import Store

    key = key if "/" in key else f"hacs.{key}"
    store = Store(hass, VERSION_STORAGE, key, encoder=JSONEncoder)
    await store.async_save(data)
Ejemplo n.º 25
0
async def unload_smartapp_endpoint(hass: HomeAssistant):
    """Tear down the component configuration."""
    if DOMAIN not in hass.data:
        return
    # Remove the cloudhook if it was created
    cloudhook_url = hass.data[DOMAIN][CONF_CLOUDHOOK_URL]
    if cloudhook_url and cloud.async_is_logged_in(hass):
        await cloud.async_delete_cloudhook(hass,
                                           hass.data[DOMAIN][CONF_WEBHOOK_ID])
        # Remove cloudhook from storage
        store = Store(hass, STORAGE_VERSION, STORAGE_KEY)
        await store.async_save({
            CONF_INSTANCE_ID:
            hass.data[DOMAIN][CONF_INSTANCE_ID],
            CONF_WEBHOOK_ID:
            hass.data[DOMAIN][CONF_WEBHOOK_ID],
            CONF_CLOUDHOOK_URL:
            None,
        })
        _LOGGER.debug("Cloudhook '%s' was removed", cloudhook_url)
    # Remove the webhook
    webhook.async_unregister(hass, hass.data[DOMAIN][CONF_WEBHOOK_ID])
    # Disconnect all brokers
    for broker in hass.data[DOMAIN][DATA_BROKERS].values():
        broker.disconnect()
    # Remove all handlers from manager
    hass.data[DOMAIN][DATA_MANAGER].dispatcher.disconnect_all()
    # Remove the component data
    hass.data.pop(DOMAIN)
Ejemplo n.º 26
0
    async def async_step_verification_code(self, user_input=None, errors=None):
        """Ask the verification code to the user."""
        if errors is None:
            errors = {}

        if user_input is None:
            return await self._show_verification_code_form(user_input, errors)

        self._verification_code = user_input[CONF_VERIFICATION_CODE]

        try:
            if self.api.requires_2fa:
                if not await self.hass.async_add_executor_job(
                    self.api.validate_2fa_code, self._verification_code
                ):
                    raise PyiCloudException("The code you entered is not valid.")
            else:
                if not await self.hass.async_add_executor_job(
                    self.api.validate_verification_code,
                    self._trusted_device,
                    self._verification_code,
                ):
                    raise PyiCloudException("The code you entered is not valid.")
        except PyiCloudException as error:
            # Reset to the initial 2FA state to allow the user to retry
            _LOGGER.error("Failed to verify verification code: %s", error)
            self._trusted_device = None
            self._verification_code = None
            errors["base"] = "validate_verification_code"

            if self.api.requires_2fa:
                try:
                    self.api = await self.hass.async_add_executor_job(
                        PyiCloudService,
                        self._username,
                        self._password,
                        Store(self.hass, STORAGE_VERSION, STORAGE_KEY).path,
                        True,
                        None,
                        self._with_family,
                    )
                    return await self.async_step_verification_code(None, errors)
                except PyiCloudFailedLoginException as error_login:
                    _LOGGER.error("Error logging into iCloud service: %s", error_login)
                    self.api = None
                    errors = {CONF_PASSWORD: "******"}
                    return self._show_setup_form(user_input, errors, "user")
            else:
                return await self.async_step_trusted_device(None, errors)

        return await self.async_step_user(
            {
                CONF_USERNAME: self._username,
                CONF_PASSWORD: self._password,
                CONF_WITH_FAMILY: self._with_family,
                CONF_MAX_INTERVAL: self._max_interval,
                CONF_GPS_ACCURACY_THRESHOLD: self._gps_accuracy_threshold,
            }
        )
Ejemplo n.º 27
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up an input select."""
    component = EntityComponent(LOGGER, DOMAIN, hass)

    # Process integration platforms right away since
    # we will create entities before firing EVENT_COMPONENT_LOADED
    await async_process_integration_platform_for_component(hass, DOMAIN)

    id_manager = IDManager()

    yaml_collection = YamlCollection(LOGGER, id_manager)
    sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component, yaml_collection,
                          Schedule.from_yaml)

    storage_collection = ScheduleStorageCollection(
        Store(
            hass,
            key=DOMAIN,
            version=STORAGE_VERSION,
            minor_version=STORAGE_VERSION_MINOR,
        ),
        logging.getLogger(f"{__name__}.storage_collection"),
        id_manager,
    )
    sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component, storage_collection,
                          Schedule)

    await yaml_collection.async_load([{
        CONF_ID: id_,
        **cfg
    } for id_, cfg in config.get(DOMAIN, {}).items()])
    await storage_collection.async_load()

    StorageCollectionWebsocket(
        storage_collection,
        DOMAIN,
        DOMAIN,
        BASE_SCHEMA | STORAGE_SCHEDULE_SCHEMA,
        BASE_SCHEMA | STORAGE_SCHEDULE_SCHEMA,
    ).async_setup(hass)

    async def reload_service_handler(service_call: ServiceCall) -> None:
        """Reload yaml entities."""
        conf = await component.async_prepare_reload(skip_reset=True)
        if conf is None:
            conf = {DOMAIN: {}}
        await yaml_collection.async_load([{
            CONF_ID: id_,
            **cfg
        } for id_, cfg in conf.get(DOMAIN, {}).items()])

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_RELOAD,
        reload_service_handler,
    )

    return True
Ejemplo n.º 28
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up an input slider."""
    component = EntityComponent(_LOGGER, DOMAIN, hass)
    id_manager = collection.IDManager()

    yaml_collection = collection.YamlCollection(
        logging.getLogger(f"{__name__}.yaml_collection"), id_manager)
    collection.sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component,
                                     yaml_collection, InputNumber.from_yaml)

    storage_collection = NumberStorageCollection(
        Store(hass, STORAGE_VERSION, STORAGE_KEY),
        logging.getLogger(f"{__name__}.storage_collection"),
        id_manager,
    )
    collection.sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component,
                                     storage_collection, InputNumber)

    await yaml_collection.async_load([{
        CONF_ID: id_,
        **(conf or {})
    } for id_, conf in config.get(DOMAIN, {}).items()])
    await storage_collection.async_load()

    collection.StorageCollectionWebsocket(storage_collection, DOMAIN, DOMAIN,
                                          CREATE_FIELDS,
                                          UPDATE_FIELDS).async_setup(hass)

    async def reload_service_handler(service_call: ServiceCall) -> None:
        """Reload yaml entities."""
        conf = await component.async_prepare_reload(skip_reset=True)
        if conf is None:
            conf = {DOMAIN: {}}
        await yaml_collection.async_load([{
            CONF_ID: id_,
            **conf
        } for id_, conf in conf.get(DOMAIN, {}).items()])

    homeassistant.helpers.service.async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_RELOAD,
        reload_service_handler,
        schema=RELOAD_SERVICE_SCHEMA,
    )

    component.async_register_entity_service(
        SERVICE_SET_VALUE,
        {vol.Required(ATTR_VALUE): vol.Coerce(float)},
        "async_set_value",
    )

    component.async_register_entity_service(SERVICE_INCREMENT, {},
                                            "async_increment")

    component.async_register_entity_service(SERVICE_DECREMENT, {},
                                            "async_decrement")

    return True
Ejemplo n.º 29
0
 def get_or_create_store(self, hass: HomeAssistant, entry: ConfigEntry) -> Store:
     """Get or create a Store instance for the given config entry."""
     return self._stores.setdefault(
         entry.entry_id,
         Store(
             hass, STORAGE_VERSION, f"esphome.{entry.entry_id}", encoder=JSONEncoder
         ),
     )
Ejemplo n.º 30
0
async def async_remove_store(hass, key):
    """Remove a store element that should no longer be used"""
    from homeassistant.helpers.storage import Store

    if "/" not in key:
        return
    store = Store(hass, VERSION_STORAGE, key, encoder=JSONEncoder)
    await store.async_remove()
Ejemplo n.º 31
0
 def __init__(self, hass):
     """Create a new entity map store."""
     self.hass = hass
     self.store = Store(
         hass,
         ENTITY_MAP_STORAGE_VERSION,
         ENTITY_MAP_STORAGE_KEY
     )
     self.storage_data = {}
Ejemplo n.º 32
0
    def __init__(self, hass: HomeAssistantType, component: EntityComponent,
                 config_persons):
        """Initialize person storage."""
        self.hass = hass
        self.component = component
        self.store = Store(hass, STORAGE_VERSION, STORAGE_KEY)
        self.storage_data = None

        config_data = self.config_data = OrderedDict()
        for conf in config_persons:
            person_id = conf[CONF_ID]

            if person_id in config_data:
                _LOGGER.error(
                    "Found config user with duplicate ID: %s", person_id)
                continue

            config_data[person_id] = conf
Ejemplo n.º 33
0
class EntityMapStorage:
    """
    Holds a cache of entity structure data from a paired HomeKit device.

    HomeKit has a cacheable entity map that describes how an IP or BLE
    endpoint is structured. This object holds the latest copy of that data.

    An endpoint is made of accessories, services and characteristics. It is
    safe to cache this data until the c# discovery data changes.

    Caching this data means we can add HomeKit devices to HA immediately at
    start even if discovery hasn't seen them yet or they are out of range. It
    is also important for BLE devices - accessing the entity structure is
    very slow for these devices.
    """

    def __init__(self, hass):
        """Create a new entity map store."""
        self.hass = hass
        self.store = Store(
            hass,
            ENTITY_MAP_STORAGE_VERSION,
            ENTITY_MAP_STORAGE_KEY
        )
        self.storage_data = {}

    async def async_initialize(self):
        """Get the pairing cache data."""
        raw_storage = await self.store.async_load()
        if not raw_storage:
            # There is no cached data about HomeKit devices yet
            return

        self.storage_data = raw_storage.get('pairings', {})

    def get_map(self, homekit_id):
        """Get a pairing cache item."""
        return self.storage_data.get(homekit_id)

    def async_create_or_update_map(self, homekit_id, config_num, accessories):
        """Create a new pairing cache."""
        data = {
            'config_num': config_num,
            'accessories': accessories,
        }
        self.storage_data[homekit_id] = data
        self._async_schedule_save()
        return data

    def async_delete_map(self, homekit_id):
        """Delete pairing cache."""
        if homekit_id not in self.storage_data:
            return

        self.storage_data.pop(homekit_id)
        self._async_schedule_save()

    @callback
    def _async_schedule_save(self):
        """Schedule saving the entity map cache."""
        self.store.async_delay_save(self._data_to_save, ENTITY_MAP_SAVE_DELAY)

    @callback
    def _data_to_save(self):
        """Return data of entity map to store in a file."""
        return {
            'pairings': self.storage_data,
        }
Ejemplo n.º 34
0
class PersonManager:
    """Manage person data."""

    def __init__(self, hass: HomeAssistantType, component: EntityComponent,
                 config_persons):
        """Initialize person storage."""
        self.hass = hass
        self.component = component
        self.store = Store(hass, STORAGE_VERSION, STORAGE_KEY)
        self.storage_data = None

        config_data = self.config_data = OrderedDict()
        for conf in config_persons:
            person_id = conf[CONF_ID]

            if person_id in config_data:
                _LOGGER.error(
                    "Found config user with duplicate ID: %s", person_id)
                continue

            config_data[person_id] = conf

    @property
    def storage_persons(self):
        """Iterate over persons stored in storage."""
        return list(self.storage_data.values())

    @property
    def config_persons(self):
        """Iterate over persons stored in config."""
        return list(self.config_data.values())

    async def async_initialize(self):
        """Get the person data."""
        raw_storage = await self.store.async_load()

        if raw_storage is None:
            raw_storage = {
                'persons': []
            }

        storage_data = self.storage_data = OrderedDict()

        for person in raw_storage['persons']:
            storage_data[person[CONF_ID]] = person

        entities = []
        seen_users = set()

        for person_conf in self.config_data.values():
            person_id = person_conf[CONF_ID]
            user_id = person_conf.get(CONF_USER_ID)

            if user_id is not None:
                if await self.hass.auth.async_get_user(user_id) is None:
                    _LOGGER.error(
                        "Invalid user_id detected for person %s", person_id)
                    continue

                if user_id in seen_users:
                    _LOGGER.error(
                        "Duplicate user_id %s detected for person %s",
                        user_id, person_id)
                    continue

                seen_users.add(user_id)

            entities.append(Person(person_conf, False))

        # To make sure IDs don't overlap between config/storage
        seen_persons = set(self.config_data)

        for person_conf in storage_data.values():
            person_id = person_conf[CONF_ID]
            user_id = person_conf[CONF_USER_ID]

            if person_id in seen_persons:
                _LOGGER.error(
                    "Skipping adding person from storage with same ID as"
                    " configuration.yaml entry: %s", person_id)
                continue

            if user_id is not None and user_id in seen_users:
                _LOGGER.error(
                    "Duplicate user_id %s detected for person %s",
                    user_id, person_id)
                continue

            # To make sure all users have just 1 person linked.
            seen_users.add(user_id)

            entities.append(Person(person_conf, True))

        if entities:
            await self.component.async_add_entities(entities)

        self.hass.bus.async_listen(EVENT_USER_REMOVED, self._user_removed)

    async def async_create_person(
            self, *, name, device_trackers=None, user_id=None):
        """Create a new person."""
        if not name:
            raise ValueError("Name is required")

        if user_id is not None:
            await self._validate_user_id(user_id)

        person = {
            CONF_ID: uuid.uuid4().hex,
            CONF_NAME: name,
            CONF_USER_ID: user_id,
            CONF_DEVICE_TRACKERS: device_trackers or [],
        }
        self.storage_data[person[CONF_ID]] = person
        self._async_schedule_save()
        await self.component.async_add_entities([Person(person, True)])
        return person

    async def async_update_person(self, person_id, *, name=_UNDEF,
                                  device_trackers=_UNDEF, user_id=_UNDEF):
        """Update person."""
        current = self.storage_data.get(person_id)

        if current is None:
            raise ValueError("Invalid person specified.")

        changes = {
            key: value for key, value in (
                (CONF_NAME, name),
                (CONF_DEVICE_TRACKERS, device_trackers),
                (CONF_USER_ID, user_id)
            ) if value is not _UNDEF and current[key] != value
        }

        if CONF_USER_ID in changes and user_id is not None:
            await self._validate_user_id(user_id)

        self.storage_data[person_id].update(changes)
        self._async_schedule_save()

        for entity in self.component.entities:
            if entity.unique_id == person_id:
                entity.person_updated()
                break

        return self.storage_data[person_id]

    async def async_delete_person(self, person_id):
        """Delete person."""
        if person_id not in self.storage_data:
            raise ValueError("Invalid person specified.")

        self.storage_data.pop(person_id)
        self._async_schedule_save()
        ent_reg = await self.hass.helpers.entity_registry.async_get_registry()

        for entity in self.component.entities:
            if entity.unique_id == person_id:
                await entity.async_remove()
                ent_reg.async_remove(entity.entity_id)
                break

    @callback
    def _async_schedule_save(self) -> None:
        """Schedule saving the area registry."""
        self.store.async_delay_save(self._data_to_save, SAVE_DELAY)

    @callback
    def _data_to_save(self) -> dict:
        """Return data of area registry to store in a file."""
        return {
            'persons': list(self.storage_data.values())
        }

    async def _validate_user_id(self, user_id):
        """Validate the used user_id."""
        if await self.hass.auth.async_get_user(user_id) is None:
            raise ValueError("User does not exist")

        if any(person for person
               in chain(self.storage_data.values(),
                        self.config_data.values())
               if person.get(CONF_USER_ID) == user_id):
            raise ValueError("User already taken")

    async def _user_removed(self, event: Event):
        """Handle event that a person is removed."""
        user_id = event.data['user_id']
        for person in self.storage_data.values():
            if person[CONF_USER_ID] == user_id:
                await self.async_update_person(
                    person_id=person[CONF_ID],
                    user_id=None
                )