hass-core/homeassistant/components/acmeda/hub.py
Alan Murray 65e509ed8f
Add Acmeda integration (#33384)
* First cut of Rollease Acmeda Pulse Hub integration.

* Acmeda integration improvements:
 - Moved common code into a base entity
 - Battery level sensor added
 - Localisation now working

* Added requirement for aiopulse now that it has been uploaded to PyPI.

* Exclude acmeda integration from coverage check as it relies on a hub being present.

* Fix Travis CI build issues.

* Remove unused constants.

* Remove unused group logic from cover.py

* Removed commented code from base.py

* Remove sensors (battery entities) on removal of hub.

* Remove unused groups from sensor.py

* Acmeda device and entity update made fully asynchronous using subscriptions to remove need for config polling.

* Updated aiopulse version dependency.
Removed non-functional battery charging indication.

* Rationalised common code to update entities into helpers.py

* Fix linting issue.

* Correct additional CI pylint errors.

* Index config_entries by entry_id.
Move entity loading and unloading to __init__.py
Add entry_id to dispatcher signal
Removed now unused polling code hub
Added config_flow unit tests

* Tweak to integration config_entry title.

* Bumped aiopulse module to 0.3.2.
Reduced verbosity of aiopulse module.

* Changed to using direct write of device state.
Removed old style async_step_init config_flow step.

* Remove superfluous battery_level and device_state_attributes from battery entity.

* Removal of unused strings.
Removal of unused create_config_flow helper.
Removal of stale comment.

* Remove use of shared container to track existing enities.
Moved removal and deregistration of entities to base class through use of dispatch helper.

* Fixed strings.json

* Fix incorrect use of remove instead of pop on dict.

* Add support for tilting covers, bump aiopulse version number.

* Bump aiopulse version to v0.3.4.
Fixed bug in cover supported_features.

* Bumped aiopulse version to 0.4.0
Update acmeda .coveragerc exclusions

* Removed already configured hub check from __init__.py async_setup_entry
Removed passing in hass reference to base entity class
Renamed entity async_reset to async_will_remove_from_hass
Changed device_info and properties
Migrated to CoveEntity from CoverDevice
Added dispatched_connect cleanup on hub removal
Removed unused entries from manifest
Removed override of battery icon
Renamed translations folder

* Reversed unintended change to .coveragerc

* Fixed config flow for multi-hub discovery.

* Acmeda enhancements as requested by MartinHjelmare

* Force import to connect to hub to retrieve id prior to creating entry

* Remove YAML configuration support.

* Tidied up config_flow and tests:
 - removed unnecessary steps
 - fixed typos

* Removed storage of hub in config_flow.
2020-05-17 12:15:06 +02:00

88 lines
2.6 KiB
Python

"""Code to handle a Pulse Hub."""
import asyncio
from typing import Optional
import aiopulse
from homeassistant.helpers.dispatcher import async_dispatcher_send
from .const import ACMEDA_ENTITY_REMOVE, ACMEDA_HUB_UPDATE, LOGGER
from .helpers import update_devices
class PulseHub:
"""Manages a single Pulse Hub."""
def __init__(self, hass, config_entry):
"""Initialize the system."""
self.config_entry = config_entry
self.hass = hass
self.api: Optional[aiopulse.Hub] = None
self.tasks = []
self.current_rollers = {}
self.cleanup_callbacks = []
@property
def title(self):
"""Return the title of the hub shown in the integrations list."""
return f"{self.api.id} ({self.api.host})"
@property
def host(self):
"""Return the host of this hub."""
return self.config_entry.data["host"]
async def async_setup(self, tries=0):
"""Set up a hub based on host parameter."""
host = self.host
hub = aiopulse.Hub(host)
self.api = hub
hub.callback_subscribe(self.async_notify_update)
self.tasks.append(asyncio.create_task(hub.run()))
LOGGER.debug("Hub setup complete")
return True
async def async_reset(self):
"""Reset this hub to default state."""
for cleanup_callback in self.cleanup_callbacks:
cleanup_callback()
# If not setup
if self.api is None:
return False
self.api.callback_unsubscribe(self.async_notify_update)
await self.api.stop()
del self.api
self.api = None
# Wait for any running tasks to complete
await asyncio.wait(self.tasks)
return True
async def async_notify_update(self, update_type):
"""Evaluate entities when hub reports that update has occurred."""
LOGGER.debug("Hub {update_type.name} updated")
if update_type == aiopulse.UpdateType.rollers:
await update_devices(self.hass, self.config_entry, self.api.rollers)
self.hass.config_entries.async_update_entry(
self.config_entry, title=self.title
)
async_dispatcher_send(
self.hass, ACMEDA_HUB_UPDATE.format(self.config_entry.entry_id)
)
for unique_id in list(self.current_rollers):
if unique_id not in self.api.rollers:
LOGGER.debug("Notifying remove of %s", unique_id)
self.current_rollers.pop(unique_id)
async_dispatcher_send(
self.hass, ACMEDA_ENTITY_REMOVE.format(unique_id)
)