diff --git a/.coveragerc b/.coveragerc index f06e9356d21e39..bb0be2d94336e3 100644 --- a/.coveragerc +++ b/.coveragerc @@ -104,6 +104,9 @@ omit = homeassistant/components/fritzbox.py homeassistant/components/switch/fritzbox.py + homeassistant/components/ecovacs.py + homeassistant/components/*/ecovacs.py + homeassistant/components/eufy.py homeassistant/components/*/eufy.py @@ -113,6 +116,12 @@ omit = homeassistant/components/google.py homeassistant/components/*/google.py + homeassistant/components/hangouts/__init__.py + homeassistant/components/hangouts/const.py + homeassistant/components/hangouts/hangouts_bot.py + homeassistant/components/hangouts/hangups_utils.py + homeassistant/components/*/hangouts.py + homeassistant/components/hdmi_cec.py homeassistant/components/*/hdmi_cec.py @@ -133,12 +142,13 @@ omit = homeassistant/components/ihc/* homeassistant/components/*/ihc.py + + homeassistant/components/insteon/* + homeassistant/components/*/insteon.py homeassistant/components/insteon_local.py - homeassistant/components/*/insteon_local.py - - homeassistant/components/insteon_plm/* - homeassistant/components/*/insteon_plm.py + + homeassistant/components/insteon_plm.py homeassistant/components/ios.py homeassistant/components/*/ios.py @@ -683,7 +693,9 @@ omit = homeassistant/components/sensor/mvglive.py homeassistant/components/sensor/nederlandse_spoorwegen.py homeassistant/components/sensor/netdata.py + homeassistant/components/sensor/netdata_public.py homeassistant/components/sensor/neurio_energy.py + homeassistant/components/sensor/noaa_tides.py homeassistant/components/sensor/nsw_fuel_station.py homeassistant/components/sensor/nut.py homeassistant/components/sensor/nzbget.py diff --git a/CODEOWNERS b/CODEOWNERS index 53f577d02ebe80..c756cb383d473b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -87,6 +87,8 @@ homeassistant/components/*/axis.py @kane610 homeassistant/components/*/bmw_connected_drive.py @ChristianKuehnel homeassistant/components/*/broadlink.py @danielhiversen homeassistant/components/*/deconz.py @kane610 +homeassistant/components/ecovacs.py @OverloadUT +homeassistant/components/*/ecovacs.py @OverloadUT homeassistant/components/eight_sleep.py @mezz64 homeassistant/components/*/eight_sleep.py @mezz64 homeassistant/components/hive.py @Rendili @KJonline diff --git a/docs/source/api/homeassistant.rst b/docs/source/api/homeassistant.rst index f5ff069451d1ab..599f5fb801957c 100644 --- a/docs/source/api/homeassistant.rst +++ b/docs/source/api/homeassistant.rst @@ -60,14 +60,6 @@ loader module :undoc-members: :show-inheritance: -remote module ---------------------------- - -.. automodule:: homeassistant.remote - :members: - :undoc-members: - :show-inheritance: - Module contents --------------- diff --git a/homeassistant/auth/__init__.py b/homeassistant/auth/__init__.py index 148f97702e3a2c..4ef8440de62a2b 100644 --- a/homeassistant/auth/__init__.py +++ b/homeassistant/auth/__init__.py @@ -2,7 +2,7 @@ import asyncio import logging from collections import OrderedDict -from typing import List, Awaitable +from typing import Any, Dict, List, Optional, Tuple, cast import jwt @@ -10,60 +10,75 @@ from homeassistant.core import callback, HomeAssistant from homeassistant.util import dt as dt_util -from . import auth_store -from .providers import auth_provider_from_config +from . import auth_store, models +from .mfa_modules import auth_mfa_module_from_config, MultiFactorAuthModule +from .providers import auth_provider_from_config, AuthProvider, LoginFlow _LOGGER = logging.getLogger(__name__) +_MfaModuleDict = Dict[str, MultiFactorAuthModule] +_ProviderKey = Tuple[str, Optional[str]] +_ProviderDict = Dict[_ProviderKey, AuthProvider] async def auth_manager_from_config( hass: HomeAssistant, - provider_configs: List[dict]) -> Awaitable['AuthManager']: - """Initialize an auth manager from config.""" + provider_configs: List[Dict[str, Any]], + module_configs: List[Dict[str, Any]]) -> 'AuthManager': + """Initialize an auth manager from config. + + CORE_CONFIG_SCHEMA will make sure do duplicated auth providers or + mfa modules exist in configs. + """ store = auth_store.AuthStore(hass) if provider_configs: providers = await asyncio.gather( *[auth_provider_from_config(hass, store, config) for config in provider_configs]) else: - providers = [] + providers = () # So returned auth providers are in same order as config - provider_hash = OrderedDict() + provider_hash = OrderedDict() # type: _ProviderDict for provider in providers: - if provider is None: - continue - key = (provider.type, provider.id) + provider_hash[key] = provider - if key in provider_hash: - _LOGGER.error( - 'Found duplicate provider: %s. Please add unique IDs if you ' - 'want to have the same provider twice.', key) - continue + if module_configs: + modules = await asyncio.gather( + *[auth_mfa_module_from_config(hass, config) + for config in module_configs]) + else: + modules = () + # So returned auth modules are in same order as config + module_hash = OrderedDict() # type: _MfaModuleDict + for module in modules: + module_hash[module.id] = module - provider_hash[key] = provider - manager = AuthManager(hass, store, provider_hash) + manager = AuthManager(hass, store, provider_hash, module_hash) return manager class AuthManager: """Manage the authentication for Home Assistant.""" - def __init__(self, hass, store, providers): + def __init__(self, hass: HomeAssistant, store: auth_store.AuthStore, + providers: _ProviderDict, mfa_modules: _MfaModuleDict) \ + -> None: """Initialize the auth manager.""" + self.hass = hass self._store = store self._providers = providers + self._mfa_modules = mfa_modules self.login_flow = data_entry_flow.FlowManager( hass, self._async_create_login_flow, self._async_finish_login_flow) @property - def active(self): + def active(self) -> bool: """Return if any auth providers are registered.""" return bool(self._providers) @property - def support_legacy(self): + def support_legacy(self) -> bool: """ Return if legacy_api_password auth providers are registered. @@ -75,19 +90,39 @@ def support_legacy(self): return False @property - def auth_providers(self): + def auth_providers(self) -> List[AuthProvider]: """Return a list of available auth providers.""" return list(self._providers.values()) - async def async_get_users(self): + @property + def auth_mfa_modules(self) -> List[MultiFactorAuthModule]: + """Return a list of available auth modules.""" + return list(self._mfa_modules.values()) + + def get_auth_mfa_module(self, module_id: str) \ + -> Optional[MultiFactorAuthModule]: + """Return an multi-factor auth module, None if not found.""" + return self._mfa_modules.get(module_id) + + async def async_get_users(self) -> List[models.User]: """Retrieve all users.""" return await self._store.async_get_users() - async def async_get_user(self, user_id): + async def async_get_user(self, user_id: str) -> Optional[models.User]: """Retrieve a user.""" return await self._store.async_get_user(user_id) - async def async_create_system_user(self, name): + async def async_get_user_by_credentials( + self, credentials: models.Credentials) -> Optional[models.User]: + """Get a user by credential, return None if not found.""" + for user in await self.async_get_users(): + for creds in user.credentials: + if creds.id == credentials.id: + return user + + return None + + async def async_create_system_user(self, name: str) -> models.User: """Create a system user.""" return await self._store.async_create_user( name=name, @@ -95,27 +130,27 @@ async def async_create_system_user(self, name): is_active=True, ) - async def async_create_user(self, name): + async def async_create_user(self, name: str) -> models.User: """Create a user.""" kwargs = { 'name': name, 'is_active': True, - } + } # type: Dict[str, Any] if await self._user_should_be_owner(): kwargs['is_owner'] = True return await self._store.async_create_user(**kwargs) - async def async_get_or_create_user(self, credentials): + async def async_get_or_create_user(self, credentials: models.Credentials) \ + -> models.User: """Get or create a user.""" if not credentials.is_new: - for user in await self._store.async_get_users(): - for creds in user.credentials: - if creds.id == credentials.id: - return user - - raise ValueError('Unable to find the user.') + user = await self.async_get_user_by_credentials(credentials) + if user is None: + raise ValueError('Unable to find the user.') + else: + return user auth_provider = self._async_get_auth_provider(credentials) @@ -127,15 +162,16 @@ async def async_get_or_create_user(self, credentials): return await self._store.async_create_user( credentials=credentials, - name=info.get('name'), - is_active=info.get('is_active', False) + name=info.name, + is_active=info.is_active, ) - async def async_link_user(self, user, credentials): + async def async_link_user(self, user: models.User, + credentials: models.Credentials) -> None: """Link credentials to an existing user.""" await self._store.async_link_user(user, credentials) - async def async_remove_user(self, user): + async def async_remove_user(self, user: models.User) -> None: """Remove a user.""" tasks = [ self.async_remove_credentials(credentials) @@ -147,27 +183,68 @@ async def async_remove_user(self, user): await self._store.async_remove_user(user) - async def async_activate_user(self, user): + async def async_activate_user(self, user: models.User) -> None: """Activate a user.""" await self._store.async_activate_user(user) - async def async_deactivate_user(self, user): + async def async_deactivate_user(self, user: models.User) -> None: """Deactivate a user.""" if user.is_owner: raise ValueError('Unable to deactive the owner') await self._store.async_deactivate_user(user) - async def async_remove_credentials(self, credentials): + async def async_remove_credentials( + self, credentials: models.Credentials) -> None: """Remove credentials.""" provider = self._async_get_auth_provider(credentials) if (provider is not None and hasattr(provider, 'async_will_remove_credentials')): - await provider.async_will_remove_credentials(credentials) + # https://github.com/python/mypy/issues/1424 + await provider.async_will_remove_credentials( # type: ignore + credentials) await self._store.async_remove_credentials(credentials) - async def async_create_refresh_token(self, user, client_id=None): + async def async_enable_user_mfa(self, user: models.User, + mfa_module_id: str, data: Any) -> None: + """Enable a multi-factor auth module for user.""" + if user.system_generated: + raise ValueError('System generated users cannot enable ' + 'multi-factor auth module.') + + module = self.get_auth_mfa_module(mfa_module_id) + if module is None: + raise ValueError('Unable find multi-factor auth module: {}' + .format(mfa_module_id)) + + await module.async_setup_user(user.id, data) + + async def async_disable_user_mfa(self, user: models.User, + mfa_module_id: str) -> None: + """Disable a multi-factor auth module for user.""" + if user.system_generated: + raise ValueError('System generated users cannot disable ' + 'multi-factor auth module.') + + module = self.get_auth_mfa_module(mfa_module_id) + if module is None: + raise ValueError('Unable find multi-factor auth module: {}' + .format(mfa_module_id)) + + await module.async_depose_user(user.id) + + async def async_get_enabled_mfa(self, user: models.User) -> Dict[str, str]: + """List enabled mfa modules for user.""" + modules = OrderedDict() # type: Dict[str, str] + for module_id, module in self._mfa_modules.items(): + if await module.async_is_user_setup(user.id): + modules[module_id] = module.name + return modules + + async def async_create_refresh_token(self, user: models.User, + client_id: Optional[str] = None) \ + -> models.RefreshToken: """Create a new refresh token for a user.""" if not user.is_active: raise ValueError('User is not active') @@ -182,16 +259,25 @@ async def async_create_refresh_token(self, user, client_id=None): return await self._store.async_create_refresh_token(user, client_id) - async def async_get_refresh_token(self, token_id): + async def async_get_refresh_token( + self, token_id: str) -> Optional[models.RefreshToken]: """Get refresh token by id.""" return await self._store.async_get_refresh_token(token_id) - async def async_get_refresh_token_by_token(self, token): + async def async_get_refresh_token_by_token( + self, token: str) -> Optional[models.RefreshToken]: """Get refresh token by token.""" return await self._store.async_get_refresh_token_by_token(token) + async def async_remove_refresh_token(self, + refresh_token: models.RefreshToken) \ + -> None: + """Delete a refresh token.""" + await self._store.async_remove_refresh_token(refresh_token) + @callback - def async_create_access_token(self, refresh_token): + def async_create_access_token(self, + refresh_token: models.RefreshToken) -> str: """Create a new access token.""" # pylint: disable=no-self-use return jwt.encode({ @@ -200,15 +286,16 @@ def async_create_access_token(self, refresh_token): 'exp': dt_util.utcnow() + refresh_token.access_token_expiration, }, refresh_token.jwt_key, algorithm='HS256').decode() - async def async_validate_access_token(self, token): - """Return if an access token is valid.""" + async def async_validate_access_token( + self, token: str) -> Optional[models.RefreshToken]: + """Return refresh token if an access token is valid.""" try: unverif_claims = jwt.decode(token, verify=False) except jwt.InvalidTokenError: return None refresh_token = await self.async_get_refresh_token( - unverif_claims.get('iss')) + cast(str, unverif_claims.get('iss'))) if refresh_token is None: jwt_key = '' @@ -228,34 +315,63 @@ async def async_validate_access_token(self, token): except jwt.InvalidTokenError: return None - if not refresh_token.user.is_active: + if refresh_token is None or not refresh_token.user.is_active: return None return refresh_token - async def _async_create_login_flow(self, handler, *, context, data): + async def _async_create_login_flow( + self, handler: _ProviderKey, *, context: Optional[Dict], + data: Optional[Any]) -> data_entry_flow.FlowHandler: """Create a login flow.""" auth_provider = self._providers[handler] - return await auth_provider.async_credential_flow(context) + return await auth_provider.async_login_flow(context) - async def _async_finish_login_flow(self, context, result): - """Result of a credential login flow.""" + async def _async_finish_login_flow( + self, flow: LoginFlow, result: Dict[str, Any]) \ + -> Dict[str, Any]: + """Return a user as result of login flow.""" if result['type'] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY: - return None + return result + + # we got final result + if isinstance(result['data'], models.User): + result['result'] = result['data'] + return result auth_provider = self._providers[result['handler']] - return await auth_provider.async_get_or_create_credentials( + credentials = await auth_provider.async_get_or_create_credentials( result['data']) + if flow.context is not None and flow.context.get('credential_only'): + result['result'] = credentials + return result + + # multi-factor module cannot enabled for new credential + # which has not linked to a user yet + if auth_provider.support_mfa and not credentials.is_new: + user = await self.async_get_user_by_credentials(credentials) + if user is not None: + modules = await self.async_get_enabled_mfa(user) + + if modules: + flow.user = user + flow.available_mfa_modules = modules + return await flow.async_step_select_mfa_module() + + result['result'] = await self.async_get_or_create_user(credentials) + return result + @callback - def _async_get_auth_provider(self, credentials): - """Helper to get auth provider from a set of credentials.""" + def _async_get_auth_provider( + self, credentials: models.Credentials) -> Optional[AuthProvider]: + """Get auth provider from a set of credentials.""" auth_provider_key = (credentials.auth_provider_type, credentials.auth_provider_id) return self._providers.get(auth_provider_key) - async def _user_should_be_owner(self): + async def _user_should_be_owner(self) -> bool: """Determine if user should be owner. A user should be an owner if it is the first non-system user that is diff --git a/homeassistant/auth/auth_store.py b/homeassistant/auth/auth_store.py index 806cd109d78e64..0f12d69211c645 100644 --- a/homeassistant/auth/auth_store.py +++ b/homeassistant/auth/auth_store.py @@ -1,8 +1,11 @@ """Storage for auth models.""" from collections import OrderedDict from datetime import timedelta +from logging import getLogger +from typing import Any, Dict, List, Optional # noqa: F401 import hmac +from homeassistant.core import HomeAssistant, callback from homeassistant.util import dt as dt_util from . import models @@ -20,35 +23,41 @@ class AuthStore: called that needs it. """ - def __init__(self, hass): + def __init__(self, hass: HomeAssistant) -> None: """Initialize the auth store.""" self.hass = hass - self._users = None + self._users = None # type: Optional[Dict[str, models.User]] self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) - async def async_get_users(self): + async def async_get_users(self) -> List[models.User]: """Retrieve all users.""" if self._users is None: - await self.async_load() + await self._async_load() + assert self._users is not None return list(self._users.values()) - async def async_get_user(self, user_id): + async def async_get_user(self, user_id: str) -> Optional[models.User]: """Retrieve a user by id.""" if self._users is None: - await self.async_load() + await self._async_load() + assert self._users is not None return self._users.get(user_id) - async def async_create_user(self, name, is_owner=None, is_active=None, - system_generated=None, credentials=None): + async def async_create_user( + self, name: Optional[str], is_owner: Optional[bool] = None, + is_active: Optional[bool] = None, + system_generated: Optional[bool] = None, + credentials: Optional[models.Credentials] = None) -> models.User: """Create a new user.""" if self._users is None: - await self.async_load() + await self._async_load() + assert self._users is not None kwargs = { 'name': name - } + } # type: Dict[str, Any] if is_owner is not None: kwargs['is_owner'] = is_owner @@ -64,36 +73,46 @@ async def async_create_user(self, name, is_owner=None, is_active=None, self._users[new_user.id] = new_user if credentials is None: - await self.async_save() + self._async_schedule_save() return new_user # Saving is done inside the link. await self.async_link_user(new_user, credentials) return new_user - async def async_link_user(self, user, credentials): + async def async_link_user(self, user: models.User, + credentials: models.Credentials) -> None: """Add credentials to an existing user.""" user.credentials.append(credentials) - await self.async_save() + self._async_schedule_save() credentials.is_new = False - async def async_remove_user(self, user): + async def async_remove_user(self, user: models.User) -> None: """Remove a user.""" + if self._users is None: + await self._async_load() + assert self._users is not None + self._users.pop(user.id) - await self.async_save() + self._async_schedule_save() - async def async_activate_user(self, user): + async def async_activate_user(self, user: models.User) -> None: """Activate a user.""" user.is_active = True - await self.async_save() + self._async_schedule_save() - async def async_deactivate_user(self, user): + async def async_deactivate_user(self, user: models.User) -> None: """Activate a user.""" user.is_active = False - await self.async_save() + self._async_schedule_save() - async def async_remove_credentials(self, credentials): + async def async_remove_credentials( + self, credentials: models.Credentials) -> None: """Remove credentials.""" + if self._users is None: + await self._async_load() + assert self._users is not None + for user in self._users.values(): found = None @@ -106,19 +125,35 @@ async def async_remove_credentials(self, credentials): user.credentials.pop(found) break - await self.async_save() + self._async_schedule_save() - async def async_create_refresh_token(self, user, client_id=None): + async def async_create_refresh_token( + self, user: models.User, client_id: Optional[str] = None) \ + -> models.RefreshToken: """Create a new token for a user.""" refresh_token = models.RefreshToken(user=user, client_id=client_id) user.refresh_tokens[refresh_token.id] = refresh_token - await self.async_save() + self._async_schedule_save() return refresh_token - async def async_get_refresh_token(self, token_id): + async def async_remove_refresh_token( + self, refresh_token: models.RefreshToken) -> None: + """Remove a refresh token.""" + if self._users is None: + await self._async_load() + assert self._users is not None + + for user in self._users.values(): + if user.refresh_tokens.pop(refresh_token.id, None): + self._async_schedule_save() + break + + async def async_get_refresh_token( + self, token_id: str) -> Optional[models.RefreshToken]: """Get refresh token by id.""" if self._users is None: - await self.async_load() + await self._async_load() + assert self._users is not None for user in self._users.values(): refresh_token = user.refresh_tokens.get(token_id) @@ -127,10 +162,12 @@ async def async_get_refresh_token(self, token_id): return None - async def async_get_refresh_token_by_token(self, token): + async def async_get_refresh_token_by_token( + self, token: str) -> Optional[models.RefreshToken]: """Get refresh token by token.""" if self._users is None: - await self.async_load() + await self._async_load() + assert self._users is not None found = None @@ -141,7 +178,7 @@ async def async_get_refresh_token_by_token(self, token): return found - async def async_load(self): + async def _async_load(self) -> None: """Load the users.""" data = await self._store.async_load() @@ -150,7 +187,7 @@ async def async_load(self): if self._users is not None: return - users = OrderedDict() + users = OrderedDict() # type: Dict[str, models.User] if data is None: self._users = users @@ -173,11 +210,17 @@ async def async_load(self): if 'jwt_key' not in rt_dict: continue + created_at = dt_util.parse_datetime(rt_dict['created_at']) + if created_at is None: + getLogger(__name__).error( + 'Ignoring refresh token %(id)s with invalid created_at ' + '%(created_at)s for user_id %(user_id)s', rt_dict) + continue token = models.RefreshToken( id=rt_dict['id'], user=users[rt_dict['user_id']], client_id=rt_dict['client_id'], - created_at=dt_util.parse_datetime(rt_dict['created_at']), + created_at=created_at, access_token_expiration=timedelta( seconds=rt_dict['access_token_expiration']), token=rt_dict['token'], @@ -187,8 +230,19 @@ async def async_load(self): self._users = users - async def async_save(self): + @callback + def _async_schedule_save(self) -> None: """Save users.""" + if self._users is None: + return + + self._store.async_delay_save(self._data_to_save, 1) + + @callback + def _data_to_save(self) -> Dict: + """Return the data to store.""" + assert self._users is not None + users = [ { 'id': user.id, @@ -227,10 +281,8 @@ async def async_save(self): for refresh_token in user.refresh_tokens.values() ] - data = { + return { 'users': users, 'credentials': credentials, 'refresh_tokens': refresh_tokens, } - - await self._store.async_save(data, delay=1) diff --git a/homeassistant/auth/mfa_modules/__init__.py b/homeassistant/auth/mfa_modules/__init__.py new file mode 100644 index 00000000000000..603ca6ff3b16d7 --- /dev/null +++ b/homeassistant/auth/mfa_modules/__init__.py @@ -0,0 +1,177 @@ +"""Plugable auth modules for Home Assistant.""" +from datetime import timedelta +import importlib +import logging +import types +from typing import Any, Dict, Optional + +import voluptuous as vol +from voluptuous.humanize import humanize_error + +from homeassistant import requirements, data_entry_flow +from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.util.decorator import Registry + +MULTI_FACTOR_AUTH_MODULES = Registry() + +MULTI_FACTOR_AUTH_MODULE_SCHEMA = vol.Schema({ + vol.Required(CONF_TYPE): str, + vol.Optional(CONF_NAME): str, + # Specify ID if you have two mfa auth module for same type. + vol.Optional(CONF_ID): str, +}, extra=vol.ALLOW_EXTRA) + +SESSION_EXPIRATION = timedelta(minutes=5) + +DATA_REQS = 'mfa_auth_module_reqs_processed' + +_LOGGER = logging.getLogger(__name__) + + +class MultiFactorAuthModule: + """Multi-factor Auth Module of validation function.""" + + DEFAULT_TITLE = 'Unnamed auth module' + + def __init__(self, hass: HomeAssistant, config: Dict[str, Any]) -> None: + """Initialize an auth module.""" + self.hass = hass + self.config = config + + @property + def id(self) -> str: # pylint: disable=invalid-name + """Return id of the auth module. + + Default is same as type + """ + return self.config.get(CONF_ID, self.type) + + @property + def type(self) -> str: + """Return type of the module.""" + return self.config[CONF_TYPE] # type: ignore + + @property + def name(self) -> str: + """Return the name of the auth module.""" + return self.config.get(CONF_NAME, self.DEFAULT_TITLE) + + # Implement by extending class + + @property + def input_schema(self) -> vol.Schema: + """Return a voluptuous schema to define mfa auth module's input.""" + raise NotImplementedError + + async def async_setup_flow(self, user_id: str) -> 'SetupFlow': + """Return a data entry flow handler for setup module. + + Mfa module should extend SetupFlow + """ + raise NotImplementedError + + async def async_setup_user(self, user_id: str, setup_data: Any) -> Any: + """Set up user for mfa auth module.""" + raise NotImplementedError + + async def async_depose_user(self, user_id: str) -> None: + """Remove user from mfa module.""" + raise NotImplementedError + + async def async_is_user_setup(self, user_id: str) -> bool: + """Return whether user is setup.""" + raise NotImplementedError + + async def async_validation( + self, user_id: str, user_input: Dict[str, Any]) -> bool: + """Return True if validation passed.""" + raise NotImplementedError + + +class SetupFlow(data_entry_flow.FlowHandler): + """Handler for the setup flow.""" + + def __init__(self, auth_module: MultiFactorAuthModule, + setup_schema: vol.Schema, + user_id: str) -> None: + """Initialize the setup flow.""" + self._auth_module = auth_module + self._setup_schema = setup_schema + self._user_id = user_id + + async def async_step_init( + self, user_input: Optional[Dict[str, str]] = None) \ + -> Dict[str, Any]: + """Handle the first step of setup flow. + + Return self.async_show_form(step_id='init') if user_input == None. + Return self.async_create_entry(data={'result': result}) if finish. + """ + errors = {} # type: Dict[str, str] + + if user_input: + result = await self._auth_module.async_setup_user( + self._user_id, user_input) + return self.async_create_entry( + title=self._auth_module.name, + data={'result': result} + ) + + return self.async_show_form( + step_id='init', + data_schema=self._setup_schema, + errors=errors + ) + + +async def auth_mfa_module_from_config( + hass: HomeAssistant, config: Dict[str, Any]) \ + -> MultiFactorAuthModule: + """Initialize an auth module from a config.""" + module_name = config[CONF_TYPE] + module = await _load_mfa_module(hass, module_name) + + try: + config = module.CONFIG_SCHEMA(config) # type: ignore + except vol.Invalid as err: + _LOGGER.error('Invalid configuration for multi-factor module %s: %s', + module_name, humanize_error(config, err)) + raise + + return MULTI_FACTOR_AUTH_MODULES[module_name](hass, config) # type: ignore + + +async def _load_mfa_module(hass: HomeAssistant, module_name: str) \ + -> types.ModuleType: + """Load an mfa auth module.""" + module_path = 'homeassistant.auth.mfa_modules.{}'.format(module_name) + + try: + module = importlib.import_module(module_path) + except ImportError as err: + _LOGGER.error('Unable to load mfa module %s: %s', module_name, err) + raise HomeAssistantError('Unable to load mfa module {}: {}'.format( + module_name, err)) + + if hass.config.skip_pip or not hasattr(module, 'REQUIREMENTS'): + return module + + processed = hass.data.get(DATA_REQS) + if processed and module_name in processed: + return module + + processed = hass.data[DATA_REQS] = set() + + # https://github.com/python/mypy/issues/1424 + req_success = await requirements.async_process_requirements( + hass, module_path, module.REQUIREMENTS) # type: ignore + + if not req_success: + raise HomeAssistantError( + 'Unable to process requirements of mfa module {}'.format( + module_name)) + + processed.add(module_name) + return module diff --git a/homeassistant/auth/mfa_modules/insecure_example.py b/homeassistant/auth/mfa_modules/insecure_example.py new file mode 100644 index 00000000000000..9c72111ef9697f --- /dev/null +++ b/homeassistant/auth/mfa_modules/insecure_example.py @@ -0,0 +1,89 @@ +"""Example auth module.""" +import logging +from typing import Any, Dict + +import voluptuous as vol + +from homeassistant.core import HomeAssistant + +from . import MultiFactorAuthModule, MULTI_FACTOR_AUTH_MODULES, \ + MULTI_FACTOR_AUTH_MODULE_SCHEMA, SetupFlow + +CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({ + vol.Required('data'): [vol.Schema({ + vol.Required('user_id'): str, + vol.Required('pin'): str, + })] +}, extra=vol.PREVENT_EXTRA) + +_LOGGER = logging.getLogger(__name__) + + +@MULTI_FACTOR_AUTH_MODULES.register('insecure_example') +class InsecureExampleModule(MultiFactorAuthModule): + """Example auth module validate pin.""" + + DEFAULT_TITLE = 'Insecure Personal Identify Number' + + def __init__(self, hass: HomeAssistant, config: Dict[str, Any]) -> None: + """Initialize the user data store.""" + super().__init__(hass, config) + self._data = config['data'] + + @property + def input_schema(self) -> vol.Schema: + """Validate login flow input data.""" + return vol.Schema({'pin': str}) + + @property + def setup_schema(self) -> vol.Schema: + """Validate async_setup_user input data.""" + return vol.Schema({'pin': str}) + + async def async_setup_flow(self, user_id: str) -> SetupFlow: + """Return a data entry flow handler for setup module. + + Mfa module should extend SetupFlow + """ + return SetupFlow(self, self.setup_schema, user_id) + + async def async_setup_user(self, user_id: str, setup_data: Any) -> Any: + """Set up user to use mfa module.""" + # data shall has been validate in caller + pin = setup_data['pin'] + + for data in self._data: + if data['user_id'] == user_id: + # already setup, override + data['pin'] = pin + return + + self._data.append({'user_id': user_id, 'pin': pin}) + + async def async_depose_user(self, user_id: str) -> None: + """Remove user from mfa module.""" + found = None + for data in self._data: + if data['user_id'] == user_id: + found = data + break + if found: + self._data.remove(found) + + async def async_is_user_setup(self, user_id: str) -> bool: + """Return whether user is setup.""" + for data in self._data: + if data['user_id'] == user_id: + return True + return False + + async def async_validation( + self, user_id: str, user_input: Dict[str, Any]) -> bool: + """Return True if validation passed.""" + for data in self._data: + if data['user_id'] == user_id: + # user_input has been validate in caller + if data['pin'] == user_input['pin']: + return True + + return False diff --git a/homeassistant/auth/mfa_modules/totp.py b/homeassistant/auth/mfa_modules/totp.py new file mode 100644 index 00000000000000..0914658a6557e6 --- /dev/null +++ b/homeassistant/auth/mfa_modules/totp.py @@ -0,0 +1,213 @@ +"""Time-based One Time Password auth module.""" +import logging +from io import BytesIO +from typing import Any, Dict, Optional, Tuple # noqa: F401 + +import voluptuous as vol + +from homeassistant.auth.models import User +from homeassistant.core import HomeAssistant + +from . import MultiFactorAuthModule, MULTI_FACTOR_AUTH_MODULES, \ + MULTI_FACTOR_AUTH_MODULE_SCHEMA, SetupFlow + +REQUIREMENTS = ['pyotp==2.2.6', 'PyQRCode==1.2.1'] + +CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({ +}, extra=vol.PREVENT_EXTRA) + +STORAGE_VERSION = 1 +STORAGE_KEY = 'auth_module.totp' +STORAGE_USERS = 'users' +STORAGE_USER_ID = 'user_id' +STORAGE_OTA_SECRET = 'ota_secret' + +INPUT_FIELD_CODE = 'code' + +DUMMY_SECRET = 'FPPTH34D4E3MI2HG' + +_LOGGER = logging.getLogger(__name__) + + +def _generate_qr_code(data: str) -> str: + """Generate a base64 PNG string represent QR Code image of data.""" + import pyqrcode + + qr_code = pyqrcode.create(data) + + with BytesIO() as buffer: + qr_code.svg(file=buffer, scale=4) + return '{}'.format( + buffer.getvalue().decode("ascii").replace('\n', '') + .replace('' + ' Tuple[str, str, str]: + """Generate a secret, url, and QR code.""" + import pyotp + + ota_secret = pyotp.random_base32() + url = pyotp.totp.TOTP(ota_secret).provisioning_uri( + username, issuer_name="Home Assistant") + image = _generate_qr_code(url) + return ota_secret, url, image + + +@MULTI_FACTOR_AUTH_MODULES.register('totp') +class TotpAuthModule(MultiFactorAuthModule): + """Auth module validate time-based one time password.""" + + DEFAULT_TITLE = 'Time-based One Time Password' + + def __init__(self, hass: HomeAssistant, config: Dict[str, Any]) -> None: + """Initialize the user data store.""" + super().__init__(hass, config) + self._users = None # type: Optional[Dict[str, str]] + self._user_store = hass.helpers.storage.Store( + STORAGE_VERSION, STORAGE_KEY) + + @property + def input_schema(self) -> vol.Schema: + """Validate login flow input data.""" + return vol.Schema({INPUT_FIELD_CODE: str}) + + async def _async_load(self) -> None: + """Load stored data.""" + data = await self._user_store.async_load() + + if data is None: + data = {STORAGE_USERS: {}} + + self._users = data.get(STORAGE_USERS, {}) + + async def _async_save(self) -> None: + """Save data.""" + await self._user_store.async_save({STORAGE_USERS: self._users}) + + def _add_ota_secret(self, user_id: str, + secret: Optional[str] = None) -> str: + """Create a ota_secret for user.""" + import pyotp + + ota_secret = secret or pyotp.random_base32() # type: str + + self._users[user_id] = ota_secret # type: ignore + return ota_secret + + async def async_setup_flow(self, user_id: str) -> SetupFlow: + """Return a data entry flow handler for setup module. + + Mfa module should extend SetupFlow + """ + user = await self.hass.auth.async_get_user(user_id) # type: ignore + return TotpSetupFlow(self, self.input_schema, user) + + async def async_setup_user(self, user_id: str, setup_data: Any) -> str: + """Set up auth module for user.""" + if self._users is None: + await self._async_load() + + result = await self.hass.async_add_executor_job( + self._add_ota_secret, user_id, setup_data.get('secret')) + + await self._async_save() + return result + + async def async_depose_user(self, user_id: str) -> None: + """Depose auth module for user.""" + if self._users is None: + await self._async_load() + + if self._users.pop(user_id, None): # type: ignore + await self._async_save() + + async def async_is_user_setup(self, user_id: str) -> bool: + """Return whether user is setup.""" + if self._users is None: + await self._async_load() + + return user_id in self._users # type: ignore + + async def async_validation( + self, user_id: str, user_input: Dict[str, Any]) -> bool: + """Return True if validation passed.""" + if self._users is None: + await self._async_load() + + # user_input has been validate in caller + # set INPUT_FIELD_CODE as vol.Required is not user friendly + return await self.hass.async_add_executor_job( + self._validate_2fa, user_id, user_input.get(INPUT_FIELD_CODE, '')) + + def _validate_2fa(self, user_id: str, code: str) -> bool: + """Validate two factor authentication code.""" + import pyotp + + ota_secret = self._users.get(user_id) # type: ignore + if ota_secret is None: + # even we cannot find user, we still do verify + # to make timing the same as if user was found. + pyotp.TOTP(DUMMY_SECRET).verify(code) + return False + + return bool(pyotp.TOTP(ota_secret).verify(code)) + + +class TotpSetupFlow(SetupFlow): + """Handler for the setup flow.""" + + def __init__(self, auth_module: TotpAuthModule, + setup_schema: vol.Schema, + user: User) -> None: + """Initialize the setup flow.""" + super().__init__(auth_module, setup_schema, user.id) + # to fix typing complaint + self._auth_module = auth_module # type: TotpAuthModule + self._user = user + self._ota_secret = None # type: Optional[str] + self._url = None # type Optional[str] + self._image = None # type Optional[str] + + async def async_step_init( + self, user_input: Optional[Dict[str, str]] = None) \ + -> Dict[str, Any]: + """Handle the first step of setup flow. + + Return self.async_show_form(step_id='init') if user_input == None. + Return self.async_create_entry(data={'result': result}) if finish. + """ + import pyotp + + errors = {} # type: Dict[str, str] + + if user_input: + verified = await self.hass.async_add_executor_job( # type: ignore + pyotp.TOTP(self._ota_secret).verify, user_input['code']) + if verified: + result = await self._auth_module.async_setup_user( + self._user_id, {'secret': self._ota_secret}) + return self.async_create_entry( + title=self._auth_module.name, + data={'result': result} + ) + + errors['base'] = 'invalid_code' + + else: + hass = self._auth_module.hass + self._ota_secret, self._url, self._image = \ + await hass.async_add_executor_job( # type: ignore + _generate_secret_and_qr_code, str(self._user.name)) + + return self.async_show_form( + step_id='init', + data_schema=self._setup_schema, + description_placeholders={ + 'code': self._ota_secret, + 'url': self._url, + 'qr_code': self._image + }, + errors=errors + ) diff --git a/homeassistant/auth/models.py b/homeassistant/auth/models.py index 3f49c56bce67eb..a6500510e0d291 100644 --- a/homeassistant/auth/models.py +++ b/homeassistant/auth/models.py @@ -1,5 +1,6 @@ """Auth models.""" from datetime import datetime, timedelta +from typing import Dict, List, NamedTuple, Optional # noqa: F401 import uuid import attr @@ -14,17 +15,21 @@ class User: """A user.""" - name = attr.ib(type=str) + name = attr.ib(type=str) # type: Optional[str] id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex)) is_owner = attr.ib(type=bool, default=False) is_active = attr.ib(type=bool, default=False) system_generated = attr.ib(type=bool, default=False) # List of credentials of a user. - credentials = attr.ib(type=list, default=attr.Factory(list), cmp=False) + credentials = attr.ib( + type=list, default=attr.Factory(list), cmp=False + ) # type: List[Credentials] # Tokens associated with a user. - refresh_tokens = attr.ib(type=dict, default=attr.Factory(dict), cmp=False) + refresh_tokens = attr.ib( + type=dict, default=attr.Factory(dict), cmp=False + ) # type: Dict[str, RefreshToken] @attr.s(slots=True) @@ -32,7 +37,7 @@ class RefreshToken: """RefreshToken for a user to grant new access tokens.""" user = attr.ib(type=User) - client_id = attr.ib(type=str) + client_id = attr.ib(type=str) # type: Optional[str] id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex)) created_at = attr.ib(type=datetime, default=attr.Factory(dt_util.utcnow)) access_token_expiration = attr.ib(type=timedelta, @@ -48,10 +53,14 @@ class Credentials: """Credentials for a user on an auth provider.""" auth_provider_type = attr.ib(type=str) - auth_provider_id = attr.ib(type=str) + auth_provider_id = attr.ib(type=str) # type: Optional[str] # Allow the auth provider to store data to represent their auth. data = attr.ib(type=dict) id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex)) is_new = attr.ib(type=bool, default=True) + + +UserMeta = NamedTuple("UserMeta", + [('name', Optional[str]), ('is_active', bool)]) diff --git a/homeassistant/auth/providers/__init__.py b/homeassistant/auth/providers/__init__.py index ac5b6107b8ac99..3cb1c6b121e4cc 100644 --- a/homeassistant/auth/providers/__init__.py +++ b/homeassistant/auth/providers/__init__.py @@ -1,16 +1,22 @@ """Auth providers for Home Assistant.""" import importlib import logging +import types +from typing import Any, Dict, List, Optional import voluptuous as vol from voluptuous.humanize import humanize_error -from homeassistant import requirements -from homeassistant.core import callback -from homeassistant.const import CONF_TYPE, CONF_NAME, CONF_ID +from homeassistant import data_entry_flow, requirements +from homeassistant.core import callback, HomeAssistant +from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE +from homeassistant.exceptions import HomeAssistantError +from homeassistant.util import dt as dt_util from homeassistant.util.decorator import Registry -from homeassistant.auth.models import Credentials +from ..auth_store import AuthStore +from ..models import Credentials, User, UserMeta # noqa: F401 +from ..mfa_modules import SESSION_EXPIRATION _LOGGER = logging.getLogger(__name__) DATA_REQS = 'auth_prov_reqs_processed' @@ -25,66 +31,20 @@ }, extra=vol.ALLOW_EXTRA) -async def auth_provider_from_config(hass, store, config): - """Initialize an auth provider from a config.""" - provider_name = config[CONF_TYPE] - module = await load_auth_provider_module(hass, provider_name) - - if module is None: - return None - - try: - config = module.CONFIG_SCHEMA(config) - except vol.Invalid as err: - _LOGGER.error('Invalid configuration for auth provider %s: %s', - provider_name, humanize_error(config, err)) - return None - - return AUTH_PROVIDERS[provider_name](hass, store, config) - - -async def load_auth_provider_module(hass, provider): - """Load an auth provider.""" - try: - module = importlib.import_module( - 'homeassistant.auth.providers.{}'.format(provider)) - except ImportError: - _LOGGER.warning('Unable to find auth provider %s', provider) - return None - - if hass.config.skip_pip or not hasattr(module, 'REQUIREMENTS'): - return module - - processed = hass.data.get(DATA_REQS) - - if processed is None: - processed = hass.data[DATA_REQS] = set() - elif provider in processed: - return module - - req_success = await requirements.async_process_requirements( - hass, 'auth provider {}'.format(provider), module.REQUIREMENTS) - - if not req_success: - return None - - processed.add(provider) - return module - - class AuthProvider: """Provider of user authentication.""" DEFAULT_TITLE = 'Unnamed auth provider' - def __init__(self, hass, store, config): + def __init__(self, hass: HomeAssistant, store: AuthStore, + config: Dict[str, Any]) -> None: """Initialize an auth provider.""" self.hass = hass self.store = store self.config = config @property - def id(self): # pylint: disable=invalid-name + def id(self) -> Optional[str]: # pylint: disable=invalid-name """Return id of the auth provider. Optional, can be None. @@ -92,16 +52,21 @@ def id(self): # pylint: disable=invalid-name return self.config.get(CONF_ID) @property - def type(self): + def type(self) -> str: """Return type of the provider.""" - return self.config[CONF_TYPE] + return self.config[CONF_TYPE] # type: ignore @property - def name(self): + def name(self) -> str: """Return the name of the auth provider.""" return self.config.get(CONF_NAME, self.DEFAULT_TITLE) - async def async_credentials(self): + @property + def support_mfa(self) -> bool: + """Return whether multi-factor auth supported by the auth provider.""" + return True + + async def async_credentials(self) -> List[Credentials]: """Return all credentials of this provider.""" users = await self.store.async_get_users() return [ @@ -113,7 +78,7 @@ async def async_credentials(self): ] @callback - def async_create_credentials(self, data): + def async_create_credentials(self, data: Dict[str, str]) -> Credentials: """Create credentials.""" return Credentials( auth_provider_type=self.type, @@ -123,21 +88,169 @@ def async_create_credentials(self, data): # Implement by extending class - async def async_credential_flow(self, context): - """Return the data flow for logging in with auth provider.""" + async def async_login_flow(self, context: Optional[Dict]) -> 'LoginFlow': + """Return the data flow for logging in with auth provider. + + Auth provider should extend LoginFlow and return an instance. + """ raise NotImplementedError - async def async_get_or_create_credentials(self, flow_result): + async def async_get_or_create_credentials( + self, flow_result: Dict[str, str]) -> Credentials: """Get credentials based on the flow result.""" raise NotImplementedError - async def async_user_meta_for_credentials(self, credentials): + async def async_user_meta_for_credentials( + self, credentials: Credentials) -> UserMeta: """Return extra user metadata for credentials. Will be used to populate info when creating a new user. + """ + raise NotImplementedError + + +async def auth_provider_from_config( + hass: HomeAssistant, store: AuthStore, + config: Dict[str, Any]) -> AuthProvider: + """Initialize an auth provider from a config.""" + provider_name = config[CONF_TYPE] + module = await load_auth_provider_module(hass, provider_name) + + try: + config = module.CONFIG_SCHEMA(config) # type: ignore + except vol.Invalid as err: + _LOGGER.error('Invalid configuration for auth provider %s: %s', + provider_name, humanize_error(config, err)) + raise + + return AUTH_PROVIDERS[provider_name](hass, store, config) # type: ignore + + +async def load_auth_provider_module( + hass: HomeAssistant, provider: str) -> types.ModuleType: + """Load an auth provider.""" + try: + module = importlib.import_module( + 'homeassistant.auth.providers.{}'.format(provider)) + except ImportError as err: + _LOGGER.error('Unable to load auth provider %s: %s', provider, err) + raise HomeAssistantError('Unable to load auth provider {}: {}'.format( + provider, err)) - Values to populate: - - name: string - - is_active: boolean + if hass.config.skip_pip or not hasattr(module, 'REQUIREMENTS'): + return module + + processed = hass.data.get(DATA_REQS) + + if processed is None: + processed = hass.data[DATA_REQS] = set() + elif provider in processed: + return module + + # https://github.com/python/mypy/issues/1424 + reqs = module.REQUIREMENTS # type: ignore + req_success = await requirements.async_process_requirements( + hass, 'auth provider {}'.format(provider), reqs) + + if not req_success: + raise HomeAssistantError( + 'Unable to process requirements of auth provider {}'.format( + provider)) + + processed.add(provider) + return module + + +class LoginFlow(data_entry_flow.FlowHandler): + """Handler for the login flow.""" + + def __init__(self, auth_provider: AuthProvider) -> None: + """Initialize the login flow.""" + self._auth_provider = auth_provider + self._auth_module_id = None # type: Optional[str] + self._auth_manager = auth_provider.hass.auth # type: ignore + self.available_mfa_modules = {} # type: Dict[str, str] + self.created_at = dt_util.utcnow() + self.user = None # type: Optional[User] + + async def async_step_init( + self, user_input: Optional[Dict[str, str]] = None) \ + -> Dict[str, Any]: + """Handle the first step of login flow. + + Return self.async_show_form(step_id='init') if user_input == None. + Return await self.async_finish(flow_result) if login init step pass. """ - return {} + raise NotImplementedError + + async def async_step_select_mfa_module( + self, user_input: Optional[Dict[str, str]] = None) \ + -> Dict[str, Any]: + """Handle the step of select mfa module.""" + errors = {} + + if user_input is not None: + auth_module = user_input.get('multi_factor_auth_module') + if auth_module in self.available_mfa_modules: + self._auth_module_id = auth_module + return await self.async_step_mfa() + errors['base'] = 'invalid_auth_module' + + if len(self.available_mfa_modules) == 1: + self._auth_module_id = list(self.available_mfa_modules.keys())[0] + return await self.async_step_mfa() + + return self.async_show_form( + step_id='select_mfa_module', + data_schema=vol.Schema({ + 'multi_factor_auth_module': vol.In(self.available_mfa_modules) + }), + errors=errors, + ) + + async def async_step_mfa( + self, user_input: Optional[Dict[str, str]] = None) \ + -> Dict[str, Any]: + """Handle the step of mfa validation.""" + errors = {} + + auth_module = self._auth_manager.get_auth_mfa_module( + self._auth_module_id) + if auth_module is None: + # Given an invalid input to async_step_select_mfa_module + # will show invalid_auth_module error + return await self.async_step_select_mfa_module(user_input={}) + + if user_input is not None: + expires = self.created_at + SESSION_EXPIRATION + if dt_util.utcnow() > expires: + return self.async_abort( + reason='login_expired' + ) + + result = await auth_module.async_validation( + self.user.id, user_input) # type: ignore + if not result: + errors['base'] = 'invalid_code' + + if not errors: + return await self.async_finish(self.user) + + description_placeholders = { + 'mfa_module_name': auth_module.name, + 'mfa_module_id': auth_module.id + } # type: Dict[str, str] + + return self.async_show_form( + step_id='mfa', + data_schema=auth_module.input_schema, + description_placeholders=description_placeholders, + errors=errors, + ) + + async def async_finish(self, flow_result: Any) -> Dict: + """Handle the pass of login flow.""" + return self.async_create_entry( + title=self._auth_provider.name, + data=flow_result + ) diff --git a/homeassistant/auth/providers/homeassistant.py b/homeassistant/auth/providers/homeassistant.py index 5a2355264ab8ae..c743a5b7f65682 100644 --- a/homeassistant/auth/providers/homeassistant.py +++ b/homeassistant/auth/providers/homeassistant.py @@ -3,24 +3,27 @@ from collections import OrderedDict import hashlib import hmac -from typing import Dict # noqa: F401 pylint: disable=unused-import +from typing import Any, Dict, List, Optional, cast +import bcrypt import voluptuous as vol -from homeassistant import data_entry_flow from homeassistant.const import CONF_ID -from homeassistant.core import callback +from homeassistant.core import callback, HomeAssistant from homeassistant.exceptions import HomeAssistantError +from homeassistant.util.async_ import run_coroutine_threadsafe -from homeassistant.auth.util import generate_secret +from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, LoginFlow + +from ..models import Credentials, UserMeta +from ..util import generate_secret -from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS STORAGE_VERSION = 1 STORAGE_KEY = 'auth_provider.homeassistant' -def _disallow_id(conf): +def _disallow_id(conf: Dict[str, Any]) -> Dict[str, Any]: """Disallow ID in config.""" if CONF_ID in conf: raise vol.Invalid( @@ -46,13 +49,13 @@ class InvalidUser(HomeAssistantError): class Data: """Hold the user data.""" - def __init__(self, hass): + def __init__(self, hass: HomeAssistant) -> None: """Initialize the user data store.""" self.hass = hass self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) - self._data = None + self._data = None # type: Optional[Dict[str, Any]] - async def async_load(self): + async def async_load(self) -> None: """Load stored data.""" data = await self._store.async_load() @@ -65,37 +68,69 @@ async def async_load(self): self._data = data @property - def users(self): + def users(self) -> List[Dict[str, str]]: """Return users.""" - return self._data['users'] + return self._data['users'] # type: ignore def validate_login(self, username: str, password: str) -> None: """Validate a username and password. Raises InvalidAuth if auth invalid. """ - hashed = self.hash_password(password) - + dummy = b'$2b$12$CiuFGszHx9eNHxPuQcwBWez4CwDTOcLTX5CbOpV6gef2nYuXkY7BO' found = None # Compare all users to avoid timing attacks. - for user in self._data['users']: + for user in self.users: if username == user['username']: found = user if found is None: - # Do one more compare to make timing the same as if user was found. - hmac.compare_digest(hashed, hashed) + # check a hash to make timing the same as if user was found + bcrypt.checkpw(b'foo', + dummy) raise InvalidAuth - if not hmac.compare_digest(hashed, - base64.b64decode(found['password'])): + user_hash = base64.b64decode(found['password']) + + # if the hash is not a bcrypt hash... + # provide a transparant upgrade for old pbkdf2 hash format + if not (user_hash.startswith(b'$2a$') + or user_hash.startswith(b'$2b$') + or user_hash.startswith(b'$2x$') + or user_hash.startswith(b'$2y$')): + # IMPORTANT! validate the login, bail if invalid + hashed = self.legacy_hash_password(password) + if not hmac.compare_digest(hashed, user_hash): + raise InvalidAuth + # then re-hash the valid password with bcrypt + self.change_password(found['username'], password) + run_coroutine_threadsafe( + self.async_save(), self.hass.loop + ).result() + user_hash = base64.b64decode(found['password']) + + # bcrypt.checkpw is timing-safe + if not bcrypt.checkpw(password.encode(), + user_hash): raise InvalidAuth + def legacy_hash_password(self, password: str, + for_storage: bool = False) -> bytes: + """LEGACY password encoding.""" + # We're no longer storing salts in data, but if one exists we + # should be able to retrieve it. + salt = self._data['salt'].encode() # type: ignore + hashed = hashlib.pbkdf2_hmac('sha512', password.encode(), salt, 100000) + if for_storage: + hashed = base64.b64encode(hashed) + return hashed + + # pylint: disable=no-self-use def hash_password(self, password: str, for_storage: bool = False) -> bytes: """Encode a password.""" - hashed = hashlib.pbkdf2_hmac( - 'sha512', password.encode(), self._data['salt'].encode(), 100000) + hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)) \ + # type: bytes if for_storage: hashed = base64.b64encode(hashed) return hashed @@ -137,7 +172,7 @@ def change_password(self, username: str, new_password: str) -> None: else: raise InvalidUser - async def async_save(self): + async def async_save(self) -> None: """Save data.""" await self._store.async_save(self._data) @@ -150,7 +185,7 @@ class HassAuthProvider(AuthProvider): data = None - async def async_initialize(self): + async def async_initialize(self) -> None: """Initialize the auth provider.""" if self.data is not None: return @@ -158,19 +193,22 @@ async def async_initialize(self): self.data = Data(self.hass) await self.data.async_load() - async def async_credential_flow(self, context): + async def async_login_flow( + self, context: Optional[Dict]) -> LoginFlow: """Return a flow to login.""" - return LoginFlow(self) + return HassLoginFlow(self) - async def async_validate_login(self, username: str, password: str): - """Helper to validate a username and password.""" + async def async_validate_login(self, username: str, password: str) -> None: + """Validate a username and password.""" if self.data is None: await self.async_initialize() + assert self.data is not None await self.hass.async_add_executor_job( self.data.validate_login, username, password) - async def async_get_or_create_credentials(self, flow_result): + async def async_get_or_create_credentials( + self, flow_result: Dict[str, str]) -> Credentials: """Get credentials based on the flow result.""" username = flow_result['username'] @@ -183,17 +221,17 @@ async def async_get_or_create_credentials(self, flow_result): 'username': username }) - async def async_user_meta_for_credentials(self, credentials): + async def async_user_meta_for_credentials( + self, credentials: Credentials) -> UserMeta: """Get extra info for this credential.""" - return { - 'name': credentials.data['username'], - 'is_active': True, - } + return UserMeta(name=credentials.data['username'], is_active=True) - async def async_will_remove_credentials(self, credentials): + async def async_will_remove_credentials( + self, credentials: Credentials) -> None: """When credentials get removed, also remove the auth.""" if self.data is None: await self.async_initialize() + assert self.data is not None try: self.data.async_remove_auth(credentials.data['username']) @@ -203,29 +241,26 @@ async def async_will_remove_credentials(self, credentials): pass -class LoginFlow(data_entry_flow.FlowHandler): +class HassLoginFlow(LoginFlow): """Handler for the login flow.""" - def __init__(self, auth_provider): - """Initialize the login flow.""" - self._auth_provider = auth_provider - - async def async_step_init(self, user_input=None): + async def async_step_init( + self, user_input: Optional[Dict[str, str]] = None) \ + -> Dict[str, Any]: """Handle the step of the form.""" errors = {} if user_input is not None: try: - await self._auth_provider.async_validate_login( - user_input['username'], user_input['password']) + await cast(HassAuthProvider, self._auth_provider)\ + .async_validate_login(user_input['username'], + user_input['password']) except InvalidAuth: errors['base'] = 'invalid_auth' if not errors: - return self.async_create_entry( - title=self._auth_provider.name, - data=user_input - ) + user_input.pop('password') + return await self.async_finish(user_input) schema = OrderedDict() # type: Dict[str, type] schema['username'] = str diff --git a/homeassistant/auth/providers/insecure_example.py b/homeassistant/auth/providers/insecure_example.py index 96f824140ed877..72e3dfe140ac0b 100644 --- a/homeassistant/auth/providers/insecure_example.py +++ b/homeassistant/auth/providers/insecure_example.py @@ -1,14 +1,15 @@ """Example auth provider.""" from collections import OrderedDict import hmac +from typing import Any, Dict, Optional, cast import voluptuous as vol from homeassistant.exceptions import HomeAssistantError -from homeassistant import data_entry_flow from homeassistant.core import callback -from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS +from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, LoginFlow +from ..models import Credentials, UserMeta USER_SCHEMA = vol.Schema({ @@ -31,13 +32,13 @@ class InvalidAuthError(HomeAssistantError): class ExampleAuthProvider(AuthProvider): """Example auth provider based on hardcoded usernames and passwords.""" - async def async_credential_flow(self, context): + async def async_login_flow(self, context: Optional[Dict]) -> LoginFlow: """Return a flow to login.""" - return LoginFlow(self) + return ExampleLoginFlow(self) @callback - def async_validate_login(self, username, password): - """Helper to validate a username and password.""" + def async_validate_login(self, username: str, password: str) -> None: + """Validate a username and password.""" user = None # Compare all users to avoid timing attacks. @@ -56,7 +57,8 @@ def async_validate_login(self, username, password): password.encode('utf-8')): raise InvalidAuthError - async def async_get_or_create_credentials(self, flow_result): + async def async_get_or_create_credentials( + self, flow_result: Dict[str, str]) -> Credentials: """Get credentials based on the flow result.""" username = flow_result['username'] @@ -69,49 +71,45 @@ async def async_get_or_create_credentials(self, flow_result): 'username': username }) - async def async_user_meta_for_credentials(self, credentials): + async def async_user_meta_for_credentials( + self, credentials: Credentials) -> UserMeta: """Return extra user metadata for credentials. Will be used to populate info when creating a new user. """ username = credentials.data['username'] - info = { - 'is_active': True, - } + name = None for user in self.config['users']: if user['username'] == username: - info['name'] = user.get('name') + name = user.get('name') break - return info + return UserMeta(name=name, is_active=True) -class LoginFlow(data_entry_flow.FlowHandler): +class ExampleLoginFlow(LoginFlow): """Handler for the login flow.""" - def __init__(self, auth_provider): - """Initialize the login flow.""" - self._auth_provider = auth_provider - - async def async_step_init(self, user_input=None): + async def async_step_init( + self, user_input: Optional[Dict[str, str]] = None) \ + -> Dict[str, Any]: """Handle the step of the form.""" errors = {} if user_input is not None: try: - self._auth_provider.async_validate_login( - user_input['username'], user_input['password']) + cast(ExampleAuthProvider, self._auth_provider)\ + .async_validate_login(user_input['username'], + user_input['password']) except InvalidAuthError: errors['base'] = 'invalid_auth' if not errors: - return self.async_create_entry( - title=self._auth_provider.name, - data=user_input - ) + user_input.pop('password') + return await self.async_finish(user_input) - schema = OrderedDict() + schema = OrderedDict() # type: Dict[str, type] schema['username'] = str schema['password'] = str diff --git a/homeassistant/auth/providers/legacy_api_password.py b/homeassistant/auth/providers/legacy_api_password.py index f2f467e07ec1f0..f631f8e73cf3d4 100644 --- a/homeassistant/auth/providers/legacy_api_password.py +++ b/homeassistant/auth/providers/legacy_api_password.py @@ -3,16 +3,17 @@ It will be removed when auth system production ready """ -from collections import OrderedDict import hmac +from typing import Any, Dict, Optional, cast import voluptuous as vol -from homeassistant.exceptions import HomeAssistantError -from homeassistant import data_entry_flow +from homeassistant.components.http import HomeAssistantHTTP # noqa: F401 from homeassistant.core import callback +from homeassistant.exceptions import HomeAssistantError -from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS +from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, LoginFlow +from ..models import Credentials, UserMeta USER_SCHEMA = vol.Schema({ @@ -36,25 +37,21 @@ class LegacyApiPasswordAuthProvider(AuthProvider): DEFAULT_TITLE = 'Legacy API Password' - async def async_credential_flow(self, context): + async def async_login_flow(self, context: Optional[Dict]) -> LoginFlow: """Return a flow to login.""" - return LoginFlow(self) + return LegacyLoginFlow(self) @callback - def async_validate_login(self, password): - """Helper to validate a username and password.""" - if not hasattr(self.hass, 'http'): - raise ValueError('http component is not loaded') - - if self.hass.http.api_password is None: - raise ValueError('http component is not configured using' - ' api_password') + def async_validate_login(self, password: str) -> None: + """Validate a username and password.""" + hass_http = getattr(self.hass, 'http', None) # type: HomeAssistantHTTP - if not hmac.compare_digest(self.hass.http.api_password.encode('utf-8'), + if not hmac.compare_digest(hass_http.api_password.encode('utf-8'), password.encode('utf-8')): raise InvalidAuthError - async def async_get_or_create_credentials(self, flow_result): + async def async_get_or_create_credentials( + self, flow_result: Dict[str, str]) -> Credentials: """Return LEGACY_USER always.""" for credential in await self.async_credentials(): if credential.data['username'] == LEGACY_USER: @@ -64,47 +61,43 @@ async def async_get_or_create_credentials(self, flow_result): 'username': LEGACY_USER }) - async def async_user_meta_for_credentials(self, credentials): + async def async_user_meta_for_credentials( + self, credentials: Credentials) -> UserMeta: """ Set name as LEGACY_USER always. Will be used to populate info when creating a new user. """ - return { - 'name': LEGACY_USER, - 'is_active': True, - } + return UserMeta(name=LEGACY_USER, is_active=True) -class LoginFlow(data_entry_flow.FlowHandler): +class LegacyLoginFlow(LoginFlow): """Handler for the login flow.""" - def __init__(self, auth_provider): - """Initialize the login flow.""" - self._auth_provider = auth_provider - - async def async_step_init(self, user_input=None): + async def async_step_init( + self, user_input: Optional[Dict[str, str]] = None) \ + -> Dict[str, Any]: """Handle the step of the form.""" errors = {} + hass_http = getattr(self.hass, 'http', None) + if hass_http is None or not hass_http.api_password: + return self.async_abort( + reason='no_api_password_set' + ) + if user_input is not None: try: - self._auth_provider.async_validate_login( - user_input['password']) + cast(LegacyApiPasswordAuthProvider, self._auth_provider)\ + .async_validate_login(user_input['password']) except InvalidAuthError: errors['base'] = 'invalid_auth' if not errors: - return self.async_create_entry( - title=self._auth_provider.name, - data={} - ) - - schema = OrderedDict() - schema['password'] = str + return await self.async_finish({}) return self.async_show_form( step_id='init', - data_schema=vol.Schema(schema), + data_schema=vol.Schema({'password': str}), errors=errors, ) diff --git a/homeassistant/auth/providers/trusted_networks.py b/homeassistant/auth/providers/trusted_networks.py new file mode 100644 index 00000000000000..8a7e1d67c6d253 --- /dev/null +++ b/homeassistant/auth/providers/trusted_networks.py @@ -0,0 +1,129 @@ +"""Trusted Networks auth provider. + +It shows list of users if access from trusted network. +Abort login flow if not access from trusted network. +""" +from typing import Any, Dict, Optional, cast + +import voluptuous as vol + +from homeassistant.components.http import HomeAssistantHTTP # noqa: F401 +from homeassistant.core import callback +from homeassistant.exceptions import HomeAssistantError + +from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, LoginFlow +from ..models import Credentials, UserMeta + +CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend({ +}, extra=vol.PREVENT_EXTRA) + + +class InvalidAuthError(HomeAssistantError): + """Raised when try to access from untrusted networks.""" + + +class InvalidUserError(HomeAssistantError): + """Raised when try to login as invalid user.""" + + +@AUTH_PROVIDERS.register('trusted_networks') +class TrustedNetworksAuthProvider(AuthProvider): + """Trusted Networks auth provider. + + Allow passwordless access from trusted network. + """ + + DEFAULT_TITLE = 'Trusted Networks' + + @property + def support_mfa(self) -> bool: + """Trusted Networks auth provider does not support MFA.""" + return False + + async def async_login_flow(self, context: Optional[Dict]) -> LoginFlow: + """Return a flow to login.""" + assert context is not None + users = await self.store.async_get_users() + available_users = {user.id: user.name + for user in users + if not user.system_generated and user.is_active} + + return TrustedNetworksLoginFlow( + self, cast(str, context.get('ip_address')), available_users) + + async def async_get_or_create_credentials( + self, flow_result: Dict[str, str]) -> Credentials: + """Get credentials based on the flow result.""" + user_id = flow_result['user'] + + users = await self.store.async_get_users() + for user in users: + if (not user.system_generated and + user.is_active and + user.id == user_id): + for credential in await self.async_credentials(): + if credential.data['user_id'] == user_id: + return credential + cred = self.async_create_credentials({'user_id': user_id}) + await self.store.async_link_user(user, cred) + return cred + + # We only allow login as exist user + raise InvalidUserError + + async def async_user_meta_for_credentials( + self, credentials: Credentials) -> UserMeta: + """Return extra user metadata for credentials. + + Trusted network auth provider should never create new user. + """ + raise NotImplementedError + + @callback + def async_validate_access(self, ip_address: str) -> None: + """Make sure the access from trusted networks. + + Raise InvalidAuthError if not. + Raise InvalidAuthError if trusted_networks is not configured. + """ + hass_http = getattr(self.hass, 'http', None) # type: HomeAssistantHTTP + + if not hass_http or not hass_http.trusted_networks: + raise InvalidAuthError('trusted_networks is not configured') + + if not any(ip_address in trusted_network for trusted_network + in hass_http.trusted_networks): + raise InvalidAuthError('Not in trusted_networks') + + +class TrustedNetworksLoginFlow(LoginFlow): + """Handler for the login flow.""" + + def __init__(self, auth_provider: TrustedNetworksAuthProvider, + ip_address: str, available_users: Dict[str, Optional[str]]) \ + -> None: + """Initialize the login flow.""" + super().__init__(auth_provider) + self._available_users = available_users + self._ip_address = ip_address + + async def async_step_init( + self, user_input: Optional[Dict[str, str]] = None) \ + -> Dict[str, Any]: + """Handle the step of the form.""" + try: + cast(TrustedNetworksAuthProvider, self._auth_provider)\ + .async_validate_access(self._ip_address) + + except InvalidAuthError: + return self.async_abort( + reason='not_whitelisted' + ) + + if user_input is not None: + return await self.async_finish(user_input) + + return self.async_show_form( + step_id='init', + data_schema=vol.Schema({'user': vol.In(self._available_users)}), + ) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 43c7168dd2e861..2051359c0baed1 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -61,7 +61,6 @@ def from_config_dict(config: Dict[str, Any], config, hass, config_dir, enable_log, verbose, skip_pip, log_rotate_days, log_file, log_no_color) ) - return hass @@ -87,11 +86,20 @@ async def async_from_config_dict(config: Dict[str, Any], log_no_color) core_config = config.get(core.DOMAIN, {}) + has_api_password = bool((config.get('http') or {}).get('api_password')) + has_trusted_networks = bool((config.get('http') or {}) + .get('trusted_networks')) try: - await conf_util.async_process_ha_core_config(hass, core_config) - except vol.Invalid as ex: - conf_util.async_log_exception(ex, 'homeassistant', core_config, hass) + await conf_util.async_process_ha_core_config( + hass, core_config, has_api_password, has_trusted_networks) + except vol.Invalid as config_err: + conf_util.async_log_exception( + config_err, 'homeassistant', core_config, hass) + return None + except HomeAssistantError: + _LOGGER.error("Home Assistant core failed to initialize. " + "Further initialization aborted") return None await hass.async_add_executor_job( @@ -126,7 +134,7 @@ async def async_from_config_dict(config: Dict[str, Any], res = await core_components.async_setup(hass, config) if not res: _LOGGER.error("Home Assistant core failed to initialize. " - "further initialization aborted") + "Further initialization aborted") return hass await persistent_notification.async_setup(hass, config) @@ -307,7 +315,7 @@ async def async_stop_async_handler(_: Any) -> None: hass.data[DATA_LOGGING] = err_log_path else: _LOGGER.error( - "Unable to setup error log %s (access denied)", err_log_path) + "Unable to set up error log %s (access denied)", err_log_path) async def async_mount_local_lib_path(config_dir: str) -> str: diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index 0a4dd6bde784e6..63977ed88c7c76 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -26,20 +26,6 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}' -SERVICE_TO_METHOD = { - SERVICE_ALARM_DISARM: 'alarm_disarm', - SERVICE_ALARM_ARM_HOME: 'alarm_arm_home', - SERVICE_ALARM_ARM_AWAY: 'alarm_arm_away', - SERVICE_ALARM_ARM_NIGHT: 'alarm_arm_night', - SERVICE_ALARM_ARM_CUSTOM_BYPASS: 'alarm_arm_custom_bypass', - SERVICE_ALARM_TRIGGER: 'alarm_trigger' -} - -ATTR_TO_PROPERTY = [ - ATTR_CODE, - ATTR_CODE_FORMAT -] - ALARM_SERVICE_SCHEMA = vol.Schema({ vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Optional(ATTR_CODE): cv.string, @@ -126,36 +112,36 @@ def async_setup(hass, config): yield from component.async_setup(config) - @asyncio.coroutine - def async_alarm_service_handler(service): - """Map services to methods on Alarm.""" - target_alarms = component.async_extract_from_service(service) - - code = service.data.get(ATTR_CODE) - - method = "async_{}".format(SERVICE_TO_METHOD[service.service]) - - update_tasks = [] - for alarm in target_alarms: - yield from getattr(alarm, method)(code) - - if not alarm.should_poll: - continue - update_tasks.append(alarm.async_update_ha_state(True)) - - if update_tasks: - yield from asyncio.wait(update_tasks, loop=hass.loop) - - for service in SERVICE_TO_METHOD: - hass.services.async_register( - DOMAIN, service, async_alarm_service_handler, - schema=ALARM_SERVICE_SCHEMA) + component.async_register_entity_service( + SERVICE_ALARM_DISARM, ALARM_SERVICE_SCHEMA, + 'async_alarm_disarm' + ) + component.async_register_entity_service( + SERVICE_ALARM_ARM_HOME, ALARM_SERVICE_SCHEMA, + 'async_alarm_arm_home' + ) + component.async_register_entity_service( + SERVICE_ALARM_ARM_AWAY, ALARM_SERVICE_SCHEMA, + 'async_alarm_arm_away' + ) + component.async_register_entity_service( + SERVICE_ALARM_ARM_NIGHT, ALARM_SERVICE_SCHEMA, + 'async_alarm_arm_night' + ) + component.async_register_entity_service( + SERVICE_ALARM_ARM_CUSTOM_BYPASS, ALARM_SERVICE_SCHEMA, + 'async_alarm_arm_custom_bypass' + ) + component.async_register_entity_service( + SERVICE_ALARM_TRIGGER, ALARM_SERVICE_SCHEMA, + 'async_alarm_trigger' + ) return True async def async_setup_entry(hass, entry): - """Setup a config entry.""" + """Set up a config entry.""" return await hass.data[DOMAIN].async_setup_entry(entry) diff --git a/homeassistant/components/alarm_control_panel/abode.py b/homeassistant/components/alarm_control_panel/abode.py index 75eb9fd824d36a..c57666d4fe6751 100644 --- a/homeassistant/components/alarm_control_panel/abode.py +++ b/homeassistant/components/alarm_control_panel/abode.py @@ -20,7 +20,7 @@ ICON = 'mdi:security' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an alarm control panel for an Abode device.""" data = hass.data[ABODE_DOMAIN] @@ -28,7 +28,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data.devices.extend(alarm_devices) - add_devices(alarm_devices) + add_entities(alarm_devices) class AbodeAlarm(AbodeDevice, AlarmControlPanel): diff --git a/homeassistant/components/alarm_control_panel/alarmdecoder.py b/homeassistant/components/alarm_control_panel/alarmdecoder.py index 626022e362a5bc..5606209d1e65cf 100644 --- a/homeassistant/components/alarm_control_panel/alarmdecoder.py +++ b/homeassistant/components/alarm_control_panel/alarmdecoder.py @@ -26,10 +26,10 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up for AlarmDecoder alarm panels.""" device = AlarmDecoderAlarmPanel() - add_devices([device]) + add_entities([device]) def alarm_toggle_chime_handler(service): """Register toggle chime handler.""" diff --git a/homeassistant/components/alarm_control_panel/alarmdotcom.py b/homeassistant/components/alarm_control_panel/alarmdotcom.py index 736334c956ae14..98766deb3b6af8 100644 --- a/homeassistant/components/alarm_control_panel/alarmdotcom.py +++ b/homeassistant/components/alarm_control_panel/alarmdotcom.py @@ -33,7 +33,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up a Alarm.com control panel.""" name = config.get(CONF_NAME) code = config.get(CONF_CODE) @@ -42,7 +43,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): alarmdotcom = AlarmDotCom(hass, name, code, username, password) yield from alarmdotcom.async_login() - async_add_devices([alarmdotcom]) + async_add_entities([alarmdotcom]) class AlarmDotCom(alarm.AlarmControlPanel): diff --git a/homeassistant/components/alarm_control_panel/arlo.py b/homeassistant/components/alarm_control_panel/arlo.py index 0f8913f85a01c7..8842c710a05a5c 100644 --- a/homeassistant/components/alarm_control_panel/arlo.py +++ b/homeassistant/components/alarm_control_panel/arlo.py @@ -38,7 +38,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Arlo Alarm Control Panels.""" arlo = hass.data[DATA_ARLO] @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for base_station in arlo.base_stations: base_stations.append(ArloBaseStation(base_station, home_mode_name, away_mode_name)) - add_devices(base_stations, True) + add_entities(base_stations, True) class ArloBaseStation(AlarmControlPanel): diff --git a/homeassistant/components/alarm_control_panel/canary.py b/homeassistant/components/alarm_control_panel/canary.py index 3cd44dcc84ca95..b22a76fdb3b0aa 100644 --- a/homeassistant/components/alarm_control_panel/canary.py +++ b/homeassistant/components/alarm_control_panel/canary.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Canary alarms.""" data = hass.data[DATA_CANARY] devices = [] @@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for location in data.locations: devices.append(CanaryAlarm(data, location.location_id)) - add_devices(devices, True) + add_entities(devices, True) class CanaryAlarm(AlarmControlPanel): diff --git a/homeassistant/components/alarm_control_panel/concord232.py b/homeassistant/components/alarm_control_panel/concord232.py index 9a65fdaff06d10..e3c2b4a7ec7e02 100644 --- a/homeassistant/components/alarm_control_panel/concord232.py +++ b/homeassistant/components/alarm_control_panel/concord232.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Concord232 alarm control panel platform.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): url = 'http://{}:{}'.format(host, port) try: - add_devices([Concord232Alarm(hass, url, name)]) + add_entities([Concord232Alarm(hass, url, name)]) except requests.exceptions.ConnectionError as ex: _LOGGER.error("Unable to connect to Concord232: %s", str(ex)) return diff --git a/homeassistant/components/alarm_control_panel/demo.py b/homeassistant/components/alarm_control_panel/demo.py index d2366e5836c91e..a3fbe49477e53e 100644 --- a/homeassistant/components/alarm_control_panel/demo.py +++ b/homeassistant/components/alarm_control_panel/demo.py @@ -13,9 +13,9 @@ CONF_PENDING_TIME, CONF_TRIGGER_TIME) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Demo alarm control panel platform.""" - add_devices([ + add_entities([ manual.ManualAlarm(hass, 'Alarm', '1234', None, False, { STATE_ALARM_ARMED_AWAY: { CONF_DELAY_TIME: datetime.timedelta(seconds=0), diff --git a/homeassistant/components/alarm_control_panel/egardia.py b/homeassistant/components/alarm_control_panel/egardia.py index f0db378ec15d25..4e278c10e074fd 100644 --- a/homeassistant/components/alarm_control_panel/egardia.py +++ b/homeassistant/components/alarm_control_panel/egardia.py @@ -34,7 +34,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Egardia platform.""" if discovery_info is None: return @@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): discovery_info.get(CONF_REPORT_SERVER_CODES), discovery_info[CONF_REPORT_SERVER_PORT]) # add egardia alarm device - add_devices([device], True) + add_entities([device], True) class EgardiaAlarm(alarm.AlarmControlPanel): diff --git a/homeassistant/components/alarm_control_panel/envisalink.py b/homeassistant/components/alarm_control_panel/envisalink.py index 25224484c797ad..df91884b32c38a 100644 --- a/homeassistant/components/alarm_control_panel/envisalink.py +++ b/homeassistant/components/alarm_control_panel/envisalink.py @@ -33,7 +33,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Perform the setup for Envisalink alarm panels.""" configured_partitions = discovery_info['partitions'] code = discovery_info[CONF_CODE] @@ -53,7 +54,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): ) devices.append(device) - async_add_devices(devices) + async_add_entities(devices) @callback def alarm_keypress_handler(service): diff --git a/homeassistant/components/alarm_control_panel/homematicip_cloud.py b/homeassistant/components/alarm_control_panel/homematicip_cloud.py index 79f872951dbe22..8c483121650c2a 100644 --- a/homeassistant/components/alarm_control_panel/homematicip_cloud.py +++ b/homeassistant/components/alarm_control_panel/homematicip_cloud.py @@ -1,36 +1,35 @@ """ -Support for HomematicIP alarm control panel. +Support for HomematicIP Cloud alarm control panel. -For more details about this component, please refer to the documentation at +For more details about this platform, please refer to the documentation at https://home-assistant.io/components/alarm_control_panel.homematicip_cloud/ """ import logging +from homeassistant.components.alarm_control_panel import AlarmControlPanel +from homeassistant.components.homematicip_cloud import ( + HMIPC_HAPID, HomematicipGenericDevice) +from homeassistant.components.homematicip_cloud import DOMAIN as HMIPC_DOMAIN from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED) -from homeassistant.components.alarm_control_panel import AlarmControlPanel -from homeassistant.components.homematicip_cloud import ( - HomematicipGenericDevice, DOMAIN as HMIPC_DOMAIN, - HMIPC_HAPID) +_LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['homematicip_cloud'] -_LOGGER = logging.getLogger(__name__) - HMIP_ZONE_AWAY = 'EXTERNAL' HMIP_ZONE_HOME = 'INTERNAL' -async def async_setup_platform(hass, config, async_add_devices, - discovery_info=None): - """Set up the HomematicIP alarm control devices.""" +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the HomematicIP Cloud alarm control devices.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the HomematicIP alarm control panel from a config entry.""" from homematicip.aio.group import AsyncSecurityZoneGroup @@ -41,11 +40,11 @@ async def async_setup_entry(hass, config_entry, async_add_devices): devices.append(HomematicipSecurityZone(home, group)) if devices: - async_add_devices(devices) + async_add_entities(devices) class HomematicipSecurityZone(HomematicipGenericDevice, AlarmControlPanel): - """Representation of an HomematicIP security zone group.""" + """Representation of an HomematicIP Cloud security zone group.""" def __init__(self, home, device): """Initialize the security zone group.""" diff --git a/homeassistant/components/alarm_control_panel/ialarm.py b/homeassistant/components/alarm_control_panel/ialarm.py index 2dc7e11d21ebd0..3f41ee579024e7 100644 --- a/homeassistant/components/alarm_control_panel/ialarm.py +++ b/homeassistant/components/alarm_control_panel/ialarm.py @@ -40,7 +40,7 @@ def no_application_protocol(value): }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an iAlarm control panel.""" name = config.get(CONF_NAME) username = config.get(CONF_USERNAME) @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): url = 'http://{}'.format(host) ialarm = IAlarmPanel(name, username, password, url) - add_devices([ialarm], True) + add_entities([ialarm], True) class IAlarmPanel(alarm.AlarmControlPanel): diff --git a/homeassistant/components/alarm_control_panel/ifttt.py b/homeassistant/components/alarm_control_panel/ifttt.py index 9941f70a2e4a8e..49c5dc488c0d21 100644 --- a/homeassistant/components/alarm_control_panel/ifttt.py +++ b/homeassistant/components/alarm_control_panel/ifttt.py @@ -59,7 +59,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a control panel managed through IFTTT.""" if DATA_IFTTT_ALARM not in hass.data: hass.data[DATA_IFTTT_ALARM] = [] @@ -75,7 +75,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): alarmpanel = IFTTTAlarmPanel(name, code, event_away, event_home, event_night, event_disarm, optimistic) hass.data[DATA_IFTTT_ALARM].append(alarmpanel) - add_devices([alarmpanel]) + add_entities([alarmpanel]) async def push_state_update(service): """Set the service state as device state attribute.""" diff --git a/homeassistant/components/alarm_control_panel/manual.py b/homeassistant/components/alarm_control_panel/manual.py index b2b7c45d410146..41f7d6988a8ad1 100644 --- a/homeassistant/components/alarm_control_panel/manual.py +++ b/homeassistant/components/alarm_control_panel/manual.py @@ -103,9 +103,9 @@ def _state_schema(state): }, _state_validator)) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the manual alarm platform.""" - add_devices([ManualAlarm( + add_entities([ManualAlarm( hass, config[CONF_NAME], config.get(CONF_CODE), diff --git a/homeassistant/components/alarm_control_panel/manual_mqtt.py b/homeassistant/components/alarm_control_panel/manual_mqtt.py index 942d0dc159a8c5..7bf9443424cd18 100644 --- a/homeassistant/components/alarm_control_panel/manual_mqtt.py +++ b/homeassistant/components/alarm_control_panel/manual_mqtt.py @@ -123,9 +123,9 @@ def _state_schema(state): }), _state_validator)) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the manual MQTT alarm platform.""" - add_devices([ManualMQTTAlarm( + add_entities([ManualMQTTAlarm( hass, config[CONF_NAME], config.get(CONF_CODE), diff --git a/homeassistant/components/alarm_control_panel/mqtt.py b/homeassistant/components/alarm_control_panel/mqtt.py index 54b85ffbe232a4..e5ad54c4147910 100644 --- a/homeassistant/components/alarm_control_panel/mqtt.py +++ b/homeassistant/components/alarm_control_panel/mqtt.py @@ -47,12 +47,13 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the MQTT Alarm Control Panel platform.""" if discovery_info is not None: config = PLATFORM_SCHEMA(discovery_info) - async_add_devices([MqttAlarm( + async_add_entities([MqttAlarm( config.get(CONF_NAME), config.get(CONF_STATE_TOPIC), config.get(CONF_COMMAND_TOPIC), diff --git a/homeassistant/components/alarm_control_panel/nx584.py b/homeassistant/components/alarm_control_panel/nx584.py index ca6f1a44a6f4c6..67ec73bceba5f0 100644 --- a/homeassistant/components/alarm_control_panel/nx584.py +++ b/homeassistant/components/alarm_control_panel/nx584.py @@ -31,7 +31,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the NX584 platform.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) @@ -40,7 +40,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): url = 'http://{}:{}'.format(host, port) try: - add_devices([NX584Alarm(hass, url, name)]) + add_entities([NX584Alarm(hass, url, name)]) except requests.exceptions.ConnectionError as ex: _LOGGER.error("Unable to connect to NX584: %s", str(ex)) return False diff --git a/homeassistant/components/alarm_control_panel/satel_integra.py b/homeassistant/components/alarm_control_panel/satel_integra.py index 4ac3a93fff4b73..86603763396a52 100644 --- a/homeassistant/components/alarm_control_panel/satel_integra.py +++ b/homeassistant/components/alarm_control_panel/satel_integra.py @@ -19,14 +19,15 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up for Satel Integra alarm panels.""" if not discovery_info: return device = SatelIntegraAlarmPanel( "Alarm Panel", discovery_info.get(CONF_ARM_HOME_MODE)) - async_add_devices([device]) + async_add_entities([device]) class SatelIntegraAlarmPanel(alarm.AlarmControlPanel): diff --git a/homeassistant/components/alarm_control_panel/simplisafe.py b/homeassistant/components/alarm_control_panel/simplisafe.py index b400a927b5e475..2c3b25330d9309 100644 --- a/homeassistant/components/alarm_control_panel/simplisafe.py +++ b/homeassistant/components/alarm_control_panel/simplisafe.py @@ -34,7 +34,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SimpliSafe platform.""" from simplipy.api import SimpliSafeApiInterface, SimpliSafeAPIException name = config.get(CONF_NAME) @@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): try: simplisafe = SimpliSafeApiInterface(username, password) except SimpliSafeAPIException: - _LOGGER.error("Failed to setup SimpliSafe") + _LOGGER.error("Failed to set up SimpliSafe") return systems = [] @@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for system in simplisafe.get_systems(): systems.append(SimpliSafeAlarm(system, name, code)) - add_devices(systems) + add_entities(systems) class SimpliSafeAlarm(AlarmControlPanel): diff --git a/homeassistant/components/alarm_control_panel/spc.py b/homeassistant/components/alarm_control_panel/spc.py index 5d5b2284bab67e..2aa157a5cadbdc 100644 --- a/homeassistant/components/alarm_control_panel/spc.py +++ b/homeassistant/components/alarm_control_panel/spc.py @@ -29,7 +29,8 @@ def _get_alarm_state(spc_mode): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the SPC alarm control panel platform.""" if (discovery_info is None or discovery_info[ATTR_DISCOVER_AREAS] is None): @@ -39,7 +40,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): devices = [SpcAlarm(api, area) for area in discovery_info[ATTR_DISCOVER_AREAS]] - async_add_devices(devices) + async_add_entities(devices) class SpcAlarm(alarm.AlarmControlPanel): diff --git a/homeassistant/components/alarm_control_panel/totalconnect.py b/homeassistant/components/alarm_control_panel/totalconnect.py index 674eac97f8c590..f594a798dce42e 100644 --- a/homeassistant/components/alarm_control_panel/totalconnect.py +++ b/homeassistant/components/alarm_control_panel/totalconnect.py @@ -31,14 +31,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a TotalConnect control panel.""" name = config.get(CONF_NAME) username = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) total_connect = TotalConnect(name, username, password) - add_devices([total_connect], True) + add_entities([total_connect], True) class TotalConnect(alarm.AlarmControlPanel): diff --git a/homeassistant/components/alarm_control_panel/verisure.py b/homeassistant/components/alarm_control_panel/verisure.py index 59bfe15fa9b4cd..f5a631df3907b6 100644 --- a/homeassistant/components/alarm_control_panel/verisure.py +++ b/homeassistant/components/alarm_control_panel/verisure.py @@ -17,13 +17,13 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Verisure platform.""" alarms = [] if int(hub.config.get(CONF_ALARM, 1)): hub.update_overview() alarms.append(VerisureAlarm()) - add_devices(alarms) + add_entities(alarms) def set_arm_state(state, code=None): diff --git a/homeassistant/components/alarm_control_panel/wink.py b/homeassistant/components/alarm_control_panel/wink.py index 771d157efe0e02..d75fad30c96b68 100644 --- a/homeassistant/components/alarm_control_panel/wink.py +++ b/homeassistant/components/alarm_control_panel/wink.py @@ -20,7 +20,7 @@ STATE_ALARM_PRIVACY = 'Private' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink platform.""" import pywink @@ -32,7 +32,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): except AttributeError: _id = camera.object_id() + camera.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkCameraDevice(camera, hass)]) + add_entities([WinkCameraDevice(camera, hass)]) class WinkCameraDevice(WinkDevice, alarm.AlarmControlPanel): diff --git a/homeassistant/components/alert.py b/homeassistant/components/alert.py index 80a02b3275d6d2..3ec01fc6ab8e47 100644 --- a/homeassistant/components/alert.py +++ b/homeassistant/components/alert.py @@ -111,6 +111,7 @@ def async_handle_alert_service(service_call): for alert_id in alert_ids: alert = all_alerts[alert_id] + alert.async_set_context(service_call.context) if service_call.service == SERVICE_TURN_ON: yield from alert.async_turn_on() elif service_call.service == SERVICE_TOGGLE: diff --git a/homeassistant/components/alexa/smart_home.py b/homeassistant/components/alexa/smart_home.py index 042d878fceb023..eab725c4653efc 100644 --- a/homeassistant/components/alexa/smart_home.py +++ b/homeassistant/components/alexa/smart_home.py @@ -13,12 +13,13 @@ from homeassistant.util.temperature import convert as convert_temperature from homeassistant.util.decorator import Registry from homeassistant.const import ( - ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, CONF_NAME, - SERVICE_LOCK, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, - SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP, + ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, + ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, SERVICE_LOCK, + SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, + SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP, SERVICE_SET_COVER_POSITION, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_UNLOCK, SERVICE_VOLUME_SET, TEMP_FAHRENHEIT, TEMP_CELSIUS, - CONF_UNIT_OF_MEASUREMENT, STATE_LOCKED, STATE_UNLOCKED, STATE_ON) + STATE_LOCKED, STATE_UNLOCKED, STATE_ON) from .const import CONF_FILTER, CONF_ENTITY_CONFIG @@ -53,6 +54,7 @@ HANDLERS = Registry() ENTITY_ADAPTERS = Registry() +EVENT_ALEXA_SMART_HOME = 'alexa_smart_home' class _DisplayCategory: @@ -159,7 +161,8 @@ class _AlexaEntity: The API handlers should manipulate entities only through this interface. """ - def __init__(self, config, entity): + def __init__(self, hass, config, entity): + self.hass = hass self.config = config self.entity = entity self.entity_conf = config.entity_config.get(entity.entity_id, {}) @@ -383,6 +386,10 @@ def name(self): class _AlexaTemperatureSensor(_AlexaInterface): + def __init__(self, hass, entity): + _AlexaInterface.__init__(self, entity) + self.hass = hass + def name(self): return 'Alexa.TemperatureSensor' @@ -396,9 +403,10 @@ def get_property(self, name): if name != 'temperature': raise _UnsupportedProperty(name) - unit = self.entity.attributes[CONF_UNIT_OF_MEASUREMENT] + unit = self.entity.attributes.get(ATTR_UNIT_OF_MEASUREMENT) temp = self.entity.state if self.entity.domain == climate.DOMAIN: + unit = self.hass.config.units.temperature_unit temp = self.entity.attributes.get( climate.ATTR_CURRENT_TEMPERATURE) return { @@ -408,6 +416,10 @@ def get_property(self, name): class _AlexaThermostatController(_AlexaInterface): + def __init__(self, hass, entity): + _AlexaInterface.__init__(self, entity) + self.hass = hass + def name(self): return 'Alexa.ThermostatController' @@ -438,8 +450,7 @@ def get_property(self, name): raise _UnsupportedProperty(name) return mode - unit = self.entity.attributes[CONF_UNIT_OF_MEASUREMENT] - temp = None + unit = self.hass.config.units.temperature_unit if name == 'targetSetpoint': temp = self.entity.attributes.get(climate.ATTR_TEMPERATURE) elif name == 'lowerSetpoint': @@ -490,8 +501,8 @@ def default_display_categories(self): return [_DisplayCategory.THERMOSTAT] def interfaces(self): - yield _AlexaThermostatController(self.entity) - yield _AlexaTemperatureSensor(self.entity) + yield _AlexaThermostatController(self.hass, self.entity) + yield _AlexaTemperatureSensor(self.hass, self.entity) @ENTITY_ADAPTERS.register(cover.DOMAIN) @@ -608,11 +619,11 @@ def default_display_categories(self): def interfaces(self): attrs = self.entity.attributes - if attrs.get(CONF_UNIT_OF_MEASUREMENT) in ( + if attrs.get(ATTR_UNIT_OF_MEASUREMENT) in ( TEMP_FAHRENHEIT, TEMP_CELSIUS, ): - yield _AlexaTemperatureSensor(self.entity) + yield _AlexaTemperatureSensor(self.hass, self.entity) class _Cause: @@ -703,24 +714,47 @@ def post(self, request): return b'' if response is None else self.json(response) -@asyncio.coroutine -def async_handle_message(hass, config, message): +async def async_handle_message(hass, config, request, context=None): """Handle incoming API messages.""" - assert message[API_DIRECTIVE][API_HEADER]['payloadVersion'] == '3' + assert request[API_DIRECTIVE][API_HEADER]['payloadVersion'] == '3' + + if context is None: + context = ha.Context() # Read head data - message = message[API_DIRECTIVE] - namespace = message[API_HEADER]['namespace'] - name = message[API_HEADER]['name'] + request = request[API_DIRECTIVE] + namespace = request[API_HEADER]['namespace'] + name = request[API_HEADER]['name'] # Do we support this API request? funct_ref = HANDLERS.get((namespace, name)) - if not funct_ref: + if funct_ref: + response = await funct_ref(hass, config, request, context) + else: _LOGGER.warning( "Unsupported API request %s/%s", namespace, name) - return api_error(message) + response = api_error(request) - return (yield from funct_ref(hass, config, message)) + request_info = { + 'namespace': namespace, + 'name': name, + } + + if API_ENDPOINT in request and 'endpointId' in request[API_ENDPOINT]: + request_info['entity_id'] = \ + request[API_ENDPOINT]['endpointId'].replace('#', '.') + + response_header = response[API_EVENT][API_HEADER] + + hass.bus.async_fire(EVENT_ALEXA_SMART_HOME, { + 'request': request_info, + 'response': { + 'namespace': response_header['namespace'], + 'name': response_header['name'], + } + }, context=context) + + return response def api_message(request, @@ -784,8 +818,7 @@ def api_error(request, @HANDLERS.register(('Alexa.Discovery', 'Discover')) -@asyncio.coroutine -def async_api_discovery(hass, config, request): +async def async_api_discovery(hass, config, request, context): """Create a API formatted discovery response. Async friendly. @@ -800,7 +833,7 @@ def async_api_discovery(hass, config, request): if entity.domain not in ENTITY_ADAPTERS: continue - alexa_entity = ENTITY_ADAPTERS[entity.domain](config, entity) + alexa_entity = ENTITY_ADAPTERS[entity.domain](hass, config, entity) endpoint = { 'displayCategories': alexa_entity.display_categories(), @@ -827,8 +860,7 @@ def async_api_discovery(hass, config, request): def extract_entity(funct): """Decorate for extract entity object from request.""" - @asyncio.coroutine - def async_api_entity_wrapper(hass, config, request): + async def async_api_entity_wrapper(hass, config, request, context): """Process a turn on request.""" entity_id = request[API_ENDPOINT]['endpointId'].replace('#', '.') @@ -839,15 +871,14 @@ def async_api_entity_wrapper(hass, config, request): request[API_HEADER]['name'], entity_id) return api_error(request, error_type='NO_SUCH_ENDPOINT') - return (yield from funct(hass, config, request, entity)) + return await funct(hass, config, request, context, entity) return async_api_entity_wrapper @HANDLERS.register(('Alexa.PowerController', 'TurnOn')) @extract_entity -@asyncio.coroutine -def async_api_turn_on(hass, config, request, entity): +async def async_api_turn_on(hass, config, request, context, entity): """Process a turn on request.""" domain = entity.domain if entity.domain == group.DOMAIN: @@ -857,17 +888,16 @@ def async_api_turn_on(hass, config, request, entity): if entity.domain == cover.DOMAIN: service = cover.SERVICE_OPEN_COVER - yield from hass.services.async_call(domain, service, { + await hass.services.async_call(domain, service, { ATTR_ENTITY_ID: entity.entity_id - }, blocking=False) + }, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.PowerController', 'TurnOff')) @extract_entity -@asyncio.coroutine -def async_api_turn_off(hass, config, request, entity): +async def async_api_turn_off(hass, config, request, context, entity): """Process a turn off request.""" domain = entity.domain if entity.domain == group.DOMAIN: @@ -877,32 +907,30 @@ def async_api_turn_off(hass, config, request, entity): if entity.domain == cover.DOMAIN: service = cover.SERVICE_CLOSE_COVER - yield from hass.services.async_call(domain, service, { + await hass.services.async_call(domain, service, { ATTR_ENTITY_ID: entity.entity_id - }, blocking=False) + }, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.BrightnessController', 'SetBrightness')) @extract_entity -@asyncio.coroutine -def async_api_set_brightness(hass, config, request, entity): +async def async_api_set_brightness(hass, config, request, context, entity): """Process a set brightness request.""" brightness = int(request[API_PAYLOAD]['brightness']) - yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, { + await hass.services.async_call(entity.domain, SERVICE_TURN_ON, { ATTR_ENTITY_ID: entity.entity_id, light.ATTR_BRIGHTNESS_PCT: brightness, - }, blocking=False) + }, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.BrightnessController', 'AdjustBrightness')) @extract_entity -@asyncio.coroutine -def async_api_adjust_brightness(hass, config, request, entity): +async def async_api_adjust_brightness(hass, config, request, context, entity): """Process an adjust brightness request.""" brightness_delta = int(request[API_PAYLOAD]['brightnessDelta']) @@ -915,18 +943,17 @@ def async_api_adjust_brightness(hass, config, request, entity): # set brightness brightness = max(0, brightness_delta + current) - yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, { + await hass.services.async_call(entity.domain, SERVICE_TURN_ON, { ATTR_ENTITY_ID: entity.entity_id, light.ATTR_BRIGHTNESS_PCT: brightness, - }, blocking=False) + }, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.ColorController', 'SetColor')) @extract_entity -@asyncio.coroutine -def async_api_set_color(hass, config, request, entity): +async def async_api_set_color(hass, config, request, context, entity): """Process a set color request.""" rgb = color_util.color_hsb_to_RGB( float(request[API_PAYLOAD]['color']['hue']), @@ -934,25 +961,25 @@ def async_api_set_color(hass, config, request, entity): float(request[API_PAYLOAD]['color']['brightness']) ) - yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, { + await hass.services.async_call(entity.domain, SERVICE_TURN_ON, { ATTR_ENTITY_ID: entity.entity_id, light.ATTR_RGB_COLOR: rgb, - }, blocking=False) + }, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.ColorTemperatureController', 'SetColorTemperature')) @extract_entity -@asyncio.coroutine -def async_api_set_color_temperature(hass, config, request, entity): +async def async_api_set_color_temperature(hass, config, request, context, + entity): """Process a set color temperature request.""" kelvin = int(request[API_PAYLOAD]['colorTemperatureInKelvin']) - yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, { + await hass.services.async_call(entity.domain, SERVICE_TURN_ON, { ATTR_ENTITY_ID: entity.entity_id, light.ATTR_KELVIN: kelvin, - }, blocking=False) + }, blocking=False, context=context) return api_message(request) @@ -960,17 +987,17 @@ def async_api_set_color_temperature(hass, config, request, entity): @HANDLERS.register( ('Alexa.ColorTemperatureController', 'DecreaseColorTemperature')) @extract_entity -@asyncio.coroutine -def async_api_decrease_color_temp(hass, config, request, entity): +async def async_api_decrease_color_temp(hass, config, request, context, + entity): """Process a decrease color temperature request.""" current = int(entity.attributes.get(light.ATTR_COLOR_TEMP)) max_mireds = int(entity.attributes.get(light.ATTR_MAX_MIREDS)) value = min(max_mireds, current + 50) - yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, { + await hass.services.async_call(entity.domain, SERVICE_TURN_ON, { ATTR_ENTITY_ID: entity.entity_id, light.ATTR_COLOR_TEMP: value, - }, blocking=False) + }, blocking=False, context=context) return api_message(request) @@ -978,31 +1005,30 @@ def async_api_decrease_color_temp(hass, config, request, entity): @HANDLERS.register( ('Alexa.ColorTemperatureController', 'IncreaseColorTemperature')) @extract_entity -@asyncio.coroutine -def async_api_increase_color_temp(hass, config, request, entity): +async def async_api_increase_color_temp(hass, config, request, context, + entity): """Process an increase color temperature request.""" current = int(entity.attributes.get(light.ATTR_COLOR_TEMP)) min_mireds = int(entity.attributes.get(light.ATTR_MIN_MIREDS)) value = max(min_mireds, current - 50) - yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, { + await hass.services.async_call(entity.domain, SERVICE_TURN_ON, { ATTR_ENTITY_ID: entity.entity_id, light.ATTR_COLOR_TEMP: value, - }, blocking=False) + }, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.SceneController', 'Activate')) @extract_entity -@asyncio.coroutine -def async_api_activate(hass, config, request, entity): +async def async_api_activate(hass, config, request, context, entity): """Process an activate request.""" domain = entity.domain - yield from hass.services.async_call(domain, SERVICE_TURN_ON, { + await hass.services.async_call(domain, SERVICE_TURN_ON, { ATTR_ENTITY_ID: entity.entity_id - }, blocking=False) + }, blocking=False, context=context) payload = { 'cause': {'type': _Cause.VOICE_INTERACTION}, @@ -1019,14 +1045,13 @@ def async_api_activate(hass, config, request, entity): @HANDLERS.register(('Alexa.SceneController', 'Deactivate')) @extract_entity -@asyncio.coroutine -def async_api_deactivate(hass, config, request, entity): +async def async_api_deactivate(hass, config, request, context, entity): """Process a deactivate request.""" domain = entity.domain - yield from hass.services.async_call(domain, SERVICE_TURN_OFF, { + await hass.services.async_call(domain, SERVICE_TURN_OFF, { ATTR_ENTITY_ID: entity.entity_id - }, blocking=False) + }, blocking=False, context=context) payload = { 'cause': {'type': _Cause.VOICE_INTERACTION}, @@ -1043,8 +1068,7 @@ def async_api_deactivate(hass, config, request, entity): @HANDLERS.register(('Alexa.PercentageController', 'SetPercentage')) @extract_entity -@asyncio.coroutine -def async_api_set_percentage(hass, config, request, entity): +async def async_api_set_percentage(hass, config, request, context, entity): """Process a set percentage request.""" percentage = int(request[API_PAYLOAD]['percentage']) service = None @@ -1066,16 +1090,15 @@ def async_api_set_percentage(hass, config, request, entity): service = SERVICE_SET_COVER_POSITION data[cover.ATTR_POSITION] = percentage - yield from hass.services.async_call( - entity.domain, service, data, blocking=False) + await hass.services.async_call( + entity.domain, service, data, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.PercentageController', 'AdjustPercentage')) @extract_entity -@asyncio.coroutine -def async_api_adjust_percentage(hass, config, request, entity): +async def async_api_adjust_percentage(hass, config, request, context, entity): """Process an adjust percentage request.""" percentage_delta = int(request[API_PAYLOAD]['percentageDelta']) service = None @@ -1114,20 +1137,19 @@ def async_api_adjust_percentage(hass, config, request, entity): data[cover.ATTR_POSITION] = max(0, percentage_delta + current) - yield from hass.services.async_call( - entity.domain, service, data, blocking=False) + await hass.services.async_call( + entity.domain, service, data, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.LockController', 'Lock')) @extract_entity -@asyncio.coroutine -def async_api_lock(hass, config, request, entity): +async def async_api_lock(hass, config, request, context, entity): """Process a lock request.""" - yield from hass.services.async_call(entity.domain, SERVICE_LOCK, { + await hass.services.async_call(entity.domain, SERVICE_LOCK, { ATTR_ENTITY_ID: entity.entity_id - }, blocking=False) + }, blocking=False, context=context) # Alexa expects a lockState in the response, we don't know the actual # lockState at this point but assume it is locked. It is reported @@ -1144,20 +1166,18 @@ def async_api_lock(hass, config, request, entity): # Not supported by Alexa yet @HANDLERS.register(('Alexa.LockController', 'Unlock')) @extract_entity -@asyncio.coroutine -def async_api_unlock(hass, config, request, entity): +async def async_api_unlock(hass, config, request, context, entity): """Process an unlock request.""" - yield from hass.services.async_call(entity.domain, SERVICE_UNLOCK, { + await hass.services.async_call(entity.domain, SERVICE_UNLOCK, { ATTR_ENTITY_ID: entity.entity_id - }, blocking=False) + }, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.Speaker', 'SetVolume')) @extract_entity -@asyncio.coroutine -def async_api_set_volume(hass, config, request, entity): +async def async_api_set_volume(hass, config, request, context, entity): """Process a set volume request.""" volume = round(float(request[API_PAYLOAD]['volume'] / 100), 2) @@ -1166,17 +1186,16 @@ def async_api_set_volume(hass, config, request, entity): media_player.ATTR_MEDIA_VOLUME_LEVEL: volume, } - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, SERVICE_VOLUME_SET, - data, blocking=False) + data, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.InputController', 'SelectInput')) @extract_entity -@asyncio.coroutine -def async_api_select_input(hass, config, request, entity): +async def async_api_select_input(hass, config, request, context, entity): """Process a set input request.""" media_input = request[API_PAYLOAD]['input'] @@ -1200,17 +1219,16 @@ def async_api_select_input(hass, config, request, entity): media_player.ATTR_INPUT_SOURCE: media_input, } - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, media_player.SERVICE_SELECT_SOURCE, - data, blocking=False) + data, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.Speaker', 'AdjustVolume')) @extract_entity -@asyncio.coroutine -def async_api_adjust_volume(hass, config, request, entity): +async def async_api_adjust_volume(hass, config, request, context, entity): """Process an adjust volume request.""" volume_delta = int(request[API_PAYLOAD]['volume']) @@ -1229,17 +1247,16 @@ def async_api_adjust_volume(hass, config, request, entity): media_player.ATTR_MEDIA_VOLUME_LEVEL: volume, } - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, media_player.SERVICE_VOLUME_SET, - data, blocking=False) + data, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.StepSpeaker', 'AdjustVolume')) @extract_entity -@asyncio.coroutine -def async_api_adjust_volume_step(hass, config, request, entity): +async def async_api_adjust_volume_step(hass, config, request, context, entity): """Process an adjust volume step request.""" # media_player volume up/down service does not support specifying steps # each component handles it differently e.g. via config. @@ -1252,13 +1269,13 @@ def async_api_adjust_volume_step(hass, config, request, entity): } if volume_step > 0: - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, media_player.SERVICE_VOLUME_UP, - data, blocking=False) + data, blocking=False, context=context) elif volume_step < 0: - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, media_player.SERVICE_VOLUME_DOWN, - data, blocking=False) + data, blocking=False, context=context) return api_message(request) @@ -1266,8 +1283,7 @@ def async_api_adjust_volume_step(hass, config, request, entity): @HANDLERS.register(('Alexa.StepSpeaker', 'SetMute')) @HANDLERS.register(('Alexa.Speaker', 'SetMute')) @extract_entity -@asyncio.coroutine -def async_api_set_mute(hass, config, request, entity): +async def async_api_set_mute(hass, config, request, context, entity): """Process a set mute request.""" mute = bool(request[API_PAYLOAD]['mute']) @@ -1276,98 +1292,94 @@ def async_api_set_mute(hass, config, request, entity): media_player.ATTR_MEDIA_VOLUME_MUTED: mute, } - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, media_player.SERVICE_VOLUME_MUTE, - data, blocking=False) + data, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.PlaybackController', 'Play')) @extract_entity -@asyncio.coroutine -def async_api_play(hass, config, request, entity): +async def async_api_play(hass, config, request, context, entity): """Process a play request.""" data = { ATTR_ENTITY_ID: entity.entity_id } - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, SERVICE_MEDIA_PLAY, - data, blocking=False) + data, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.PlaybackController', 'Pause')) @extract_entity -@asyncio.coroutine -def async_api_pause(hass, config, request, entity): +async def async_api_pause(hass, config, request, context, entity): """Process a pause request.""" data = { ATTR_ENTITY_ID: entity.entity_id } - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, SERVICE_MEDIA_PAUSE, - data, blocking=False) + data, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.PlaybackController', 'Stop')) @extract_entity -@asyncio.coroutine -def async_api_stop(hass, config, request, entity): +async def async_api_stop(hass, config, request, context, entity): """Process a stop request.""" data = { ATTR_ENTITY_ID: entity.entity_id } - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, SERVICE_MEDIA_STOP, - data, blocking=False) + data, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.PlaybackController', 'Next')) @extract_entity -@asyncio.coroutine -def async_api_next(hass, config, request, entity): +async def async_api_next(hass, config, request, context, entity): """Process a next request.""" data = { ATTR_ENTITY_ID: entity.entity_id } - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, SERVICE_MEDIA_NEXT_TRACK, - data, blocking=False) + data, blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa.PlaybackController', 'Previous')) @extract_entity -@asyncio.coroutine -def async_api_previous(hass, config, request, entity): +async def async_api_previous(hass, config, request, context, entity): """Process a previous request.""" data = { ATTR_ENTITY_ID: entity.entity_id } - yield from hass.services.async_call( + await hass.services.async_call( entity.domain, SERVICE_MEDIA_PREVIOUS_TRACK, - data, blocking=False) + data, blocking=False, context=context) return api_message(request) -def api_error_temp_range(request, temp, min_temp, max_temp, unit): +def api_error_temp_range(hass, request, temp, min_temp, max_temp): """Create temperature value out of range API error response. Async friendly. """ + unit = hass.config.units.temperature_unit temp_range = { 'minimumValue': { 'value': min_temp, @@ -1388,8 +1400,9 @@ def api_error_temp_range(request, temp, min_temp, max_temp, unit): ) -def temperature_from_object(temp_obj, to_unit, interval=False): +def temperature_from_object(hass, temp_obj, interval=False): """Get temperature from Temperature object in requested unit.""" + to_unit = hass.config.units.temperature_unit from_unit = TEMP_CELSIUS temp = float(temp_obj['value']) @@ -1405,9 +1418,8 @@ def temperature_from_object(temp_obj, to_unit, interval=False): @HANDLERS.register(('Alexa.ThermostatController', 'SetTargetTemperature')) @extract_entity -async def async_api_set_target_temp(hass, config, request, entity): +async def async_api_set_target_temp(hass, config, request, context, entity): """Process a set target temperature request.""" - unit = entity.attributes[CONF_UNIT_OF_MEASUREMENT] min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP) max_temp = entity.attributes.get(climate.ATTR_MAX_TEMP) @@ -1417,48 +1429,45 @@ async def async_api_set_target_temp(hass, config, request, entity): payload = request[API_PAYLOAD] if 'targetSetpoint' in payload: - temp = temperature_from_object( - payload['targetSetpoint'], unit) + temp = temperature_from_object(hass, payload['targetSetpoint']) if temp < min_temp or temp > max_temp: return api_error_temp_range( - request, temp, min_temp, max_temp, unit) + hass, request, temp, min_temp, max_temp) data[ATTR_TEMPERATURE] = temp if 'lowerSetpoint' in payload: - temp_low = temperature_from_object( - payload['lowerSetpoint'], unit) + temp_low = temperature_from_object(hass, payload['lowerSetpoint']) if temp_low < min_temp or temp_low > max_temp: return api_error_temp_range( - request, temp_low, min_temp, max_temp, unit) + hass, request, temp_low, min_temp, max_temp) data[climate.ATTR_TARGET_TEMP_LOW] = temp_low if 'upperSetpoint' in payload: - temp_high = temperature_from_object( - payload['upperSetpoint'], unit) + temp_high = temperature_from_object(hass, payload['upperSetpoint']) if temp_high < min_temp or temp_high > max_temp: return api_error_temp_range( - request, temp_high, min_temp, max_temp, unit) + hass, request, temp_high, min_temp, max_temp) data[climate.ATTR_TARGET_TEMP_HIGH] = temp_high await hass.services.async_call( - entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False) + entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False, + context=context) return api_message(request) @HANDLERS.register(('Alexa.ThermostatController', 'AdjustTargetTemperature')) @extract_entity -async def async_api_adjust_target_temp(hass, config, request, entity): +async def async_api_adjust_target_temp(hass, config, request, context, entity): """Process an adjust target temperature request.""" - unit = entity.attributes[CONF_UNIT_OF_MEASUREMENT] min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP) max_temp = entity.attributes.get(climate.ATTR_MAX_TEMP) temp_delta = temperature_from_object( - request[API_PAYLOAD]['targetSetpointDelta'], unit, interval=True) + hass, request[API_PAYLOAD]['targetSetpointDelta'], interval=True) target_temp = float(entity.attributes.get(ATTR_TEMPERATURE)) + temp_delta if target_temp < min_temp or target_temp > max_temp: return api_error_temp_range( - request, target_temp, min_temp, max_temp, unit) + hass, request, target_temp, min_temp, max_temp) data = { ATTR_ENTITY_ID: entity.entity_id, @@ -1466,14 +1475,16 @@ async def async_api_adjust_target_temp(hass, config, request, entity): } await hass.services.async_call( - entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False) + entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False, + context=context) return api_message(request) @HANDLERS.register(('Alexa.ThermostatController', 'SetThermostatMode')) @extract_entity -async def async_api_set_thermostat_mode(hass, config, request, entity): +async def async_api_set_thermostat_mode(hass, config, request, context, + entity): """Process a set thermostat mode request.""" mode = request[API_PAYLOAD]['thermostatMode'] mode = mode if isinstance(mode, str) else mode['value'] @@ -1499,17 +1510,16 @@ async def async_api_set_thermostat_mode(hass, config, request, entity): await hass.services.async_call( entity.domain, climate.SERVICE_SET_OPERATION_MODE, data, - blocking=False) + blocking=False, context=context) return api_message(request) @HANDLERS.register(('Alexa', 'ReportState')) @extract_entity -@asyncio.coroutine -def async_api_reportstate(hass, config, request, entity): +async def async_api_reportstate(hass, config, request, context, entity): """Process a ReportState request.""" - alexa_entity = ENTITY_ADAPTERS[entity.domain](config, entity) + alexa_entity = ENTITY_ADAPTERS[entity.domain](hass, config, entity) properties = [] for interface in alexa_entity.interfaces(): properties.extend(interface.serialize_properties()) diff --git a/homeassistant/components/api.py b/homeassistant/components/api.py index de28eeff5ca506..0fbb4de39f1e53 100644 --- a/homeassistant/components/api.py +++ b/homeassistant/components/api.py @@ -24,7 +24,7 @@ from homeassistant.helpers import template from homeassistant.helpers.service import async_get_all_descriptions from homeassistant.helpers.state import AsyncTrackStates -import homeassistant.remote as rem +from homeassistant.helpers.json import JSONEncoder _LOGGER = logging.getLogger(__name__) @@ -102,7 +102,7 @@ async def forward_events(event): if event.event_type == EVENT_HOMEASSISTANT_STOP: data = stop_obj else: - data = json.dumps(event, cls=rem.JSONEncoder) + data = json.dumps(event, cls=JSONEncoder) await to_write.put(data) diff --git a/homeassistant/components/auth/.translations/ca.json b/homeassistant/components/auth/.translations/ca.json new file mode 100644 index 00000000000000..1b3b25dbcff66a --- /dev/null +++ b/homeassistant/components/auth/.translations/ca.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "Codi no v\u00e0lid, si us plau torni a provar-ho. Si obteniu aquest error repetidament, assegureu-vos que la data i hora de Home Assistant sigui correcta i precisa." + }, + "step": { + "init": { + "description": "Per activar la verificaci\u00f3 en dos passos mitjan\u00e7ant contrasenyes d'un sol \u00fas basades en temps, escanegeu el codi QR amb la vostre aplicaci\u00f3 de verificaci\u00f3. Si no en teniu cap, us recomanem [Google Authenticator](https://support.google.com/accounts/answer/1066447) o b\u00e9 [Authy](https://authy.com/). \n\n {qr_code} \n \nDespr\u00e9s d'escanejar el codi QR, introdu\u00efu el codi de sis d\u00edgits proporcionat per l'aplicaci\u00f3. Si teniu problemes per escanejar el codi QR, feu una configuraci\u00f3 manual amb el codi **`{code}`**.", + "title": "Configureu la verificaci\u00f3 en dos passos utilitzant TOTP" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/en.json b/homeassistant/components/auth/.translations/en.json new file mode 100644 index 00000000000000..a0fd20e9d083b1 --- /dev/null +++ b/homeassistant/components/auth/.translations/en.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "Invalid code, please try again. If you get this error consistently, please make sure the clock of your Home Assistant system is accurate." + }, + "step": { + "init": { + "description": "To activate two factor authentication using time-based one-time passwords, scan the QR code with your authentication app. If you don't have one, we recommend either [Google Authenticator](https://support.google.com/accounts/answer/1066447) or [Authy](https://authy.com/).\n\n{qr_code}\n\nAfter scanning the code, enter the six digit code from your app to verify the setup. If you have problems scanning the QR code, do a manual setup with code **`{code}`**.", + "title": "Set up two-factor authentication using TOTP" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/ko.json b/homeassistant/components/auth/.translations/ko.json new file mode 100644 index 00000000000000..726fa6a6cd1ed4 --- /dev/null +++ b/homeassistant/components/auth/.translations/ko.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "\uc798\ubabb\ub41c \ucf54\ub4dc \uc785\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694. \uc774 \uc624\ub958\uac00 \uc9c0\uc18d\uc801\uc73c\ub85c \ubc1c\uc0dd\ud55c\ub2e4\uba74 Home Assistant \uc758 \uc2dc\uacc4\uac00 \uc815\ud655\ud55c\uc9c0 \ud655\uc778\ud574\ubcf4\uc138\uc694." + }, + "step": { + "init": { + "description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574 \uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \ub098 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.", + "title": "TOTP \ub97c \uc0ac\uc6a9\ud558\uc5ec 2 \ub2e8\uacc4 \uc778\uc99d \uad6c\uc131" + } + }, + "title": "TOTP (\uc2dc\uac04 \uae30\ubc18 OTP)" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/lb.json b/homeassistant/components/auth/.translations/lb.json new file mode 100644 index 00000000000000..f55ae4b97ba0e6 --- /dev/null +++ b/homeassistant/components/auth/.translations/lb.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "Ong\u00ebltege Login, prob\u00e9iert w.e.g. nach emol. Falls d\u00ebse Feeler Message \u00ebmmer er\u00ebm optr\u00ebtt dann iwwerpr\u00e9ift op d'Z\u00e4it vum Home Assistant System richteg ass." + }, + "step": { + "init": { + "description": "Fir d'Zwee-Faktor-Authentifikatioun m\u00ebttels engem Z\u00e4it bas\u00e9ierten eemolege Passwuert z'aktiv\u00e9ieren, scannt de QR Code mat enger Authentifikatioun's App.\nFalls dir keng hutt, recommand\u00e9iere mir entweder [Google Authenticator](https://support.google.com/accounts/answer/1066447) oder [Authy](https://authy.com/).\n\n{qr_code}\n\nNodeems de Code gescannt ass, gitt de sechs stellege Code vun der App a fir d'Konfiguratioun z'iwwerpr\u00e9iwen. Am Fall vu Problemer fir de QR Code ze scannen, gitt de folgende Code **`{code}`** a fir ee manuelle Setup.", + "title": "Zwee Faktor Authentifikatioun mat TOTP konfigur\u00e9ieren" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/ru.json b/homeassistant/components/auth/.translations/ru.json new file mode 100644 index 00000000000000..b4b5b58f9fa7c5 --- /dev/null +++ b/homeassistant/components/auth/.translations/ru.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043e\u0434. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430. \u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0435 \u044d\u0442\u0443 \u043e\u0448\u0438\u0431\u043a\u0443, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0447\u0430\u0441\u044b \u0432 \u0432\u0430\u0448\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 Home Assistant \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f." + }, + "step": { + "init": { + "description": "\u0427\u0442\u043e\u0431\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0445 \u043f\u0430\u0440\u043e\u043b\u0435\u0439, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043e\u0442\u0441\u043a\u0430\u043d\u0438\u0440\u0443\u0439\u0442\u0435 QR-\u043a\u043e\u0434 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0433\u043e \u043d\u0435\u0442, \u043c\u044b \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u0438\u0431\u043e [Google Authenticator] (https://support.google.com/accounts/answer/1066447), \u043b\u0438\u0431\u043e [Authy] (https://authy.com/). \n\n {qr_code} \n \n \u041f\u043e\u0441\u043b\u0435 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f QR-\u043a\u043e\u0434\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0448\u0435\u0441\u0442\u0438\u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u0437 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c QR-\u043a\u043e\u0434\u0430, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0440\u0443\u0447\u043d\u0443\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0441 \u043a\u043e\u0434\u043e\u043c ** ` {code} ` **.", + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c TOTP" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/zh-Hans.json b/homeassistant/components/auth/.translations/zh-Hans.json new file mode 100644 index 00000000000000..c5b397a8e12a60 --- /dev/null +++ b/homeassistant/components/auth/.translations/zh-Hans.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "\u53e3\u4ee4\u65e0\u6548\uff0c\u8bf7\u91cd\u65b0\u8f93\u5165\u3002\u5982\u679c\u9519\u8bef\u53cd\u590d\u51fa\u73b0\uff0c\u8bf7\u786e\u4fdd Home Assistant \u7cfb\u7edf\u7684\u65f6\u95f4\u51c6\u786e\u65e0\u8bef\u3002" + }, + "step": { + "init": { + "description": "\u8981\u6fc0\u6d3b\u57fa\u4e8e\u65f6\u95f4\u52a8\u6001\u53e3\u4ee4\u7684\u53cc\u91cd\u8ba4\u8bc1\uff0c\u8bf7\u7528\u8eab\u4efd\u9a8c\u8bc1\u5e94\u7528\u626b\u63cf\u4ee5\u4e0b\u4e8c\u7ef4\u7801\u3002\u5982\u679c\u60a8\u8fd8\u6ca1\u6709\u8eab\u4efd\u9a8c\u8bc1\u5e94\u7528\uff0c\u63a8\u8350\u4f7f\u7528 [Google \u8eab\u4efd\u9a8c\u8bc1\u5668](https://support.google.com/accounts/answer/1066447) \u6216 [Authy](https://authy.com/)\u3002\n\n{qr_code}\n\n\u626b\u63cf\u4e8c\u7ef4\u7801\u4ee5\u540e\uff0c\u8f93\u5165\u5e94\u7528\u4e0a\u7684\u516d\u4f4d\u6570\u5b57\u53e3\u4ee4\u6765\u9a8c\u8bc1\u914d\u7f6e\u3002\u5982\u679c\u5728\u626b\u63cf\u4e8c\u7ef4\u7801\u65f6\u9047\u5230\u95ee\u9898\uff0c\u8bf7\u4f7f\u7528\u4ee3\u7801 **`{code}`** \u624b\u52a8\u914d\u7f6e\u3002", + "title": "\u7528\u65f6\u95f4\u52a8\u6001\u53e3\u4ee4\u8bbe\u7f6e\u53cc\u91cd\u8ba4\u8bc1" + } + }, + "title": "\u65f6\u95f4\u52a8\u6001\u53e3\u4ee4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/zh-Hant.json b/homeassistant/components/auth/.translations/zh-Hant.json new file mode 100644 index 00000000000000..ef41ea8724811f --- /dev/null +++ b/homeassistant/components/auth/.translations/zh-Hant.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "\u9a57\u8b49\u78bc\u7121\u6548\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002\u5047\u5982\u932f\u8aa4\u6301\u7e8c\u767c\u751f\uff0c\u8acb\u5148\u78ba\u5b9a\u60a8\u7684 Home Assistant \u7cfb\u7d71\u4e0a\u7684\u6642\u9593\u8a2d\u5b9a\u6b63\u78ba\u5f8c\uff0c\u518d\u8a66\u4e00\u6b21\u3002" + }, + "step": { + "init": { + "description": "\u6b32\u555f\u7528\u4e00\u6b21\u6027\u4e14\u5177\u6642\u6548\u6027\u7684\u5bc6\u78bc\u4e4b\u5169\u6b65\u9a5f\u9a57\u8b49\u529f\u80fd\uff0c\u8acb\u4f7f\u7528\u60a8\u7684\u9a57\u8b49 App \u6383\u7784\u4e0b\u65b9\u7684 QR code \u3002\u5018\u82e5\u60a8\u5c1a\u672a\u5b89\u88dd\u4efb\u4f55 App\uff0c\u63a8\u85a6\u60a8\u4f7f\u7528 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \u6216 [Authy](https://authy.com/)\u3002\n\n{qr_code}\n\n\u65bc\u6383\u63cf\u4e4b\u5f8c\uff0c\u8f38\u5165 App \u4e2d\u7684\u516d\u4f4d\u6578\u5b57\u9032\u884c\u8a2d\u5b9a\u9a57\u8b49\u3002\u5047\u5982\u6383\u63cf\u51fa\u73fe\u554f\u984c\uff0c\u8acb\u624b\u52d5\u8f38\u5165\u4ee5\u4e0b\u9a57\u8b49\u78bc **`{code}`**\u3002", + "title": "\u4f7f\u7528 TOTP \u8a2d\u5b9a\u5169\u6b65\u9a5f\u9a57\u8b49" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/__init__.py b/homeassistant/components/auth/__init__.py index 102bfe58b55a69..a87e646761c472 100644 --- a/homeassistant/components/auth/__init__.py +++ b/homeassistant/components/auth/__init__.py @@ -44,21 +44,36 @@ "expires_in": 1800, "token_type": "Bearer" } + +## Revoking a refresh token + +It is also possible to revoke a refresh token and all access tokens that have +ever been granted by that refresh token. Response code will ALWAYS be 200. + +{ + "token": "IJKLMNOPQRST", + "action": "revoke" +} + """ import logging import uuid from datetime import timedelta +from aiohttp import web import voluptuous as vol +from homeassistant.auth.models import User, Credentials from homeassistant.components import websocket_api from homeassistant.components.http.ban import log_invalid_auth from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.view import HomeAssistantView -from homeassistant.core import callback +from homeassistant.core import callback, HomeAssistant from homeassistant.util import dt as dt_util + from . import indieauth from . import login_flow +from . import mfa_setup_flow DOMAIN = 'auth' DEPENDENCIES = ['http'] @@ -68,37 +83,41 @@ vol.Required('type'): WS_TYPE_CURRENT_USER, }) +RESULT_TYPE_CREDENTIALS = 'credentials' +RESULT_TYPE_USER = 'user' + _LOGGER = logging.getLogger(__name__) async def async_setup(hass, config): """Component to allow users to login.""" - store_credentials, retrieve_credentials = _create_cred_store() + store_result, retrieve_result = _create_auth_code_store() - hass.http.register_view(GrantTokenView(retrieve_credentials)) - hass.http.register_view(LinkUserView(retrieve_credentials)) + hass.http.register_view(TokenView(retrieve_result)) + hass.http.register_view(LinkUserView(retrieve_result)) hass.components.websocket_api.async_register_command( WS_TYPE_CURRENT_USER, websocket_current_user, SCHEMA_WS_CURRENT_USER ) - await login_flow.async_setup(hass, store_credentials) + await login_flow.async_setup(hass, store_result) + await mfa_setup_flow.async_setup(hass) return True -class GrantTokenView(HomeAssistantView): - """View to grant tokens.""" +class TokenView(HomeAssistantView): + """View to issue or revoke tokens.""" url = '/auth/token' name = 'api:auth:token' requires_auth = False cors_allowed = True - def __init__(self, retrieve_credentials): - """Initialize the grant token view.""" - self._retrieve_credentials = retrieve_credentials + def __init__(self, retrieve_user): + """Initialize the token view.""" + self._retrieve_user = retrieve_user @log_invalid_auth async def post(self, request): @@ -108,6 +127,13 @@ async def post(self, request): grant_type = data.get('grant_type') + # IndieAuth 6.3.5 + # The revocation endpoint is the same as the token endpoint. + # The revocation request includes an additional parameter, + # action=revoke. + if data.get('action') == 'revoke': + return await self._async_handle_revoke_token(hass, data) + if grant_type == 'authorization_code': return await self._async_handle_auth_code(hass, data) @@ -118,6 +144,25 @@ async def post(self, request): 'error': 'unsupported_grant_type', }, status_code=400) + async def _async_handle_revoke_token(self, hass, data): + """Handle revoke token request.""" + # OAuth 2.0 Token Revocation [RFC7009] + # 2.2 The authorization server responds with HTTP status code 200 + # if the token has been revoked successfully or if the client + # submitted an invalid token. + token = data.get('token') + + if token is None: + return web.Response(status=200) + + refresh_token = await hass.auth.async_get_refresh_token_by_token(token) + + if refresh_token is None: + return web.Response(status=200) + + await hass.auth.async_remove_refresh_token(refresh_token) + return web.Response(status=200) + async def _async_handle_auth_code(self, hass, data): """Handle authorization code request.""" client_id = data.get('client_id') @@ -132,17 +177,19 @@ async def _async_handle_auth_code(self, hass, data): if code is None: return self.json({ 'error': 'invalid_request', + 'error_description': 'Invalid code', }, status_code=400) - credentials = self._retrieve_credentials(client_id, code) + user = self._retrieve_user(client_id, RESULT_TYPE_USER, code) - if credentials is None: + if user is None or not isinstance(user, User): return self.json({ 'error': 'invalid_request', 'error_description': 'Invalid code', }, status_code=400) - user = await hass.auth.async_get_or_create_user(credentials) + # refresh user + user = await hass.auth.async_get_user(user.id) if not user.is_active: return self.json({ @@ -220,7 +267,7 @@ async def post(self, request, data): user = request['hass_user'] credentials = self._retrieve_credentials( - data['client_id'], data['code']) + data['client_id'], RESULT_TYPE_CREDENTIALS, data['code']) if credentials is None: return self.json_message('Invalid code', status_code=400) @@ -230,54 +277,69 @@ async def post(self, request, data): @callback -def _create_cred_store(): - """Create a credential store.""" - temp_credentials = {} +def _create_auth_code_store(): + """Create an in memory store.""" + temp_results = {} @callback - def store_credentials(client_id, credentials): - """Store credentials and return a code to retrieve it.""" + def store_result(client_id, result): + """Store flow result and return a code to retrieve it.""" + if isinstance(result, User): + result_type = RESULT_TYPE_USER + elif isinstance(result, Credentials): + result_type = RESULT_TYPE_CREDENTIALS + else: + raise ValueError('result has to be either User or Credentials') + code = uuid.uuid4().hex - temp_credentials[(client_id, code)] = (dt_util.utcnow(), credentials) + temp_results[(client_id, result_type, code)] = \ + (dt_util.utcnow(), result_type, result) return code @callback - def retrieve_credentials(client_id, code): - """Retrieve credentials.""" - key = (client_id, code) + def retrieve_result(client_id, result_type, code): + """Retrieve flow result.""" + key = (client_id, result_type, code) - if key not in temp_credentials: + if key not in temp_results: return None - created, credentials = temp_credentials.pop(key) + created, _, result = temp_results.pop(key) # OAuth 4.2.1 # The authorization code MUST expire shortly after it is issued to # mitigate the risk of leaks. A maximum authorization code lifetime of # 10 minutes is RECOMMENDED. if dt_util.utcnow() - created < timedelta(minutes=10): - return credentials + return result return None - return store_credentials, retrieve_credentials + return store_result, retrieve_result +@websocket_api.ws_require_user() @callback -def websocket_current_user(hass, connection, msg): +def websocket_current_user( + hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Return the current user.""" - user = connection.request.get('hass_user') - - if user is None: - connection.to_write.put_nowait(websocket_api.error_message( - msg['id'], 'no_user', 'Not authenticated as a user')) - return - - connection.to_write.put_nowait(websocket_api.result_message(msg['id'], { - 'id': user.id, - 'name': user.name, - 'is_owner': user.is_owner, - 'credentials': [{'auth_provider_type': c.auth_provider_type, - 'auth_provider_id': c.auth_provider_id} - for c in user.credentials] - })) + async def async_get_current_user(user): + """Get current user.""" + enabled_modules = await hass.auth.async_get_enabled_mfa(user) + + connection.send_message_outside( + websocket_api.result_message(msg['id'], { + 'id': user.id, + 'name': user.name, + 'is_owner': user.is_owner, + 'credentials': [{'auth_provider_type': c.auth_provider_type, + 'auth_provider_id': c.auth_provider_id} + for c in user.credentials], + 'mfa_modules': [{ + 'id': module.id, + 'name': module.name, + 'enabled': module.id in enabled_modules, + } for module in hass.auth.auth_mfa_modules], + })) + + hass.async_create_task(async_get_current_user(connection.user)) diff --git a/homeassistant/components/auth/indieauth.py b/homeassistant/components/auth/indieauth.py index 48f7ab06ab4933..bcf73258ffa883 100644 --- a/homeassistant/components/auth/indieauth.py +++ b/homeassistant/components/auth/indieauth.py @@ -4,6 +4,7 @@ from ipaddress import ip_address, ip_network from urllib.parse import urlparse, urljoin +import aiohttp from aiohttp.client_exceptions import ClientError # IP addresses of loopback interfaces @@ -76,18 +77,17 @@ async def fetch_redirect_uris(hass, url): We do not implement extracting redirect uris from headers. """ - session = hass.helpers.aiohttp_client.async_get_clientsession() parser = LinkTagParser('redirect_uri') chunks = 0 try: - resp = await session.get(url, timeout=5) - - async for data in resp.content.iter_chunked(1024): - parser.feed(data.decode()) - chunks += 1 - - if chunks == 10: - break + async with aiohttp.ClientSession() as session: + async with session.get(url, timeout=5) as resp: + async for data in resp.content.iter_chunked(1024): + parser.feed(data.decode()) + chunks += 1 + + if chunks == 10: + break except (asyncio.TimeoutError, ClientError): pass diff --git a/homeassistant/components/auth/login_flow.py b/homeassistant/components/auth/login_flow.py index 7b80e52a8d712d..a518bdde4154c7 100644 --- a/homeassistant/components/auth/login_flow.py +++ b/homeassistant/components/auth/login_flow.py @@ -22,10 +22,14 @@ Pass in parameter 'handler' to specify the auth provider to use. Auth providers are identified by type and id. +And optional parameter 'type' has to set as 'link_user' if login flow used for +link credential to exist user. Default 'type' is 'authorize'. + { "client_id": "https://hassbian.local:8123/", "handler": ["local_provider", null], - "redirect_url": "https://hassbian.local:8123/" + "redirect_url": "https://hassbian.local:8123/", + "type': "authorize" } Return value will be a step in a data entry flow. See the docs for data entry @@ -49,6 +53,9 @@ Progress the flow. Most flows will be 1 page, but could optionally add extra login challenges, like TFA. Once the flow has finished, the returned step will have type "create_entry" and "result" key will contain an authorization code. +The authorization code associated with an authorized user by default, it will +associate with an credential if "type" set to "link_user" in +"/auth/login_flow" { "flow_id": "8f7e42faab604bcab7ac43c44ca34d58", @@ -63,6 +70,7 @@ import voluptuous as vol from homeassistant import data_entry_flow +from homeassistant.components.http import KEY_REAL_IP from homeassistant.components.http.ban import process_wrong_login, \ log_invalid_auth from homeassistant.components.http.data_validator import RequestDataValidator @@ -70,12 +78,12 @@ from . import indieauth -async def async_setup(hass, store_credentials): +async def async_setup(hass, store_result): """Component to allow users to login.""" hass.http.register_view(AuthProvidersView) hass.http.register_view(LoginFlowIndexView(hass.auth.login_flow)) hass.http.register_view( - LoginFlowResourceView(hass.auth.login_flow, store_credentials)) + LoginFlowResourceView(hass.auth.login_flow, store_result)) class AuthProvidersView(HomeAssistantView): @@ -137,6 +145,7 @@ async def get(self, request): vol.Required('client_id'): str, vol.Required('handler'): vol.Any(str, list), vol.Required('redirect_uri'): str, + vol.Optional('type', default='authorize'): str, })) @log_invalid_auth async def post(self, request, data): @@ -151,7 +160,11 @@ async def post(self, request, data): handler = data['handler'] try: - result = await self._flow_mgr.async_init(handler, context={}) + result = await self._flow_mgr.async_init( + handler, context={ + 'ip_address': request[KEY_REAL_IP], + 'credential_only': data.get('type') == 'link_user', + }) except data_entry_flow.UnknownHandler: return self.json_message('Invalid handler specified', 404) except data_entry_flow.UnknownStep: @@ -167,10 +180,10 @@ class LoginFlowResourceView(HomeAssistantView): name = 'api:auth:login_flow:resource' requires_auth = False - def __init__(self, flow_mgr, store_credentials): + def __init__(self, flow_mgr, store_result): """Initialize the login flow resource view.""" self._flow_mgr = flow_mgr - self._store_credentials = store_credentials + self._store_result = store_result async def get(self, request): """Do not allow getting status of a flow in progress.""" @@ -188,6 +201,13 @@ async def post(self, request, flow_id, data): return self.json_message('Invalid client id', 400) try: + # do not allow change ip during login flow + for flow in self._flow_mgr.async_progress(): + if (flow['flow_id'] == flow_id and + flow['context']['ip_address'] != + request.get(KEY_REAL_IP)): + return self.json_message('IP address changed', 400) + result = await self._flow_mgr.async_configure(flow_id, data) except data_entry_flow.UnknownFlow: return self.json_message('Invalid flow specified', 404) @@ -203,7 +223,7 @@ async def post(self, request, flow_id, data): return self.json(_prepare_result_json(result)) result.pop('data') - result['result'] = self._store_credentials(client_id, result['result']) + result['result'] = self._store_result(client_id, result['result']) return self.json(result) diff --git a/homeassistant/components/auth/mfa_setup_flow.py b/homeassistant/components/auth/mfa_setup_flow.py new file mode 100644 index 00000000000000..82eb913d890824 --- /dev/null +++ b/homeassistant/components/auth/mfa_setup_flow.py @@ -0,0 +1,134 @@ +"""Helpers to setup multi-factor auth module.""" +import logging + +import voluptuous as vol + +from homeassistant import data_entry_flow +from homeassistant.components import websocket_api +from homeassistant.core import callback, HomeAssistant + +WS_TYPE_SETUP_MFA = 'auth/setup_mfa' +SCHEMA_WS_SETUP_MFA = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ + vol.Required('type'): WS_TYPE_SETUP_MFA, + vol.Exclusive('mfa_module_id', 'module_or_flow_id'): str, + vol.Exclusive('flow_id', 'module_or_flow_id'): str, + vol.Optional('user_input'): object, +}) + +WS_TYPE_DEPOSE_MFA = 'auth/depose_mfa' +SCHEMA_WS_DEPOSE_MFA = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ + vol.Required('type'): WS_TYPE_DEPOSE_MFA, + vol.Required('mfa_module_id'): str, +}) + +DATA_SETUP_FLOW_MGR = 'auth_mfa_setup_flow_manager' + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup(hass): + """Init mfa setup flow manager.""" + async def _async_create_setup_flow(handler, context, data): + """Create a setup flow. hanlder is a mfa module.""" + mfa_module = hass.auth.get_auth_mfa_module(handler) + if mfa_module is None: + raise ValueError('Mfa module {} is not found'.format(handler)) + + user_id = data.pop('user_id') + return await mfa_module.async_setup_flow(user_id) + + async def _async_finish_setup_flow(flow, flow_result): + _LOGGER.debug('flow_result: %s', flow_result) + return flow_result + + hass.data[DATA_SETUP_FLOW_MGR] = data_entry_flow.FlowManager( + hass, _async_create_setup_flow, _async_finish_setup_flow) + + hass.components.websocket_api.async_register_command( + WS_TYPE_SETUP_MFA, websocket_setup_mfa, SCHEMA_WS_SETUP_MFA) + + hass.components.websocket_api.async_register_command( + WS_TYPE_DEPOSE_MFA, websocket_depose_mfa, SCHEMA_WS_DEPOSE_MFA) + + +@callback +@websocket_api.ws_require_user(allow_system_user=False) +def websocket_setup_mfa( + hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): + """Return a setup flow for mfa auth module.""" + async def async_setup_flow(msg): + """Return a setup flow for mfa auth module.""" + flow_manager = hass.data[DATA_SETUP_FLOW_MGR] + + flow_id = msg.get('flow_id') + if flow_id is not None: + result = await flow_manager.async_configure( + flow_id, msg.get('user_input')) + connection.send_message_outside( + websocket_api.result_message( + msg['id'], _prepare_result_json(result))) + return + + mfa_module_id = msg.get('mfa_module_id') + mfa_module = hass.auth.get_auth_mfa_module(mfa_module_id) + if mfa_module is None: + connection.send_message_outside(websocket_api.error_message( + msg['id'], 'no_module', + 'MFA module {} is not found'.format(mfa_module_id))) + return + + result = await flow_manager.async_init( + mfa_module_id, data={'user_id': connection.user.id}) + + connection.send_message_outside( + websocket_api.result_message( + msg['id'], _prepare_result_json(result))) + + hass.async_create_task(async_setup_flow(msg)) + + +@callback +@websocket_api.ws_require_user(allow_system_user=False) +def websocket_depose_mfa( + hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): + """Remove user from mfa module.""" + async def async_depose(msg): + """Remove user from mfa auth module.""" + mfa_module_id = msg['mfa_module_id'] + try: + await hass.auth.async_disable_user_mfa( + connection.user, msg['mfa_module_id']) + except ValueError as err: + connection.send_message_outside(websocket_api.error_message( + msg['id'], 'disable_failed', + 'Cannot disable MFA Module {}: {}'.format( + mfa_module_id, err))) + return + + connection.send_message_outside( + websocket_api.result_message( + msg['id'], 'done')) + + hass.async_create_task(async_depose(msg)) + + +def _prepare_result_json(result): + """Convert result to JSON.""" + if result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + data = result.copy() + return data + + if result['type'] != data_entry_flow.RESULT_TYPE_FORM: + return result + + import voluptuous_serialize + + data = result.copy() + + schema = data['data_schema'] + if schema is None: + data['data_schema'] = [] + else: + data['data_schema'] = voluptuous_serialize.convert(schema) + + return data diff --git a/homeassistant/components/auth/strings.json b/homeassistant/components/auth/strings.json new file mode 100644 index 00000000000000..b0083ab577b4c0 --- /dev/null +++ b/homeassistant/components/auth/strings.json @@ -0,0 +1,16 @@ +{ + "mfa_setup":{ + "totp": { + "title": "TOTP", + "step": { + "init": { + "title": "Set up two-factor authentication using TOTP", + "description": "To activate two factor authentication using time-based one-time passwords, scan the QR code with your authentication app. If you don't have one, we recommend either [Google Authenticator](https://support.google.com/accounts/answer/1066447) or [Authy](https://authy.com/).\n\n{qr_code}\n\nAfter scanning the code, enter the six digit code from your app to verify the setup. If you have problems scanning the QR code, do a manual setup with code **`{code}`**." + } + }, + "error": { + "invalid_code": "Invalid code, please try again. If you get this error consistently, please make sure the clock of your Home Assistant system is accurate." + } + } + } +} diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 8b1cd3cad84596..c6c0af90d15a45 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -1,5 +1,5 @@ """ -Allow to setup simple automation rules via the config file. +Allow to set up simple automation rules via the config file. For more details about this component, please refer to the documentation at https://home-assistant.io/components/automation/ diff --git a/homeassistant/components/binary_sensor/__init__.py b/homeassistant/components/binary_sensor/__init__.py index 26878044fe28ad..7b2da21ff6a686 100644 --- a/homeassistant/components/binary_sensor/__init__.py +++ b/homeassistant/components/binary_sensor/__init__.py @@ -58,7 +58,7 @@ async def async_setup(hass, config): async def async_setup_entry(hass, entry): - """Setup a config entry.""" + """Set up a config entry.""" return await hass.data[DOMAIN].async_setup_entry(entry) diff --git a/homeassistant/components/binary_sensor/abode.py b/homeassistant/components/binary_sensor/abode.py index 8ad401589584ec..a821abf445b67b 100644 --- a/homeassistant/components/binary_sensor/abode.py +++ b/homeassistant/components/binary_sensor/abode.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for an Abode device.""" import abodepy.helpers.constants as CONST import abodepy.helpers.timeline as TIMELINE @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data.devices.extend(devices) - add_devices(devices) + add_entities(devices) class AbodeBinarySensor(AbodeDevice, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/ads.py b/homeassistant/components/binary_sensor/ads.py index b7f0ebcc9d3da2..d46ff5ec2eee84 100644 --- a/homeassistant/components/binary_sensor/ads.py +++ b/homeassistant/components/binary_sensor/ads.py @@ -27,7 +27,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Binary Sensor platform for ADS.""" ads_hub = hass.data.get(DATA_ADS) @@ -36,7 +36,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): device_class = config.get(CONF_DEVICE_CLASS) ads_sensor = AdsBinarySensor(ads_hub, name, ads_var, device_class) - add_devices([ads_sensor]) + add_entities([ads_sensor]) class AdsBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/alarmdecoder.py b/homeassistant/components/binary_sensor/alarmdecoder.py index fcc77d474e1941..82bcc50259fcc2 100644 --- a/homeassistant/components/binary_sensor/alarmdecoder.py +++ b/homeassistant/components/binary_sensor/alarmdecoder.py @@ -28,7 +28,7 @@ ATTR_RF_LOOP1 = 'rf_loop1' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the AlarmDecoder binary sensor devices.""" configured_zones = discovery_info[CONF_ZONES] @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): zone_num, zone_name, zone_type, zone_rfid, relay_addr, relay_chan) devices.append(device) - add_devices(devices) + add_entities(devices) return True diff --git a/homeassistant/components/binary_sensor/android_ip_webcam.py b/homeassistant/components/binary_sensor/android_ip_webcam.py index b1940f432ae8c9..58de81c30e7a85 100644 --- a/homeassistant/components/binary_sensor/android_ip_webcam.py +++ b/homeassistant/components/binary_sensor/android_ip_webcam.py @@ -14,7 +14,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the IP Webcam binary sensors.""" if discovery_info is None: return @@ -23,7 +24,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): name = discovery_info[CONF_NAME] ipcam = hass.data[DATA_IP_WEBCAM][host] - async_add_devices( + async_add_entities( [IPWebcamBinarySensor(name, host, ipcam, 'motion_active')], True) diff --git a/homeassistant/components/binary_sensor/apcupsd.py b/homeassistant/components/binary_sensor/apcupsd.py index 412656a3b56677..f876b8cc34b1c2 100644 --- a/homeassistant/components/binary_sensor/apcupsd.py +++ b/homeassistant/components/binary_sensor/apcupsd.py @@ -20,9 +20,9 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an APCUPSd Online Status binary sensor.""" - add_devices([OnlineStatus(config, apcupsd.DATA)], True) + add_entities([OnlineStatus(config, apcupsd.DATA)], True) class OnlineStatus(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/arest.py b/homeassistant/components/binary_sensor/arest.py index 0366f753ba6c49..b70620df3e21b2 100644 --- a/homeassistant/components/binary_sensor/arest.py +++ b/homeassistant/components/binary_sensor/arest.py @@ -29,7 +29,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the aREST binary sensor.""" resource = config.get(CONF_RESOURCE) pin = config.get(CONF_PIN) @@ -47,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): arest = ArestData(resource, pin) - add_devices([ArestBinarySensor( + add_entities([ArestBinarySensor( arest, resource, config.get(CONF_NAME, response[CONF_NAME]), device_class, pin)], True) diff --git a/homeassistant/components/binary_sensor/august.py b/homeassistant/components/binary_sensor/august.py index 8df50a1bfb6bc7..7f5da390906d3a 100644 --- a/homeassistant/components/binary_sensor/august.py +++ b/homeassistant/components/binary_sensor/august.py @@ -53,7 +53,7 @@ def _activity_time_based_state(data, doorbell, activity_types): } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the August binary sensors.""" data = hass.data[DATA_AUGUST] devices = [] @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for sensor_type in SENSOR_TYPES: devices.append(AugustBinarySensor(data, sensor_type, doorbell)) - add_devices(devices, True) + add_entities(devices, True) class AugustBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/aurora.py b/homeassistant/components/binary_sensor/aurora.py index 0c33877854fd98..04b402722b218f 100644 --- a/homeassistant/components/binary_sensor/aurora.py +++ b/homeassistant/components/binary_sensor/aurora.py @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the aurora sensor.""" if None in (hass.config.latitude, hass.config.longitude): _LOGGER.error("Lat. or long. not set in Home Assistant config") @@ -57,7 +57,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "Connection to aurora forecast service failed: %s", error) return False - add_devices([AuroraSensor(aurora_data, name)], True) + add_entities([AuroraSensor(aurora_data, name)], True) class AuroraSensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/axis.py b/homeassistant/components/binary_sensor/axis.py index 84137d95b06bfb..b66a766ca4a527 100644 --- a/homeassistant/components/binary_sensor/axis.py +++ b/homeassistant/components/binary_sensor/axis.py @@ -18,9 +18,9 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Axis binary devices.""" - add_devices([AxisBinarySensor(hass, discovery_info)], True) + add_entities([AxisBinarySensor(hass, discovery_info)], True) class AxisBinarySensor(AxisDeviceEvent, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/bayesian.py b/homeassistant/components/binary_sensor/bayesian.py index 75906e8ac5d58c..88669d67d80310 100644 --- a/homeassistant/components/binary_sensor/bayesian.py +++ b/homeassistant/components/binary_sensor/bayesian.py @@ -75,7 +75,8 @@ def update_probability(prior, prob_true, prob_false): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Bayesian Binary sensor.""" name = config.get(CONF_NAME) observations = config.get(CONF_OBSERVATIONS) @@ -83,7 +84,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): probability_threshold = config.get(CONF_PROBABILITY_THRESHOLD) device_class = config.get(CONF_DEVICE_CLASS) - async_add_devices([ + async_add_entities([ BayesianBinarySensor( name, prior, observations, probability_threshold, device_class) ], True) diff --git a/homeassistant/components/binary_sensor/bbb_gpio.py b/homeassistant/components/binary_sensor/bbb_gpio.py index 690d1651db9f17..8968b680369838 100644 --- a/homeassistant/components/binary_sensor/bbb_gpio.py +++ b/homeassistant/components/binary_sensor/bbb_gpio.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Beaglebone Black GPIO devices.""" pins = config.get(CONF_PINS) @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for pin, params in pins.items(): binary_sensors.append(BBBGPIOBinarySensor(pin, params)) - add_devices(binary_sensors) + add_entities(binary_sensors) class BBBGPIOBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/blink.py b/homeassistant/components/binary_sensor/blink.py index 4d8617b3811135..6ade20b72b90bf 100644 --- a/homeassistant/components/binary_sensor/blink.py +++ b/homeassistant/components/binary_sensor/blink.py @@ -10,7 +10,7 @@ DEPENDENCIES = ['blink'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the blink binary sensors.""" if discovery_info is None: return @@ -20,7 +20,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for name in data.cameras: devs.append(BlinkCameraMotionSensor(name, data)) devs.append(BlinkSystemSensor(data)) - add_devices(devs, True) + add_entities(devs, True) class BlinkCameraMotionSensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/bloomsky.py b/homeassistant/components/binary_sensor/bloomsky.py index 3080cc6553251e..ecffb3accf3ab9 100644 --- a/homeassistant/components/binary_sensor/bloomsky.py +++ b/homeassistant/components/binary_sensor/bloomsky.py @@ -28,7 +28,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available BloomSky weather binary sensors.""" bloomsky = hass.components.bloomsky # Default needed in case of discovery @@ -36,7 +36,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for device in bloomsky.BLOOMSKY.devices.values(): for variable in sensors: - add_devices( + add_entities( [BloomSkySensor(bloomsky.BLOOMSKY, device, variable)], True) diff --git a/homeassistant/components/binary_sensor/bmw_connected_drive.py b/homeassistant/components/binary_sensor/bmw_connected_drive.py index 308298d1bcd103..36229828d63b34 100644 --- a/homeassistant/components/binary_sensor/bmw_connected_drive.py +++ b/homeassistant/components/binary_sensor/bmw_connected_drive.py @@ -31,7 +31,7 @@ SENSOR_TYPES_ELEC.update(SENSOR_TYPES) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the BMW sensors.""" accounts = hass.data[BMW_DOMAIN] _LOGGER.debug('Found BMW accounts: %s', @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): device = BMWConnectedDriveSensor(account, vehicle, key, value[0], value[1]) devices.append(device) - add_devices(devices, True) + add_entities(devices, True) class BMWConnectedDriveSensor(BinarySensorDevice): @@ -71,7 +71,10 @@ def __init__(self, account, vehicle, attribute: str, sensor_name, @property def should_poll(self) -> bool: - """Data update is triggered from BMWConnectedDriveEntity.""" + """Return False. + + Data update is triggered from BMWConnectedDriveEntity. + """ return False @property diff --git a/homeassistant/components/binary_sensor/command_line.py b/homeassistant/components/binary_sensor/command_line.py index c2045c2df5e035..a3f1595787d166 100644 --- a/homeassistant/components/binary_sensor/command_line.py +++ b/homeassistant/components/binary_sensor/command_line.py @@ -40,7 +40,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Command line Binary Sensor.""" name = config.get(CONF_NAME) command = config.get(CONF_COMMAND) @@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): value_template.hass = hass data = CommandSensorData(hass, command, command_timeout) - add_devices([CommandBinarySensor( + add_entities([CommandBinarySensor( hass, data, name, device_class, payload_on, payload_off, value_template)], True) diff --git a/homeassistant/components/binary_sensor/concord232.py b/homeassistant/components/binary_sensor/concord232.py index f2acef47e820d4..26f35d60305825 100644 --- a/homeassistant/components/binary_sensor/concord232.py +++ b/homeassistant/components/binary_sensor/concord232.py @@ -42,7 +42,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Concord232 binary sensor platform.""" from concord232 import client as concord232_client @@ -79,7 +79,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ) ) - add_devices(sensors, True) + add_entities(sensors, True) def get_opening_type(zone): diff --git a/homeassistant/components/binary_sensor/deconz.py b/homeassistant/components/binary_sensor/deconz.py index 0a370d754eea4d..1fb6212440715d 100644 --- a/homeassistant/components/binary_sensor/deconz.py +++ b/homeassistant/components/binary_sensor/deconz.py @@ -7,21 +7,22 @@ from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.deconz.const import ( ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DATA_DECONZ, - DATA_DECONZ_ID, DATA_DECONZ_UNSUB) + DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DECONZ_DOMAIN) from homeassistant.const import ATTR_BATTERY_LEVEL from homeassistant.core import callback +from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE from homeassistant.helpers.dispatcher import async_dispatcher_connect DEPENDENCIES = ['deconz'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Old way of setting up deCONZ binary sensors.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the deCONZ binary sensor.""" @callback def async_add_sensor(sensors): @@ -33,7 +34,7 @@ def async_add_sensor(sensors): if sensor.type in DECONZ_BINARY_SENSOR and \ not (not allow_clip_sensor and sensor.type.startswith('CLIP')): entities.append(DeconzBinarySensor(sensor)) - async_add_devices(entities, True) + async_add_entities(entities, True) hass.data[DATA_DECONZ_UNSUB].append( async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_sensor)) @@ -113,3 +114,19 @@ def device_state_attributes(self): if self._sensor.type in PRESENCE and self._sensor.dark is not None: attr[ATTR_DARK] = self._sensor.dark return attr + + @property + def device_info(self): + """Return a device description for device registry.""" + if (self._sensor.uniqueid is None or + self._sensor.uniqueid.count(':') != 7): + return None + serial = self._sensor.uniqueid.split('-', 1)[0] + return { + 'connections': {(CONNECTION_ZIGBEE, serial)}, + 'identifiers': {(DECONZ_DOMAIN, serial)}, + 'manufacturer': self._sensor.manufacturer, + 'model': self._sensor.modelid, + 'name': self._sensor.name, + 'sw_version': self._sensor.swversion, + } diff --git a/homeassistant/components/binary_sensor/demo.py b/homeassistant/components/binary_sensor/demo.py index 10077c60ed18ab..d656b79e8ed2e6 100644 --- a/homeassistant/components/binary_sensor/demo.py +++ b/homeassistant/components/binary_sensor/demo.py @@ -7,9 +7,9 @@ from homeassistant.components.binary_sensor import BinarySensorDevice -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Demo binary sensor platform.""" - add_devices([ + add_entities([ DemoBinarySensor('Basement Floor Wet', False, 'moisture'), DemoBinarySensor('Movement Backyard', True, 'motion'), ]) diff --git a/homeassistant/components/binary_sensor/digital_ocean.py b/homeassistant/components/binary_sensor/digital_ocean.py index 1eb86d4eb82bad..0f604c525e016f 100644 --- a/homeassistant/components/binary_sensor/digital_ocean.py +++ b/homeassistant/components/binary_sensor/digital_ocean.py @@ -28,7 +28,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Digital Ocean droplet sensor.""" digital = hass.data.get(DATA_DIGITAL_OCEAN) if not digital: @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return False dev.append(DigitalOceanBinarySensor(digital, droplet_id)) - add_devices(dev, True) + add_entities(dev, True) class DigitalOceanBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/ecobee.py b/homeassistant/components/binary_sensor/ecobee.py index 15efa21b226df2..37f25476bd012b 100644 --- a/homeassistant/components/binary_sensor/ecobee.py +++ b/homeassistant/components/binary_sensor/ecobee.py @@ -12,7 +12,7 @@ ECOBEE_CONFIG_FILE = 'ecobee.conf' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Ecobee sensors.""" if discovery_info is None: return @@ -26,7 +26,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev.append(EcobeeBinarySensor(sensor['name'], index)) - add_devices(dev, True) + add_entities(dev, True) class EcobeeBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/egardia.py b/homeassistant/components/binary_sensor/egardia.py index 76d90e78376be3..0db2cac667f226 100644 --- a/homeassistant/components/binary_sensor/egardia.py +++ b/homeassistant/components/binary_sensor/egardia.py @@ -19,7 +19,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Initialize the platform.""" if (discovery_info is None or discovery_info[ATTR_DISCOVER_DEVICES] is None): @@ -27,7 +28,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): disc_info = discovery_info[ATTR_DISCOVER_DEVICES] # multiple devices here! - async_add_devices( + async_add_entities( ( EgardiaBinarySensor( sensor_id=disc_info[sensor]['id'], @@ -58,7 +59,7 @@ def update(self): @property def name(self): - """The name of the device.""" + """Return the name of the device.""" return self._name @property @@ -74,5 +75,5 @@ def hidden(self): @property def device_class(self): - """The device class.""" + """Return the device class.""" return self._device_class diff --git a/homeassistant/components/binary_sensor/eight_sleep.py b/homeassistant/components/binary_sensor/eight_sleep.py index 40ca491e1f3cc0..34d3a7a13ca357 100644 --- a/homeassistant/components/binary_sensor/eight_sleep.py +++ b/homeassistant/components/binary_sensor/eight_sleep.py @@ -15,7 +15,7 @@ DEPENDENCIES = ['eight_sleep'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the eight sleep binary sensor.""" if discovery_info is None: @@ -30,7 +30,7 @@ async def async_setup_platform(hass, config, async_add_devices, for sensor in sensors: all_sensors.append(EightHeatSensor(name, eight, sensor)) - async_add_devices(all_sensors, True) + async_add_entities(all_sensors, True) class EightHeatSensor(EightSleepHeatEntity, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/enocean.py b/homeassistant/components/binary_sensor/enocean.py index d2ecd5e17e1cfb..c883897c2eac3e 100644 --- a/homeassistant/components/binary_sensor/enocean.py +++ b/homeassistant/components/binary_sensor/enocean.py @@ -27,13 +27,13 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Binary Sensor platform for EnOcean.""" dev_id = config.get(CONF_ID) devname = config.get(CONF_NAME) device_class = config.get(CONF_DEVICE_CLASS) - add_devices([EnOceanBinarySensor(dev_id, devname, device_class)]) + add_entities([EnOceanBinarySensor(dev_id, devname, device_class)]) class EnOceanBinarySensor(enocean.EnOceanDevice, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/envisalink.py b/homeassistant/components/binary_sensor/envisalink.py index f358f814dc5cf8..2568879bcc6dd3 100644 --- a/homeassistant/components/binary_sensor/envisalink.py +++ b/homeassistant/components/binary_sensor/envisalink.py @@ -23,7 +23,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Envisalink binary sensor devices.""" configured_zones = discovery_info['zones'] @@ -40,7 +41,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): ) devices.append(device) - async_add_devices(devices) + async_add_entities(devices) class EnvisalinkBinarySensor(EnvisalinkDevice, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/ffmpeg_motion.py b/homeassistant/components/binary_sensor/ffmpeg_motion.py index 75a9fa1d046c3c..365bcafbd693ec 100644 --- a/homeassistant/components/binary_sensor/ffmpeg_motion.py +++ b/homeassistant/components/binary_sensor/ffmpeg_motion.py @@ -47,7 +47,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the FFmpeg binary motion sensor.""" manager = hass.data[DATA_FFMPEG] @@ -55,7 +56,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): return entity = FFmpegMotion(hass, manager, config) - async_add_devices([entity]) + async_add_entities([entity]) class FFmpegBinarySensor(FFmpegBase, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/ffmpeg_noise.py b/homeassistant/components/binary_sensor/ffmpeg_noise.py index db7647d9b2ca4d..73c84ac336df9d 100644 --- a/homeassistant/components/binary_sensor/ffmpeg_noise.py +++ b/homeassistant/components/binary_sensor/ffmpeg_noise.py @@ -44,7 +44,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the FFmpeg noise binary sensor.""" manager = hass.data[DATA_FFMPEG] @@ -52,7 +53,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): return entity = FFmpegNoise(hass, manager, config) - async_add_devices([entity]) + async_add_entities([entity]) class FFmpegNoise(FFmpegBinarySensor): diff --git a/homeassistant/components/binary_sensor/gc100.py b/homeassistant/components/binary_sensor/gc100.py index 515d7e7123d4ad..27466f64cfb727 100644 --- a/homeassistant/components/binary_sensor/gc100.py +++ b/homeassistant/components/binary_sensor/gc100.py @@ -23,7 +23,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the GC100 devices.""" binary_sensors = [] ports = config.get(CONF_PORTS) @@ -31,7 +31,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for port_addr, port_name in port.items(): binary_sensors.append(GC100BinarySensor( port_name, port_addr, hass.data[DATA_GC100])) - add_devices(binary_sensors, True) + add_entities(binary_sensors, True) class GC100BinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/hikvision.py b/homeassistant/components/binary_sensor/hikvision.py index de6ad8223d729d..78e8f9a973eba4 100644 --- a/homeassistant/components/binary_sensor/hikvision.py +++ b/homeassistant/components/binary_sensor/hikvision.py @@ -90,7 +90,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): data = HikvisionData(hass, url, port, name, username, password) if data.sensors is None: - _LOGGER.error("Hikvision event stream has no data, unable to setup") + _LOGGER.error("Hikvision event stream has no data, unable to set up") return False entities = [] diff --git a/homeassistant/components/binary_sensor/hive.py b/homeassistant/components/binary_sensor/hive.py index 46dd1b193e86fe..68f3264187226b 100644 --- a/homeassistant/components/binary_sensor/hive.py +++ b/homeassistant/components/binary_sensor/hive.py @@ -13,13 +13,13 @@ 'contactsensor': 'opening'} -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Hive sensor devices.""" if discovery_info is None: return session = hass.data.get(DATA_HIVE) - add_devices([HiveBinarySensorEntity(session, discovery_info)]) + add_entities([HiveBinarySensorEntity(session, discovery_info)]) class HiveBinarySensorEntity(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/homematic.py b/homeassistant/components/binary_sensor/homematic.py index d85c10f9a34c82..9cfe4bbd6a7578 100644 --- a/homeassistant/components/binary_sensor/homematic.py +++ b/homeassistant/components/binary_sensor/homematic.py @@ -5,31 +5,32 @@ https://home-assistant.io/components/binary_sensor.homematic/ """ import logging -from homeassistant.const import STATE_UNKNOWN + from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.homematic import HMDevice, ATTR_DISCOVER_DEVICES +from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice +from homeassistant.const import STATE_UNKNOWN _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['homematic'] SENSOR_TYPES_CLASS = { - 'Remote': None, - 'ShutterContact': 'opening', - 'MaxShutterContact': 'opening', 'IPShutterContact': 'opening', - 'Smoke': 'smoke', - 'SmokeV2': 'smoke', + 'MaxShutterContact': 'opening', 'Motion': 'motion', 'MotionV2': 'motion', + 'PresenceIP': 'motion', + 'Remote': None, 'RemoteMotion': None, - 'WeatherSensor': None, + 'ShutterContact': 'opening', + 'Smoke': 'smoke', + 'SmokeV2': 'smoke', 'TiltSensor': None, - 'PresenceIP': 'motion', + 'WeatherSensor': None, } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the HomeMatic binary sensor platform.""" if discovery_info is None: return @@ -39,7 +40,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): new_device = HMBinarySensor(conf) devices.append(new_device) - add_devices(devices) + add_entities(devices) class HMBinarySensor(HMDevice, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/homematicip_cloud.py b/homeassistant/components/binary_sensor/homematicip_cloud.py index 962817827f0ac6..dd22a835504f0d 100644 --- a/homeassistant/components/binary_sensor/homematicip_cloud.py +++ b/homeassistant/components/binary_sensor/homematicip_cloud.py @@ -1,16 +1,15 @@ """ -Support for HomematicIP binary sensor. +Support for HomematicIP Cloud binary sensor. -For more details about this component, please refer to the documentation at +For more details about this platform, please refer to the documentation at https://home-assistant.io/components/binary_sensor.homematicip_cloud/ """ - import logging from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.homematicip_cloud import ( - HomematicipGenericDevice, DOMAIN as HMIPC_DOMAIN, - HMIPC_HAPID) + HMIPC_HAPID, HomematicipGenericDevice) +from homeassistant.components.homematicip_cloud import DOMAIN as HMIPC_DOMAIN DEPENDENCIES = ['homematicip_cloud'] @@ -19,14 +18,14 @@ STATE_SMOKE_OFF = 'IDLE_OFF' -async def async_setup_platform(hass, config, async_add_devices, - discovery_info=None): - """Set up the binary sensor devices.""" +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the HomematicIP Cloud binary sensor devices.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): - """Set up the HomematicIP binary sensor from a config entry.""" +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up the HomematicIP Cloud binary sensor from a config entry.""" from homematicip.aio.device import ( AsyncShutterContact, AsyncMotionDetectorIndoor, AsyncSmokeDetector) @@ -41,11 +40,11 @@ async def async_setup_entry(hass, config_entry, async_add_devices): devices.append(HomematicipSmokeDetector(home, device)) if devices: - async_add_devices(devices) + async_add_entities(devices) class HomematicipShutterContact(HomematicipGenericDevice, BinarySensorDevice): - """HomematicIP shutter contact.""" + """Representation of a HomematicIP Cloud shutter contact.""" @property def device_class(self): @@ -65,7 +64,7 @@ def is_on(self): class HomematicipMotionDetector(HomematicipGenericDevice, BinarySensorDevice): - """HomematicIP motion detector.""" + """Representation of a HomematicIP Cloud motion detector.""" @property def device_class(self): @@ -81,7 +80,7 @@ def is_on(self): class HomematicipSmokeDetector(HomematicipGenericDevice, BinarySensorDevice): - """HomematicIP smoke detector.""" + """Representation of a HomematicIP Cloud smoke detector.""" @property def device_class(self): diff --git a/homeassistant/components/binary_sensor/hydrawise.py b/homeassistant/components/binary_sensor/hydrawise.py index a3e0ebd782db5e..38b660c506fd5b 100644 --- a/homeassistant/components/binary_sensor/hydrawise.py +++ b/homeassistant/components/binary_sensor/hydrawise.py @@ -26,7 +26,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for a Hydrawise device.""" hydrawise = hass.data[DATA_HYDRAWISE].data @@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): hydrawise.controller_status.get('running', False) sensors.append(HydrawiseBinarySensor(zone_data, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) class HydrawiseBinarySensor(HydrawiseEntity, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/ihc.py b/homeassistant/components/binary_sensor/ihc.py index 25435d373fd850..20937af6bfcbfd 100644 --- a/homeassistant/components/binary_sensor/ihc.py +++ b/homeassistant/components/binary_sensor/ihc.py @@ -30,7 +30,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the IHC binary sensor platform.""" ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER] info = hass.data[IHC_DATA][IHC_INFO] @@ -56,7 +56,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensor_type, inverting) devices.append(sensor) - add_devices(devices) + add_entities(devices) class IHCBinarySensor(IHCDevice, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/insteon_plm.py b/homeassistant/components/binary_sensor/insteon.py similarity index 70% rename from homeassistant/components/binary_sensor/insteon_plm.py rename to homeassistant/components/binary_sensor/insteon.py index 25fc3fb5d73f17..533ff2d76c0e68 100644 --- a/homeassistant/components/binary_sensor/insteon_plm.py +++ b/homeassistant/components/binary_sensor/insteon.py @@ -2,15 +2,15 @@ Support for INSTEON dimmers via PowerLinc Modem. For more details about this component, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.insteon_plm/ +https://home-assistant.io/components/binary_sensor.insteon/ """ import asyncio import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.insteon_plm import InsteonPLMEntity +from homeassistant.components.insteon import InsteonEntity -DEPENDENCIES = ['insteon_plm'] +DEPENDENCIES = ['insteon'] _LOGGER = logging.getLogger(__name__) @@ -23,28 +23,29 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): - """Set up the INSTEON PLM device class for the hass platform.""" - plm = hass.data['insteon_plm'].get('plm') +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): + """Set up the INSTEON device class for the hass platform.""" + insteon_modem = hass.data['insteon'].get('modem') address = discovery_info['address'] - device = plm.devices[address] + device = insteon_modem.devices[address] state_key = discovery_info['state_key'] name = device.states[state_key].name if name != 'dryLeakSensor': _LOGGER.debug('Adding device %s entity %s to Binary Sensor platform', device.address.hex, device.states[state_key].name) - new_entity = InsteonPLMBinarySensor(device, state_key) + new_entity = InsteonBinarySensor(device, state_key) - async_add_devices([new_entity]) + async_add_entities([new_entity]) -class InsteonPLMBinarySensor(InsteonPLMEntity, BinarySensorDevice): +class InsteonBinarySensor(InsteonEntity, BinarySensorDevice): """A Class for an Insteon device entity.""" def __init__(self, device, state_key): - """Initialize the INSTEON PLM binary sensor.""" + """Initialize the INSTEON binary sensor.""" super().__init__(device, state_key) self._sensor_type = SENSOR_TYPES.get(self._insteon_device_state.name) diff --git a/homeassistant/components/binary_sensor/iss.py b/homeassistant/components/binary_sensor/iss.py index d0654317248eee..b986c51ddaa14d 100644 --- a/homeassistant/components/binary_sensor/iss.py +++ b/homeassistant/components/binary_sensor/iss.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ISS sensor.""" if None in (hass.config.latitude, hass.config.longitude): _LOGGER.error("Latitude or longitude not set in Home Assistant config") @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name = config.get(CONF_NAME) show_on_map = config.get(CONF_SHOW_ON_MAP) - add_devices([IssBinarySensor(iss_data, name, show_on_map)], True) + add_entities([IssBinarySensor(iss_data, name, show_on_map)], True) class IssBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/isy994.py b/homeassistant/components/binary_sensor/isy994.py index b6d582b7793736..36dacb06738dd6 100644 --- a/homeassistant/components/binary_sensor/isy994.py +++ b/homeassistant/components/binary_sensor/isy994.py @@ -29,7 +29,7 @@ def setup_platform(hass, config: ConfigType, - add_devices: Callable[[list], None], discovery_info=None): + add_entities: Callable[[list], None], discovery_info=None): """Set up the ISY994 binary sensor platform.""" devices = [] devices_by_nid = {} @@ -75,7 +75,7 @@ def setup_platform(hass, config: ConfigType, for name, status, _ in hass.data[ISY994_PROGRAMS][DOMAIN]: devices.append(ISYBinarySensorProgram(name, status)) - add_devices(devices) + add_entities(devices) def _detect_device_type(node) -> str: diff --git a/homeassistant/components/binary_sensor/knx.py b/homeassistant/components/binary_sensor/knx.py index e6b28047cb8f2f..d0707b0f067be4 100644 --- a/homeassistant/components/binary_sensor/knx.py +++ b/homeassistant/components/binary_sensor/knx.py @@ -54,27 +54,27 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up binary sensor(s) for KNX platform.""" if discovery_info is not None: - async_add_devices_discovery(hass, discovery_info, async_add_devices) + async_add_entities_discovery(hass, discovery_info, async_add_entities) else: - async_add_devices_config(hass, config, async_add_devices) + async_add_entities_config(hass, config, async_add_entities) @callback -def async_add_devices_discovery(hass, discovery_info, async_add_devices): +def async_add_entities_discovery(hass, discovery_info, async_add_entities): """Set up binary sensors for KNX platform configured via xknx.yaml.""" entities = [] for device_name in discovery_info[ATTR_DISCOVER_DEVICES]: device = hass.data[DATA_KNX].xknx.devices[device_name] entities.append(KNXBinarySensor(hass, device)) - async_add_devices(entities) + async_add_entities(entities) @callback -def async_add_devices_config(hass, config, async_add_devices): +def async_add_entities_config(hass, config, async_add_entities): """Set up binary senor for KNX platform configured within platform.""" name = config.get(CONF_NAME) import xknx @@ -97,7 +97,7 @@ def async_add_devices_config(hass, config, async_add_devices): entity.automations.append(KNXAutomation( hass=hass, device=binary_sensor, hook=hook, action=action, counter=counter)) - async_add_devices([entity]) + async_add_entities([entity]) class KNXBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/konnected.py b/homeassistant/components/binary_sensor/konnected.py index 9a16ca5e1ab1d0..e91d3f6136ad01 100644 --- a/homeassistant/components/binary_sensor/konnected.py +++ b/homeassistant/components/binary_sensor/konnected.py @@ -20,7 +20,7 @@ DEPENDENCIES = ['konnected'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up binary sensors attached to a Konnected device.""" if discovery_info is None: @@ -31,7 +31,7 @@ async def async_setup_platform(hass, config, async_add_devices, sensors = [KonnectedBinarySensor(device_id, pin_num, pin_data) for pin_num, pin_data in data[CONF_DEVICES][device_id][CONF_BINARY_SENSORS].items()] - async_add_devices(sensors) + async_add_entities(sensors) class KonnectedBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/linode.py b/homeassistant/components/binary_sensor/linode.py index d4fc60696cdafd..24abc3dd8be40b 100644 --- a/homeassistant/components/binary_sensor/linode.py +++ b/homeassistant/components/binary_sensor/linode.py @@ -27,7 +27,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Linode droplet sensor.""" linode = hass.data.get(DATA_LINODE) nodes = config.get(CONF_NODES) @@ -40,7 +40,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return dev.append(LinodeBinarySensor(linode, node_id)) - add_devices(dev, True) + add_entities(dev, True) class LinodeBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/maxcube.py b/homeassistant/components/binary_sensor/maxcube.py index c131de5420a5ec..6bb9278d8d5f25 100644 --- a/homeassistant/components/binary_sensor/maxcube.py +++ b/homeassistant/components/binary_sensor/maxcube.py @@ -13,7 +13,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Iterate through all MAX! Devices and add window shutters.""" devices = [] for handler in hass.data[DATA_KEY].values(): @@ -28,7 +28,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): MaxCubeShutter(handler, name, device.rf_address)) if devices: - add_devices(devices) + add_entities(devices) class MaxCubeShutter(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/modbus.py b/homeassistant/components/binary_sensor/modbus.py index 1a45235f15a84b..f9f2597593e641 100644 --- a/homeassistant/components/binary_sensor/modbus.py +++ b/homeassistant/components/binary_sensor/modbus.py @@ -28,7 +28,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Modbus binary sensors.""" sensors = [] for coil in config.get(CONF_COILS): @@ -36,7 +36,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): coil.get(CONF_NAME), coil.get(CONF_SLAVE), coil.get(CONF_COIL))) - add_devices(sensors) + add_entities(sensors) class ModbusCoilSensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/mqtt.py b/homeassistant/components/binary_sensor/mqtt.py index cb943ac3f188d0..37a26a27214b4c 100644 --- a/homeassistant/components/binary_sensor/mqtt.py +++ b/homeassistant/components/binary_sensor/mqtt.py @@ -45,7 +45,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the MQTT binary sensor.""" if discovery_info is not None: config = PLATFORM_SCHEMA(discovery_info) @@ -54,7 +55,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): if value_template is not None: value_template.hass = hass - async_add_devices([MqttBinarySensor( + async_add_entities([MqttBinarySensor( config.get(CONF_NAME), config.get(CONF_STATE_TOPIC), config.get(CONF_AVAILABILITY_TOPIC), diff --git a/homeassistant/components/binary_sensor/mychevy.py b/homeassistant/components/binary_sensor/mychevy.py index 905e60c34d9ca7..d1438379da1179 100644 --- a/homeassistant/components/binary_sensor/mychevy.py +++ b/homeassistant/components/binary_sensor/mychevy.py @@ -23,7 +23,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the MyChevy sensors.""" if discovery_info is None: return @@ -34,7 +35,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for car in hub.cars: sensors.append(EVBinarySensor(hub, sconfig, car.vid)) - async_add_devices(sensors) + async_add_entities(sensors) class EVBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/mysensors.py b/homeassistant/components/binary_sensor/mysensors.py index abb19129d5205d..f0b7832cf2581f 100644 --- a/homeassistant/components/binary_sensor/mysensors.py +++ b/homeassistant/components/binary_sensor/mysensors.py @@ -22,11 +22,11 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the mysensors platform for binary sensors.""" mysensors.setup_mysensors_platform( hass, DOMAIN, discovery_info, MySensorsBinarySensor, - async_add_devices=async_add_devices) + async_add_entities=async_add_entities) class MySensorsBinarySensor( diff --git a/homeassistant/components/binary_sensor/mystrom.py b/homeassistant/components/binary_sensor/mystrom.py index 5c1d9a576a6852..23f40ce0a7ff2f 100644 --- a/homeassistant/components/binary_sensor/mystrom.py +++ b/homeassistant/components/binary_sensor/mystrom.py @@ -17,9 +17,10 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up myStrom Binary Sensor.""" - hass.http.register_view(MyStromView(async_add_devices)) + hass.http.register_view(MyStromView(async_add_entities)) return True @@ -31,10 +32,10 @@ class MyStromView(HomeAssistantView): name = 'api:mystrom' supported_actions = ['single', 'double', 'long', 'touch'] - def __init__(self, add_devices): + def __init__(self, add_entities): """Initialize the myStrom URL endpoint.""" self.buttons = {} - self.add_devices = add_devices + self.add_entities = add_entities @asyncio.coroutine def get(self, request): @@ -62,7 +63,7 @@ def _handle(self, hass, data): button_id, button_action) self.buttons[entity_id] = MyStromBinarySensor( '{}_{}'.format(button_id, button_action)) - hass.async_add_job(self.add_devices, [self.buttons[entity_id]]) + self.add_entities([self.buttons[entity_id]]) else: new_state = True if self.buttons[entity_id].state == 'off' \ else False diff --git a/homeassistant/components/binary_sensor/nest.py b/homeassistant/components/binary_sensor/nest.py index 31460c1eedca0d..c60463a8663e66 100644 --- a/homeassistant/components/binary_sensor/nest.py +++ b/homeassistant/components/binary_sensor/nest.py @@ -54,14 +54,14 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Nest binary sensors. No longer used. """ -async def async_setup_entry(hass, entry, async_add_devices): +async def async_setup_entry(hass, entry, async_add_entities): """Set up a Nest binary sensor based on a config entry.""" nest = hass.data[DATA_NEST] @@ -112,7 +112,7 @@ def get_binary_sensors(): return sensors - async_add_devices(await hass.async_add_job(get_binary_sensors), True) + async_add_entities(await hass.async_add_job(get_binary_sensors), True) class NestBinarySensor(NestSensorDevice, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/netatmo.py b/homeassistant/components/binary_sensor/netatmo.py index 73a373a15ffa06..2cafacf401c59d 100644 --- a/homeassistant/components/binary_sensor/netatmo.py +++ b/homeassistant/components/binary_sensor/netatmo.py @@ -57,7 +57,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the access to Netatmo binary sensor.""" netatmo = hass.components.netatmo home = config.get(CONF_HOME) @@ -89,7 +89,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): camera_name not in config[CONF_CAMERAS]: continue for variable in welcome_sensors: - add_devices([NetatmoBinarySensor( + add_entities([NetatmoBinarySensor( data, camera_name, module_name, home, timeout, camera_type, variable)], True) if camera_type == 'NOC': @@ -98,14 +98,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): camera_name not in config[CONF_CAMERAS]: continue for variable in presence_sensors: - add_devices([NetatmoBinarySensor( + add_entities([NetatmoBinarySensor( data, camera_name, module_name, home, timeout, camera_type, variable)], True) for module_name in data.get_module_names(camera_name): for variable in tag_sensors: camera_type = None - add_devices([NetatmoBinarySensor( + add_entities([NetatmoBinarySensor( data, camera_name, module_name, home, timeout, camera_type, variable)], True) diff --git a/homeassistant/components/binary_sensor/nx584.py b/homeassistant/components/binary_sensor/nx584.py index 4dff263f79a08e..2929acc27095f8 100644 --- a/homeassistant/components/binary_sensor/nx584.py +++ b/homeassistant/components/binary_sensor/nx584.py @@ -40,7 +40,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the NX584 binary sensor platform.""" from nx584 import client as nx584_client @@ -68,7 +68,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for zone in zones if zone['number'] not in exclude} if zone_sensors: - add_devices(zone_sensors.values()) + add_entities(zone_sensors.values()) watcher = NX584Watcher(client, zone_sensors) watcher.start() else: diff --git a/homeassistant/components/binary_sensor/octoprint.py b/homeassistant/components/binary_sensor/octoprint.py index 1a1967b9014a0b..3dd1ee2be8c641 100644 --- a/homeassistant/components/binary_sensor/octoprint.py +++ b/homeassistant/components/binary_sensor/octoprint.py @@ -33,7 +33,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available OctoPrint binary sensors.""" octoprint_api = hass.data[DOMAIN]["api"] name = config.get(CONF_NAME) @@ -47,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name, SENSOR_TYPES[octo_type][3], SENSOR_TYPES[octo_type][0], SENSOR_TYPES[octo_type][1], 'flags') devices.append(new_sensor) - add_devices(devices, True) + add_entities(devices, True) class OctoPrintBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/openuv.py b/homeassistant/components/binary_sensor/openuv.py index 3a2732d3be037b..0b299529a46101 100644 --- a/homeassistant/components/binary_sensor/openuv.py +++ b/homeassistant/components/binary_sensor/openuv.py @@ -25,7 +25,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the OpenUV binary sensor platform.""" if discovery_info is None: return @@ -38,7 +38,7 @@ async def async_setup_platform( binary_sensors.append( OpenUvBinarySensor(openuv, sensor_type, name, icon)) - async_add_devices(binary_sensors, True) + async_add_entities(binary_sensors, True) class OpenUvBinarySensor(OpenUvEntity, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/pilight.py b/homeassistant/components/binary_sensor/pilight.py index 69dc3b834855c1..abffffe865160b 100644 --- a/homeassistant/components/binary_sensor/pilight.py +++ b/homeassistant/components/binary_sensor/pilight.py @@ -44,11 +44,11 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Pilight Binary Sensor.""" disarm = config.get(CONF_DISARM_AFTER_TRIGGER) if disarm: - add_devices([PilightTriggerSensor( + add_entities([PilightTriggerSensor( hass=hass, name=config.get(CONF_NAME), variable=config.get(CONF_VARIABLE), @@ -58,7 +58,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): rst_dly_sec=config.get(CONF_RESET_DELAY_SEC), )]) else: - add_devices([PilightBinarySensor( + add_entities([PilightBinarySensor( hass=hass, name=config.get(CONF_NAME), variable=config.get(CONF_VARIABLE), diff --git a/homeassistant/components/binary_sensor/ping.py b/homeassistant/components/binary_sensor/ping.py index bb597f208e667b..4c597dd63e1e34 100644 --- a/homeassistant/components/binary_sensor/ping.py +++ b/homeassistant/components/binary_sensor/ping.py @@ -48,13 +48,13 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Ping Binary sensor.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) count = config.get(CONF_PING_COUNT) - add_devices([PingBinarySensor(name, PingData(host, count))], True) + add_entities([PingBinarySensor(name, PingData(host, count))], True) class PingBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/qwikswitch.py b/homeassistant/components/binary_sensor/qwikswitch.py index 067021b0c7a84a..2fe14773d3a03f 100644 --- a/homeassistant/components/binary_sensor/qwikswitch.py +++ b/homeassistant/components/binary_sensor/qwikswitch.py @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) -async def async_setup_platform(hass, _, add_devices, discovery_info=None): +async def async_setup_platform(hass, _, add_entities, discovery_info=None): """Add binary sensor from the main Qwikswitch component.""" if discovery_info is None: return @@ -24,7 +24,7 @@ async def async_setup_platform(hass, _, add_devices, discovery_info=None): _LOGGER.debug("Setup qwikswitch.binary_sensor %s, %s", qsusb, discovery_info) devs = [QSBinarySensor(sensor) for sensor in discovery_info[QWIKSWITCH]] - add_devices(devs) + add_entities(devs) class QSBinarySensor(QSEntity, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/rachio.py b/homeassistant/components/binary_sensor/rachio.py index 59bf8a2106471d..798b6a754d1632 100644 --- a/homeassistant/components/binary_sensor/rachio.py +++ b/homeassistant/components/binary_sensor/rachio.py @@ -24,13 +24,13 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Rachio binary sensors.""" devices = [] for controller in hass.data[DOMAIN_RACHIO].controllers: devices.append(RachioControllerOnlineBinarySensor(hass, controller)) - add_devices(devices) + add_entities(devices) _LOGGER.info("%d Rachio binary sensor(s) added", len(devices)) diff --git a/homeassistant/components/binary_sensor/raincloud.py b/homeassistant/components/binary_sensor/raincloud.py index 3cbd179154fd21..810c7d201cbfb0 100644 --- a/homeassistant/components/binary_sensor/raincloud.py +++ b/homeassistant/components/binary_sensor/raincloud.py @@ -25,7 +25,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for a raincloud device.""" raincloud = hass.data[DATA_RAINCLOUD].data @@ -43,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for zone in raincloud.controller.faucet.zones: sensors.append(RainCloudBinarySensor(zone, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) return True diff --git a/homeassistant/components/binary_sensor/rainmachine.py b/homeassistant/components/binary_sensor/rainmachine.py index b2f44696fbdc25..12c9b3e98f0ca9 100644 --- a/homeassistant/components/binary_sensor/rainmachine.py +++ b/homeassistant/components/binary_sensor/rainmachine.py @@ -21,7 +21,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the RainMachine Switch platform.""" if discovery_info is None: return @@ -34,7 +34,7 @@ async def async_setup_platform( binary_sensors.append( RainMachineBinarySensor(rainmachine, sensor_type, name, icon)) - async_add_devices(binary_sensors, True) + async_add_entities(binary_sensors, True) class RainMachineBinarySensor(RainMachineEntity, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/random.py b/homeassistant/components/binary_sensor/random.py index ab6c1e5d479e68..9bdc57c6e468d3 100644 --- a/homeassistant/components/binary_sensor/random.py +++ b/homeassistant/components/binary_sensor/random.py @@ -24,12 +24,12 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Random binary sensor.""" name = config.get(CONF_NAME) device_class = config.get(CONF_DEVICE_CLASS) - async_add_devices([RandomSensor(name, device_class)], True) + async_add_entities([RandomSensor(name, device_class)], True) class RandomSensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/raspihats.py b/homeassistant/components/binary_sensor/raspihats.py index 9ab56a5a20da75..feef5396d88938 100644 --- a/homeassistant/components/binary_sensor/raspihats.py +++ b/homeassistant/components/binary_sensor/raspihats.py @@ -42,7 +42,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the raspihats binary_sensor devices.""" I2CHatBinarySensor.I2C_HATS_MANAGER = hass.data[I2C_HATS_MANAGER] binary_sensors = [] @@ -65,7 +65,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): except I2CHatsException as ex: _LOGGER.error("Failed to register %s I2CHat@%s %s", board, hex(address), str(ex)) - add_devices(binary_sensors) + add_entities(binary_sensors) class I2CHatBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/rest.py b/homeassistant/components/binary_sensor/rest.py index e9cb40f6747891..412aeb46a3a423 100644 --- a/homeassistant/components/binary_sensor/rest.py +++ b/homeassistant/components/binary_sensor/rest.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the REST binary sensor.""" name = config.get(CONF_NAME) resource = config.get(CONF_RESOURCE) @@ -71,7 +71,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Unable to fetch REST data from %s", resource) return False - add_devices([RestBinarySensor( + add_entities([RestBinarySensor( hass, rest, name, device_class, value_template)], True) diff --git a/homeassistant/components/binary_sensor/rfxtrx.py b/homeassistant/components/binary_sensor/rfxtrx.py index 6ac604a4f1ebb4..1e88c72e19d5a4 100644 --- a/homeassistant/components/binary_sensor/rfxtrx.py +++ b/homeassistant/components/binary_sensor/rfxtrx.py @@ -42,7 +42,7 @@ }, extra=vol.ALLOW_EXTRA) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Binary Sensor platform to RFXtrx.""" import RFXtrx as rfxtrxmod sensors = [] @@ -71,7 +71,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(device) rfxtrx.RFX_DEVICES[device_id] = device - add_devices(sensors) + add_entities(sensors) def binary_sensor_update(event): """Call for control updates from the RFXtrx gateway.""" @@ -101,7 +101,7 @@ def binary_sensor_update(event): sensor = RfxtrxBinarySensor(event, pkt_id) sensor.hass = hass rfxtrx.RFX_DEVICES[device_id] = sensor - add_devices([sensor]) + add_entities([sensor]) _LOGGER.info( "Added binary sensor %s (Device ID: %s Class: %s Sub: %s)", pkt_id, slugify(event.device.id_string.lower()), diff --git a/homeassistant/components/binary_sensor/ring.py b/homeassistant/components/binary_sensor/ring.py index 4f2ea408e7f46a..102e22cbe2d244 100644 --- a/homeassistant/components/binary_sensor/ring.py +++ b/homeassistant/components/binary_sensor/ring.py @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for a Ring device.""" ring = hass.data[DATA_RING] @@ -56,7 +56,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(RingBinarySensor(hass, device, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) return True diff --git a/homeassistant/components/binary_sensor/rpi_gpio.py b/homeassistant/components/binary_sensor/rpi_gpio.py index 31a518dc1dc112..2fe4e0766ed7e0 100644 --- a/homeassistant/components/binary_sensor/rpi_gpio.py +++ b/homeassistant/components/binary_sensor/rpi_gpio.py @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Raspberry PI GPIO devices.""" pull_mode = config.get(CONF_PULL_MODE) bouncetime = config.get(CONF_BOUNCETIME) @@ -50,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for port_num, port_name in ports.items(): binary_sensors.append(RPiGPIOBinarySensor( port_name, port_num, pull_mode, bouncetime, invert_logic)) - add_devices(binary_sensors, True) + add_entities(binary_sensors, True) class RPiGPIOBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/rpi_pfio.py b/homeassistant/components/binary_sensor/rpi_pfio.py index a1126bdd2f90ee..61d1f8ac285f6d 100644 --- a/homeassistant/components/binary_sensor/rpi_pfio.py +++ b/homeassistant/components/binary_sensor/rpi_pfio.py @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the PiFace Digital Input devices.""" binary_sensors = [] ports = config.get(CONF_PORTS) @@ -50,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): binary_sensors.append(RPiPFIOBinarySensor( hass, port, name, settle_time, invert_logic)) - add_devices(binary_sensors, True) + add_entities(binary_sensors, True) rpi_pfio.activate_listener(hass) diff --git a/homeassistant/components/binary_sensor/satel_integra.py b/homeassistant/components/binary_sensor/satel_integra.py index f373809f7c062f..3500f0a0576725 100644 --- a/homeassistant/components/binary_sensor/satel_integra.py +++ b/homeassistant/components/binary_sensor/satel_integra.py @@ -21,7 +21,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Satel Integra binary sensor devices.""" if not discovery_info: return @@ -36,7 +37,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): device = SatelIntegraBinarySensor(zone_num, zone_name, zone_type) devices.append(device) - async_add_devices(devices) + async_add_entities(devices) class SatelIntegraBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/skybell.py b/homeassistant/components/binary_sensor/skybell.py index 44cad11e3f0979..7d8b3a84a2a2b5 100644 --- a/homeassistant/components/binary_sensor/skybell.py +++ b/homeassistant/components/binary_sensor/skybell.py @@ -37,7 +37,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the platform for a Skybell device.""" skybell = hass.data.get(SKYBELL_DOMAIN) @@ -46,7 +46,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for device in skybell.get_devices(): sensors.append(SkybellBinarySensor(device, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) class SkybellBinarySensor(SkybellDevice, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/sleepiq.py b/homeassistant/components/binary_sensor/sleepiq.py index 3a6c27db3869f9..808eda4967db28 100644 --- a/homeassistant/components/binary_sensor/sleepiq.py +++ b/homeassistant/components/binary_sensor/sleepiq.py @@ -10,7 +10,7 @@ DEPENDENCIES = ['sleepiq'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SleepIQ sensors.""" if discovery_info is None: return @@ -22,7 +22,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for bed_id, _ in data.beds.items(): for side in sleepiq.SIDES: dev.append(IsInBedBinarySensor(data, bed_id, side)) - add_devices(dev) + add_entities(dev) class IsInBedBinarySensor(sleepiq.SleepIQSensor, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/spc.py b/homeassistant/components/binary_sensor/spc.py index 95723f938704f0..9afd4fe40151e6 100644 --- a/homeassistant/components/binary_sensor/spc.py +++ b/homeassistant/components/binary_sensor/spc.py @@ -45,13 +45,14 @@ def _create_sensor(hass, zone): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the SPC binary sensor.""" if (discovery_info is None or discovery_info[ATTR_DISCOVER_DEVICES] is None): return - async_add_devices( + async_add_entities( _create_sensor(hass, zone) for zone in discovery_info[ATTR_DISCOVER_DEVICES] if _get_device_class(zone['type'])) diff --git a/homeassistant/components/binary_sensor/tahoma.py b/homeassistant/components/binary_sensor/tahoma.py index efcfb629f39601..7af5a730c43f3b 100644 --- a/homeassistant/components/binary_sensor/tahoma.py +++ b/homeassistant/components/binary_sensor/tahoma.py @@ -21,14 +21,14 @@ SCAN_INTERVAL = timedelta(seconds=120) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tahoma controller devices.""" _LOGGER.debug("Setup Tahoma Binary sensor platform") controller = hass.data[TAHOMA_DOMAIN]['controller'] devices = [] for device in hass.data[TAHOMA_DOMAIN]['devices']['smoke']: devices.append(TahomaBinarySensor(device, controller)) - add_devices(devices, True) + add_entities(devices, True) class TahomaBinarySensor(TahomaDevice, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/tapsaff.py b/homeassistant/components/binary_sensor/tapsaff.py index 5b8e133b5f4233..1978a127c17e16 100644 --- a/homeassistant/components/binary_sensor/tapsaff.py +++ b/homeassistant/components/binary_sensor/tapsaff.py @@ -30,14 +30,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Taps Aff binary sensor.""" name = config.get(CONF_NAME) location = config.get(CONF_LOCATION) taps_aff_data = TapsAffData(location) - add_devices([TapsAffSensor(taps_aff_data, name)], True) + add_entities([TapsAffSensor(taps_aff_data, name)], True) class TapsAffSensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/tcp.py b/homeassistant/components/binary_sensor/tcp.py index cfaa8057798cee..764b6829c91b75 100644 --- a/homeassistant/components/binary_sensor/tcp.py +++ b/homeassistant/components/binary_sensor/tcp.py @@ -15,9 +15,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({}) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the TCP binary sensor.""" - add_devices([TcpBinarySensor(hass, config)]) + add_entities([TcpBinarySensor(hass, config)]) class TcpBinarySensor(BinarySensorDevice, TcpSensor): diff --git a/homeassistant/components/binary_sensor/tellduslive.py b/homeassistant/components/binary_sensor/tellduslive.py index e5d2d83fe47ced..450a5e580bdc09 100644 --- a/homeassistant/components/binary_sensor/tellduslive.py +++ b/homeassistant/components/binary_sensor/tellduslive.py @@ -15,11 +15,11 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tellstick sensors.""" if discovery_info is None: return - add_devices( + add_entities( TelldusLiveSensor(hass, binary_sensor) for binary_sensor in discovery_info ) diff --git a/homeassistant/components/binary_sensor/template.py b/homeassistant/components/binary_sensor/template.py index 68ffbf77af2523..c5bfa593022416 100644 --- a/homeassistant/components/binary_sensor/template.py +++ b/homeassistant/components/binary_sensor/template.py @@ -47,7 +47,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up template binary sensors.""" sensors = [] @@ -82,7 +83,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): _LOGGER.error("No sensors added") return False - async_add_devices(sensors) + async_add_entities(sensors) return True diff --git a/homeassistant/components/binary_sensor/tesla.py b/homeassistant/components/binary_sensor/tesla.py index 3d494a28ea8ea2..f7613d74dfbbab 100644 --- a/homeassistant/components/binary_sensor/tesla.py +++ b/homeassistant/components/binary_sensor/tesla.py @@ -15,13 +15,13 @@ DEPENDENCIES = ['tesla'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tesla binary sensor.""" devices = [ TeslaBinarySensor( device, hass.data[TESLA_DOMAIN]['controller'], 'connectivity') for device in hass.data[TESLA_DOMAIN]['devices']['binary_sensor']] - add_devices(devices, True) + add_entities(devices, True) class TeslaBinarySensor(TeslaDevice, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/threshold.py b/homeassistant/components/binary_sensor/threshold.py index 39681c894b3cc4..fd7ead08822aef 100644 --- a/homeassistant/components/binary_sensor/threshold.py +++ b/homeassistant/components/binary_sensor/threshold.py @@ -55,7 +55,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Threshold sensor.""" entity_id = config.get(CONF_ENTITY_ID) name = config.get(CONF_NAME) @@ -64,7 +65,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): hysteresis = config.get(CONF_HYSTERESIS) device_class = config.get(CONF_DEVICE_CLASS) - async_add_devices([ThresholdSensor( + async_add_entities([ThresholdSensor( hass, entity_id, name, lower, upper, hysteresis, device_class)], True) diff --git a/homeassistant/components/binary_sensor/trend.py b/homeassistant/components/binary_sensor/trend.py index 78f471d125bc0a..ae6fd5562bf10a 100644 --- a/homeassistant/components/binary_sensor/trend.py +++ b/homeassistant/components/binary_sensor/trend.py @@ -23,7 +23,7 @@ from homeassistant.helpers.event import async_track_state_change from homeassistant.util import utcnow -REQUIREMENTS = ['numpy==1.15.0'] +REQUIREMENTS = ['numpy==1.15.1'] _LOGGER = logging.getLogger(__name__) @@ -57,7 +57,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the trend sensors.""" sensors = [] @@ -80,7 +80,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if not sensors: _LOGGER.error("No sensors added") return False - add_devices(sensors) + add_entities(sensors) return True diff --git a/homeassistant/components/binary_sensor/upcloud.py b/homeassistant/components/binary_sensor/upcloud.py index 868a2e8ddd0621..c7b8a284dc97fb 100644 --- a/homeassistant/components/binary_sensor/upcloud.py +++ b/homeassistant/components/binary_sensor/upcloud.py @@ -23,7 +23,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the UpCloud server binary sensor.""" upcloud = hass.data[DATA_UPCLOUD] @@ -31,7 +31,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices = [UpCloudBinarySensor(upcloud, uuid) for uuid in servers] - add_devices(devices, True) + add_entities(devices, True) class UpCloudBinarySensor(UpCloudServerEntity, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/uptimerobot.py b/homeassistant/components/binary_sensor/uptimerobot.py index 9e72d188c99552..dbb83e53e9fd11 100644 --- a/homeassistant/components/binary_sensor/uptimerobot.py +++ b/homeassistant/components/binary_sensor/uptimerobot.py @@ -26,7 +26,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Uptime Robot binary_sensors.""" from pyuptimerobot import UptimeRobot @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): api_key, up_robot, monitor['id'], monitor['friendly_name'], monitor['url'])) - add_devices(devices, True) + add_entities(devices, True) class UptimeRobotBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/velbus.py b/homeassistant/components/binary_sensor/velbus.py index 8438be0d7845df..b123b958560fd8 100644 --- a/homeassistant/components/binary_sensor/velbus.py +++ b/homeassistant/components/binary_sensor/velbus.py @@ -15,7 +15,7 @@ DEPENDENCIES = ['velbus'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up Velbus binary sensors.""" if discovery_info is None: @@ -25,7 +25,7 @@ async def async_setup_platform(hass, config, async_add_devices, module = hass.data[VELBUS_DOMAIN].get_module(sensor[0]) channel = sensor[1] sensors.append(VelbusBinarySensor(module, channel)) - async_add_devices(sensors) + async_add_entities(sensors) class VelbusBinarySensor(VelbusEntity, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/vera.py b/homeassistant/components/binary_sensor/vera.py index 310e2289cbc9bf..bb1e7331de826b 100644 --- a/homeassistant/components/binary_sensor/vera.py +++ b/homeassistant/components/binary_sensor/vera.py @@ -16,9 +16,9 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Perform the setup for Vera controller devices.""" - add_devices( + add_entities( [VeraBinarySensor(device, hass.data[VERA_CONTROLLER]) for device in hass.data[VERA_DEVICES]['binary_sensor']], True) diff --git a/homeassistant/components/binary_sensor/verisure.py b/homeassistant/components/binary_sensor/verisure.py index 7068d51f6a3632..e040da959eaa10 100644 --- a/homeassistant/components/binary_sensor/verisure.py +++ b/homeassistant/components/binary_sensor/verisure.py @@ -13,7 +13,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Verisure binary sensors.""" sensors = [] hub.update_overview() @@ -23,7 +23,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): VerisureDoorWindowSensor(device_label) for device_label in hub.get( "$.doorWindow.doorWindowDevice[*].deviceLabel")]) - add_devices(sensors) + add_entities(sensors) class VerisureDoorWindowSensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/volvooncall.py b/homeassistant/components/binary_sensor/volvooncall.py index 402feefa99f0a2..e70d3098874405 100644 --- a/homeassistant/components/binary_sensor/volvooncall.py +++ b/homeassistant/components/binary_sensor/volvooncall.py @@ -12,11 +12,11 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Volvo sensors.""" if discovery_info is None: return - add_devices([VolvoSensor(hass, *discovery_info)]) + add_entities([VolvoSensor(hass, *discovery_info)]) class VolvoSensor(VolvoEntity, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/vultr.py b/homeassistant/components/binary_sensor/vultr.py index eecd3f87c40437..149a6c282901ee 100644 --- a/homeassistant/components/binary_sensor/vultr.py +++ b/homeassistant/components/binary_sensor/vultr.py @@ -30,7 +30,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vultr subscription (server) binary sensor.""" vultr = hass.data[DATA_VULTR] @@ -41,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Subscription %s not found", subscription) return - add_devices([VultrBinarySensor(vultr, subscription, name)], True) + add_entities([VultrBinarySensor(vultr, subscription, name)], True) class VultrBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/wemo.py b/homeassistant/components/binary_sensor/wemo.py index a589ab4e8c8999..1071aae50dd926 100644 --- a/homeassistant/components/binary_sensor/wemo.py +++ b/homeassistant/components/binary_sensor/wemo.py @@ -5,25 +5,33 @@ https://home-assistant.io/components/binary_sensor.wemo/ """ import logging +import requests from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.exceptions import PlatformNotReady DEPENDENCIES = ['wemo'] _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Register discovered WeMo binary sensors.""" from pywemo import discovery if discovery_info is not None: location = discovery_info['ssdp_description'] mac = discovery_info['mac_address'] - device = discovery.device_from_description(location, mac) + + try: + device = discovery.device_from_description(location, mac) + except (requests.exceptions.ConnectionError, + requests.exceptions.Timeout) as err: + _LOGGER.error('Unable to access %s (%s)', location, err) + raise PlatformNotReady if device: - add_devices_callback([WemoBinarySensor(hass, device)]) + add_entities_callback([WemoBinarySensor(hass, device)]) class WemoBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/wink.py b/homeassistant/components/binary_sensor/wink.py index 575507cd04780c..1976e49f4467ae 100644 --- a/homeassistant/components/binary_sensor/wink.py +++ b/homeassistant/components/binary_sensor/wink.py @@ -31,7 +31,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink binary sensor platform.""" import pywink @@ -39,49 +39,49 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _id = sensor.object_id() + sensor.name() if _id not in hass.data[DOMAIN]['unique_ids']: if sensor.capability() in SENSOR_TYPES: - add_devices([WinkBinarySensorDevice(sensor, hass)]) + add_entities([WinkBinarySensorDevice(sensor, hass)]) for key in pywink.get_keys(): _id = key.object_id() + key.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkBinarySensorDevice(key, hass)]) + add_entities([WinkBinarySensorDevice(key, hass)]) for sensor in pywink.get_smoke_and_co_detectors(): _id = sensor.object_id() + sensor.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkSmokeDetector(sensor, hass)]) + add_entities([WinkSmokeDetector(sensor, hass)]) for hub in pywink.get_hubs(): _id = hub.object_id() + hub.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkHub(hub, hass)]) + add_entities([WinkHub(hub, hass)]) for remote in pywink.get_remotes(): _id = remote.object_id() + remote.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkRemote(remote, hass)]) + add_entities([WinkRemote(remote, hass)]) for button in pywink.get_buttons(): _id = button.object_id() + button.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkButton(button, hass)]) + add_entities([WinkButton(button, hass)]) for gang in pywink.get_gangs(): _id = gang.object_id() + gang.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkGang(gang, hass)]) + add_entities([WinkGang(gang, hass)]) for door_bell_sensor in pywink.get_door_bells(): _id = door_bell_sensor.object_id() + door_bell_sensor.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkBinarySensorDevice(door_bell_sensor, hass)]) + add_entities([WinkBinarySensorDevice(door_bell_sensor, hass)]) for camera_sensor in pywink.get_cameras(): _id = camera_sensor.object_id() + camera_sensor.name() if _id not in hass.data[DOMAIN]['unique_ids']: try: if camera_sensor.capability() in SENSOR_TYPES: - add_devices([WinkBinarySensorDevice(camera_sensor, hass)]) + add_entities([WinkBinarySensorDevice(camera_sensor, hass)]) except AttributeError: _LOGGER.info("Device isn't a sensor, skipping") diff --git a/homeassistant/components/binary_sensor/wirelesstag.py b/homeassistant/components/binary_sensor/wirelesstag.py index bfc2d44fc6e8bd..4bec3a824c395a 100644 --- a/homeassistant/components/binary_sensor/wirelesstag.py +++ b/homeassistant/components/binary_sensor/wirelesstag.py @@ -107,7 +107,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the platform for a WirelessTags.""" platform = hass.data.get(WIRELESSTAG_DOMAIN) @@ -120,7 +120,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(WirelessTagBinarySensor(platform, tag, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) hass.add_job(platform.install_push_notifications, sensors) diff --git a/homeassistant/components/binary_sensor/workday.py b/homeassistant/components/binary_sensor/workday.py index 4a9809e9974f1f..3fb2d7f7f86f59 100644 --- a/homeassistant/components/binary_sensor/workday.py +++ b/homeassistant/components/binary_sensor/workday.py @@ -58,7 +58,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Workday sensor.""" import holidays @@ -92,7 +92,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for date, name in sorted(obj_holidays.items()): _LOGGER.debug("%s %s", date, name) - add_devices([IsWorkdaySensor( + add_entities([IsWorkdaySensor( obj_holidays, workdays, excludes, days_offset, sensor_name)], True) diff --git a/homeassistant/components/binary_sensor/xiaomi_aqara.py b/homeassistant/components/binary_sensor/xiaomi_aqara.py index 2a9746b4a01ad5..c42090e3b7afe3 100644 --- a/homeassistant/components/binary_sensor/xiaomi_aqara.py +++ b/homeassistant/components/binary_sensor/xiaomi_aqara.py @@ -19,7 +19,7 @@ ATTR_DENSITY = 'Density' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Perform the setup for Xiaomi devices.""" devices = [] for (_, gateway) in hass.data[PY_XIAOMI_GATEWAY].gateways.items(): @@ -28,11 +28,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if model in ['motion', 'sensor_motion', 'sensor_motion.aq2']: devices.append(XiaomiMotionSensor(device, hass, gateway)) elif model in ['magnet', 'sensor_magnet', 'sensor_magnet.aq2']: - if 'proto' not in device or int(device['proto'][0:1]) == 1: - data_key = 'status' - else: - data_key = 'window_status' - devices.append(XiaomiDoorSensor(device, data_key, gateway)) + devices.append(XiaomiDoorSensor(device, gateway)) elif model == 'sensor_wleak.aq1': devices.append(XiaomiWaterLeakSensor(device, gateway)) elif model in ['smoke', 'sensor_smoke']: @@ -44,22 +40,32 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if 'proto' not in device or int(device['proto'][0:1]) == 1: data_key = 'status' else: - data_key = 'channel_0' + data_key = 'button_0' devices.append(XiaomiButton(device, 'Switch', data_key, hass, gateway)) elif model in ['86sw1', 'sensor_86sw1', 'sensor_86sw1.aq1']: - devices.append(XiaomiButton(device, 'Wall Switch', 'channel_0', + if 'proto' not in device or int(device['proto'][0:1]) == 1: + data_key = 'channel_0' + else: + data_key = 'button_0' + devices.append(XiaomiButton(device, 'Wall Switch', data_key, hass, gateway)) elif model in ['86sw2', 'sensor_86sw2', 'sensor_86sw2.aq1']: + if 'proto' not in device or int(device['proto'][0:1]) == 1: + data_key_left = 'channel_0' + data_key_right = 'channel_1' + else: + data_key_left = 'button_0' + data_key_right = 'button_1' devices.append(XiaomiButton(device, 'Wall Switch (Left)', - 'channel_0', hass, gateway)) + data_key_left, hass, gateway)) devices.append(XiaomiButton(device, 'Wall Switch (Right)', - 'channel_1', hass, gateway)) + data_key_right, hass, gateway)) devices.append(XiaomiButton(device, 'Wall Switch (Both)', 'dual_channel', hass, gateway)) elif model in ['cube', 'sensor_cube', 'sensor_cube.aqgl01']: devices.append(XiaomiCube(device, hass, gateway)) - add_devices(devices) + add_entities(devices) class XiaomiBinarySensor(XiaomiDevice, BinarySensorDevice): @@ -119,7 +125,7 @@ def parse_data(self, data, raw_data): if value is None: return False - if value == '1': + if value in ('1', '2'): if self._state: return False self._state = True @@ -194,9 +200,13 @@ def parse_data(self, data, raw_data): class XiaomiDoorSensor(XiaomiBinarySensor): """Representation of a XiaomiDoorSensor.""" - def __init__(self, device, data_key, xiaomi_hub): + def __init__(self, device, xiaomi_hub): """Initialize the XiaomiDoorSensor.""" self._open_since = 0 + if 'proto' not in device or int(device['proto'][0:1]) == 1: + data_key = 'status' + else: + data_key = 'window_status' XiaomiBinarySensor.__init__(self, device, 'Door Window Sensor', xiaomi_hub, data_key, 'opening') @@ -237,8 +247,12 @@ class XiaomiWaterLeakSensor(XiaomiBinarySensor): def __init__(self, device, xiaomi_hub): """Initialize the XiaomiWaterLeakSensor.""" + if 'proto' not in device or int(device['proto'][0:1]) == 1: + data_key = 'status' + else: + data_key = 'wleak_status' XiaomiBinarySensor.__init__(self, device, 'Water Leak Sensor', - xiaomi_hub, 'status', 'moisture') + xiaomi_hub, data_key, 'moisture') def parse_data(self, data, raw_data): """Parse data sent by gateway.""" @@ -285,7 +299,7 @@ def parse_data(self, data, raw_data): if value is None: return False - if value == '1': + if value in ('1', '2'): if self._state: return False self._state = True @@ -359,6 +373,10 @@ def __init__(self, device, hass, xiaomi_hub): self._hass = hass self._last_action = None self._state = False + if 'proto' not in device or int(device['proto'][0:1]) == 1: + self._data_key = 'status' + else: + self._data_key = 'cube_status' XiaomiBinarySensor.__init__(self, device, 'Cube', xiaomi_hub, None, None) @@ -371,12 +389,12 @@ def device_state_attributes(self): def parse_data(self, data, raw_data): """Parse data sent by gateway.""" - if 'status' in data: + if self._data_key in data: self._hass.bus.fire('cube_action', { 'entity_id': self.entity_id, - 'action_type': data['status'] + 'action_type': data[self._data_key] }) - self._last_action = data['status'] + self._last_action = data[self._data_key] if 'rotate' in data: self._hass.bus.fire('cube_action', { diff --git a/homeassistant/components/binary_sensor/zha.py b/homeassistant/components/binary_sensor/zha.py index 224d694e0f5b63..cabbbd704a0014 100644 --- a/homeassistant/components/binary_sensor/zha.py +++ b/homeassistant/components/binary_sensor/zha.py @@ -24,7 +24,7 @@ } -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Zigbee Home Automation binary sensors.""" discovery_info = zha.get_discovery_info(hass, discovery_info) @@ -34,14 +34,14 @@ async def async_setup_platform(hass, config, async_add_devices, from zigpy.zcl.clusters.general import OnOff from zigpy.zcl.clusters.security import IasZone if IasZone.cluster_id in discovery_info['in_clusters']: - await _async_setup_iaszone(hass, config, async_add_devices, + await _async_setup_iaszone(hass, config, async_add_entities, discovery_info) elif OnOff.cluster_id in discovery_info['out_clusters']: - await _async_setup_remote(hass, config, async_add_devices, + await _async_setup_remote(hass, config, async_add_entities, discovery_info) -async def _async_setup_iaszone(hass, config, async_add_devices, +async def _async_setup_iaszone(hass, config, async_add_entities, discovery_info): device_class = None from zigpy.zcl.clusters.security import IasZone @@ -59,10 +59,11 @@ async def _async_setup_iaszone(hass, config, async_add_devices, pass sensor = BinarySensor(device_class, **discovery_info) - async_add_devices([sensor], update_before_add=True) + async_add_entities([sensor], update_before_add=True) -async def _async_setup_remote(hass, config, async_add_devices, discovery_info): +async def _async_setup_remote(hass, config, async_add_entities, + discovery_info): async def safe(coro): """Run coro, catching ZigBee delivery errors, and ignoring them.""" @@ -85,7 +86,7 @@ async def safe(coro): await safe(cluster.configure_reporting(0, 1, 600, 1)) sensor = Switch(**discovery_info) - async_add_devices([sensor], update_before_add=True) + async_add_entities([sensor], update_before_add=True) class BinarySensor(zha.Entity, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/zigbee.py b/homeassistant/components/binary_sensor/zigbee.py index 659d82f809bbb6..6b89258209ef0e 100644 --- a/homeassistant/components/binary_sensor/zigbee.py +++ b/homeassistant/components/binary_sensor/zigbee.py @@ -22,9 +22,9 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZigBee binary sensor platform.""" - add_devices( + add_entities( [ZigBeeBinarySensor(hass, ZigBeeDigitalInConfig(config))], True) diff --git a/homeassistant/components/bmw_connected_drive/__init__.py b/homeassistant/components/bmw_connected_drive/__init__.py index 061b09c1b3b52d..b0ad1a867a8269 100644 --- a/homeassistant/components/bmw_connected_drive/__init__.py +++ b/homeassistant/components/bmw_connected_drive/__init__.py @@ -20,6 +20,7 @@ DOMAIN = 'bmw_connected_drive' CONF_REGION = 'region' +CONF_READ_ONLY = 'read_only' ATTR_VIN = 'vin' ACCOUNT_SCHEMA = vol.Schema({ @@ -27,6 +28,7 @@ vol.Required(CONF_PASSWORD): cv.string, vol.Required(CONF_REGION): vol.Any('north_america', 'china', 'rest_of_world'), + vol.Optional(CONF_READ_ONLY, default=False): cv.boolean, }) CONFIG_SCHEMA = vol.Schema({ @@ -82,8 +84,10 @@ def setup_account(account_config: dict, hass, name: str) \ username = account_config[CONF_USERNAME] password = account_config[CONF_PASSWORD] region = account_config[CONF_REGION] + read_only = account_config[CONF_READ_ONLY] _LOGGER.debug('Adding new account %s', name) - cd_account = BMWConnectedDriveAccount(username, password, region, name) + cd_account = BMWConnectedDriveAccount(username, password, region, name, + read_only) def execute_service(call): """Execute a service for a vehicle. @@ -99,13 +103,13 @@ def execute_service(call): function_name = _SERVICE_MAP[call.service] function_call = getattr(vehicle.remote_services, function_name) function_call() - - # register the remote services - for service in _SERVICE_MAP: - hass.services.register( - DOMAIN, service, - execute_service, - schema=SERVICE_SCHEMA) + if not read_only: + # register the remote services + for service in _SERVICE_MAP: + hass.services.register( + DOMAIN, service, + execute_service, + schema=SERVICE_SCHEMA) # update every UPDATE_INTERVAL minutes, starting now # this should even out the load on the servers @@ -122,13 +126,14 @@ class BMWConnectedDriveAccount: """Representation of a BMW vehicle.""" def __init__(self, username: str, password: str, region_str: str, - name: str) -> None: + name: str, read_only) -> None: """Constructor.""" from bimmer_connected.account import ConnectedDriveAccount from bimmer_connected.country_selector import get_region_from_name region = get_region_from_name(region_str) + self.read_only = read_only self.account = ConnectedDriveAccount(username, password, region) self.name = name self._update_listeners = [] diff --git a/homeassistant/components/calendar/caldav.py b/homeassistant/components/calendar/caldav.py index 3db24790aaf4f4..cb8874a817c5cc 100644 --- a/homeassistant/components/calendar/caldav.py +++ b/homeassistant/components/calendar/caldav.py @@ -49,7 +49,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15) -def setup_platform(hass, config, add_devices, disc_info=None): +def setup_platform(hass, config, add_entities, disc_info=None): """Set up the WebDav Calendar platform.""" import caldav @@ -98,7 +98,7 @@ def setup_platform(hass, config, add_devices, disc_info=None): WebDavCalendarEventDevice(hass, device_data, calendar) ) - add_devices(calendar_devices) + add_entities(calendar_devices) class WebDavCalendarEventDevice(CalendarEventDevice): diff --git a/homeassistant/components/calendar/demo.py b/homeassistant/components/calendar/demo.py index 0bf09f6f2c7646..bf9d4abeb58475 100644 --- a/homeassistant/components/calendar/demo.py +++ b/homeassistant/components/calendar/demo.py @@ -11,11 +11,11 @@ from homeassistant.components.google import CONF_DEVICE_ID, CONF_NAME -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Demo Calendar platform.""" calendar_data_future = DemoGoogleCalendarDataFuture() calendar_data_current = DemoGoogleCalendarDataCurrent() - add_devices([ + add_entities([ DemoGoogleCalendar(hass, calendar_data_future, { CONF_NAME: 'Calendar 1', CONF_DEVICE_ID: 'calendar_1', diff --git a/homeassistant/components/calendar/google.py b/homeassistant/components/calendar/google.py index 925bbcacddf1dd..041b98dc24b019 100644 --- a/homeassistant/components/calendar/google.py +++ b/homeassistant/components/calendar/google.py @@ -2,7 +2,7 @@ Support for Google Calendar Search binary sensors. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.google_calendar/ +https://home-assistant.io/components/calendar.google/ """ import logging from datetime import timedelta @@ -25,7 +25,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15) -def setup_platform(hass, config, add_devices, disc_info=None): +def setup_platform(hass, config, add_entities, disc_info=None): """Set up the calendar platform for event devices.""" if disc_info is None: return @@ -34,9 +34,9 @@ def setup_platform(hass, config, add_devices, disc_info=None): return calendar_service = GoogleCalendarService(hass.config.path(TOKEN_FILE)) - add_devices([GoogleCalendarEventDevice(hass, calendar_service, - disc_info[CONF_CAL_ID], data) - for data in disc_info[CONF_ENTITIES] if data[CONF_TRACK]]) + add_entities([GoogleCalendarEventDevice(hass, calendar_service, + disc_info[CONF_CAL_ID], data) + for data in disc_info[CONF_ENTITIES] if data[CONF_TRACK]]) class GoogleCalendarEventDevice(CalendarEventDevice): diff --git a/homeassistant/components/calendar/todoist.py b/homeassistant/components/calendar/todoist.py index ba1f60027ba3cb..b5eaed4e6c9a8a 100644 --- a/homeassistant/components/calendar/todoist.py +++ b/homeassistant/components/calendar/todoist.py @@ -116,7 +116,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Todoist platform.""" token = config.get(CONF_TOKEN) @@ -178,7 +178,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ) ) - add_devices(project_devices) + add_entities(project_devices) def handle_new_task(call): """Call when a user creates a new Todoist Task from HASS.""" diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 736bcec1e9ca39..768607021655db 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -145,7 +145,7 @@ async def async_get_image(hass, entity_id, timeout=10): component = hass.data.get(DOMAIN) if component is None: - raise HomeAssistantError('Camera component not setup') + raise HomeAssistantError('Camera component not set up') camera = component.get_entity(entity_id) @@ -189,80 +189,32 @@ def update_tokens(time): hass.helpers.event.async_track_time_interval( update_tokens, TOKEN_CHANGE_INTERVAL) - async def async_handle_camera_service(service): - """Handle calls to the camera services.""" - target_cameras = component.async_extract_from_service(service) - - update_tasks = [] - for camera in target_cameras: - if service.service == SERVICE_ENABLE_MOTION: - await camera.async_enable_motion_detection() - elif service.service == SERVICE_DISABLE_MOTION: - await camera.async_disable_motion_detection() - elif service.service == SERVICE_TURN_OFF and \ - camera.supported_features & SUPPORT_ON_OFF: - await camera.async_turn_off() - elif service.service == SERVICE_TURN_ON and \ - camera.supported_features & SUPPORT_ON_OFF: - await camera.async_turn_on() - - if not camera.should_poll: - continue - update_tasks.append(camera.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - async def async_handle_snapshot_service(service): - """Handle snapshot services calls.""" - target_cameras = component.async_extract_from_service(service) - filename = service.data[ATTR_FILENAME] - filename.hass = hass - - for camera in target_cameras: - snapshot_file = filename.async_render( - variables={ATTR_ENTITY_ID: camera}) - - # check if we allow to access to that file - if not hass.config.is_allowed_path(snapshot_file): - _LOGGER.error( - "Can't write %s, no access to path!", snapshot_file) - continue - - image = await camera.async_camera_image() - - def _write_image(to_file, image_data): - """Executor helper to write image.""" - with open(to_file, 'wb') as img_file: - img_file.write(image_data) - - try: - await hass.async_add_job( - _write_image, snapshot_file, image) - except OSError as err: - _LOGGER.error("Can't write image to file: %s", err) - - hass.services.async_register( - DOMAIN, SERVICE_TURN_OFF, async_handle_camera_service, - schema=CAMERA_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_TURN_ON, async_handle_camera_service, - schema=CAMERA_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_ENABLE_MOTION, async_handle_camera_service, - schema=CAMERA_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_DISABLE_MOTION, async_handle_camera_service, - schema=CAMERA_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_SNAPSHOT, async_handle_snapshot_service, - schema=CAMERA_SERVICE_SNAPSHOT) + component.async_register_entity_service( + SERVICE_ENABLE_MOTION, CAMERA_SERVICE_SCHEMA, + 'async_enable_motion_detection' + ) + component.async_register_entity_service( + SERVICE_DISABLE_MOTION, CAMERA_SERVICE_SCHEMA, + 'async_disable_motion_detection' + ) + component.async_register_entity_service( + SERVICE_TURN_OFF, CAMERA_SERVICE_SCHEMA, + 'async_turn_off' + ) + component.async_register_entity_service( + SERVICE_TURN_ON, CAMERA_SERVICE_SCHEMA, + 'async_turn_on' + ) + component.async_register_entity_service( + SERVICE_SNAPSHOT, CAMERA_SERVICE_SNAPSHOT, + async_handle_snapshot_service + ) return True async def async_setup_entry(hass, entry): - """Setup a config entry.""" + """Set up a config entry.""" return await hass.data[DOMAIN].async_setup_entry(entry) @@ -553,3 +505,32 @@ async def send_camera_still(): msg['id'], 'image_fetch_failed', 'Unable to fetch image')) hass.async_add_job(send_camera_still()) + + +async def async_handle_snapshot_service(camera, service): + """Handle snapshot services calls.""" + hass = camera.hass + filename = service.data[ATTR_FILENAME] + filename.hass = hass + + snapshot_file = filename.async_render( + variables={ATTR_ENTITY_ID: camera}) + + # check if we allow to access to that file + if not hass.config.is_allowed_path(snapshot_file): + _LOGGER.error( + "Can't write %s, no access to path!", snapshot_file) + return + + image = await camera.async_camera_image() + + def _write_image(to_file, image_data): + """Executor helper to write image.""" + with open(to_file, 'wb') as img_file: + img_file.write(image_data) + + try: + await hass.async_add_executor_job( + _write_image, snapshot_file, image) + except OSError as err: + _LOGGER.error("Can't write image to file: %s", err) diff --git a/homeassistant/components/camera/abode.py b/homeassistant/components/camera/abode.py index ee739810a61427..fbab1620a39fa9 100644 --- a/homeassistant/components/camera/abode.py +++ b/homeassistant/components/camera/abode.py @@ -22,7 +22,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Abode camera devices.""" import abodepy.helpers.constants as CONST import abodepy.helpers.timeline as TIMELINE @@ -38,7 +38,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data.devices.extend(devices) - add_devices(devices) + add_entities(devices) class AbodeCamera(AbodeDevice, Camera): diff --git a/homeassistant/components/camera/amcrest.py b/homeassistant/components/camera/amcrest.py index 4cb218bc0197d4..9f4b84db2cc2bb 100644 --- a/homeassistant/components/camera/amcrest.py +++ b/homeassistant/components/camera/amcrest.py @@ -22,7 +22,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up an Amcrest IP Camera.""" if discovery_info is None: return @@ -30,7 +31,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): device_name = discovery_info[CONF_NAME] amcrest = hass.data[DATA_AMCREST][device_name] - async_add_devices([AmcrestCam(hass, amcrest)], True) + async_add_entities([AmcrestCam(hass, amcrest)], True) return True diff --git a/homeassistant/components/camera/arlo.py b/homeassistant/components/camera/arlo.py index 1a98ade55183ea..af931c74cfaf86 100644 --- a/homeassistant/components/camera/arlo.py +++ b/homeassistant/components/camera/arlo.py @@ -47,7 +47,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an Arlo IP Camera.""" arlo = hass.data[DATA_ARLO] @@ -55,7 +55,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for camera in arlo.cameras: cameras.append(ArloCam(hass, camera, config)) - add_devices(cameras) + add_entities(cameras) class ArloCam(Camera): diff --git a/homeassistant/components/camera/august.py b/homeassistant/components/camera/august.py index d3bc080bfc69e6..dcce5e13588c11 100644 --- a/homeassistant/components/camera/august.py +++ b/homeassistant/components/camera/august.py @@ -16,7 +16,7 @@ SCAN_INTERVAL = timedelta(seconds=5) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up August cameras.""" data = hass.data[DATA_AUGUST] devices = [] @@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for doorbell in data.doorbells: devices.append(AugustCamera(data, doorbell, DEFAULT_TIMEOUT)) - add_devices(devices, True) + add_entities(devices, True) class AugustCamera(Camera): diff --git a/homeassistant/components/camera/axis.py b/homeassistant/components/camera/axis.py index 5b39718939abf9..5630759a7f862e 100644 --- a/homeassistant/components/camera/axis.py +++ b/homeassistant/components/camera/axis.py @@ -27,7 +27,7 @@ def _get_image_url(host, port, mode): return 'http://{}:{}/axis-cgi/jpg/image.cgi'.format(host, port) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Axis camera.""" camera_config = { CONF_NAME: discovery_info[CONF_NAME], @@ -41,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): 'single'), CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION, } - add_devices([AxisCamera( + add_entities([AxisCamera( hass, camera_config, str(discovery_info[CONF_PORT]))]) diff --git a/homeassistant/components/camera/blink.py b/homeassistant/components/camera/blink.py index 8475ca8fd1e826..217849138c37a6 100644 --- a/homeassistant/components/camera/blink.py +++ b/homeassistant/components/camera/blink.py @@ -20,7 +20,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a Blink Camera.""" if discovery_info is None: return @@ -30,7 +30,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for name in data.cameras: devs.append(BlinkCamera(hass, config, data, name)) - add_devices(devs) + add_entities(devs) class BlinkCamera(Camera): diff --git a/homeassistant/components/camera/bloomsky.py b/homeassistant/components/camera/bloomsky.py index 775289926745e6..01e20e3ccd3d81 100644 --- a/homeassistant/components/camera/bloomsky.py +++ b/homeassistant/components/camera/bloomsky.py @@ -13,11 +13,11 @@ DEPENDENCIES = ['bloomsky'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up access to BloomSky cameras.""" bloomsky = hass.components.bloomsky for device in bloomsky.BLOOMSKY.devices.values(): - add_devices([BloomSkyCamera(bloomsky.BLOOMSKY, device)]) + add_entities([BloomSkyCamera(bloomsky.BLOOMSKY, device)]) class BloomSkyCamera(Camera): diff --git a/homeassistant/components/camera/canary.py b/homeassistant/components/camera/canary.py index a230e0f6d4a211..9031c27b1a9f20 100644 --- a/homeassistant/components/camera/canary.py +++ b/homeassistant/components/camera/canary.py @@ -30,7 +30,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Canary sensors.""" data = hass.data[DATA_CANARY] devices = [] @@ -42,7 +42,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): CanaryCamera(hass, data, location, device, DEFAULT_TIMEOUT, config.get(CONF_FFMPEG_ARGUMENTS))) - add_devices(devices, True) + add_entities(devices, True) class CanaryCamera(Camera): diff --git a/homeassistant/components/camera/demo.py b/homeassistant/components/camera/demo.py index 0e77e6e95adccb..f950edb5c6ca79 100644 --- a/homeassistant/components/camera/demo.py +++ b/homeassistant/components/camera/demo.py @@ -12,10 +12,10 @@ _LOGGER = logging.getLogger(__name__) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Demo camera platform.""" - async_add_devices([ + async_add_entities([ DemoCamera('Demo camera') ]) diff --git a/homeassistant/components/camera/doorbird.py b/homeassistant/components/camera/doorbird.py index 6680258d95d253..7af3e7634d0496 100644 --- a/homeassistant/components/camera/doorbird.py +++ b/homeassistant/components/camera/doorbird.py @@ -28,11 +28,12 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the DoorBird camera platform.""" for doorstation in hass.data[DOORBIRD_DOMAIN]: device = doorstation.device - async_add_devices([ + async_add_entities([ DoorBirdCamera( device.live_image_url, _CAMERA_LIVE.format(doorstation.name), diff --git a/homeassistant/components/camera/familyhub.py b/homeassistant/components/camera/familyhub.py index e78d341713b760..f3dd8b6d0c9166 100644 --- a/homeassistant/components/camera/familyhub.py +++ b/homeassistant/components/camera/familyhub.py @@ -27,7 +27,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Family Hub Camera.""" from pyfamilyhublocal import FamilyHubCam address = config.get(CONF_IP_ADDRESS) @@ -36,7 +36,7 @@ async def async_setup_platform( session = async_get_clientsession(hass) family_hub_cam = FamilyHubCam(address, hass.loop, session) - async_add_devices([FamilyHubCamera(name, family_hub_cam)], True) + async_add_entities([FamilyHubCamera(name, family_hub_cam)], True) class FamilyHubCamera(Camera): diff --git a/homeassistant/components/camera/ffmpeg.py b/homeassistant/components/camera/ffmpeg.py index 3da0f19fbf03de..c458188695a02b 100644 --- a/homeassistant/components/camera/ffmpeg.py +++ b/homeassistant/components/camera/ffmpeg.py @@ -29,12 +29,12 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up a FFmpeg camera.""" if not hass.data[DATA_FFMPEG].async_run_test(config.get(CONF_INPUT)): return - async_add_devices([FFmpegCamera(hass, config)]) + async_add_entities([FFmpegCamera(hass, config)]) class FFmpegCamera(Camera): diff --git a/homeassistant/components/camera/foscam.py b/homeassistant/components/camera/foscam.py index 4ea733139a90b6..ceec57f77557cc 100644 --- a/homeassistant/components/camera/foscam.py +++ b/homeassistant/components/camera/foscam.py @@ -33,9 +33,9 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a Foscam IP Camera.""" - add_devices([FoscamCam(config)]) + add_entities([FoscamCam(config)]) class FoscamCam(Camera): diff --git a/homeassistant/components/camera/generic.py b/homeassistant/components/camera/generic.py index 911c14e72325b0..b707c9134359e1 100644 --- a/homeassistant/components/camera/generic.py +++ b/homeassistant/components/camera/generic.py @@ -15,7 +15,7 @@ from homeassistant.const import ( CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_AUTHENTICATION, - HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION) + HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION, CONF_VERIFY_SSL) from homeassistant.exceptions import TemplateError from homeassistant.components.camera import ( PLATFORM_SCHEMA, DEFAULT_CONTENT_TYPE, Camera) @@ -42,13 +42,15 @@ vol.Optional(CONF_USERNAME): cv.string, vol.Optional(CONF_CONTENT_TYPE, default=DEFAULT_CONTENT_TYPE): cv.string, vol.Optional(CONF_FRAMERATE, default=2): cv.positive_int, + vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, }) @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up a generic IP Camera.""" - async_add_devices([GenericCamera(hass, config)]) + async_add_entities([GenericCamera(hass, config)]) class GenericCamera(Camera): @@ -65,6 +67,7 @@ def __init__(self, hass, device_info): self._limit_refetch = device_info[CONF_LIMIT_REFETCH_TO_URL_CHANGE] self._frame_interval = 1 / device_info[CONF_FRAMERATE] self.content_type = device_info[CONF_CONTENT_TYPE] + self.verify_ssl = device_info[CONF_VERIFY_SSL] username = device_info.get(CONF_USERNAME) password = device_info.get(CONF_PASSWORD) @@ -108,7 +111,8 @@ def async_camera_image(self): def fetch(): """Read image from a URL.""" try: - response = requests.get(url, timeout=10, auth=self._auth) + response = requests.get(url, timeout=10, auth=self._auth, + verify=self.verify_ssl) return response.content except requests.exceptions.RequestException as error: _LOGGER.error("Error getting camera image: %s", error) @@ -119,7 +123,8 @@ def fetch(): # async else: try: - websession = async_get_clientsession(self.hass) + websession = async_get_clientsession( + self.hass, verify_ssl=self.verify_ssl) with async_timeout.timeout(10, loop=self.hass.loop): response = yield from websession.get( url, auth=self._auth) diff --git a/homeassistant/components/camera/local_file.py b/homeassistant/components/camera/local_file.py index 95eade48568f4c..d306509b76258a 100644 --- a/homeassistant/components/camera/local_file.py +++ b/homeassistant/components/camera/local_file.py @@ -31,7 +31,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Camera that works with local files.""" file_path = config[CONF_FILE_PATH] camera = LocalFile(config[CONF_NAME], file_path) @@ -48,7 +48,7 @@ def update_file_path_service(call): update_file_path_service, schema=CAMERA_SERVICE_UPDATE_FILE_PATH) - add_devices([camera]) + add_entities([camera]) class LocalFile(Camera): diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index 757a1b5fc09421..ed7d58658ed318 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -42,11 +42,12 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up a MJPEG IP Camera.""" if discovery_info: config = PLATFORM_SCHEMA(discovery_info) - async_add_devices([MjpegCamera(hass, config)]) + async_add_entities([MjpegCamera(hass, config)]) def extract_image_from_mjpeg(stream): diff --git a/homeassistant/components/camera/mqtt.py b/homeassistant/components/camera/mqtt.py index dc991644b8e5cb..cf5c969c6505ff 100644 --- a/homeassistant/components/camera/mqtt.py +++ b/homeassistant/components/camera/mqtt.py @@ -30,12 +30,13 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the MQTT Camera.""" if discovery_info is not None: config = PLATFORM_SCHEMA(discovery_info) - async_add_devices([MqttCamera( + async_add_entities([MqttCamera( config.get(CONF_NAME), config.get(CONF_TOPIC) )]) diff --git a/homeassistant/components/camera/neato.py b/homeassistant/components/camera/neato.py index 3a8a137c1fe28e..b080dbbae10ebd 100644 --- a/homeassistant/components/camera/neato.py +++ b/homeassistant/components/camera/neato.py @@ -18,14 +18,14 @@ SCAN_INTERVAL = timedelta(minutes=10) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Neato Camera.""" dev = [] for robot in hass.data[NEATO_ROBOTS]: if 'maps' in robot.traits: dev.append(NeatoCleaningMap(hass, robot)) _LOGGER.debug("Adding robots for cleaning maps %s", dev) - add_devices(dev, True) + add_entities(dev, True) class NeatoCleaningMap(Camera): diff --git a/homeassistant/components/camera/nest.py b/homeassistant/components/camera/nest.py index bf6700371fd26f..e1d26371984a82 100644 --- a/homeassistant/components/camera/nest.py +++ b/homeassistant/components/camera/nest.py @@ -23,20 +23,20 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({}) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a Nest Cam. No longer in use. """ -async def async_setup_entry(hass, entry, async_add_devices): +async def async_setup_entry(hass, entry, async_add_entities): """Set up a Nest sensor based on a config entry.""" camera_devices = \ await hass.async_add_job(hass.data[nest.DATA_NEST].cameras) cameras = [NestCamera(structure, device) for structure, device in camera_devices] - async_add_devices(cameras, True) + async_add_entities(cameras, True) class NestCamera(Camera): diff --git a/homeassistant/components/camera/netatmo.py b/homeassistant/components/camera/netatmo.py index 1c7dc4c7ce00c0..93ad2cd055b7cd 100644 --- a/homeassistant/components/camera/netatmo.py +++ b/homeassistant/components/camera/netatmo.py @@ -29,7 +29,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up access to Netatmo cameras.""" netatmo = hass.components.netatmo home = config.get(CONF_HOME) @@ -43,8 +43,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if config[CONF_CAMERAS] != [] and \ camera_name not in config[CONF_CAMERAS]: continue - add_devices([NetatmoCamera(data, camera_name, home, - camera_type, verify_ssl)]) + add_entities([NetatmoCamera(data, camera_name, home, + camera_type, verify_ssl)]) except pyatmo.NoDevice: return None diff --git a/homeassistant/components/camera/onvif.py b/homeassistant/components/camera/onvif.py index 32f8e15748d7b1..9cf21dca9f955d 100644 --- a/homeassistant/components/camera/onvif.py +++ b/homeassistant/components/camera/onvif.py @@ -71,7 +71,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a ONVIF camera.""" if not hass.data[DATA_FFMPEG].async_run_test(config.get(CONF_HOST)): return @@ -94,7 +94,7 @@ def handle_ptz(service): hass.services.async_register(DOMAIN, SERVICE_PTZ, handle_ptz, schema=SERVICE_PTZ_SCHEMA) - add_devices([ONVIFHassCamera(hass, config)]) + add_entities([ONVIFHassCamera(hass, config)]) class ONVIFHassCamera(Camera): @@ -183,7 +183,7 @@ def perform_ptz(self, pan, tilt, zoom): _LOGGER.debug("Camera '%s' doesn't support PTZ.", self._name) async def async_added_to_hass(self): - """Callback when entity is added to hass.""" + """Handle entity addition to hass.""" if ONVIF_DATA not in self.hass.data: self.hass.data[ONVIF_DATA] = {} self.hass.data[ONVIF_DATA][ENTITIES] = [] diff --git a/homeassistant/components/camera/proxy.py b/homeassistant/components/camera/proxy.py index a695848d1fa91d..6c245ffdf434dc 100644 --- a/homeassistant/components/camera/proxy.py +++ b/homeassistant/components/camera/proxy.py @@ -48,9 +48,9 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Proxy camera platform.""" - async_add_devices([ProxyCamera(hass, config)]) + async_add_entities([ProxyCamera(hass, config)]) def _resize_image(image, opts): @@ -64,7 +64,10 @@ def _resize_image(image, opts): quality = opts.quality or DEFAULT_QUALITY new_width = opts.max_width - img = Image.open(io.BytesIO(image)) + try: + img = Image.open(io.BytesIO(image)) + except IOError: + return image imgfmt = str(img.format) if imgfmt not in ('PNG', 'JPEG'): _LOGGER.debug("Image is of unsupported type: %s", imgfmt) diff --git a/homeassistant/components/camera/push.py b/homeassistant/components/camera/push.py index def5c53dd3f06c..305e29d62d34e4 100644 --- a/homeassistant/components/camera/push.py +++ b/homeassistant/components/camera/push.py @@ -21,6 +21,8 @@ _LOGGER = logging.getLogger(__name__) +DEPENDENCIES = ['http'] + CONF_BUFFER_SIZE = 'buffer' CONF_IMAGE_FIELD = 'field' @@ -40,7 +42,7 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Push Camera platform.""" if PUSH_CAMERA_DATA not in hass.data: @@ -53,7 +55,7 @@ async def async_setup_platform(hass, config, async_add_devices, hass.http.register_view(CameraPushReceiver(hass, config[CONF_IMAGE_FIELD])) - async_add_devices(cameras) + async_add_entities(cameras) class CameraPushReceiver(HomeAssistantView): @@ -111,7 +113,7 @@ async def async_added_to_hass(self): @property def state(self): - """Current state of the camera.""" + """Return current state of the camera.""" return self._state async def update_image(self, image, filename): diff --git a/homeassistant/components/camera/ring.py b/homeassistant/components/camera/ring.py index 96956d24eec6ad..f629b501819928 100644 --- a/homeassistant/components/camera/ring.py +++ b/homeassistant/components/camera/ring.py @@ -40,7 +40,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up a Ring Door Bell and StickUp Camera.""" ring = hass.data[DATA_RING] @@ -73,7 +74,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID) - async_add_devices(cams, True) + async_add_entities(cams, True) return True diff --git a/homeassistant/components/camera/rpi_camera.py b/homeassistant/components/camera/rpi_camera.py index 91edf7d1053c19..ba6f5e93304b5f 100644 --- a/homeassistant/components/camera/rpi_camera.py +++ b/homeassistant/components/camera/rpi_camera.py @@ -62,7 +62,7 @@ def kill_raspistill(*args): stderr=subprocess.STDOUT) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Raspberry Camera.""" if shutil.which("raspistill") is None: _LOGGER.error("'raspistill' was not found") @@ -106,7 +106,7 @@ def delete_temp_file(*args): _LOGGER.error("'%s' is not a whitelisted directory", file_path) return False - add_devices([RaspberryCamera(setup_config)]) + add_entities([RaspberryCamera(setup_config)]) class RaspberryCamera(Camera): diff --git a/homeassistant/components/camera/skybell.py b/homeassistant/components/camera/skybell.py index be3504dab78b3e..9a7d7a069447bd 100644 --- a/homeassistant/components/camera/skybell.py +++ b/homeassistant/components/camera/skybell.py @@ -20,7 +20,7 @@ SCAN_INTERVAL = timedelta(seconds=90) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the platform for a Skybell device.""" skybell = hass.data.get(SKYBELL_DOMAIN) @@ -28,7 +28,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for device in skybell.get_devices(): sensors.append(SkybellCamera(device)) - add_devices(sensors, True) + add_entities(sensors, True) class SkybellCamera(SkybellDevice, Camera): diff --git a/homeassistant/components/camera/synology.py b/homeassistant/components/camera/synology.py index 8bbb3e8c632ca6..3e587fff2342dc 100644 --- a/homeassistant/components/camera/synology.py +++ b/homeassistant/components/camera/synology.py @@ -39,7 +39,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up a Synology IP Camera.""" verify_ssl = config.get(CONF_VERIFY_SSL) timeout = config.get(CONF_TIMEOUT) @@ -66,7 +67,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): device = SynologyCamera(surveillance, camera.camera_id, verify_ssl) devices.append(device) - async_add_devices(devices) + async_add_entities(devices) class SynologyCamera(Camera): diff --git a/homeassistant/components/camera/usps.py b/homeassistant/components/camera/usps.py index 6c76d0d66d8507..d23359d8c57abb 100644 --- a/homeassistant/components/camera/usps.py +++ b/homeassistant/components/camera/usps.py @@ -17,13 +17,13 @@ SCAN_INTERVAL = timedelta(seconds=10) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up USPS mail camera.""" if discovery_info is None: return usps = hass.data[DATA_USPS] - add_devices([USPSCamera(usps)]) + add_entities([USPSCamera(usps)]) class USPSCamera(Camera): diff --git a/homeassistant/components/camera/uvc.py b/homeassistant/components/camera/uvc.py index b5306c31c84bc9..0e65ac77c1fa03 100644 --- a/homeassistant/components/camera/uvc.py +++ b/homeassistant/components/camera/uvc.py @@ -34,7 +34,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Discover cameras on a Unifi NVR.""" addr = config[CONF_NVR] key = config[CONF_KEY] @@ -63,11 +63,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Unable to connect to NVR: %s", str(ex)) raise PlatformNotReady - add_devices([UnifiVideoCamera(nvrconn, - camera[identifier], - camera['name'], - password) - for camera in cameras]) + add_entities([UnifiVideoCamera(nvrconn, + camera[identifier], + camera['name'], + password) + for camera in cameras]) return True diff --git a/homeassistant/components/camera/verisure.py b/homeassistant/components/camera/verisure.py index 554f877d0bd34b..01e4e82f3bcc13 100644 --- a/homeassistant/components/camera/verisure.py +++ b/homeassistant/components/camera/verisure.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Verisure Camera.""" if not int(hub.config.get(CONF_SMARTCAM, 1)): return False @@ -30,7 +30,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): VerisureSmartcam(hass, device_label, directory_path) for device_label in hub.get( "$.customerImageCameras[*].deviceLabel")]) - add_devices(smartcams) + add_entities(smartcams) class VerisureSmartcam(Camera): diff --git a/homeassistant/components/camera/xeoma.py b/homeassistant/components/camera/xeoma.py index 2a4d15268180c5..c268c3533e0ab0 100644 --- a/homeassistant/components/camera/xeoma.py +++ b/homeassistant/components/camera/xeoma.py @@ -40,7 +40,7 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Discover and setup Xeoma Cameras.""" from pyxeoma.xeoma import Xeoma, XeomaError @@ -78,7 +78,7 @@ async def async_setup_platform(hass, config, async_add_devices, camera[CONF_HIDE] = cam[CONF_HIDE] cameras = list(filter(lambda c: not c[CONF_HIDE], discovered_cameras)) - async_add_devices( + async_add_entities( [XeomaCamera(xeoma, camera[CONF_IMAGE_NAME], camera[CONF_NAME], camera[CONF_VIEWER_USERNAME], camera[CONF_VIEWER_PASSWORD]) for camera in cameras]) diff --git a/homeassistant/components/camera/xiaomi.py b/homeassistant/components/camera/xiaomi.py index e80f4b7532acff..da36299a209d48 100644 --- a/homeassistant/components/camera/xiaomi.py +++ b/homeassistant/components/camera/xiaomi.py @@ -45,11 +45,11 @@ async def async_setup_platform(hass, config, - async_add_devices, + async_add_entities, discovery_info=None): """Set up a Xiaomi Camera.""" _LOGGER.debug('Received configuration for model %s', config[CONF_MODEL]) - async_add_devices([XiaomiCamera(hass, config)]) + async_add_entities([XiaomiCamera(hass, config)]) class XiaomiCamera(Camera): diff --git a/homeassistant/components/camera/yi.py b/homeassistant/components/camera/yi.py index 4efc2c7d8ba9dc..eb26c1cc887beb 100644 --- a/homeassistant/components/camera/yi.py +++ b/homeassistant/components/camera/yi.py @@ -41,9 +41,9 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up a Yi Camera.""" - async_add_devices([YiCamera(hass, config)], True) + async_add_entities([YiCamera(hass, config)], True) class YiCamera(Camera): diff --git a/homeassistant/components/camera/zoneminder.py b/homeassistant/components/camera/zoneminder.py index be59a1c1f50e56..e48caa42a34069 100644 --- a/homeassistant/components/camera/zoneminder.py +++ b/homeassistant/components/camera/zoneminder.py @@ -49,7 +49,8 @@ def _get_image_url(hass, monitor, mode): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the ZoneMinder cameras.""" cameras = [] monitors = zoneminder.get_state('api/monitors.json') @@ -77,7 +78,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): _LOGGER.warning("No active cameras found") return - async_add_devices(cameras) + async_add_entities(cameras) class ZoneMinderCamera(MjpegCamera): diff --git a/homeassistant/components/cast/.translations/da.json b/homeassistant/components/cast/.translations/da.json new file mode 100644 index 00000000000000..5d8ab2362377c1 --- /dev/null +++ b/homeassistant/components/cast/.translations/da.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "no_devices_found": "Ingen Google Cast enheder kunne findes p\u00e5 netv\u00e6rket.", + "single_instance_allowed": "Det er kun n\u00f8dvendigt med en ops\u00e6tning af Google Cast" + }, + "step": { + "confirm": { + "description": "Vil du ops\u00e6tte Google Cast?", + "title": "Google Cast" + } + }, + "title": "Google Cast" + } +} \ No newline at end of file diff --git a/homeassistant/components/cast/.translations/en.json b/homeassistant/components/cast/.translations/en.json index 55d79a7d560a9b..f908f41e3289e6 100644 --- a/homeassistant/components/cast/.translations/en.json +++ b/homeassistant/components/cast/.translations/en.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Do you want to setup Google Cast?", + "description": "Do you want to set up Google Cast?", "title": "Google Cast" } }, diff --git a/homeassistant/components/cast/.translations/es.json b/homeassistant/components/cast/.translations/es.json new file mode 100644 index 00000000000000..9188055849c5f5 --- /dev/null +++ b/homeassistant/components/cast/.translations/es.json @@ -0,0 +1,5 @@ +{ + "config": { + "title": "Google Cast" + } +} \ No newline at end of file diff --git a/homeassistant/components/cast/.translations/he.json b/homeassistant/components/cast/.translations/he.json new file mode 100644 index 00000000000000..40d2514b59ce06 --- /dev/null +++ b/homeassistant/components/cast/.translations/he.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9 Google Cast \u05d1\u05e8\u05e9\u05ea.", + "single_instance_allowed": "\u05e8\u05e7 \u05d4\u05d2\u05d3\u05e8\u05d4 \u05d0\u05d7\u05ea \u05e9\u05dc Google Cast \u05e0\u05d7\u05d5\u05e6\u05d4." + }, + "step": { + "confirm": { + "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea Google Cast?", + "title": "Google Cast" + } + }, + "title": "Google Cast" + } +} \ No newline at end of file diff --git a/homeassistant/components/cast/strings.json b/homeassistant/components/cast/strings.json index 7f480de0e8bea3..eecdecbfdf9e5f 100644 --- a/homeassistant/components/cast/strings.json +++ b/homeassistant/components/cast/strings.json @@ -4,7 +4,7 @@ "step": { "confirm": { "title": "Google Cast", - "description": "Do you want to setup Google Cast?" + "description": "Do you want to set up Google Cast?" } }, "abort": { diff --git a/homeassistant/components/climate/__init__.py b/homeassistant/components/climate/__init__.py index 9584422e2b41c1..a3273f67cc2d6a 100644 --- a/homeassistant/components/climate/__init__.py +++ b/homeassistant/components/climate/__init__.py @@ -4,7 +4,6 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/climate/ """ -import asyncio from datetime import timedelta import logging import functools as ft @@ -250,215 +249,52 @@ async def async_setup(hass, config): EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL) await component.async_setup(config) - async def async_away_mode_set_service(service): - """Set away mode on target climate devices.""" - target_climate = component.async_extract_from_service(service) - - away_mode = service.data.get(ATTR_AWAY_MODE) - - update_tasks = [] - for climate in target_climate: - if away_mode: - await climate.async_turn_away_mode_on() - else: - await climate.async_turn_away_mode_off() - - if not climate.should_poll: - continue - update_tasks.append(climate.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_AWAY_MODE, async_away_mode_set_service, - schema=SET_AWAY_MODE_SCHEMA) - - async def async_hold_mode_set_service(service): - """Set hold mode on target climate devices.""" - target_climate = component.async_extract_from_service(service) - - hold_mode = service.data.get(ATTR_HOLD_MODE) - - update_tasks = [] - for climate in target_climate: - await climate.async_set_hold_mode(hold_mode) - - if not climate.should_poll: - continue - update_tasks.append(climate.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_HOLD_MODE, async_hold_mode_set_service, - schema=SET_HOLD_MODE_SCHEMA) - - async def async_aux_heat_set_service(service): - """Set auxiliary heater on target climate devices.""" - target_climate = component.async_extract_from_service(service) - - aux_heat = service.data.get(ATTR_AUX_HEAT) - - update_tasks = [] - for climate in target_climate: - if aux_heat: - await climate.async_turn_aux_heat_on() - else: - await climate.async_turn_aux_heat_off() - - if not climate.should_poll: - continue - update_tasks.append(climate.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_AUX_HEAT, async_aux_heat_set_service, - schema=SET_AUX_HEAT_SCHEMA) - - async def async_temperature_set_service(service): - """Set temperature on the target climate devices.""" - target_climate = component.async_extract_from_service(service) - - update_tasks = [] - for climate in target_climate: - kwargs = {} - for value, temp in service.data.items(): - if value in CONVERTIBLE_ATTRIBUTE: - kwargs[value] = convert_temperature( - temp, - hass.config.units.temperature_unit, - climate.temperature_unit - ) - else: - kwargs[value] = temp - - await climate.async_set_temperature(**kwargs) - - if not climate.should_poll: - continue - update_tasks.append(climate.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_TEMPERATURE, async_temperature_set_service, - schema=SET_TEMPERATURE_SCHEMA) - - async def async_humidity_set_service(service): - """Set humidity on the target climate devices.""" - target_climate = component.async_extract_from_service(service) - - humidity = service.data.get(ATTR_HUMIDITY) - - update_tasks = [] - for climate in target_climate: - await climate.async_set_humidity(humidity) - if not climate.should_poll: - continue - update_tasks.append(climate.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_HUMIDITY, async_humidity_set_service, - schema=SET_HUMIDITY_SCHEMA) - - async def async_fan_mode_set_service(service): - """Set fan mode on target climate devices.""" - target_climate = component.async_extract_from_service(service) - - fan = service.data.get(ATTR_FAN_MODE) - - update_tasks = [] - for climate in target_climate: - await climate.async_set_fan_mode(fan) - if not climate.should_poll: - continue - update_tasks.append(climate.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_FAN_MODE, async_fan_mode_set_service, - schema=SET_FAN_MODE_SCHEMA) - - async def async_operation_set_service(service): - """Set operating mode on the target climate devices.""" - target_climate = component.async_extract_from_service(service) - - operation_mode = service.data.get(ATTR_OPERATION_MODE) - - update_tasks = [] - for climate in target_climate: - await climate.async_set_operation_mode(operation_mode) - if not climate.should_poll: - continue - update_tasks.append(climate.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_OPERATION_MODE, async_operation_set_service, - schema=SET_OPERATION_MODE_SCHEMA) - - async def async_swing_set_service(service): - """Set swing mode on the target climate devices.""" - target_climate = component.async_extract_from_service(service) - - swing_mode = service.data.get(ATTR_SWING_MODE) - - update_tasks = [] - for climate in target_climate: - await climate.async_set_swing_mode(swing_mode) - if not climate.should_poll: - continue - update_tasks.append(climate.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_SWING_MODE, async_swing_set_service, - schema=SET_SWING_MODE_SCHEMA) - - async def async_on_off_service(service): - """Handle on/off calls.""" - target_climate = component.async_extract_from_service(service) - - update_tasks = [] - for climate in target_climate: - if service.service == SERVICE_TURN_ON: - await climate.async_turn_on() - elif service.service == SERVICE_TURN_OFF: - await climate.async_turn_off() - - if not climate.should_poll: - continue - update_tasks.append(climate.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_TURN_OFF, async_on_off_service, - schema=ON_OFF_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_TURN_ON, async_on_off_service, - schema=ON_OFF_SERVICE_SCHEMA) + component.async_register_entity_service( + SERVICE_SET_AWAY_MODE, SET_AWAY_MODE_SCHEMA, + async_service_away_mode + ) + component.async_register_entity_service( + SERVICE_SET_HOLD_MODE, SET_HOLD_MODE_SCHEMA, + 'async_set_hold_mode' + ) + component.async_register_entity_service( + SERVICE_SET_AUX_HEAT, SET_AUX_HEAT_SCHEMA, + async_service_aux_heat + ) + component.async_register_entity_service( + SERVICE_SET_TEMPERATURE, SET_TEMPERATURE_SCHEMA, + async_service_temperature_set + ) + component.async_register_entity_service( + SERVICE_SET_HUMIDITY, SET_HUMIDITY_SCHEMA, + 'async_set_humidity' + ) + component.async_register_entity_service( + SERVICE_SET_FAN_MODE, SET_FAN_MODE_SCHEMA, + 'async_set_fan_mode' + ) + component.async_register_entity_service( + SERVICE_SET_OPERATION_MODE, SET_OPERATION_MODE_SCHEMA, + 'async_set_operation_mode' + ) + component.async_register_entity_service( + SERVICE_SET_SWING_MODE, SET_SWING_MODE_SCHEMA, + 'async_set_swing_mode' + ) + component.async_register_entity_service( + SERVICE_TURN_OFF, ON_OFF_SERVICE_SCHEMA, + 'async_turn_off' + ) + component.async_register_entity_service( + SERVICE_TURN_ON, ON_OFF_SERVICE_SCHEMA, + 'async_turn_on' + ) return True async def async_setup_entry(hass, entry): - """Setup a config entry.""" + """Set up a config entry.""" return await hass.data[DOMAIN].async_setup_entry(entry) @@ -484,7 +320,7 @@ def state(self): @property def precision(self): """Return the precision of the system.""" - if self.unit_of_measurement == TEMP_CELSIUS: + if self.hass.config.units.temperature_unit == TEMP_CELSIUS: return PRECISION_TENTHS return PRECISION_WHOLE @@ -558,11 +394,6 @@ def state_attributes(self): return data - @property - def unit_of_measurement(self): - """Return the unit of measurement to display.""" - return self.hass.config.units.temperature_unit - @property def temperature_unit(self): """Return the unit of measurement used by the platform.""" @@ -812,3 +643,37 @@ def min_humidity(self): def max_humidity(self): """Return the maximum humidity.""" return DEFAULT_MAX_HUMIDITY + + +async def async_service_away_mode(entity, service): + """Handle away mode service.""" + if service.data[ATTR_AWAY_MODE]: + await entity.async_turn_away_mode_on() + else: + await entity.async_turn_away_mode_off() + + +async def async_service_aux_heat(entity, service): + """Handle aux heat service.""" + if service.data[ATTR_AUX_HEAT]: + await entity.async_turn_aux_heat_on() + else: + await entity.async_turn_aux_heat_off() + + +async def async_service_temperature_set(entity, service): + """Handle set temperature service.""" + hass = entity.hass + kwargs = {} + + for value, temp in service.data.items(): + if value in CONVERTIBLE_ATTRIBUTE: + kwargs[value] = convert_temperature( + temp, + hass.config.units.temperature_unit, + entity.temperature_unit + ) + else: + kwargs[value] = temp + + await entity.async_set_temperature(**kwargs) diff --git a/homeassistant/components/climate/daikin.py b/homeassistant/components/climate/daikin.py index 50501025f0c2c4..6743bf034dc4dd 100644 --- a/homeassistant/components/climate/daikin.py +++ b/homeassistant/components/climate/daikin.py @@ -50,7 +50,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Daikin HVAC platform.""" if discovery_info is not None: host = discovery_info.get('ip') @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.debug("Added Daikin AC on %s", host) api = daikin_api_setup(hass, host, name) - add_devices([DaikinClimate(api)], True) + add_entities([DaikinClimate(api)], True) class DaikinClimate(ClimateDevice): diff --git a/homeassistant/components/climate/demo.py b/homeassistant/components/climate/demo.py index 44491b8cd21e4d..bc0b9bd52ee5bc 100644 --- a/homeassistant/components/climate/demo.py +++ b/homeassistant/components/climate/demo.py @@ -17,9 +17,9 @@ SUPPORT_FLAGS = SUPPORT_TARGET_HUMIDITY_LOW | SUPPORT_TARGET_HUMIDITY_HIGH -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Demo climate devices.""" - add_devices([ + add_entities([ DemoClimate('HeatPump', 68, TEMP_FAHRENHEIT, None, None, 77, None, None, None, None, 'heat', None, None, None, True), diff --git a/homeassistant/components/climate/ecobee.py b/homeassistant/components/climate/ecobee.py index 718788271535bb..46fc5c297526bc 100644 --- a/homeassistant/components/climate/ecobee.py +++ b/homeassistant/components/climate/ecobee.py @@ -53,7 +53,7 @@ SUPPORT_TARGET_TEMPERATURE_LOW | SUPPORT_FAN_MODE) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Ecobee Thermostat Platform.""" if discovery_info is None: return @@ -64,7 +64,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): hold_temp) devices = [Thermostat(data, index, hold_temp) for index in range(len(data.ecobee.thermostats))] - add_devices(devices) + add_entities(devices) def fan_min_on_time_set_service(service): """Set the minimum fan on time on the target thermostats.""" diff --git a/homeassistant/components/climate/econet.py b/homeassistant/components/climate/econet.py index 0591178391a3f2..9350b8f853d0b2 100644 --- a/homeassistant/components/climate/econet.py +++ b/homeassistant/components/climate/econet.py @@ -66,7 +66,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the EcoNet water heaters.""" from pyeconet.api import PyEcoNet @@ -80,7 +80,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): water_heaters = econet.get_water_heaters() hass_water_heaters = [ EcoNetWaterHeater(water_heater) for water_heater in water_heaters] - add_devices(hass_water_heaters) + add_entities(hass_water_heaters) hass.data[ECONET_DATA]['water_heaters'].extend(hass_water_heaters) def service_handle(service): diff --git a/homeassistant/components/climate/ephember.py b/homeassistant/components/climate/ephember.py index 419237b4645ae5..cd410cf3be455e 100644 --- a/homeassistant/components/climate/ephember.py +++ b/homeassistant/components/climate/ephember.py @@ -9,26 +9,37 @@ import voluptuous as vol from homeassistant.components.climate import ( - ClimateDevice, PLATFORM_SCHEMA, STATE_HEAT, STATE_IDLE, SUPPORT_AUX_HEAT, + ClimateDevice, PLATFORM_SCHEMA, STATE_HEAT, STATE_OFF, + STATE_AUTO, SUPPORT_AUX_HEAT, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ( TEMP_CELSIUS, CONF_USERNAME, CONF_PASSWORD, ATTR_TEMPERATURE) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyephember==0.1.1'] +REQUIREMENTS = ['pyephember==0.2.0'] _LOGGER = logging.getLogger(__name__) # Return cached results if last scan was less then this time ago SCAN_INTERVAL = timedelta(seconds=120) +OPERATION_LIST = [STATE_AUTO, STATE_HEAT, STATE_OFF] + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string }) +EPH_TO_HA_STATE = { + 'AUTO': STATE_AUTO, + 'ON': STATE_HEAT, + 'OFF': STATE_OFF +} + +HA_STATE_TO_EPH = {value: key for key, value in EPH_TO_HA_STATE.items()} + -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ephember thermostat.""" from pyephember.pyephember import EphEmber @@ -39,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ember = EphEmber(username, password) zones = ember.get_zones() for zone in zones: - add_devices([EphEmberThermostat(ember, zone)]) + add_entities([EphEmberThermostat(ember, zone)]) except RuntimeError: _LOGGER.error("Cannot connect to EphEmber") return @@ -61,9 +72,11 @@ def __init__(self, ember, zone): def supported_features(self): """Return the list of supported features.""" if self._hot_water: - return SUPPORT_AUX_HEAT + return SUPPORT_AUX_HEAT | SUPPORT_OPERATION_MODE - return SUPPORT_TARGET_TEMPERATURE | SUPPORT_AUX_HEAT + return (SUPPORT_TARGET_TEMPERATURE | + SUPPORT_AUX_HEAT | + SUPPORT_OPERATION_MODE) @property def name(self): @@ -93,12 +106,40 @@ def target_temperature_step(self): return 1 + @property + def device_state_attributes(self): + """Show Device Attributes.""" + attributes = { + 'currently_active': self._zone['isCurrentlyActive'] + } + return attributes + @property def current_operation(self): """Return current operation ie. heat, cool, idle.""" + mode = self._ember.get_zone_mode(self._zone_name) + return self.map_mode_eph_hass(mode) + + @property + def operation_list(self): + """Return the supported operations.""" + return OPERATION_LIST + + def set_operation_mode(self, operation_mode): + """Set the operation mode.""" + mode = self.map_mode_hass_eph(operation_mode) + if mode is not None: + self._ember.set_mode_by_name(self._zone_name, mode) + else: + _LOGGER.error("Invalid operation mode provided %s", operation_mode) + + @property + def is_on(self): + """Return current state.""" if self._zone['isCurrentlyActive']: - return STATE_HEAT - return STATE_IDLE + return True + + return None @property def is_aux_heat_on(self): @@ -152,3 +193,14 @@ def max_temp(self): def update(self): """Get the latest data.""" self._zone = self._ember.get_zone(self._zone_name) + + @staticmethod + def map_mode_hass_eph(operation_mode): + """Map from home assistant mode to eph mode.""" + from pyephember.pyephember import ZoneMode + return getattr(ZoneMode, HA_STATE_TO_EPH.get(operation_mode), None) + + @staticmethod + def map_mode_eph_hass(operation_mode): + """Map from eph mode to home assistant mode.""" + return EPH_TO_HA_STATE.get(operation_mode.name, STATE_AUTO) diff --git a/homeassistant/components/climate/eq3btsmart.py b/homeassistant/components/climate/eq3btsmart.py index 10fd879e386296..904d8222e88acb 100644 --- a/homeassistant/components/climate/eq3btsmart.py +++ b/homeassistant/components/climate/eq3btsmart.py @@ -42,7 +42,7 @@ SUPPORT_AWAY_MODE) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the eQ-3 BLE thermostats.""" devices = [] @@ -50,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): mac = device_cfg[CONF_MAC] devices.append(EQ3BTSmartThermostat(mac, name)) - add_devices(devices) + add_entities(devices) # pylint: disable=import-error diff --git a/homeassistant/components/climate/flexit.py b/homeassistant/components/climate/flexit.py index 6c340e4a5f01f4..de74d2facb57b9 100644 --- a/homeassistant/components/climate/flexit.py +++ b/homeassistant/components/climate/flexit.py @@ -36,11 +36,11 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Flexit Platform.""" modbus_slave = config.get(CONF_SLAVE, None) name = config.get(CONF_NAME, None) - add_devices([Flexit(modbus_slave, name)], True) + add_entities([Flexit(modbus_slave, name)], True) class Flexit(ClimateDevice): diff --git a/homeassistant/components/climate/fritzbox.py b/homeassistant/components/climate/fritzbox.py index fa3ca31c770725..3eedb89a3b782b 100644 --- a/homeassistant/components/climate/fritzbox.py +++ b/homeassistant/components/climate/fritzbox.py @@ -35,7 +35,7 @@ OFF_REPORT_SET_TEMPERATURE = 0.0 -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Fritzbox smarthome thermostat platform.""" devices = [] fritz_list = hass.data[FRITZBOX_DOMAIN] @@ -46,7 +46,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if device.has_thermostat: devices.append(FritzboxThermostat(device, fritz)) - add_devices(devices) + add_entities(devices) class FritzboxThermostat(ClimateDevice): diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index 3f1d9a208ac5fd..fec18329878920 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -16,9 +16,8 @@ ATTR_OPERATION_MODE, ATTR_AWAY_MODE, SUPPORT_OPERATION_MODE, SUPPORT_AWAY_MODE, SUPPORT_TARGET_TEMPERATURE, PLATFORM_SCHEMA) from homeassistant.const import ( - ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF, ATTR_TEMPERATURE, - CONF_NAME, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, - STATE_UNKNOWN) + STATE_ON, STATE_OFF, ATTR_TEMPERATURE, CONF_NAME, ATTR_ENTITY_ID, + SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_UNKNOWN) from homeassistant.helpers import condition from homeassistant.helpers.event import ( async_track_state_change, async_track_time_interval) @@ -69,7 +68,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the generic thermostat platform.""" name = config.get(CONF_NAME) heater_entity_id = config.get(CONF_HEATER) @@ -85,7 +85,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): initial_operation_mode = config.get(CONF_INITIAL_OPERATION_MODE) away_temp = config.get(CONF_AWAY_TEMP) - async_add_devices([GenericThermostat( + async_add_entities([GenericThermostat( hass, name, heater_entity_id, sensor_entity_id, min_temp, max_temp, target_temp, ac_mode, min_cycle_duration, cold_tolerance, hot_tolerance, keep_alive, initial_operation_mode, away_temp)]) @@ -123,6 +123,7 @@ def __init__(self, hass, name, heater_entity_id, sensor_entity_id, self._enabled = True self._active = False self._cur_temp = None + self._temp_lock = asyncio.Lock() self._min_temp = min_temp self._max_temp = max_temp self._target_temp = target_temp @@ -140,7 +141,7 @@ def __init__(self, hass, name, heater_entity_id, sensor_entity_id, if self._keep_alive: async_track_time_interval( - hass, self._async_keep_alive, self._keep_alive) + hass, self._async_control_heating, self._keep_alive) sensor_state = hass.states.get(sensor_entity_id) if sensor_state and sensor_state.state != STATE_UNKNOWN: @@ -234,31 +235,30 @@ async def async_set_operation_mode(self, operation_mode): if operation_mode == STATE_HEAT: self._current_operation = STATE_HEAT self._enabled = True - self._async_control_heating() + await self._async_control_heating() elif operation_mode == STATE_COOL: self._current_operation = STATE_COOL self._enabled = True - self._async_control_heating() + await self._async_control_heating() elif operation_mode == STATE_OFF: self._current_operation = STATE_OFF self._enabled = False if self._is_device_active: - self._heater_turn_off() + await self._async_heater_turn_off() else: _LOGGER.error("Unrecognized operation mode: %s", operation_mode) return # Ensure we update the current operation after changing the mode self.schedule_update_ha_state() - @asyncio.coroutine - def async_set_temperature(self, **kwargs): + async def async_set_temperature(self, **kwargs): """Set new target temperature.""" temperature = kwargs.get(ATTR_TEMPERATURE) if temperature is None: return self._target_temp = temperature - self._async_control_heating() - yield from self.async_update_ha_state() + await self._async_control_heating() + await self.async_update_ha_state() @property def min_temp(self): @@ -278,15 +278,14 @@ def max_temp(self): # Get default temp from super class return super().max_temp - @asyncio.coroutine - def _async_sensor_changed(self, entity_id, old_state, new_state): + async def _async_sensor_changed(self, entity_id, old_state, new_state): """Handle temperature changes.""" if new_state is None: return self._async_update_temp(new_state) - self._async_control_heating() - yield from self.async_update_ha_state() + await self._async_control_heating() + await self.async_update_ha_state() @callback def _async_switch_changed(self, entity_id, old_state, new_state): @@ -295,81 +294,59 @@ def _async_switch_changed(self, entity_id, old_state, new_state): return self.async_schedule_update_ha_state() - @callback - def _async_keep_alive(self, time): - """Call at constant intervals for keep-alive purposes.""" - if self._is_device_active: - self._heater_turn_on() - else: - self._heater_turn_off() - @callback def _async_update_temp(self, state): """Update thermostat with latest state from sensor.""" - unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) - try: - self._cur_temp = self.hass.config.units.temperature( - float(state.state), unit) + self._cur_temp = float(state.state) except ValueError as ex: _LOGGER.error("Unable to update from sensor: %s", ex) - @callback - def _async_control_heating(self): + async def _async_control_heating(self, time=None): """Check if we need to turn heating on or off.""" - if not self._active and None not in (self._cur_temp, - self._target_temp): - self._active = True - _LOGGER.info("Obtained current and target temperature. " - "Generic thermostat active. %s, %s", - self._cur_temp, self._target_temp) - - if not self._active: - return - - if not self._enabled: - return - - if self.min_cycle_duration: - if self._is_device_active: - current_state = STATE_ON - else: - current_state = STATE_OFF - long_enough = condition.state( - self.hass, self.heater_entity_id, current_state, - self.min_cycle_duration) - if not long_enough: + async with self._temp_lock: + if not self._active and None not in (self._cur_temp, + self._target_temp): + self._active = True + _LOGGER.info("Obtained current and target temperature. " + "Generic thermostat active. %s, %s", + self._cur_temp, self._target_temp) + + if not self._active or not self._enabled: return - if self.ac_mode: - is_cooling = self._is_device_active - if is_cooling: - too_cold = self._target_temp - self._cur_temp >= \ - self._cold_tolerance - if too_cold: - _LOGGER.info("Turning off AC %s", self.heater_entity_id) - self._heater_turn_off() - else: - too_hot = self._cur_temp - self._target_temp >= \ - self._hot_tolerance - if too_hot: - _LOGGER.info("Turning on AC %s", self.heater_entity_id) - self._heater_turn_on() - else: - is_heating = self._is_device_active - if is_heating: - too_hot = self._cur_temp - self._target_temp >= \ - self._hot_tolerance - if too_hot: + if self.min_cycle_duration: + if self._is_device_active: + current_state = STATE_ON + else: + current_state = STATE_OFF + long_enough = condition.state( + self.hass, self.heater_entity_id, current_state, + self.min_cycle_duration) + if not long_enough: + return + + too_cold = \ + self._target_temp - self._cur_temp >= self._cold_tolerance + too_hot = \ + self._cur_temp - self._target_temp >= self._hot_tolerance + if self._is_device_active: + if (self.ac_mode and too_cold) or \ + (not self.ac_mode and too_hot): _LOGGER.info("Turning off heater %s", self.heater_entity_id) - self._heater_turn_off() + await self._async_heater_turn_off() + elif time is not None: + # The time argument is passed only in keep-alive case + await self._async_heater_turn_on() else: - too_cold = self._target_temp - self._cur_temp >= \ - self._cold_tolerance - if too_cold: + if (self.ac_mode and too_hot) or \ + (not self.ac_mode and too_cold): _LOGGER.info("Turning on heater %s", self.heater_entity_id) - self._heater_turn_on() + await self._async_heater_turn_on() + elif time is not None: + # The time argument is passed only in keep-alive case + await self._async_heater_turn_off() @property def _is_device_active(self): @@ -381,36 +358,32 @@ def supported_features(self): """Return the list of supported features.""" return self._support_flags - @callback - def _heater_turn_on(self): + async def _async_heater_turn_on(self): """Turn heater toggleable device on.""" data = {ATTR_ENTITY_ID: self.heater_entity_id} - self.hass.async_add_job( - self.hass.services.async_call(HA_DOMAIN, SERVICE_TURN_ON, data)) + await self.hass.services.async_call(HA_DOMAIN, SERVICE_TURN_ON, data) - @callback - def _heater_turn_off(self): + async def _async_heater_turn_off(self): """Turn heater toggleable device off.""" data = {ATTR_ENTITY_ID: self.heater_entity_id} - self.hass.async_add_job( - self.hass.services.async_call(HA_DOMAIN, SERVICE_TURN_OFF, data)) + await self.hass.services.async_call(HA_DOMAIN, SERVICE_TURN_OFF, data) @property def is_away_mode_on(self): """Return true if away mode is on.""" return self._is_away - def turn_away_mode_on(self): + async def async_turn_away_mode_on(self): """Turn away mode on by setting it on away hold indefinitely.""" self._is_away = True self._saved_target_temp = self._target_temp self._target_temp = self._away_temp - self._async_control_heating() - self.schedule_update_ha_state() + await self._async_control_heating() + await self.async_update_ha_state() - def turn_away_mode_off(self): + async def async_turn_away_mode_off(self): """Turn away off.""" self._is_away = False self._target_temp = self._saved_target_temp - self._async_control_heating() - self.schedule_update_ha_state() + await self._async_control_heating() + await self.async_update_ha_state() diff --git a/homeassistant/components/climate/heatmiser.py b/homeassistant/components/climate/heatmiser.py index 12057e886472f9..a03d1567e01aa8 100644 --- a/homeassistant/components/climate/heatmiser.py +++ b/homeassistant/components/climate/heatmiser.py @@ -34,7 +34,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the heatmiser thermostat.""" from heatmiserV3 import heatmiser, connection @@ -46,7 +46,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): serport.open() for tstat in tstats.values(): - add_devices([ + add_entities([ HeatmiserV3Thermostat( heatmiser, tstat.get(CONF_ID), tstat.get(CONF_NAME), serport) ]) @@ -58,7 +58,6 @@ class HeatmiserV3Thermostat(ClimateDevice): def __init__(self, heatmiser, device, name, serport): """Initialize the thermostat.""" self.heatmiser = heatmiser - self.device = device self.serport = serport self._current_temperature = None self._name = name diff --git a/homeassistant/components/climate/hive.py b/homeassistant/components/climate/hive.py index eb3aecae3a10b0..37289d45c452cc 100644 --- a/homeassistant/components/climate/hive.py +++ b/homeassistant/components/climate/hive.py @@ -21,13 +21,13 @@ SUPPORT_AUX_HEAT) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Hive climate devices.""" if discovery_info is None: return session = hass.data.get(DATA_HIVE) - add_devices([HiveClimateEntity(session, discovery_info)]) + add_entities([HiveClimateEntity(session, discovery_info)]) class HiveClimateEntity(ClimateDevice): diff --git a/homeassistant/components/climate/homekit_controller.py b/homeassistant/components/climate/homekit_controller.py index f9178c2e0d55af..f720fb60277344 100644 --- a/homeassistant/components/climate/homekit_controller.py +++ b/homeassistant/components/climate/homekit_controller.py @@ -28,11 +28,11 @@ MODE_HASS_TO_HOMEKIT = {v: k for k, v in MODE_HOMEKIT_TO_HASS.items()} -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Homekit climate.""" if discovery_info is not None: accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] - add_devices([HomeKitClimateDevice(accessory, discovery_info)], True) + add_entities([HomeKitClimateDevice(accessory, discovery_info)], True) class HomeKitClimateDevice(HomeKitEntity, ClimateDevice): diff --git a/homeassistant/components/climate/homematic.py b/homeassistant/components/climate/homematic.py index a2725f6f3aa5d3..5b741a87b4554e 100644 --- a/homeassistant/components/climate/homematic.py +++ b/homeassistant/components/climate/homematic.py @@ -5,12 +5,13 @@ https://home-assistant.io/components/climate.homematic/ """ import logging + from homeassistant.components.climate import ( - ClimateDevice, STATE_AUTO, SUPPORT_TARGET_TEMPERATURE, - SUPPORT_OPERATION_MODE) + STATE_AUTO, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, + ClimateDevice) from homeassistant.components.homematic import ( - HMDevice, ATTR_DISCOVER_DEVICES, HM_ATTRIBUTE_SUPPORT) -from homeassistant.const import TEMP_CELSIUS, STATE_UNKNOWN, ATTR_TEMPERATURE + ATTR_DISCOVER_DEVICES, HM_ATTRIBUTE_SUPPORT, HMDevice) +from homeassistant.const import ATTR_TEMPERATURE, STATE_UNKNOWN, TEMP_CELSIUS DEPENDENCIES = ['homematic'] @@ -45,7 +46,7 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Homematic thermostat platform.""" if discovery_info is None: return @@ -55,7 +56,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): new_device = HMThermostat(conf) devices.append(new_device) - add_devices(devices) + add_entities(devices) class HMThermostat(HMDevice, ClimateDevice): diff --git a/homeassistant/components/climate/homematicip_cloud.py b/homeassistant/components/climate/homematicip_cloud.py index 8cf47159c103fd..966cd95ade1879 100644 --- a/homeassistant/components/climate/homematicip_cloud.py +++ b/homeassistant/components/climate/homematicip_cloud.py @@ -1,19 +1,18 @@ """ -Support for HomematicIP climate. +Support for HomematicIP Cloud climate devices. -For more details about this component, please refer to the documentation at +For more details about this platform, please refer to the documentation at https://home-assistant.io/components/climate.homematicip_cloud/ """ - import logging from homeassistant.components.climate import ( - ClimateDevice, SUPPORT_TARGET_TEMPERATURE, ATTR_TEMPERATURE, - STATE_AUTO, STATE_MANUAL) -from homeassistant.const import TEMP_CELSIUS + ATTR_TEMPERATURE, STATE_AUTO, STATE_MANUAL, SUPPORT_TARGET_TEMPERATURE, + ClimateDevice) from homeassistant.components.homematicip_cloud import ( - HomematicipGenericDevice, DOMAIN as HMIPC_DOMAIN, - HMIPC_HAPID) + HMIPC_HAPID, HomematicipGenericDevice) +from homeassistant.components.homematicip_cloud import DOMAIN as HMIPC_DOMAIN +from homeassistant.const import TEMP_CELSIUS _LOGGER = logging.getLogger(__name__) @@ -27,13 +26,13 @@ HMIP_STATE_TO_HA = {value: key for key, value in HA_STATE_TO_HMIP.items()} -async def async_setup_platform(hass, config, async_add_devices, - discovery_info=None): - """Set up the HomematicIP climate devices.""" +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the HomematicIP Cloud climate devices.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the HomematicIP climate from a config entry.""" from homematicip.group import HeatingGroup @@ -44,11 +43,11 @@ async def async_setup_entry(hass, config_entry, async_add_devices): devices.append(HomematicipHeatingGroup(home, device)) if devices: - async_add_devices(devices) + async_add_entities(devices) class HomematicipHeatingGroup(HomematicipGenericDevice, ClimateDevice): - """Representation of a MomematicIP heating group.""" + """Representation of a HomematicIP heating group.""" def __init__(self, home, device): """Initialize heating group.""" diff --git a/homeassistant/components/climate/honeywell.py b/homeassistant/components/climate/honeywell.py index 04d705d6b49bd6..6d54695fa7a1fc 100644 --- a/homeassistant/components/climate/honeywell.py +++ b/homeassistant/components/climate/honeywell.py @@ -51,19 +51,19 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Honeywell thermostat.""" username = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) region = config.get(CONF_REGION) if region == 'us': - return _setup_us(username, password, config, add_devices) + return _setup_us(username, password, config, add_entities) - return _setup_round(username, password, config, add_devices) + return _setup_round(username, password, config, add_entities) -def _setup_round(username, password, config, add_devices): +def _setup_round(username, password, config, add_entities): """Set up the rounding function.""" from evohomeclient import EvohomeClient @@ -73,7 +73,7 @@ def _setup_round(username, password, config, add_devices): try: zones = evo_api.temperatures(force_refresh=True) for i, zone in enumerate(zones): - add_devices( + add_entities( [RoundThermostat(evo_api, zone['id'], i == 0, away_temp)], True ) @@ -85,7 +85,7 @@ def _setup_round(username, password, config, add_devices): # config will be used later -def _setup_us(username, password, config, add_devices): +def _setup_us(username, password, config, add_entities): """Set up the user.""" import somecomfort @@ -103,12 +103,12 @@ def _setup_us(username, password, config, add_devices): cool_away_temp = config.get(CONF_COOL_AWAY_TEMPERATURE) heat_away_temp = config.get(CONF_HEAT_AWAY_TEMPERATURE) - add_devices([HoneywellUSThermostat(client, device, cool_away_temp, - heat_away_temp, username, password) - for location in client.locations_by_id.values() - for device in location.devices_by_id.values() - if ((not loc_id or location.locationid == loc_id) and - (not dev_id or device.deviceid == dev_id))]) + add_entities([HoneywellUSThermostat(client, device, cool_away_temp, + heat_away_temp, username, password) + for location in client.locations_by_id.values() + for device in location.devices_by_id.values() + if ((not loc_id or location.locationid == loc_id) and + (not dev_id or device.deviceid == dev_id))]) return True diff --git a/homeassistant/components/climate/knx.py b/homeassistant/components/climate/knx.py index f53cf2491dc5c5..4eada356653fc4 100644 --- a/homeassistant/components/climate/knx.py +++ b/homeassistant/components/climate/knx.py @@ -60,27 +60,27 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up climate(s) for KNX platform.""" if discovery_info is not None: - async_add_devices_discovery(hass, discovery_info, async_add_devices) + async_add_entities_discovery(hass, discovery_info, async_add_entities) else: - async_add_devices_config(hass, config, async_add_devices) + async_add_entities_config(hass, config, async_add_entities) @callback -def async_add_devices_discovery(hass, discovery_info, async_add_devices): +def async_add_entities_discovery(hass, discovery_info, async_add_entities): """Set up climates for KNX platform configured within platform.""" entities = [] for device_name in discovery_info[ATTR_DISCOVER_DEVICES]: device = hass.data[DATA_KNX].xknx.devices[device_name] entities.append(KNXClimate(hass, device)) - async_add_devices(entities) + async_add_entities(entities) @callback -def async_add_devices_config(hass, config, async_add_devices): +def async_add_entities_config(hass, config, async_add_entities): """Set up climate for KNX platform configured within platform.""" import xknx @@ -110,7 +110,7 @@ def async_add_devices_config(hass, config, async_add_devices): group_address_operation_mode_comfort=config.get( CONF_OPERATION_MODE_COMFORT_ADDRESS)) hass.data[DATA_KNX].xknx.devices.add(climate) - async_add_devices([KNXClimate(hass, climate)]) + async_add_entities([KNXClimate(hass, climate)]) class KNXClimate(ClimateDevice): @@ -122,8 +122,6 @@ def __init__(self, hass, device): self.hass = hass self.async_register_callbacks() - self._unit_of_measurement = TEMP_CELSIUS - @property def supported_features(self): """Return the list of supported features.""" @@ -157,7 +155,7 @@ def should_poll(self): @property def temperature_unit(self): """Return the unit of measurement.""" - return self._unit_of_measurement + return TEMP_CELSIUS @property def current_temperature(self): diff --git a/homeassistant/components/climate/maxcube.py b/homeassistant/components/climate/maxcube.py index 712ebb4f4ce6e0..328cdabde626cc 100644 --- a/homeassistant/components/climate/maxcube.py +++ b/homeassistant/components/climate/maxcube.py @@ -22,7 +22,7 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Iterate through all MAX! Devices and add thermostats.""" devices = [] for handler in hass.data[DATA_KEY].values(): @@ -36,7 +36,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): MaxCubeClimate(handler, name, device.rf_address)) if devices: - add_devices(devices) + add_entities(devices) class MaxCubeClimate(ClimateDevice): @@ -45,7 +45,6 @@ class MaxCubeClimate(ClimateDevice): def __init__(self, handler, name, rf_address): """Initialize MAX! Cube ClimateDevice.""" self._name = name - self._unit_of_measurement = TEMP_CELSIUS self._operation_list = [STATE_AUTO, STATE_MANUAL, STATE_BOOST, STATE_VACATION] self._rf_address = rf_address @@ -81,7 +80,7 @@ def max_temp(self): @property def temperature_unit(self): """Return the unit of measurement.""" - return self._unit_of_measurement + return TEMP_CELSIUS @property def current_temperature(self): diff --git a/homeassistant/components/climate/melissa.py b/homeassistant/components/climate/melissa.py index a0adc12bfbfb98..c8e67c14835097 100644 --- a/homeassistant/components/climate/melissa.py +++ b/homeassistant/components/climate/melissa.py @@ -34,7 +34,7 @@ ] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Iterate through and add all Melissa devices.""" api = hass.data[DATA_MELISSA] devices = api.fetch_devices().values() @@ -46,7 +46,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): all_devices.append(MelissaClimate( api, device['serial_number'], device)) - add_devices(all_devices) + add_entities(all_devices) class MelissaClimate(ClimateDevice): @@ -166,7 +166,7 @@ def turn_off(self): self.send({self._api.STATE: self._api.STATE_OFF}) def send(self, value): - """Sending action to service.""" + """Send action to service.""" try: old_value = self._cur_settings.copy() self._cur_settings.update(value) diff --git a/homeassistant/components/climate/modbus.py b/homeassistant/components/climate/modbus.py index e567340efc9f82..1c5c03e4502a18 100644 --- a/homeassistant/components/climate/modbus.py +++ b/homeassistant/components/climate/modbus.py @@ -50,7 +50,7 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Modbus Thermostat Platform.""" name = config.get(CONF_NAME) modbus_slave = config.get(CONF_SLAVE) @@ -60,9 +60,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None): count = config.get(CONF_COUNT) precision = config.get(CONF_PRECISION) - add_devices([ModbusThermostat(name, modbus_slave, - target_temp_register, current_temp_register, - data_type, count, precision)], True) + add_entities([ModbusThermostat(name, modbus_slave, + target_temp_register, current_temp_register, + data_type, count, precision)], True) class ModbusThermostat(ClimateDevice): diff --git a/homeassistant/components/climate/mqtt.py b/homeassistant/components/climate/mqtt.py index 1426ff31af90e5..9e227e002b5fac 100644 --- a/homeassistant/components/climate/mqtt.py +++ b/homeassistant/components/climate/mqtt.py @@ -127,7 +127,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the MQTT climate devices.""" if discovery_info is not None: config = PLATFORM_SCHEMA(discovery_info) @@ -152,7 +153,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): value_templates[key] = config.get(key) value_templates[key].hass = hass - async_add_devices([ + async_add_entities([ MqttClimate( hass, config.get(CONF_NAME), diff --git a/homeassistant/components/climate/mysensors.py b/homeassistant/components/climate/mysensors.py index a2043c2434bfbb..66c634d8cd9b10 100644 --- a/homeassistant/components/climate/mysensors.py +++ b/homeassistant/components/climate/mysensors.py @@ -31,11 +31,11 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the mysensors climate.""" mysensors.setup_mysensors_platform( hass, DOMAIN, discovery_info, MySensorsHVAC, - async_add_devices=async_add_devices) + async_add_entities=async_add_entities) class MySensorsHVAC(mysensors.device.MySensorsEntity, ClimateDevice): diff --git a/homeassistant/components/climate/nest.py b/homeassistant/components/climate/nest.py index fa3943c3e276fd..321559f10eefca 100644 --- a/homeassistant/components/climate/nest.py +++ b/homeassistant/components/climate/nest.py @@ -31,14 +31,14 @@ NEST_MODE_HEAT_COOL = 'heat-cool' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Nest thermostat. No longer in use. """ -async def async_setup_entry(hass, entry, async_add_devices): +async def async_setup_entry(hass, entry, async_add_entities): """Set up the Nest climate device based on a config entry.""" temp_unit = hass.config.units.temperature_unit @@ -47,7 +47,7 @@ async def async_setup_entry(hass, entry, async_add_devices): all_devices = [NestThermostat(structure, device, temp_unit) for structure, device in thermostats] - async_add_devices(all_devices, True) + async_add_entities(all_devices, True) class NestThermostat(ClimateDevice): @@ -124,7 +124,7 @@ def supported_features(self): @property def unique_id(self): - """Unique ID for this device.""" + """Return unique ID for this device.""" return self.device.serial @property diff --git a/homeassistant/components/climate/netatmo.py b/homeassistant/components/climate/netatmo.py index b4bed3678785ba..8849ada5ccc785 100644 --- a/homeassistant/components/climate/netatmo.py +++ b/homeassistant/components/climate/netatmo.py @@ -39,7 +39,7 @@ SUPPORT_AWAY_MODE) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the NetAtmo Thermostat.""" netatmo = hass.components.netatmo device = config.get(CONF_RELAY) @@ -52,7 +52,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if config[CONF_THERMOSTAT] != [] and \ module_name not in config[CONF_THERMOSTAT]: continue - add_devices([NetatmoThermostat(data, module_name)], True) + add_entities([NetatmoThermostat(data, module_name)], True) except pyatmo.NoDevice: return None diff --git a/homeassistant/components/climate/nuheat.py b/homeassistant/components/climate/nuheat.py index 39c66ff94f2047..d0bfe5add58151 100644 --- a/homeassistant/components/climate/nuheat.py +++ b/homeassistant/components/climate/nuheat.py @@ -56,7 +56,7 @@ SUPPORT_OPERATION_MODE) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the NuHeat thermostat(s).""" if discovery_info is None: return @@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): NuHeatThermostat(api, serial_number, temperature_unit) for serial_number in serial_numbers ] - add_devices(thermostats, True) + add_entities(thermostats, True) def resume_program_set_service(service): """Resume the program on the target thermostats.""" diff --git a/homeassistant/components/climate/oem.py b/homeassistant/components/climate/oem.py index 59f8db033187ce..e006242331c1bc 100644 --- a/homeassistant/components/climate/oem.py +++ b/homeassistant/components/climate/oem.py @@ -38,7 +38,7 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_AWAY_MODE -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the oemthermostat platform.""" from oemthermostat import Thermostat @@ -55,7 +55,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): except (ValueError, AssertionError, requests.RequestException): return False - add_devices((ThermostatDevice(hass, therm, name, away_temp), ), True) + add_entities((ThermostatDevice(hass, therm, name, away_temp), ), True) class ThermostatDevice(ClimateDevice): diff --git a/homeassistant/components/climate/proliphix.py b/homeassistant/components/climate/proliphix.py index 9338c219fe5e22..76160a28c6e517 100644 --- a/homeassistant/components/climate/proliphix.py +++ b/homeassistant/components/climate/proliphix.py @@ -24,7 +24,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Proliphix thermostats.""" username = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) @@ -34,7 +34,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): pdp = proliphix.PDP(host, username, password) - add_devices([ProliphixThermostat(pdp)]) + add_entities([ProliphixThermostat(pdp)]) class ProliphixThermostat(ClimateDevice): diff --git a/homeassistant/components/climate/radiotherm.py b/homeassistant/components/climate/radiotherm.py index c8441a9f7af0ea..429b544aefc40d 100644 --- a/homeassistant/components/climate/radiotherm.py +++ b/homeassistant/components/climate/radiotherm.py @@ -83,7 +83,7 @@ def round_temp(temperature): SUPPORT_FAN_MODE | SUPPORT_AWAY_MODE) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Radio Thermostat.""" import radiotherm @@ -112,7 +112,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.exception("Unable to connect to Radio Thermostat: %s", host) - add_devices(tstats, True) + add_entities(tstats, True) class RadioThermostat(ClimateDevice): @@ -137,8 +137,8 @@ def __init__(self, device, hold_temp, away_temps): # Fan circulate mode is only supported by the CT80 models. import radiotherm - self._is_model_ct80 = isinstance(self.device, - radiotherm.thermostat.CT80) + self._is_model_ct80 = isinstance( + self.device, radiotherm.thermostat.CT80) @property def supported_features(self): diff --git a/homeassistant/components/climate/sensibo.py b/homeassistant/components/climate/sensibo.py index 363653608e86ca..ef33ee8495e2e6 100644 --- a/homeassistant/components/climate/sensibo.py +++ b/homeassistant/components/climate/sensibo.py @@ -59,7 +59,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up Sensibo devices.""" import pysensibo @@ -71,14 +72,15 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for dev in ( yield from client.async_get_devices(_INITIAL_FETCH_FIELDS)): if config[CONF_ID] == ALL or dev['id'] in config[CONF_ID]: - devices.append(SensiboClimate(client, dev)) + devices.append(SensiboClimate( + client, dev, hass.config.units.temperature_unit)) except (aiohttp.client_exceptions.ClientConnectorError, asyncio.TimeoutError): _LOGGER.exception('Failed to connect to Sensibo servers.') raise PlatformNotReady if devices: - async_add_devices(devices) + async_add_entities(devices) @asyncio.coroutine def async_assume_state(service): @@ -106,7 +108,7 @@ def async_assume_state(service): class SensiboClimate(ClimateDevice): """Representation of a Sensibo device.""" - def __init__(self, client, data): + def __init__(self, client, data, units): """Build SensiboClimate. client: aiohttp session. @@ -115,6 +117,7 @@ def __init__(self, client, data): self._client = client self._id = data['id'] self._external_state = None + self._units = units self._do_update(data) @property @@ -139,7 +142,7 @@ def _do_update(self, data): self._temperatures_list = self._current_capabilities[ 'temperatures'].get(temperature_unit_key, {}).get('values', []) else: - self._temperature_unit = self.unit_of_measurement + self._temperature_unit = self._units self._temperatures_list = [] self._supported_features = 0 for key in self._ac_states: @@ -175,7 +178,7 @@ def target_temperature(self): @property def target_temperature_step(self): """Return the supported step of target temperature.""" - if self.temperature_unit == self.unit_of_measurement: + if self.temperature_unit == self.hass.config.units.temperature_unit: # We are working in same units as the a/c unit. Use whole degrees # like the API supports. return 1 diff --git a/homeassistant/components/climate/spider.py b/homeassistant/components/climate/spider.py index a6916b22a25aa6..a9d966bd499c35 100644 --- a/homeassistant/components/climate/spider.py +++ b/homeassistant/components/climate/spider.py @@ -31,14 +31,14 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Spider thermostat.""" if discovery_info is None: return devices = [SpiderThermostat(hass.data[SPIDER_DOMAIN]['controller'], device) for device in hass.data[SPIDER_DOMAIN]['thermostats']] - add_devices(devices, True) + add_entities(devices, True) class SpiderThermostat(ClimateDevice): diff --git a/homeassistant/components/climate/tado.py b/homeassistant/components/climate/tado.py index b3734e020e00e2..1e52c1636244b3 100644 --- a/homeassistant/components/climate/tado.py +++ b/homeassistant/components/climate/tado.py @@ -48,7 +48,7 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tado climate platform.""" tado = hass.data[DATA_TADO] @@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): climate_devices.append(device) if climate_devices: - add_devices(climate_devices, True) + add_entities(climate_devices, True) def create_climate_device(tado, hass, zone, name, zone_id): diff --git a/homeassistant/components/climate/tesla.py b/homeassistant/components/climate/tesla.py index 225c13d975dc34..ef5f2227c11c21 100644 --- a/homeassistant/components/climate/tesla.py +++ b/homeassistant/components/climate/tesla.py @@ -23,11 +23,11 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tesla climate platform.""" devices = [TeslaThermostat(device, hass.data[TESLA_DOMAIN]['controller']) for device in hass.data[TESLA_DOMAIN]['devices']['climate']] - add_devices(devices, True) + add_entities(devices, True) class TeslaThermostat(TeslaDevice, ClimateDevice): diff --git a/homeassistant/components/climate/toon.py b/homeassistant/components/climate/toon.py index 330801fc231901..e759e922ee1474 100644 --- a/homeassistant/components/climate/toon.py +++ b/homeassistant/components/climate/toon.py @@ -16,9 +16,9 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Toon climate device.""" - add_devices([ThermostatDevice(hass)], True) + add_entities([ThermostatDevice(hass)], True) class ThermostatDevice(ClimateDevice): diff --git a/homeassistant/components/climate/touchline.py b/homeassistant/components/climate/touchline.py index f9c5676629bc8e..641f6e9a1d8b7a 100644 --- a/homeassistant/components/climate/touchline.py +++ b/homeassistant/components/climate/touchline.py @@ -24,7 +24,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Touchline devices.""" from pytouchline import PyTouchline host = config[CONF_HOST] @@ -33,7 +33,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices = [] for device_id in range(0, number_of_devices): devices.append(Touchline(PyTouchline(device_id))) - add_devices(devices, True) + add_entities(devices, True) class Touchline(ClimateDevice): diff --git a/homeassistant/components/climate/tuya.py b/homeassistant/components/climate/tuya.py index 19267d693a04bc..2da46fee15d6b0 100644 --- a/homeassistant/components/climate/tuya.py +++ b/homeassistant/components/climate/tuya.py @@ -37,7 +37,7 @@ FAN_MODES = {SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH} -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tuya Climate devices.""" if discovery_info is None: return @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if device is None: continue devices.append(TuyaClimateDevice(device)) - add_devices(devices) + add_entities(devices) class TuyaClimateDevice(TuyaDevice, ClimateDevice): diff --git a/homeassistant/components/climate/venstar.py b/homeassistant/components/climate/venstar.py index 4bacf64cf9e3bd..16c0b2061546e0 100644 --- a/homeassistant/components/climate/venstar.py +++ b/homeassistant/components/climate/venstar.py @@ -50,7 +50,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Venstar thermostat.""" import venstarcolortouch @@ -69,7 +69,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): addr=host, timeout=timeout, user=username, password=password, proto=proto) - add_devices([VenstarThermostat(client, humidifier)], True) + add_entities([VenstarThermostat(client, humidifier)], True) class VenstarThermostat(ClimateDevice): diff --git a/homeassistant/components/climate/vera.py b/homeassistant/components/climate/vera.py index 0f89b15e5a1877..e97bd6cd8adc6c 100644 --- a/homeassistant/components/climate/vera.py +++ b/homeassistant/components/climate/vera.py @@ -29,9 +29,9 @@ SUPPORT_FAN_MODE) -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up of Vera thermostats.""" - add_devices_callback( + add_entities_callback( [VeraThermostat(device, hass.data[VERA_CONTROLLER]) for device in hass.data[VERA_DEVICES]['climate']], True) diff --git a/homeassistant/components/climate/wink.py b/homeassistant/components/climate/wink.py index 15e555db8b9f2c..d8e6843bec84e3 100644 --- a/homeassistant/components/climate/wink.py +++ b/homeassistant/components/climate/wink.py @@ -67,21 +67,21 @@ SUPPORT_AWAY_MODE) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink climate devices.""" import pywink for climate in pywink.get_thermostats(): _id = climate.object_id() + climate.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkThermostat(climate, hass)]) + add_entities([WinkThermostat(climate, hass)]) for climate in pywink.get_air_conditioners(): _id = climate.object_id() + climate.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkAC(climate, hass)]) + add_entities([WinkAC(climate, hass)]) for water_heater in pywink.get_water_heaters(): _id = water_heater.object_id() + water_heater.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkWaterHeater(water_heater, hass)]) + add_entities([WinkWaterHeater(water_heater, hass)]) class WinkThermostat(WinkDevice, ClimateDevice): diff --git a/homeassistant/components/climate/zhong_hong.py b/homeassistant/components/climate/zhong_hong.py index 7ff19871ee7bd5..46d590a94121b9 100644 --- a/homeassistant/components/climate/zhong_hong.py +++ b/homeassistant/components/climate/zhong_hong.py @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZhongHong HVAC platform.""" from zhong_hong_hvac.hub import ZhongHongGateway host = config.get(CONF_HOST) @@ -69,7 +69,7 @@ async def startup(): async_dispatcher_connect(hass, SIGNAL_DEVICE_ADDED, startup) # add devices after SIGNAL_DEVICE_SETTED_UP event is listend - add_devices(devices) + add_entities(devices) def stop_listen(event): """Stop ZhongHongHub socket.""" @@ -100,7 +100,7 @@ async def async_added_to_hass(self): async_dispatcher_send(self.hass, SIGNAL_DEVICE_ADDED) def _after_update(self, climate): - """Callback to update state.""" + """Handle state update.""" _LOGGER.debug("async update ha state") if self._device.current_operation: self._current_operation = self._device.current_operation.lower() diff --git a/homeassistant/components/config/entity_registry.py b/homeassistant/components/config/entity_registry.py index 7c0867e3852c3c..2fac420c39c92c 100644 --- a/homeassistant/components/config/entity_registry.py +++ b/homeassistant/components/config/entity_registry.py @@ -101,7 +101,7 @@ async def update_entity(): @callback def _entry_dict(entry): - """Helper to convert entry to API format.""" + """Convert entry to API format.""" return { 'entity_id': entry.entity_id, 'name': entry.name diff --git a/homeassistant/components/config/zwave.py b/homeassistant/components/config/zwave.py index 84927712741cba..fcdab835052c85 100644 --- a/homeassistant/components/config/zwave.py +++ b/homeassistant/components/config/zwave.py @@ -212,7 +212,7 @@ async def get(self, request, node_id): network = hass.data.get(const.DATA_NETWORK) def _fetch_protection(): - """Helper to get protection data.""" + """Get protection data.""" node = network.nodes.get(nodeid) if node is None: return self.json_message('Node not found', HTTP_NOT_FOUND) @@ -236,7 +236,7 @@ async def post(self, request, node_id): protection_data = await request.json() def _set_protection(): - """Helper to get protection data.""" + """Set protection data.""" node = network.nodes.get(nodeid) selection = protection_data["selection"] value_id = int(protection_data[const.ATTR_VALUE_ID]) diff --git a/homeassistant/components/conversation/__init__.py b/homeassistant/components/conversation/__init__.py index 9cb00a84583ae5..d8d386f5ca048a 100644 --- a/homeassistant/components/conversation/__init__.py +++ b/homeassistant/components/conversation/__init__.py @@ -11,6 +11,7 @@ from homeassistant import core from homeassistant.components import http +from homeassistant.components.conversation.util import create_matcher from homeassistant.components.http.data_validator import ( RequestDataValidator) from homeassistant.components.cover import (INTENT_OPEN_COVER, @@ -74,7 +75,7 @@ def async_register(hass, intent_type, utterances): if isinstance(utterance, REGEX_TYPE): conf.append(utterance) else: - conf.append(_create_matcher(utterance)) + conf.append(create_matcher(utterance)) async def async_setup(hass, config): @@ -91,7 +92,7 @@ async def async_setup(hass, config): if conf is None: conf = intents[intent_type] = [] - conf.extend(_create_matcher(utterance) for utterance in utterances) + conf.extend(create_matcher(utterance) for utterance in utterances) async def process(service): """Parse text into commands.""" @@ -146,39 +147,6 @@ def component_loaded(event): return True -def _create_matcher(utterance): - """Create a regex that matches the utterance.""" - # Split utterance into parts that are type: NORMAL, GROUP or OPTIONAL - # Pattern matches (GROUP|OPTIONAL): Change light to [the color] {name} - parts = re.split(r'({\w+}|\[[\w\s]+\] *)', utterance) - # Pattern to extract name from GROUP part. Matches {name} - group_matcher = re.compile(r'{(\w+)}') - # Pattern to extract text from OPTIONAL part. Matches [the color] - optional_matcher = re.compile(r'\[([\w ]+)\] *') - - pattern = ['^'] - for part in parts: - group_match = group_matcher.match(part) - optional_match = optional_matcher.match(part) - - # Normal part - if group_match is None and optional_match is None: - pattern.append(part) - continue - - # Group part - if group_match is not None: - pattern.append( - r'(?P<{}>[\w ]+?)\s*'.format(group_match.groups()[0])) - - # Optional part - elif optional_match is not None: - pattern.append(r'(?:{} *)?'.format(optional_match.groups()[0])) - - pattern.append('$') - return re.compile(''.join(pattern), re.I) - - async def _process(hass, text): """Process a line of text.""" intents = hass.data.get(DOMAIN, {}) diff --git a/homeassistant/components/conversation/util.py b/homeassistant/components/conversation/util.py new file mode 100644 index 00000000000000..60d861afdbe4a7 --- /dev/null +++ b/homeassistant/components/conversation/util.py @@ -0,0 +1,35 @@ +"""Util for Conversation.""" +import re + + +def create_matcher(utterance): + """Create a regex that matches the utterance.""" + # Split utterance into parts that are type: NORMAL, GROUP or OPTIONAL + # Pattern matches (GROUP|OPTIONAL): Change light to [the color] {name} + parts = re.split(r'({\w+}|\[[\w\s]+\] *)', utterance) + # Pattern to extract name from GROUP part. Matches {name} + group_matcher = re.compile(r'{(\w+)}') + # Pattern to extract text from OPTIONAL part. Matches [the color] + optional_matcher = re.compile(r'\[([\w ]+)\] *') + + pattern = ['^'] + for part in parts: + group_match = group_matcher.match(part) + optional_match = optional_matcher.match(part) + + # Normal part + if group_match is None and optional_match is None: + pattern.append(part) + continue + + # Group part + if group_match is not None: + pattern.append( + r'(?P<{}>[\w ]+?)\s*'.format(group_match.groups()[0])) + + # Optional part + elif optional_match is not None: + pattern.append(r'(?:{} *)?'.format(optional_match.groups()[0])) + + pattern.append('$') + return re.compile(''.join(pattern), re.I) diff --git a/homeassistant/components/counter/__init__.py b/homeassistant/components/counter/__init__.py index 03e5b2734682e1..d720819a0ab255 100644 --- a/homeassistant/components/counter/__init__.py +++ b/homeassistant/components/counter/__init__.py @@ -4,7 +4,6 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/counter/ """ -import asyncio import logging import voluptuous as vol @@ -114,27 +113,15 @@ async def async_setup(hass, config): if not entities: return False - async def async_handler_service(service): - """Handle a call to the counter services.""" - target_counters = component.async_extract_from_service(service) - - if service.service == SERVICE_INCREMENT: - attr = 'async_increment' - elif service.service == SERVICE_DECREMENT: - attr = 'async_decrement' - elif service.service == SERVICE_RESET: - attr = 'async_reset' - - tasks = [getattr(counter, attr)() for counter in target_counters] - if tasks: - await asyncio.wait(tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_INCREMENT, async_handler_service) - hass.services.async_register( - DOMAIN, SERVICE_DECREMENT, async_handler_service) - hass.services.async_register( - DOMAIN, SERVICE_RESET, async_handler_service) + component.async_register_entity_service( + SERVICE_INCREMENT, SERVICE_SCHEMA, + 'async_increment') + component.async_register_entity_service( + SERVICE_DECREMENT, SERVICE_SCHEMA, + 'async_decrement') + component.async_register_entity_service( + SERVICE_RESET, SERVICE_SCHEMA, + 'async_reset') await component.async_add_entities(entities) return True diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index f5d3d798e2eb6b..05c5e46e44ea13 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -4,7 +4,6 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/cover/ """ -import asyncio from datetime import timedelta import functools as ft import logging @@ -73,21 +72,6 @@ vol.All(vol.Coerce(int), vol.Range(min=0, max=100)), }) -SERVICE_TO_METHOD = { - SERVICE_OPEN_COVER: {'method': 'async_open_cover'}, - SERVICE_CLOSE_COVER: {'method': 'async_close_cover'}, - SERVICE_SET_COVER_POSITION: { - 'method': 'async_set_cover_position', - 'schema': COVER_SET_COVER_POSITION_SCHEMA}, - SERVICE_STOP_COVER: {'method': 'async_stop_cover'}, - SERVICE_OPEN_COVER_TILT: {'method': 'async_open_cover_tilt'}, - SERVICE_CLOSE_COVER_TILT: {'method': 'async_close_cover_tilt'}, - SERVICE_STOP_COVER_TILT: {'method': 'async_stop_cover_tilt'}, - SERVICE_SET_COVER_TILT_POSITION: { - 'method': 'async_set_cover_tilt_position', - 'schema': COVER_SET_COVER_TILT_POSITION_SCHEMA}, -} - @bind_hass def is_closed(hass, entity_id=None): @@ -161,30 +145,46 @@ async def async_setup(hass, config): await component.async_setup(config) - async def async_handle_cover_service(service): - """Handle calls to the cover services.""" - covers = component.async_extract_from_service(service) - method = SERVICE_TO_METHOD.get(service.service) - params = service.data.copy() - params.pop(ATTR_ENTITY_ID, None) - - # call method - update_tasks = [] - for cover in covers: - await getattr(cover, method['method'])(**params) - if not cover.should_poll: - continue - update_tasks.append(cover.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - for service_name in SERVICE_TO_METHOD: - schema = SERVICE_TO_METHOD[service_name].get( - 'schema', COVER_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, service_name, async_handle_cover_service, - schema=schema) + component.async_register_entity_service( + SERVICE_OPEN_COVER, COVER_SERVICE_SCHEMA, + 'async_open_cover' + ) + + component.async_register_entity_service( + SERVICE_CLOSE_COVER, COVER_SERVICE_SCHEMA, + 'async_close_cover' + ) + + component.async_register_entity_service( + SERVICE_SET_COVER_POSITION, COVER_SET_COVER_POSITION_SCHEMA, + 'async_set_cover_position' + ) + + component.async_register_entity_service( + SERVICE_STOP_COVER, COVER_SERVICE_SCHEMA, + 'async_stop_cover' + ) + + component.async_register_entity_service( + SERVICE_OPEN_COVER_TILT, COVER_SERVICE_SCHEMA, + 'async_open_cover_tilt' + ) + + component.async_register_entity_service( + SERVICE_CLOSE_COVER_TILT, COVER_SERVICE_SCHEMA, + 'async_close_cover_tilt' + ) + + component.async_register_entity_service( + SERVICE_STOP_COVER_TILT, COVER_SERVICE_SCHEMA, + 'async_stop_cover_tilt' + ) + + component.async_register_entity_service( + SERVICE_SET_COVER_TILT_POSITION, COVER_SET_COVER_TILT_POSITION_SCHEMA, + 'async_set_cover_tilt_position' + ) + hass.helpers.intent.async_register(intent.ServiceIntentHandler( INTENT_OPEN_COVER, DOMAIN, SERVICE_OPEN_COVER, "Opened {}")) diff --git a/homeassistant/components/cover/abode.py b/homeassistant/components/cover/abode.py index 6eb0369aa3f202..3ba3fb118f3217 100644 --- a/homeassistant/components/cover/abode.py +++ b/homeassistant/components/cover/abode.py @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Abode cover devices.""" import abodepy.helpers.constants as CONST @@ -30,7 +30,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data.devices.extend(devices) - add_devices(devices) + add_entities(devices) class AbodeCover(AbodeDevice, CoverDevice): diff --git a/homeassistant/components/cover/aladdin_connect.py b/homeassistant/components/cover/aladdin_connect.py index efaea39bb864e1..4627ba7778148e 100644 --- a/homeassistant/components/cover/aladdin_connect.py +++ b/homeassistant/components/cover/aladdin_connect.py @@ -14,7 +14,7 @@ STATE_OPENING, STATE_CLOSING, STATE_OPEN) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['aladdin_connect==0.1'] +REQUIREMENTS = ['aladdin_connect==0.3'] _LOGGER = logging.getLogger(__name__) @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Aladdin Connect platform.""" from aladdin_connect import AladdinConnectClient @@ -47,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): try: if not acc.login(): raise ValueError("Username or Password is incorrect") - add_devices(AladdinDevice(acc, door) for door in acc.get_doors()) + add_entities(AladdinDevice(acc, door) for door in acc.get_doors()) except (TypeError, KeyError, NameError, ValueError) as ex: _LOGGER.error("%s", ex) hass.components.persistent_notification.create( @@ -79,6 +79,11 @@ def supported_features(self): """Flag supported features.""" return SUPPORTED_FEATURES + @property + def unique_id(self): + """Return a unique ID.""" + return '{}-{}'.format(self._device_id, self._number) + @property def name(self): """Return the name of the garage door.""" diff --git a/homeassistant/components/cover/brunt.py b/homeassistant/components/cover/brunt.py index 713f06db7359da..746f3840a01ff8 100644 --- a/homeassistant/components/cover/brunt.py +++ b/homeassistant/components/cover/brunt.py @@ -18,7 +18,7 @@ ) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['brunt==0.1.2'] +REQUIREMENTS = ['brunt==0.1.3'] _LOGGER = logging.getLogger(__name__) @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the brunt platform.""" # pylint: disable=no-name-in-module from brunt import BruntAPI @@ -52,7 +52,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if not things: _LOGGER.error("No things present in account.") else: - add_devices([BruntDevice( + add_entities([BruntDevice( bapi, thing['NAME'], thing['thingUri']) for thing in things], True) except (TypeError, KeyError, NameError, ValueError) as ex: diff --git a/homeassistant/components/cover/command_line.py b/homeassistant/components/cover/command_line.py index 6d43b1d2166e07..bebf78b1db697b 100644 --- a/homeassistant/components/cover/command_line.py +++ b/homeassistant/components/cover/command_line.py @@ -31,7 +31,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up cover controlled by shell commands.""" devices = config.get(CONF_COVERS, {}) covers = [] @@ -57,7 +57,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("No covers added") return False - add_devices(covers) + add_entities(covers) class CommandCover(CoverDevice): diff --git a/homeassistant/components/cover/demo.py b/homeassistant/components/cover/demo.py index b81ac4e45e1a1e..21add0a6c62ae5 100644 --- a/homeassistant/components/cover/demo.py +++ b/homeassistant/components/cover/demo.py @@ -10,9 +10,9 @@ from homeassistant.helpers.event import track_utc_time_change -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Demo covers.""" - add_devices([ + add_entities([ DemoCover(hass, 'Kitchen Window'), DemoCover(hass, 'Hall Window', 10), DemoCover(hass, 'Living Room Window', 70, 50), diff --git a/homeassistant/components/cover/garadget.py b/homeassistant/components/cover/garadget.py index 70f6956810984f..7a04aa4c71ab6f 100644 --- a/homeassistant/components/cover/garadget.py +++ b/homeassistant/components/cover/garadget.py @@ -51,7 +51,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Garadget covers.""" covers = [] devices = config.get(CONF_COVERS) @@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): covers.append(GaradgetCover(hass, args)) - add_devices(covers) + add_entities(covers) class GaradgetCover(CoverDevice): diff --git a/homeassistant/components/cover/gogogate2.py b/homeassistant/components/cover/gogogate2.py index 2b91591e71b9d9..accc4f9ec98222 100644 --- a/homeassistant/components/cover/gogogate2.py +++ b/homeassistant/components/cover/gogogate2.py @@ -32,7 +32,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Gogogate2 component.""" from pygogogate2 import Gogogate2API as pygogogate2 @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): raise ValueError( "Username or Password is incorrect or no devices found") - add_devices(MyGogogate2Device( + add_entities(MyGogogate2Device( mygogogate2, door, name) for door in devices) except (TypeError, KeyError, NameError, ValueError) as ex: diff --git a/homeassistant/components/cover/group.py b/homeassistant/components/cover/group.py index c1ea33a9cc72ea..0424c900747bc2 100644 --- a/homeassistant/components/cover/group.py +++ b/homeassistant/components/cover/group.py @@ -39,10 +39,10 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Group Cover platform.""" - async_add_devices( + async_add_entities( [CoverGroup(config[CONF_NAME], config[CONF_ENTITIES])]) diff --git a/homeassistant/components/cover/homematic.py b/homeassistant/components/cover/homematic.py index 2736b656a15229..935743212031da 100644 --- a/homeassistant/components/cover/homematic.py +++ b/homeassistant/components/cover/homematic.py @@ -6,9 +6,9 @@ """ import logging -from homeassistant.components.cover import CoverDevice, ATTR_POSITION,\ - ATTR_TILT_POSITION -from homeassistant.components.homematic import HMDevice, ATTR_DISCOVER_DEVICES +from homeassistant.components.cover import ( + ATTR_POSITION, ATTR_TILT_POSITION, CoverDevice) +from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice from homeassistant.const import STATE_UNKNOWN _LOGGER = logging.getLogger(__name__) @@ -16,7 +16,7 @@ DEPENDENCIES = ['homematic'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the platform.""" if discovery_info is None: return @@ -26,7 +26,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): new_device = HMCover(conf) devices.append(new_device) - add_devices(devices) + add_entities(devices) class HMCover(HMDevice, CoverDevice): diff --git a/homeassistant/components/cover/isy994.py b/homeassistant/components/cover/isy994.py index 0ccfe267989e71..428c1f326e4116 100644 --- a/homeassistant/components/cover/isy994.py +++ b/homeassistant/components/cover/isy994.py @@ -26,7 +26,7 @@ def setup_platform(hass, config: ConfigType, - add_devices: Callable[[list], None], discovery_info=None): + add_entities: Callable[[list], None], discovery_info=None): """Set up the ISY994 cover platform.""" devices = [] for node in hass.data[ISY994_NODES][DOMAIN]: @@ -35,7 +35,7 @@ def setup_platform(hass, config: ConfigType, for name, status, actions in hass.data[ISY994_PROGRAMS][DOMAIN]: devices.append(ISYCoverProgram(name, status, actions)) - add_devices(devices) + add_entities(devices) class ISYCoverDevice(ISYDevice, CoverDevice): diff --git a/homeassistant/components/cover/knx.py b/homeassistant/components/cover/knx.py index 7bb20e4cf1f2cf..43a87fab367361 100644 --- a/homeassistant/components/cover/knx.py +++ b/homeassistant/components/cover/knx.py @@ -49,27 +49,27 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up cover(s) for KNX platform.""" if discovery_info is not None: - async_add_devices_discovery(hass, discovery_info, async_add_devices) + async_add_entities_discovery(hass, discovery_info, async_add_entities) else: - async_add_devices_config(hass, config, async_add_devices) + async_add_entities_config(hass, config, async_add_entities) @callback -def async_add_devices_discovery(hass, discovery_info, async_add_devices): +def async_add_entities_discovery(hass, discovery_info, async_add_entities): """Set up covers for KNX platform configured via xknx.yaml.""" entities = [] for device_name in discovery_info[ATTR_DISCOVER_DEVICES]: device = hass.data[DATA_KNX].xknx.devices[device_name] entities.append(KNXCover(hass, device)) - async_add_devices(entities) + async_add_entities(entities) @callback -def async_add_devices_config(hass, config, async_add_devices): +def async_add_entities_config(hass, config, async_add_entities): """Set up cover for KNX platform configured within platform.""" import xknx cover = xknx.devices.Cover( @@ -88,7 +88,7 @@ def async_add_devices_config(hass, config, async_add_devices): invert_angle=config.get(CONF_INVERT_ANGLE)) hass.data[DATA_KNX].xknx.devices.add(cover) - async_add_devices([KNXCover(hass, cover)]) + async_add_entities([KNXCover(hass, cover)]) class KNXCover(CoverDevice): diff --git a/homeassistant/components/cover/lutron.py b/homeassistant/components/cover/lutron.py index 599bdb1cebab7f..7ea7abf882d2c0 100644 --- a/homeassistant/components/cover/lutron.py +++ b/homeassistant/components/cover/lutron.py @@ -17,14 +17,14 @@ DEPENDENCIES = ['lutron'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Lutron shades.""" devs = [] for (area_name, device) in hass.data[LUTRON_DEVICES]['cover']: dev = LutronCover(area_name, device, hass.data[LUTRON_CONTROLLER]) devs.append(dev) - add_devices(devs, True) + add_entities(devs, True) return True diff --git a/homeassistant/components/cover/lutron_caseta.py b/homeassistant/components/cover/lutron_caseta.py index 87821b802ba6b9..37b7c1be42c4e4 100644 --- a/homeassistant/components/cover/lutron_caseta.py +++ b/homeassistant/components/cover/lutron_caseta.py @@ -17,7 +17,7 @@ DEPENDENCIES = ['lutron_caseta'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Lutron Caseta shades as a cover device.""" devs = [] @@ -27,7 +27,7 @@ async def async_setup_platform(hass, config, async_add_devices, dev = LutronCasetaCover(cover_device, bridge) devs.append(dev) - async_add_devices(devs, True) + async_add_entities(devs, True) class LutronCasetaCover(LutronCasetaDevice, CoverDevice): diff --git a/homeassistant/components/cover/mqtt.py b/homeassistant/components/cover/mqtt.py index e1775e2f968fd0..977353cb318e26 100644 --- a/homeassistant/components/cover/mqtt.py +++ b/homeassistant/components/cover/mqtt.py @@ -92,7 +92,7 @@ }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the MQTT Cover.""" if discovery_info is not None: @@ -105,7 +105,7 @@ async def async_setup_platform(hass, config, async_add_devices, if set_position_template is not None: set_position_template.hass = hass - async_add_devices([MqttCover( + async_add_entities([MqttCover( config.get(CONF_NAME), config.get(CONF_STATE_TOPIC), config.get(CONF_COMMAND_TOPIC), diff --git a/homeassistant/components/cover/myq.py b/homeassistant/components/cover/myq.py index a4682172feee46..bedc041fcccfa4 100644 --- a/homeassistant/components/cover/myq.py +++ b/homeassistant/components/cover/myq.py @@ -29,7 +29,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the MyQ component.""" from pymyq import MyQAPI as pymyq @@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if not myq.is_login_valid(): raise ValueError("Username or Password is incorrect") - add_devices(MyQDevice(myq, door) for door in myq.get_garage_doors()) + add_entities(MyQDevice(myq, door) for door in myq.get_garage_doors()) return True except (TypeError, KeyError, NameError, ValueError) as ex: diff --git a/homeassistant/components/cover/mysensors.py b/homeassistant/components/cover/mysensors.py index c815cf44df2d02..60ff7aeef1d6ce 100644 --- a/homeassistant/components/cover/mysensors.py +++ b/homeassistant/components/cover/mysensors.py @@ -10,11 +10,11 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the mysensors platform for covers.""" mysensors.setup_mysensors_platform( hass, DOMAIN, discovery_info, MySensorsCover, - async_add_devices=async_add_devices) + async_add_entities=async_add_entities) class MySensorsCover(mysensors.device.MySensorsEntity, CoverDevice): diff --git a/homeassistant/components/cover/opengarage.py b/homeassistant/components/cover/opengarage.py index fe6c7763cc7779..19a87c5bf7ce80 100644 --- a/homeassistant/components/cover/opengarage.py +++ b/homeassistant/components/cover/opengarage.py @@ -50,7 +50,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the OpenGarage covers.""" covers = [] devices = config.get(CONF_COVERS) @@ -66,7 +66,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): covers.append(OpenGarageCover(hass, args)) - add_devices(covers, True) + add_entities(covers, True) class OpenGarageCover(CoverDevice): diff --git a/homeassistant/components/cover/rflink.py b/homeassistant/components/cover/rflink.py index 3357bf2d204fb0..e50fa488b92757 100644 --- a/homeassistant/components/cover/rflink.py +++ b/homeassistant/components/cover/rflink.py @@ -78,10 +78,10 @@ def devices_from_config(domain_config, hass=None): return devices -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Rflink cover platform.""" - async_add_devices(devices_from_config(config, hass)) + async_add_entities(devices_from_config(config, hass)) class RflinkCover(RflinkCommand, CoverDevice): diff --git a/homeassistant/components/cover/rfxtrx.py b/homeassistant/components/cover/rfxtrx.py index 5079a3b60c2902..d486b60197779e 100644 --- a/homeassistant/components/cover/rfxtrx.py +++ b/homeassistant/components/cover/rfxtrx.py @@ -29,12 +29,12 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the RFXtrx cover.""" import RFXtrx as rfxtrxmod covers = rfxtrx.get_devices_from_config(config, RfxtrxCover) - add_devices(covers) + add_entities(covers) def cover_update(event): """Handle cover updates from the RFXtrx gateway.""" @@ -45,7 +45,7 @@ def cover_update(event): new_device = rfxtrx.get_new_device(event, config, RfxtrxCover) if new_device: - add_devices([new_device]) + add_entities([new_device]) rfxtrx.apply_received_command(event) diff --git a/homeassistant/components/cover/rpi_gpio.py b/homeassistant/components/cover/rpi_gpio.py index 2f6951cfc0d254..828f5e8e0fee0e 100644 --- a/homeassistant/components/cover/rpi_gpio.py +++ b/homeassistant/components/cover/rpi_gpio.py @@ -54,7 +54,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the RPi cover platform.""" relay_time = config.get(CONF_RELAY_TIME) state_pull_mode = config.get(CONF_STATE_PULL_MODE) @@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): covers.append(RPiGPIOCover( cover[CONF_NAME], cover[CONF_RELAY_PIN], cover[CONF_STATE_PIN], state_pull_mode, relay_time, invert_state, invert_relay)) - add_devices(covers) + add_entities(covers) class RPiGPIOCover(CoverDevice): diff --git a/homeassistant/components/cover/ryobi_gdo.py b/homeassistant/components/cover/ryobi_gdo.py index a11d70dd3adbad..fec91f843fdc4f 100644 --- a/homeassistant/components/cover/ryobi_gdo.py +++ b/homeassistant/components/cover/ryobi_gdo.py @@ -29,7 +29,7 @@ SUPPORTED_FEATURES = (SUPPORT_OPEN | SUPPORT_CLOSE) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Ryobi covers.""" from py_ryobi_gdo import RyobiGDO as ryobi_door covers = [] @@ -52,7 +52,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): covers.append(RyobiCover(hass, my_door)) if covers: _LOGGER.debug("Adding covers") - add_devices(covers, True) + add_entities(covers, True) class RyobiCover(CoverDevice): diff --git a/homeassistant/components/cover/scsgate.py b/homeassistant/components/cover/scsgate.py index 04bf0ef1d32a84..a6f09c7237d395 100644 --- a/homeassistant/components/cover/scsgate.py +++ b/homeassistant/components/cover/scsgate.py @@ -22,7 +22,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SCSGate cover.""" devices = config.get(CONF_DEVICES) covers = [] @@ -42,7 +42,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): scsgate.SCSGATE.add_device(cover) covers.append(cover) - add_devices(covers) + add_entities(covers) class SCSGateCover(CoverDevice): diff --git a/homeassistant/components/cover/tahoma.py b/homeassistant/components/cover/tahoma.py index b38a863ebe04d0..baf32073c444da 100644 --- a/homeassistant/components/cover/tahoma.py +++ b/homeassistant/components/cover/tahoma.py @@ -24,13 +24,13 @@ ATTR_LOCK_ORIG = 'lock_originator' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tahoma covers.""" controller = hass.data[TAHOMA_DOMAIN]['controller'] devices = [] for device in hass.data[TAHOMA_DOMAIN]['devices']['cover']: devices.append(TahomaCover(device, controller)) - add_devices(devices, True) + add_entities(devices, True) class TahomaCover(TahomaDevice, CoverDevice): diff --git a/homeassistant/components/cover/tellduslive.py b/homeassistant/components/cover/tellduslive.py index 4a78cb96d0626c..9d292d9e8b5bb3 100644 --- a/homeassistant/components/cover/tellduslive.py +++ b/homeassistant/components/cover/tellduslive.py @@ -14,12 +14,12 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Telldus Live covers.""" if discovery_info is None: return - add_devices(TelldusLiveCover(hass, cover) for cover in discovery_info) + add_entities(TelldusLiveCover(hass, cover) for cover in discovery_info) class TelldusLiveCover(TelldusLiveEntity, CoverDevice): diff --git a/homeassistant/components/cover/tellstick.py b/homeassistant/components/cover/tellstick.py index 56a5a24b4095a2..88608ac42e9a24 100644 --- a/homeassistant/components/cover/tellstick.py +++ b/homeassistant/components/cover/tellstick.py @@ -12,7 +12,7 @@ DATA_TELLSTICK, TellstickDevice) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tellstick covers.""" if (discovery_info is None or discovery_info[ATTR_DISCOVER_DEVICES] is None): @@ -21,10 +21,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): signal_repetitions = discovery_info.get( ATTR_DISCOVER_CONFIG, DEFAULT_SIGNAL_REPETITIONS) - add_devices([TellstickCover(hass.data[DATA_TELLSTICK][tellcore_id], - signal_repetitions) - for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]], - True) + add_entities([TellstickCover(hass.data[DATA_TELLSTICK][tellcore_id], + signal_repetitions) + for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]], + True) class TellstickCover(TellstickDevice, CoverDevice): diff --git a/homeassistant/components/cover/template.py b/homeassistant/components/cover/template.py index d9d0d61c77a8f0..e02cdc32319597 100644 --- a/homeassistant/components/cover/template.py +++ b/homeassistant/components/cover/template.py @@ -71,7 +71,7 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Template cover.""" covers = [] @@ -141,7 +141,7 @@ async def async_setup_platform(hass, config, async_add_devices, _LOGGER.error("No covers added") return False - async_add_devices(covers) + async_add_entities(covers) return True diff --git a/homeassistant/components/cover/tuya.py b/homeassistant/components/cover/tuya.py index 7b5fefee58aab1..6ab8581602f18f 100644 --- a/homeassistant/components/cover/tuya.py +++ b/homeassistant/components/cover/tuya.py @@ -11,7 +11,7 @@ DEPENDENCIES = ['tuya'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tuya cover devices.""" if discovery_info is None: return @@ -23,7 +23,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if device is None: continue devices.append(TuyaCover(device)) - add_devices(devices) + add_entities(devices) class TuyaCover(TuyaDevice, CoverDevice): diff --git a/homeassistant/components/cover/velbus.py b/homeassistant/components/cover/velbus.py index fd060e7a7e1c07..a850177888434a 100644 --- a/homeassistant/components/cover/velbus.py +++ b/homeassistant/components/cover/velbus.py @@ -32,7 +32,7 @@ DEPENDENCIES = ['velbus'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up cover controlled by Velbus.""" devices = config.get(CONF_COVERS, {}) covers = [] @@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("No covers added") return False - add_devices(covers) + add_entities(covers) class VelbusCover(CoverDevice): diff --git a/homeassistant/components/cover/vera.py b/homeassistant/components/cover/vera.py index 9b2e8f3aad02b1..279e4a4307d210 100644 --- a/homeassistant/components/cover/vera.py +++ b/homeassistant/components/cover/vera.py @@ -16,9 +16,9 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vera covers.""" - add_devices( + add_entities( [VeraCover(device, hass.data[VERA_CONTROLLER]) for device in hass.data[VERA_DEVICES]['cover']], True) diff --git a/homeassistant/components/cover/wink.py b/homeassistant/components/cover/wink.py index 2206de05041435..857283b9b6c92c 100644 --- a/homeassistant/components/cover/wink.py +++ b/homeassistant/components/cover/wink.py @@ -11,22 +11,22 @@ DEPENDENCIES = ['wink'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink cover platform.""" import pywink for shade in pywink.get_shades(): _id = shade.object_id() + shade.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkCoverDevice(shade, hass)]) + add_entities([WinkCoverDevice(shade, hass)]) for shade in pywink.get_shade_groups(): _id = shade.object_id() + shade.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkCoverDevice(shade, hass)]) + add_entities([WinkCoverDevice(shade, hass)]) for door in pywink.get_garage_doors(): _id = door.object_id() + door.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkCoverDevice(door, hass)]) + add_entities([WinkCoverDevice(door, hass)]) class WinkCoverDevice(WinkDevice, CoverDevice): diff --git a/homeassistant/components/cover/xiaomi_aqara.py b/homeassistant/components/cover/xiaomi_aqara.py index 14321149148a52..3ed0a70b1e05d9 100644 --- a/homeassistant/components/cover/xiaomi_aqara.py +++ b/homeassistant/components/cover/xiaomi_aqara.py @@ -10,7 +10,7 @@ ATTR_CURTAIN_LEVEL = 'curtain_level' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Perform the setup for Xiaomi devices.""" devices = [] for (_, gateway) in hass.data[PY_XIAOMI_GATEWAY].gateways.items(): @@ -21,7 +21,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): {'status': 'status', 'pos': 'curtain_level'}, gateway)) - add_devices(devices) + add_entities(devices) class XiaomiGenericCover(XiaomiDevice, CoverDevice): diff --git a/homeassistant/components/deconz/.translations/da.json b/homeassistant/components/deconz/.translations/da.json index 698f55c59ec7a8..7f9aad83160d92 100644 --- a/homeassistant/components/deconz/.translations/da.json +++ b/homeassistant/components/deconz/.translations/da.json @@ -1,10 +1,25 @@ { "config": { + "abort": { + "no_bridges": "Ingen deConz bridge fundet", + "one_instance_only": "Komponenten underst\u00f8tter kun \u00e9n deCONZ forekomst" + }, + "error": { + "no_key": "Kunne ikke f\u00e5 en API-n\u00f8gle" + }, "step": { "init": { "data": { - "host": "V\u00e6rt" + "host": "V\u00e6rt", + "port": "Port (standardv\u00e6rdi: '80')" } + }, + "options": { + "data": { + "allow_clip_sensor": "Tillad import af virtuelle sensorer", + "allow_deconz_groups": "Tillad importering af deCONZ grupper" + }, + "title": "Ekstra konfiguration valgmuligheder for deCONZ" } } } diff --git a/homeassistant/components/deconz/.translations/he.json b/homeassistant/components/deconz/.translations/he.json new file mode 100644 index 00000000000000..b4b3d54e07567b --- /dev/null +++ b/homeassistant/components/deconz/.translations/he.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "\u05d4\u05de\u05d2\u05e9\u05e8 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8", + "no_bridges": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05d2\u05e9\u05e8\u05d9 deCONZ" + }, + "error": { + "no_key": "\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05d4\u05d9\u05d4 \u05dc\u05e7\u05d1\u05dc \u05de\u05e4\u05ea\u05d7 API" + }, + "step": { + "init": { + "data": { + "host": "\u05de\u05d0\u05e8\u05d7", + "port": "\u05e4\u05d5\u05e8\u05d8 (\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc: '80')" + }, + "title": "\u05d4\u05d2\u05d3\u05e8 \u05de\u05d2\u05e9\u05e8 deCONZ Zigbee" + }, + "link": { + "description": "\u05d1\u05d8\u05dc \u05d0\u05ea \u05e0\u05e2\u05d9\u05dc\u05ea \u05d4\u05de\u05e9\u05e8 deCONZ \u05e9\u05dc\u05da \u05db\u05d3\u05d9 \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05e2\u05dd Home Assistant.\n\n 1. \u05e2\u05d1\u05d5\u05e8 \u05d0\u05dc \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05de\u05e2\u05e8\u05db\u05ea deCONZ \n .2 \u05dc\u05d7\u05e5 \u05e2\u05dc \"Unlock Gateway\"", + "title": "\u05e7\u05e9\u05e8 \u05e2\u05dd deCONZ" + }, + "options": { + "data": { + "allow_clip_sensor": "\u05d0\u05e4\u05e9\u05e8 \u05dc\u05d9\u05d9\u05d1\u05d0 \u05d7\u05d9\u05d9\u05e9\u05e0\u05d9\u05dd \u05d5\u05d9\u05e8\u05d8\u05d5\u05d0\u05dc\u05d9\u05d9\u05dd", + "allow_deconz_groups": "\u05d0\u05e4\u05e9\u05e8 \u05dc\u05d9\u05d9\u05d1\u05d0 \u05e7\u05d1\u05d5\u05e6\u05d5\u05ea deCONZ" + }, + "title": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05ea\u05e6\u05d5\u05e8\u05d4 \u05e0\u05d5\u05e1\u05e4\u05d5\u05ea \u05e2\u05d1\u05d5\u05e8 deCONZ" + } + }, + "title": "\u05de\u05d2\u05e9\u05e8 deCONZ Zigbee" + } +} \ No newline at end of file diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index eacfe22e818c01..a4edc009ea15db 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -12,6 +12,7 @@ CONF_ID, CONF_PORT, EVENT_HOMEASSISTANT_STOP) from homeassistant.core import EventOrigin, callback from homeassistant.helpers import aiohttp_client, config_validation as cv +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send) from homeassistant.util import slugify @@ -23,7 +24,7 @@ CONF_ALLOW_CLIP_SENSOR, CONFIG_FILE, DATA_DECONZ_EVENT, DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DOMAIN, _LOGGER) -REQUIREMENTS = ['pydeconz==43'] +REQUIREMENTS = ['pydeconz==44'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -82,7 +83,7 @@ async def async_setup_entry(hass, config_entry): @callback def async_add_device_callback(device_type, device): - """Called when a new device has been created in deCONZ.""" + """Handle event of new device creation in deCONZ.""" async_dispatcher_send( hass, 'deconz_new_{}'.format(device_type), [device]) @@ -105,7 +106,7 @@ def async_add_device_callback(device_type, device): @callback def async_add_remote(sensors): - """Setup remote from deCONZ.""" + """Set up remote from deCONZ.""" from pydeconz.sensor import SWITCH as DECONZ_REMOTE allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True) for sensor in sensors: @@ -119,6 +120,15 @@ def async_add_remote(sensors): deconz.start() + device_registry = await \ + hass.helpers.device_registry.async_get_registry() + device_registry.async_get_or_create( + config_entry=config_entry.entry_id, + connections={(CONNECTION_NETWORK_MAC, deconz.config.mac)}, + identifiers={(DOMAIN, deconz.config.bridgeid)}, + manufacturer='Dresden Elektronik', model=deconz.config.modelid, + name=deconz.config.name, sw_version=deconz.config.swversion) + async def async_configure(call): """Set attribute of device in deCONZ. diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index e7bc5605aee400..e629d57f2017f4 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -8,6 +8,7 @@ DATA_DECONZ_EVENT = 'deconz_events' DATA_DECONZ_ID = 'deconz_entities' DATA_DECONZ_UNSUB = 'deconz_dispatchers' +DECONZ_DOMAIN = 'deconz' CONF_ALLOW_CLIP_SENSOR = 'allow_clip_sensor' CONF_ALLOW_DECONZ_GROUPS = 'allow_deconz_groups' diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 74cb0a77fef51d..408672a974f948 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -330,19 +330,18 @@ def async_see( }) # update known_devices.yaml - self.hass.async_add_job( + self.hass.async_create_task( self.async_update_config( self.hass.config.path(YAML_DEVICES), dev_id, device) ) - @asyncio.coroutine - def async_update_config(self, path, dev_id, device): + async def async_update_config(self, path, dev_id, device): """Add device to YAML configuration file. This method is a coroutine. """ - with (yield from self._is_updating): - yield from self.hass.async_add_job( + async with self._is_updating: + await self.hass.async_add_executor_job( update_config, self.hass.config.path(YAML_DEVICES), dev_id, device) @@ -681,8 +680,7 @@ def async_setup_scanner_platform(hass: HomeAssistantType, config: ConfigType, # Initial scan of each mac we also tell about host name for config seen = set() # type: Any - @asyncio.coroutine - def async_device_tracker_scan(now: dt_util.dt.datetime): + async def async_device_tracker_scan(now: dt_util.dt.datetime): """Handle interval matches.""" if update_lock.locked(): _LOGGER.warning( @@ -690,18 +688,18 @@ def async_device_tracker_scan(now: dt_util.dt.datetime): "scan interval %s", platform, interval) return - with (yield from update_lock): - found_devices = yield from scanner.async_scan_devices() + async with update_lock: + found_devices = await scanner.async_scan_devices() for mac in found_devices: if mac in seen: host_name = None else: - host_name = yield from scanner.async_get_device_name(mac) + host_name = await scanner.async_get_device_name(mac) seen.add(mac) try: - extra_attributes = (yield from + extra_attributes = (await scanner.async_get_extra_attributes(mac)) except NotImplementedError: extra_attributes = dict() diff --git a/homeassistant/components/device_tracker/keenetic_ndms2.py b/homeassistant/components/device_tracker/keenetic_ndms2.py index 4b5e3d6333d909..4be6d96eb5ac4c 100644 --- a/homeassistant/components/device_tracker/keenetic_ndms2.py +++ b/homeassistant/components/device_tracker/keenetic_ndms2.py @@ -15,7 +15,7 @@ CONF_HOST, CONF_PORT, CONF_PASSWORD, CONF_USERNAME ) -REQUIREMENTS = ['ndms2_client==0.0.3'] +REQUIREMENTS = ['ndms2_client==0.0.4'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/device_tracker/linksys_ap.py b/homeassistant/components/device_tracker/linksys_ap.py index a2a371163fd695..5fa33583567ab1 100644 --- a/homeassistant/components/device_tracker/linksys_ap.py +++ b/homeassistant/components/device_tracker/linksys_ap.py @@ -19,7 +19,7 @@ INTERFACES = 2 DEFAULT_TIMEOUT = 10 -REQUIREMENTS = ['beautifulsoup4==4.6.1'] +REQUIREMENTS = ['beautifulsoup4==4.6.3'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/device_tracker/ritassist.py b/homeassistant/components/device_tracker/ritassist.py index 9fc50de5062329..c41ae9f2ec2880 100644 --- a/homeassistant/components/device_tracker/ritassist.py +++ b/homeassistant/components/device_tracker/ritassist.py @@ -14,7 +14,7 @@ from homeassistant.const import CONF_USERNAME, CONF_PASSWORD from homeassistant.helpers.event import track_utc_time_change -REQUIREMENTS = ['ritassist==0.5'] +REQUIREMENTS = ['ritassist==0.9.2'] _LOGGER = logging.getLogger(__name__) @@ -57,7 +57,7 @@ def __init__(self, config, see): config.get(CONF_PASSWORD)) def setup(self, hass): - """Setup a timer and start gathering devices.""" + """Set up a timer and start gathering devices.""" self._refresh() track_utc_time_change(hass, lambda now: self._refresh(), @@ -78,6 +78,10 @@ def _refresh(self) -> None: for device in devices: if (not self._include or device.license_plate in self._include): + + if device.active or device.current_address is None: + device.get_map_details() + self._see(dev_id=device.plate_as_id, gps=(device.latitude, device.longitude), attributes=device.state_attributes, diff --git a/homeassistant/components/device_tracker/tplink.py b/homeassistant/components/device_tracker/tplink.py index 346f381db34b1c..9bf85e39faf0dd 100644 --- a/homeassistant/components/device_tracker/tplink.py +++ b/homeassistant/components/device_tracker/tplink.py @@ -69,11 +69,16 @@ def __init__(self, config): password = config[CONF_PASSWORD] username = config[CONF_USERNAME] - self.tplink_client = TpLinkClient( - password, host=host, username=username) + self.success_init = False + try: + self.tplink_client = TpLinkClient( + password, host=host, username=username) - self.last_results = {} - self.success_init = self._update_info() + self.last_results = {} + + self.success_init = self._update_info() + except requests.exceptions.ConnectionError: + _LOGGER.debug("ConnectionError in TplinkDeviceScanner") def scan_devices(self): """Scan for new devices and return a list with found device IDs.""" @@ -115,7 +120,11 @@ def __init__(self, config): self.password = password self.last_results = {} - self.success_init = self._update_info() + self.success_init = False + try: + self.success_init = self._update_info() + except requests.exceptions.ConnectionError: + _LOGGER.debug("ConnectionError in Tplink1DeviceScanner") def scan_devices(self): """Scan for new devices and return a list with found device IDs.""" diff --git a/homeassistant/components/device_tracker/unifi.py b/homeassistant/components/device_tracker/unifi.py index b7efe65dd0166d..627d2092a11c25 100644 --- a/homeassistant/components/device_tracker/unifi.py +++ b/homeassistant/components/device_tracker/unifi.py @@ -12,7 +12,7 @@ from homeassistant.components.device_tracker import ( DOMAIN, PLATFORM_SCHEMA, DeviceScanner) from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD -from homeassistant.const import CONF_VERIFY_SSL +from homeassistant.const import CONF_VERIFY_SSL, CONF_MONITORED_CONDITIONS import homeassistant.util.dt as dt_util REQUIREMENTS = ['pyunifi==2.13'] @@ -31,6 +31,18 @@ NOTIFICATION_ID = 'unifi_notification' NOTIFICATION_TITLE = 'Unifi Device Tracker Setup' +AVAILABLE_ATTRS = [ + '_id', '_is_guest_by_uap', '_last_seen_by_uap', '_uptime_by_uap', + 'ap_mac', 'assoc_time', 'authorized', 'bssid', 'bytes-r', 'ccq', + 'channel', 'essid', 'first_seen', 'hostname', 'idletime', 'ip', + 'is_11r', 'is_guest', 'is_wired', 'last_seen', 'latest_assoc_time', + 'mac', 'name', 'noise', 'noted', 'oui', 'powersave_enabled', + 'qos_policy_applied', 'radio', 'radio_proto', 'rssi', 'rx_bytes', + 'rx_bytes-r', 'rx_packets', 'rx_rate', 'signal', 'site_id', + 'tx_bytes', 'tx_bytes-r', 'tx_packets', 'tx_power', 'tx_rate', + 'uptime', 'user_id', 'usergroup_id', 'vlan' +] + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, vol.Optional(CONF_SITE_ID, default='default'): cv.string, @@ -41,6 +53,8 @@ cv.boolean, cv.isfile), vol.Optional(CONF_DETECTION_TIME, default=DEFAULT_DETECTION_TIME): vol.All( cv.time_period, cv.positive_timedelta), + vol.Optional(CONF_MONITORED_CONDITIONS): + vol.All(cv.ensure_list, [vol.In(AVAILABLE_ATTRS)]), vol.Optional(CONF_SSID_FILTER): vol.All(cv.ensure_list, [cv.string]) }) @@ -56,6 +70,7 @@ def get_scanner(hass, config): port = config[DOMAIN].get(CONF_PORT) verify_ssl = config[DOMAIN].get(CONF_VERIFY_SSL) detection_time = config[DOMAIN].get(CONF_DETECTION_TIME) + monitored_conditions = config[DOMAIN].get(CONF_MONITORED_CONDITIONS) ssid_filter = config[DOMAIN].get(CONF_SSID_FILTER) try: @@ -72,18 +87,20 @@ def get_scanner(hass, config): notification_id=NOTIFICATION_ID) return False - return UnifiScanner(ctrl, detection_time, ssid_filter) + return UnifiScanner(ctrl, detection_time, ssid_filter, + monitored_conditions) class UnifiScanner(DeviceScanner): """Provide device_tracker support from Unifi WAP client data.""" def __init__(self, controller, detection_time: timedelta, - ssid_filter) -> None: + ssid_filter, monitored_conditions) -> None: """Initialize the scanner.""" self._detection_time = detection_time self._controller = controller self._ssid_filter = ssid_filter + self._monitored_conditions = monitored_conditions self._update() def _update(self): @@ -125,6 +142,14 @@ def get_device_name(self, device): def get_extra_attributes(self, device): """Return the extra attributes of the device.""" + if not self._monitored_conditions: + return {} + client = self._clients.get(device, {}) - _LOGGER.debug("Device mac %s attributes %s", device, client) - return client + attributes = {} + for variable in self._monitored_conditions: + if variable in client: + attributes[variable] = client[variable] + + _LOGGER.debug("Device mac %s attributes %s", device, attributes) + return attributes diff --git a/homeassistant/components/device_tracker/xiaomi_miio.py b/homeassistant/components/device_tracker/xiaomi_miio.py index 074d6a1054ee51..b1cfc0aed4ae76 100644 --- a/homeassistant/components/device_tracker/xiaomi_miio.py +++ b/homeassistant/components/device_tracker/xiaomi_miio.py @@ -20,7 +20,7 @@ vol.Required(CONF_TOKEN): vol.All(cv.string, vol.Length(min=32, max=32)), }) -REQUIREMENTS = ['python-miio==0.4.0', 'construct==2.9.41'] +REQUIREMENTS = ['python-miio==0.4.1', 'construct==2.9.41'] def get_scanner(hass, config): @@ -73,5 +73,8 @@ async def async_scan_devices(self): return devices async def async_get_device_name(self, device): - """The repeater doesn't provide the name of the associated device.""" + """Return None. + + The repeater doesn't provide the name of the associated device. + """ return None diff --git a/homeassistant/components/doorbird.py b/homeassistant/components/doorbird.py index 6cd820816e2fef..c97289b9f07dc0 100644 --- a/homeassistant/components/doorbird.py +++ b/homeassistant/components/doorbird.py @@ -139,17 +139,17 @@ def __init__(self, device, name, events=None, custom_url=None): @property def name(self): - """Custom device name.""" + """Get custom device name.""" return self._name @property def device(self): - """The configured device.""" + """Get the configured device.""" return self._device @property def custom_url(self): - """Custom url for device.""" + """Get custom url for device.""" return self._custom_url @property diff --git a/homeassistant/components/ecovacs.py b/homeassistant/components/ecovacs.py new file mode 100644 index 00000000000000..2e51b048d15e43 --- /dev/null +++ b/homeassistant/components/ecovacs.py @@ -0,0 +1,87 @@ +"""Parent component for Ecovacs Deebot vacuums. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/ecovacs/ +""" + +import logging +import random +import string + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers import discovery +from homeassistant.const import CONF_USERNAME, CONF_PASSWORD, \ + EVENT_HOMEASSISTANT_STOP + +REQUIREMENTS = ['sucks==0.9.1'] + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = "ecovacs" + +CONF_COUNTRY = "country" +CONF_CONTINENT = "continent" + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Required(CONF_COUNTRY): vol.All(vol.Lower, cv.string), + vol.Required(CONF_CONTINENT): vol.All(vol.Lower, cv.string), + }) +}, extra=vol.ALLOW_EXTRA) + +ECOVACS_DEVICES = "ecovacs_devices" + +# Generate a random device ID on each bootup +ECOVACS_API_DEVICEID = ''.join( + random.choice(string.ascii_uppercase + string.digits) for _ in range(8) +) + + +def setup(hass, config): + """Set up the Ecovacs component.""" + _LOGGER.debug("Creating new Ecovacs component") + + hass.data[ECOVACS_DEVICES] = [] + + from sucks import EcoVacsAPI, VacBot + + ecovacs_api = EcoVacsAPI(ECOVACS_API_DEVICEID, + config[DOMAIN].get(CONF_USERNAME), + EcoVacsAPI.md5(config[DOMAIN].get(CONF_PASSWORD)), + config[DOMAIN].get(CONF_COUNTRY), + config[DOMAIN].get(CONF_CONTINENT)) + + devices = ecovacs_api.devices() + _LOGGER.debug("Ecobot devices: %s", devices) + + for device in devices: + _LOGGER.info("Discovered Ecovacs device on account: %s", + device['nick']) + vacbot = VacBot(ecovacs_api.uid, + ecovacs_api.REALM, + ecovacs_api.resource, + ecovacs_api.user_access_token, + device, + config[DOMAIN].get(CONF_CONTINENT).lower(), + monitor=True) + hass.data[ECOVACS_DEVICES].append(vacbot) + + def stop(event: object) -> None: + """Shut down open connections to Ecovacs XMPP server.""" + for device in hass.data[ECOVACS_DEVICES]: + _LOGGER.info("Shutting down connection to Ecovacs device %s", + device.vacuum['nick']) + device.disconnect() + + # Listen for HA stop to disconnect. + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop) + + if hass.data[ECOVACS_DEVICES]: + _LOGGER.debug("Starting vacuum components") + discovery.load_platform(hass, "vacuum", DOMAIN, {}, config) + + return True diff --git a/homeassistant/components/egardia.py b/homeassistant/components/egardia.py index b7da671bb15889..3547f4fc76e62c 100644 --- a/homeassistant/components/egardia.py +++ b/homeassistant/components/egardia.py @@ -101,7 +101,7 @@ def setup(hass, config): server.start() def handle_stop_event(event): - """Callback function for HA stop event.""" + """Handle HA stop event.""" server.stop() # listen to home assistant stop event diff --git a/homeassistant/components/eufy.py b/homeassistant/components/eufy.py index 69d4905228ad27..31a4dddd424c41 100644 --- a/homeassistant/components/eufy.py +++ b/homeassistant/components/eufy.py @@ -15,7 +15,7 @@ import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['lakeside==0.7'] +REQUIREMENTS = ['lakeside==0.10'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fan/__init__.py b/homeassistant/components/fan/__init__.py index db0e8c590fdabe..f2704e84bc50c0 100644 --- a/homeassistant/components/fan/__init__.py +++ b/homeassistant/components/fan/__init__.py @@ -89,33 +89,6 @@ vol.Optional(ATTR_DIRECTION): cv.string }) # type: dict -SERVICE_TO_METHOD = { - SERVICE_TURN_ON: { - 'method': 'async_turn_on', - 'schema': FAN_TURN_ON_SCHEMA, - }, - SERVICE_TURN_OFF: { - 'method': 'async_turn_off', - 'schema': FAN_TURN_OFF_SCHEMA, - }, - SERVICE_TOGGLE: { - 'method': 'async_toggle', - 'schema': FAN_TOGGLE_SCHEMA, - }, - SERVICE_SET_SPEED: { - 'method': 'async_set_speed', - 'schema': FAN_SET_SPEED_SCHEMA, - }, - SERVICE_OSCILLATE: { - 'method': 'async_oscillate', - 'schema': FAN_OSCILLATE_SCHEMA, - }, - SERVICE_SET_DIRECTION: { - 'method': 'async_set_direction', - 'schema': FAN_SET_DIRECTION_SCHEMA, - }, -} - @bind_hass def is_on(hass, entity_id: str = None) -> bool: @@ -204,30 +177,30 @@ def async_setup(hass, config: dict): yield from component.async_setup(config) - @asyncio.coroutine - def async_handle_fan_service(service): - """Handle service call for fans.""" - method = SERVICE_TO_METHOD.get(service.service) - params = service.data.copy() - - # Convert the entity ids to valid fan ids - target_fans = component.async_extract_from_service(service) - params.pop(ATTR_ENTITY_ID, None) - - update_tasks = [] - for fan in target_fans: - yield from getattr(fan, method['method'])(**params) - if not fan.should_poll: - continue - update_tasks.append(fan.async_update_ha_state(True)) - - if update_tasks: - yield from asyncio.wait(update_tasks, loop=hass.loop) - - for service_name in SERVICE_TO_METHOD: - schema = SERVICE_TO_METHOD[service_name].get('schema') - hass.services.async_register( - DOMAIN, service_name, async_handle_fan_service, schema=schema) + component.async_register_entity_service( + SERVICE_TURN_ON, FAN_TURN_ON_SCHEMA, + 'async_turn_on' + ) + component.async_register_entity_service( + SERVICE_TURN_OFF, FAN_TURN_OFF_SCHEMA, + 'async_turn_off' + ) + component.async_register_entity_service( + SERVICE_TOGGLE, FAN_TOGGLE_SCHEMA, + 'async_toggle' + ) + component.async_register_entity_service( + SERVICE_SET_SPEED, FAN_SET_SPEED_SCHEMA, + 'async_set_speed' + ) + component.async_register_entity_service( + SERVICE_OSCILLATE, FAN_OSCILLATE_SCHEMA, + 'async_oscillate' + ) + component.async_register_entity_service( + SERVICE_SET_DIRECTION, FAN_SET_DIRECTION_SCHEMA, + 'async_set_direction' + ) return True diff --git a/homeassistant/components/fan/comfoconnect.py b/homeassistant/components/fan/comfoconnect.py index fd3265b8230377..a1f13da6c09553 100644 --- a/homeassistant/components/fan/comfoconnect.py +++ b/homeassistant/components/fan/comfoconnect.py @@ -26,11 +26,11 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ComfoConnect fan platform.""" ccb = hass.data[DOMAIN] - add_devices([ComfoConnectFan(hass, name=ccb.name, ccb=ccb)], True) + add_entities([ComfoConnectFan(hass, name=ccb.name, ccb=ccb)], True) class ComfoConnectFan(FanEntity): diff --git a/homeassistant/components/fan/demo.py b/homeassistant/components/fan/demo.py index c03c492c834a11..840196c8bf0e96 100644 --- a/homeassistant/components/fan/demo.py +++ b/homeassistant/components/fan/demo.py @@ -13,9 +13,9 @@ LIMITED_SUPPORT = SUPPORT_SET_SPEED -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up the demo fan platform.""" - add_devices_callback([ + add_entities_callback([ DemoFan(hass, "Living Room Fan", FULL_SUPPORT), DemoFan(hass, "Ceiling Fan", LIMITED_SUPPORT), ]) diff --git a/homeassistant/components/fan/dyson.py b/homeassistant/components/fan/dyson.py index 3eb4646e6dcbdc..9f505c87b3d83f 100644 --- a/homeassistant/components/fan/dyson.py +++ b/homeassistant/components/fan/dyson.py @@ -32,7 +32,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dyson fan components.""" from libpurecoollink.dyson_pure_cool_link import DysonPureCoolLink @@ -46,7 +46,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dyson_entity = DysonPureCoolLinkDevice(hass, device) hass.data[DYSON_FAN_DEVICES].append(dyson_entity) - add_devices(hass.data[DYSON_FAN_DEVICES]) + add_entities(hass.data[DYSON_FAN_DEVICES]) def service_handle(service): """Handle the Dyson services.""" diff --git a/homeassistant/components/fan/insteon_plm.py b/homeassistant/components/fan/insteon.py similarity index 82% rename from homeassistant/components/fan/insteon_plm.py rename to homeassistant/components/fan/insteon.py index 0911295d090cf3..f938ae7aec1892 100644 --- a/homeassistant/components/fan/insteon_plm.py +++ b/homeassistant/components/fan/insteon.py @@ -2,7 +2,7 @@ Support for INSTEON fans via PowerLinc Modem. For more details about this component, please refer to the documentation at -https://home-assistant.io/components/fan.insteon_plm/ +https://home-assistant.io/components/fan.insteon/ """ import asyncio import logging @@ -14,9 +14,9 @@ FanEntity, SUPPORT_SET_SPEED) from homeassistant.const import STATE_OFF -from homeassistant.components.insteon_plm import InsteonPLMEntity +from homeassistant.components.insteon import InsteonEntity -DEPENDENCIES = ['insteon_plm'] +DEPENDENCIES = ['insteon'] SPEED_TO_HEX = {SPEED_OFF: 0x00, SPEED_LOW: 0x3f, @@ -29,23 +29,24 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): - """Set up the INSTEON PLM device class for the hass platform.""" - plm = hass.data['insteon_plm'].get('plm') +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): + """Set up the INSTEON device class for the hass platform.""" + insteon_modem = hass.data['insteon'].get('modem') address = discovery_info['address'] - device = plm.devices[address] + device = insteon_modem.devices[address] state_key = discovery_info['state_key'] _LOGGER.debug('Adding device %s entity %s to Fan platform', device.address.hex, device.states[state_key].name) - new_entity = InsteonPLMFan(device, state_key) + new_entity = InsteonFan(device, state_key) - async_add_devices([new_entity]) + async_add_entities([new_entity]) -class InsteonPLMFan(InsteonPLMEntity, FanEntity): +class InsteonFan(InsteonEntity, FanEntity): """An INSTEON fan component.""" @property diff --git a/homeassistant/components/fan/insteon_local.py b/homeassistant/components/fan/insteon_local.py deleted file mode 100644 index 28b93c86ed7f97..00000000000000 --- a/homeassistant/components/fan/insteon_local.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -Support for Insteon fans via local hub control. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/fan.insteon_local/ -""" -import logging -from datetime import timedelta - -from homeassistant import util -from homeassistant.components.fan import ( - ATTR_SPEED, SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH, - SUPPORT_SET_SPEED, FanEntity) - -_CONFIGURING = {} -_LOGGER = logging.getLogger(__name__) - -DEPENDENCIES = ['insteon_local'] -DOMAIN = 'fan' - -MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100) -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) - -SUPPORT_INSTEON_LOCAL = SUPPORT_SET_SPEED - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up the Insteon local fan platform.""" - insteonhub = hass.data['insteon_local'] - if discovery_info is None: - return - - linked = discovery_info['linked'] - device_list = [] - for device_id in linked: - if (linked[device_id]['cat_type'] == 'dimmer' and - linked[device_id]['sku'] == '2475F'): - device = insteonhub.fan(device_id) - device_list.append( - InsteonLocalFanDevice(device) - ) - - add_devices(device_list) - - -class InsteonLocalFanDevice(FanEntity): - """An abstract Class for an Insteon node.""" - - def __init__(self, node): - """Initialize the device.""" - self.node = node - self._speed = SPEED_OFF - - @property - def name(self): - """Return the name of the node.""" - return self.node.device_id - - @property - def unique_id(self): - """Return the ID of this Insteon node.""" - return self.node.device_id - - @property - def speed(self) -> str: - """Return the current speed.""" - return self._speed - - @property - def speed_list(self) -> list: - """Get the list of available speeds.""" - return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] - - @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) - def update(self): - """Update state of the fan.""" - resp = self.node.status() - if 'cmd2' in resp: - if resp['cmd2'] == '00': - self._speed = SPEED_OFF - elif resp['cmd2'] == '55': - self._speed = SPEED_LOW - elif resp['cmd2'] == 'AA': - self._speed = SPEED_MEDIUM - elif resp['cmd2'] == 'FF': - self._speed = SPEED_HIGH - - @property - def supported_features(self): - """Flag supported features.""" - return SUPPORT_INSTEON_LOCAL - - def turn_on(self, speed: str = None, **kwargs) -> None: - """Turn device on.""" - if speed is None: - speed = kwargs.get(ATTR_SPEED, SPEED_MEDIUM) - - self.set_speed(speed) - - def turn_off(self, **kwargs) -> None: - """Turn device off.""" - self.node.off() - - def set_speed(self, speed: str) -> None: - """Set the speed of the fan.""" - if self.node.on(speed): - self._speed = speed diff --git a/homeassistant/components/fan/isy994.py b/homeassistant/components/fan/isy994.py index 97a5f9c3bd69d2..314200ba1c4502 100644 --- a/homeassistant/components/fan/isy994.py +++ b/homeassistant/components/fan/isy994.py @@ -31,7 +31,7 @@ def setup_platform(hass, config: ConfigType, - add_devices: Callable[[list], None], discovery_info=None): + add_entities: Callable[[list], None], discovery_info=None): """Set up the ISY994 fan platform.""" devices = [] @@ -41,7 +41,7 @@ def setup_platform(hass, config: ConfigType, for name, status, actions in hass.data[ISY994_PROGRAMS][DOMAIN]: devices.append(ISYFanProgram(name, status, actions)) - add_devices(devices) + add_entities(devices) class ISYFanDevice(ISYDevice, FanEntity): diff --git a/homeassistant/components/fan/mqtt.py b/homeassistant/components/fan/mqtt.py index 5faa735801dbd3..db3cfab3608773 100644 --- a/homeassistant/components/fan/mqtt.py +++ b/homeassistant/components/fan/mqtt.py @@ -78,12 +78,12 @@ async def async_setup_platform(hass: HomeAssistantType, config: ConfigType, - async_add_devices, discovery_info=None): + async_add_entities, discovery_info=None): """Set up the MQTT fan platform.""" if discovery_info is not None: config = PLATFORM_SCHEMA(discovery_info) - async_add_devices([MqttFan( + async_add_entities([MqttFan( config.get(CONF_NAME), { key: config.get(key) for key in ( diff --git a/homeassistant/components/fan/template.py b/homeassistant/components/fan/template.py index 74fb73dae1d258..ff25afb792a8c4 100644 --- a/homeassistant/components/fan/template.py +++ b/homeassistant/components/fan/template.py @@ -67,7 +67,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None + hass, config, async_add_entities, discovery_info=None ): """Set up the Template Fans.""" fans = [] @@ -123,7 +123,7 @@ async def async_setup_platform( ) ) - async_add_devices(fans) + async_add_entities(fans) class TemplateFan(FanEntity): diff --git a/homeassistant/components/fan/tuya.py b/homeassistant/components/fan/tuya.py index f19a9e5a5f7745..9cb7cdc3f2c8e0 100644 --- a/homeassistant/components/fan/tuya.py +++ b/homeassistant/components/fan/tuya.py @@ -13,7 +13,7 @@ DEPENDENCIES = ['tuya'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tuya fan platform.""" if discovery_info is None: return @@ -25,7 +25,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if device is None: continue devices.append(TuyaFanDevice(device)) - add_devices(devices) + add_entities(devices) class TuyaFanDevice(TuyaDevice, FanEntity): diff --git a/homeassistant/components/fan/wink.py b/homeassistant/components/fan/wink.py index 4eebacbbbf2e3f..480801c48c0bb1 100644 --- a/homeassistant/components/fan/wink.py +++ b/homeassistant/components/fan/wink.py @@ -21,13 +21,13 @@ SUPPORTED_FEATURES = SUPPORT_DIRECTION + SUPPORT_SET_SPEED -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink platform.""" import pywink for fan in pywink.get_fans(): if fan.object_id() + fan.name() not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkFanDevice(fan, hass)]) + add_entities([WinkFanDevice(fan, hass)]) class WinkFanDevice(WinkDevice, FanEntity): diff --git a/homeassistant/components/fan/xiaomi_miio.py b/homeassistant/components/fan/xiaomi_miio.py index 1616d38881626d..a66e833b4b2a89 100644 --- a/homeassistant/components/fan/xiaomi_miio.py +++ b/homeassistant/components/fan/xiaomi_miio.py @@ -49,7 +49,7 @@ 'zhimi.humidifier.ca1']), }) -REQUIREMENTS = ['python-miio==0.4.0', 'construct==2.9.41'] +REQUIREMENTS = ['python-miio==0.4.1', 'construct==2.9.41'] ATTR_MODEL = 'model' @@ -314,7 +314,7 @@ } -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the miio fan device from config.""" from miio import Device, DeviceException @@ -358,7 +358,7 @@ async def async_setup_platform(hass, config, async_add_devices, return False hass.data[DATA_KEY][host] = device - async_add_devices([device], update_before_add=True) + async_add_entities([device], update_before_add=True) async def async_service_handler(service): """Map services to methods on XiaomiAirPurifier.""" diff --git a/homeassistant/components/fan/zha.py b/homeassistant/components/fan/zha.py index 983bc3a79d753b..2612c065393c20 100644 --- a/homeassistant/components/fan/zha.py +++ b/homeassistant/components/fan/zha.py @@ -39,13 +39,14 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Zigbee Home Automation fans.""" discovery_info = zha.get_discovery_info(hass, discovery_info) if discovery_info is None: return - async_add_devices([ZhaFan(**discovery_info)], update_before_add=True) + async_add_entities([ZhaFan(**discovery_info)], update_before_add=True) class ZhaFan(zha.Entity, FanEntity): diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index a436cc483ae1e0..0156a8b2cd6c98 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -26,7 +26,7 @@ from homeassistant.loader import bind_hass from homeassistant.util.yaml import load_yaml -REQUIREMENTS = ['home-assistant-frontend==20180820.0'] +REQUIREMENTS = ['home-assistant-frontend==20180829.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/homeassistant/components/google_assistant/smart_home.py b/homeassistant/components/google_assistant/smart_home.py index 63a3e641170b1b..675e86f9d3938d 100644 --- a/homeassistant/components/google_assistant/smart_home.py +++ b/homeassistant/components/google_assistant/smart_home.py @@ -77,7 +77,7 @@ def traits(self): domain = state.domain features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) - return [Trait(state) for Trait in trait.TRAITS + return [Trait(self.hass, state) for Trait in trait.TRAITS if Trait.supported(domain, features)] @callback @@ -159,7 +159,7 @@ async def execute(self, command, params): executed = False for trt in self.traits(): if trt.can_execute(command, params): - await trt.execute(self.hass, command, params) + await trt.execute(command, params) executed = True break diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index 1d369eb87dad9e..26e80e6f03b52b 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -14,7 +14,6 @@ ) from homeassistant.const import ( ATTR_ENTITY_ID, - ATTR_UNIT_OF_MEASUREMENT, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, @@ -50,15 +49,14 @@ def register_trait(trait): - """Decorator to register a trait.""" + """Decorate a function to register a trait.""" TRAITS.append(trait) return trait -def _google_temp_unit(state): +def _google_temp_unit(units): """Return Google temperature unit.""" - if (state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == - TEMP_FAHRENHEIT): + if units == TEMP_FAHRENHEIT: return 'F' return 'C' @@ -68,8 +66,9 @@ class _Trait: commands = [] - def __init__(self, state): + def __init__(self, hass, state): """Initialize a trait for a state.""" + self.hass = hass self.state = state def sync_attributes(self): @@ -84,7 +83,7 @@ def can_execute(self, command, params): """Test if command can be executed.""" return command in self.commands - async def execute(self, hass, command, params): + async def execute(self, command, params): """Execute a trait command.""" raise NotImplementedError @@ -141,24 +140,24 @@ def query_attributes(self): return response - async def execute(self, hass, command, params): + async def execute(self, command, params): """Execute a brightness command.""" domain = self.state.domain if domain == light.DOMAIN: - await hass.services.async_call( + await self.hass.services.async_call( light.DOMAIN, light.SERVICE_TURN_ON, { ATTR_ENTITY_ID: self.state.entity_id, light.ATTR_BRIGHTNESS_PCT: params['brightness'] }, blocking=True) elif domain == cover.DOMAIN: - await hass.services.async_call( + await self.hass.services.async_call( cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION, { ATTR_ENTITY_ID: self.state.entity_id, cover.ATTR_POSITION: params['brightness'] }, blocking=True) elif domain == media_player.DOMAIN: - await hass.services.async_call( + await self.hass.services.async_call( media_player.DOMAIN, media_player.SERVICE_VOLUME_SET, { ATTR_ENTITY_ID: self.state.entity_id, media_player.ATTR_MEDIA_VOLUME_LEVEL: @@ -201,7 +200,7 @@ def query_attributes(self): return {'on': self.state.state != cover.STATE_CLOSED} return {'on': self.state.state != STATE_OFF} - async def execute(self, hass, command, params): + async def execute(self, command, params): """Execute an OnOff command.""" domain = self.state.domain @@ -220,7 +219,7 @@ async def execute(self, hass, command, params): service_domain = domain service = SERVICE_TURN_ON if params['on'] else SERVICE_TURN_OFF - await hass.services.async_call(service_domain, service, { + await self.hass.services.async_call(service_domain, service, { ATTR_ENTITY_ID: self.state.entity_id }, blocking=True) @@ -268,14 +267,14 @@ def can_execute(self, command, params): return (command in self.commands and 'spectrumRGB' in params.get('color', {})) - async def execute(self, hass, command, params): + async def execute(self, command, params): """Execute a color spectrum command.""" # Convert integer to hex format and left pad with 0's till length 6 hex_value = "{0:06x}".format(params['color']['spectrumRGB']) color = color_util.color_RGB_to_hs( *color_util.rgb_hex_to_rgb_list(hex_value)) - await hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, { + await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: self.state.entity_id, light.ATTR_HS_COLOR: color }, blocking=True) @@ -331,7 +330,7 @@ def can_execute(self, command, params): return (command in self.commands and 'temperature' in params.get('color', {})) - async def execute(self, hass, command, params): + async def execute(self, command, params): """Execute a color temperature command.""" temp = color_util.color_temperature_kelvin_to_mired( params['color']['temperature']) @@ -344,7 +343,7 @@ async def execute(self, hass, command, params): "Temperature should be between {} and {}".format(min_temp, max_temp)) - await hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, { + await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: self.state.entity_id, light.ATTR_COLOR_TEMP: temp, }, blocking=True) @@ -376,12 +375,13 @@ def query_attributes(self): """Return scene query attributes.""" return {} - async def execute(self, hass, command, params): + async def execute(self, command, params): """Execute a scene command.""" # Don't block for scripts as they can be slow. - await hass.services.async_call(self.state.domain, SERVICE_TURN_ON, { - ATTR_ENTITY_ID: self.state.entity_id - }, blocking=self.state.domain != script.DOMAIN) + await self.hass.services.async_call( + self.state.domain, SERVICE_TURN_ON, { + ATTR_ENTITY_ID: self.state.entity_id + }, blocking=self.state.domain != script.DOMAIN) @register_trait @@ -425,7 +425,8 @@ def sync_attributes(self): return { 'availableThermostatModes': ','.join(modes), - 'thermostatTemperatureUnit': _google_temp_unit(self.state), + 'thermostatTemperatureUnit': _google_temp_unit( + self.hass.config.units.temperature_unit) } def query_attributes(self): @@ -437,7 +438,7 @@ def query_attributes(self): if operation is not None and operation in self.hass_to_google: response['thermostatMode'] = self.hass_to_google[operation] - unit = self.state.attributes[ATTR_UNIT_OF_MEASUREMENT] + unit = self.hass.config.units.temperature_unit current_temp = attrs.get(climate.ATTR_CURRENT_TEMPERATURE) if current_temp is not None: @@ -465,10 +466,10 @@ def query_attributes(self): return response - async def execute(self, hass, command, params): + async def execute(self, command, params): """Execute a temperature point or mode command.""" # All sent in temperatures are always in Celsius - unit = self.state.attributes[ATTR_UNIT_OF_MEASUREMENT] + unit = self.hass.config.units.temperature_unit min_temp = self.state.attributes[climate.ATTR_MIN_TEMP] max_temp = self.state.attributes[climate.ATTR_MAX_TEMP] @@ -482,7 +483,7 @@ async def execute(self, hass, command, params): "Temperature should be between {} and {}".format(min_temp, max_temp)) - await hass.services.async_call( + await self.hass.services.async_call( climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE, { ATTR_ENTITY_ID: self.state.entity_id, climate.ATTR_TEMPERATURE: temp @@ -508,7 +509,7 @@ async def execute(self, hass, command, params): "Lower bound for temperature range should be between " "{} and {}".format(min_temp, max_temp)) - await hass.services.async_call( + await self.hass.services.async_call( climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE, { ATTR_ENTITY_ID: self.state.entity_id, climate.ATTR_TARGET_TEMP_HIGH: temp_high, @@ -516,7 +517,7 @@ async def execute(self, hass, command, params): }, blocking=True) elif command == COMMAND_THERMOSTAT_SET_MODE: - await hass.services.async_call( + await self.hass.services.async_call( climate.DOMAIN, climate.SERVICE_SET_OPERATION_MODE, { ATTR_ENTITY_ID: self.state.entity_id, climate.ATTR_OPERATION_MODE: diff --git a/homeassistant/components/group/__init__.py b/homeassistant/components/group/__init__.py index a33e91f3aa959e..eda65d1895dbfc 100644 --- a/homeassistant/components/group/__init__.py +++ b/homeassistant/components/group/__init__.py @@ -550,12 +550,12 @@ async def async_update(self): self._async_update_group_state() async def async_added_to_hass(self): - """Callback when added to HASS.""" + """Handle addition to HASS.""" if self.tracking: self.async_start() async def async_will_remove_from_hass(self): - """Callback when removed from HASS.""" + """Handle removal from HASS.""" if self._async_unsub_state_changed: self._async_unsub_state_changed() self._async_unsub_state_changed = None diff --git a/homeassistant/components/hangouts/.translations/ca.json b/homeassistant/components/hangouts/.translations/ca.json new file mode 100644 index 00000000000000..ca15e59ec194e3 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/ca.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts ja est\u00e0 configurat", + "unknown": "S'ha produ\u00eft un error desconegut." + }, + "error": { + "invalid_2fa": "La verificaci\u00f3 en dos passos no \u00e9s v\u00e0lida, torna-ho a provar.", + "invalid_2fa_method": "El m\u00e8tode de verificaci\u00f3 en dos passos no \u00e9s v\u00e0lid (verifica-ho al m\u00f2bil).", + "invalid_login": "L'inici de sessi\u00f3 no \u00e9s v\u00e0lid, torna-ho a provar." + }, + "step": { + "2fa": { + "data": { + "2fa": "Pin 2FA" + }, + "title": "Verificaci\u00f3 en dos passos" + }, + "user": { + "data": { + "email": "Correu electr\u00f2nic", + "password": "Contrasenya" + }, + "title": "Inici de sessi\u00f3 de Google Hangouts" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/de.json b/homeassistant/components/hangouts/.translations/de.json new file mode 100644 index 00000000000000..4222e7f55569a0 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/de.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts ist bereits konfiguriert", + "unknown": "Ein unbekannter Fehler ist aufgetreten." + }, + "error": { + "invalid_2fa_method": "Ung\u00fcltige 2FA Methode (mit Telefon verifizieren)", + "invalid_login": "Ung\u00fcltige Daten, bitte erneut versuchen." + }, + "step": { + "2fa": { + "data": { + "2fa": "2FA PIN" + }, + "title": "2-Faktor-Authentifizierung" + }, + "user": { + "data": { + "email": "E-Mail-Adresse", + "password": "Passwort" + }, + "title": "Google Hangouts Login" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/en.json b/homeassistant/components/hangouts/.translations/en.json new file mode 100644 index 00000000000000..6e70a1f431069c --- /dev/null +++ b/homeassistant/components/hangouts/.translations/en.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts is already configured", + "unknown": "Unknown error occurred." + }, + "error": { + "invalid_2fa": "Invalid 2 Factor Authorization, please try again.", + "invalid_2fa_method": "Invalid 2FA Method (Verify on Phone).", + "invalid_login": "Invalid Login, please try again." + }, + "step": { + "2fa": { + "data": { + "2fa": "2FA Pin" + }, + "title": "2-Factor-Authorization" + }, + "user": { + "data": { + "email": "E-Mail Address", + "password": "Password" + }, + "title": "Google Hangouts Login" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/it.json b/homeassistant/components/hangouts/.translations/it.json new file mode 100644 index 00000000000000..0c609b3430ac3b --- /dev/null +++ b/homeassistant/components/hangouts/.translations/it.json @@ -0,0 +1,5 @@ +{ + "config": { + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/ko.json b/homeassistant/components/hangouts/.translations/ko.json new file mode 100644 index 00000000000000..aabf977a8cc570 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/ko.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts \uc740 \uc774\ubbf8 \uc124\uc815\ub41c \uc0c1\ud0dc\uc785\ub2c8\ub2e4", + "unknown": "\uc54c \uc218\uc5c6\ub294 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_2fa": "2\ub2e8\uacc4 \uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574 \uc8fc\uc138\uc694.", + "invalid_2fa_method": "2\ub2e8\uacc4 \uc778\uc99d \ubc29\ubc95\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. (\uc804\ud654\uae30\uc5d0\uc11c \ud655\uc778)", + "invalid_login": "\uc798\ubabb\ub41c \ub85c\uadf8\uc778\uc785\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694." + }, + "step": { + "2fa": { + "data": { + "2fa": "2\ub2e8\uacc4 \uc778\uc99d PIN" + }, + "description": "\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \uad00\ub828 \ub0b4\uc6a9\uc774 \uc544\uc9c1 \uc5c5\ub370\uc774\ud2b8 \ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ucd94\ud6c4\uc5d0 \ubc18\uc601\ub420 \uc608\uc815\uc774\ub2c8 \uc870\uae08\ub9cc \uae30\ub2e4\ub824\uc8fc\uc138\uc694.", + "title": "2\ub2e8\uacc4 \uc778\uc99d" + }, + "user": { + "data": { + "email": "\uc774\uba54\uc77c \uc8fc\uc18c", + "password": "\ube44\ubc00\ubc88\ud638" + }, + "description": "\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \uad00\ub828 \ub0b4\uc6a9\uc774 \uc544\uc9c1 \uc5c5\ub370\uc774\ud2b8 \ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ucd94\ud6c4\uc5d0 \ubc18\uc601\ub420 \uc608\uc815\uc774\ub2c8 \uc870\uae08\ub9cc \uae30\ub2e4\ub824\uc8fc\uc138\uc694.", + "title": "Google Hangouts \ub85c\uadf8\uc778" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/lb.json b/homeassistant/components/hangouts/.translations/lb.json new file mode 100644 index 00000000000000..426ab689626196 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/lb.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts ass scho konfigur\u00e9iert", + "unknown": "Onbekannten Fehler opgetrueden" + }, + "error": { + "invalid_2fa": "Ong\u00eblteg 2-Faktor Authentifikatioun, prob\u00e9iert w.e.g. nach emol.", + "invalid_2fa_method": "Ong\u00eblteg 2FA Methode (Iwwerpr\u00e9ift et um Telefon)", + "invalid_login": "Ong\u00ebltege Login, prob\u00e9iert w.e.g. nach emol." + }, + "step": { + "2fa": { + "data": { + "2fa": "2FA Pin" + }, + "description": "Eidel", + "title": "2-Faktor-Authentifikatioun" + }, + "user": { + "data": { + "email": "E-Mail Adress", + "password": "Passwuert" + }, + "description": "Eidel", + "title": "Google Hangouts Login" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/no.json b/homeassistant/components/hangouts/.translations/no.json new file mode 100644 index 00000000000000..7ea074470c76d3 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/no.json @@ -0,0 +1,13 @@ +{ + "config": { + "step": { + "user": { + "data": { + "email": "E-postadresse", + "password": "Passord" + } + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/pl.json b/homeassistant/components/hangouts/.translations/pl.json new file mode 100644 index 00000000000000..a8314761f8d39b --- /dev/null +++ b/homeassistant/components/hangouts/.translations/pl.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts jest ju\u017c skonfigurowany", + "unknown": "Wyst\u0105pi\u0142 nieznany b\u0142\u0105d." + }, + "error": { + "invalid_2fa": "Nieprawid\u0142owe uwierzytelnienie dwusk\u0142adnikowe, spr\u00f3buj ponownie.", + "invalid_2fa_method": "Nieprawid\u0142owa metoda uwierzytelniania dwusk\u0142adnikowego (u\u017cyj weryfikacji przez telefon).", + "invalid_login": "Nieprawid\u0142owy login, spr\u00f3buj ponownie." + }, + "step": { + "2fa": { + "data": { + "2fa": "PIN" + }, + "title": "Uwierzytelnianie dwusk\u0142adnikowe" + }, + "user": { + "data": { + "email": "Adres e-mail", + "password": "Has\u0142o" + }, + "title": "Logowanie do Google Hangouts" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/pt-BR.json b/homeassistant/components/hangouts/.translations/pt-BR.json new file mode 100644 index 00000000000000..4dffe492c4d84c --- /dev/null +++ b/homeassistant/components/hangouts/.translations/pt-BR.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Hangouts do Google j\u00e1 est\u00e1 configurado.", + "unknown": "Ocorreu um erro desconhecido." + }, + "error": { + "invalid_2fa_method": "M\u00e9todo 2FA inv\u00e1lido (verificar no telefone).", + "invalid_login": "Login inv\u00e1lido, por favor, tente novamente." + }, + "step": { + "2fa": { + "data": { + "2fa": "Pin 2FA" + }, + "title": "" + }, + "user": { + "data": { + "email": "Endere\u00e7o de e-mail", + "password": "Senha" + }, + "title": "Login do Hangouts do Google" + } + }, + "title": "Hangouts do Google" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/ru.json b/homeassistant/components/hangouts/.translations/ru.json new file mode 100644 index 00000000000000..c3363215201f02 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/ru.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d", + "unknown": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430" + }, + "error": { + "invalid_2fa": "\u041d\u0435\u0432\u0435\u0440\u043d\u0430\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430.", + "invalid_2fa_method": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (\u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0435).", + "invalid_login": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043b\u043e\u0433\u0438\u043d, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430." + }, + "step": { + "2fa": { + "data": { + "2fa": "\u041f\u0438\u043d-\u043a\u043e\u0434 \u0434\u043b\u044f \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" + }, + "title": "\u0414\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + }, + "user": { + "data": { + "email": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + }, + "title": "\u0412\u0445\u043e\u0434 \u0432 Google Hangouts" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/sl.json b/homeassistant/components/hangouts/.translations/sl.json new file mode 100644 index 00000000000000..d7555335820b46 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/sl.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts je \u017ee konfiguriran", + "unknown": "Pri\u0161lo je do neznane napake" + }, + "error": { + "invalid_2fa": "Neveljavna 2FA avtorizacija, prosimo, poskusite znova.", + "invalid_2fa_method": "Neveljavna 2FA metoda (preveri na telefonu).", + "invalid_login": "Neveljavna Prijava, prosimo, poskusite znova." + }, + "step": { + "2fa": { + "data": { + "2fa": "2FA Pin" + }, + "description": "prazno", + "title": "2-faktorska avtorizacija" + }, + "user": { + "data": { + "email": "E-po\u0161tni naslov", + "password": "Geslo" + }, + "description": "prazno", + "title": "Prijava za Google Hangouts" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/zh-Hans.json b/homeassistant/components/hangouts/.translations/zh-Hans.json new file mode 100644 index 00000000000000..bee6bf753dbb5b --- /dev/null +++ b/homeassistant/components/hangouts/.translations/zh-Hans.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts \u5df2\u914d\u7f6e\u5b8c\u6210", + "unknown": "\u53d1\u751f\u672a\u77e5\u9519\u8bef\u3002" + }, + "error": { + "invalid_2fa": "\u53cc\u91cd\u8ba4\u8bc1\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5\u3002", + "invalid_2fa_method": "\u65e0\u6548\u7684\u53cc\u91cd\u8ba4\u8bc1\u65b9\u6cd5\uff08\u7535\u8bdd\u9a8c\u8bc1\uff09\u3002", + "invalid_login": "\u767b\u9646\u5931\u8d25\uff0c\u8bf7\u518d\u8bd5\u4e00\u6b21\u3002" + }, + "step": { + "2fa": { + "data": { + "2fa": "2FA Pin" + }, + "title": "\u53cc\u91cd\u8ba4\u8bc1" + }, + "user": { + "data": { + "email": "\u7535\u5b50\u90ae\u4ef6\u5730\u5740", + "password": "\u5bc6\u7801" + }, + "title": "\u767b\u5f55 Google Hangouts" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/zh-Hant.json b/homeassistant/components/hangouts/.translations/zh-Hant.json new file mode 100644 index 00000000000000..16234acb193e62 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/zh-Hant.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts \u5df2\u7d93\u8a2d\u5b9a", + "unknown": "\u767c\u751f\u672a\u77e5\u932f\u8aa4\u3002" + }, + "error": { + "invalid_2fa": "\u5169\u6b65\u9a5f\u9a57\u8b49\u7121\u6548\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002", + "invalid_2fa_method": "\u8a8d\u8b49\u65b9\u5f0f\u7121\u6548\uff08\u65bc\u96fb\u8a71\u4e0a\u9a57\u8b49\uff09\u3002", + "invalid_login": "\u767b\u5165\u5931\u6557\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002" + }, + "step": { + "2fa": { + "data": { + "2fa": "\u8a8d\u8b49\u78bc" + }, + "description": "\u7a7a\u767d", + "title": "\u5169\u6b65\u9a5f\u9a57\u8b49" + }, + "user": { + "data": { + "email": "\u96fb\u5b50\u90f5\u4ef6", + "password": "\u5bc6\u78bc" + }, + "description": "\u7a7a\u767d", + "title": "\u767b\u5165 Google Hangouts" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/__init__.py b/homeassistant/components/hangouts/__init__.py new file mode 100644 index 00000000000000..ebadff57be3b14 --- /dev/null +++ b/homeassistant/components/hangouts/__init__.py @@ -0,0 +1,126 @@ +""" +The hangouts bot component. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/hangouts/ +""" +import logging + +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.const import EVENT_HOMEASSISTANT_STOP +from homeassistant.helpers import dispatcher +import homeassistant.helpers.config_validation as cv + +from .const import ( + CONF_BOT, CONF_INTENTS, CONF_REFRESH_TOKEN, DOMAIN, + EVENT_HANGOUTS_CONNECTED, EVENT_HANGOUTS_CONVERSATIONS_CHANGED, + MESSAGE_SCHEMA, SERVICE_SEND_MESSAGE, + SERVICE_UPDATE, CONF_SENTENCES, CONF_MATCHERS, + CONF_ERROR_SUPPRESSED_CONVERSATIONS, INTENT_SCHEMA, TARGETS_SCHEMA) + +# We need an import from .config_flow, without it .config_flow is never loaded. +from .config_flow import HangoutsFlowHandler # noqa: F401 + + +REQUIREMENTS = ['hangups==0.4.5'] + +_LOGGER = logging.getLogger(__name__) + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Optional(CONF_INTENTS, default={}): vol.Schema({ + cv.string: INTENT_SCHEMA + }), + vol.Optional(CONF_ERROR_SUPPRESSED_CONVERSATIONS, default=[]): + [TARGETS_SCHEMA] + }) +}, extra=vol.ALLOW_EXTRA) + + +async def async_setup(hass, config): + """Set up the Hangouts bot component.""" + from homeassistant.components.conversation import create_matcher + + config = config.get(DOMAIN) + if config is None: + hass.data[DOMAIN] = { + CONF_INTENTS: {}, + CONF_ERROR_SUPPRESSED_CONVERSATIONS: [], + } + return True + + hass.data[DOMAIN] = { + CONF_INTENTS: config[CONF_INTENTS], + CONF_ERROR_SUPPRESSED_CONVERSATIONS: + config[CONF_ERROR_SUPPRESSED_CONVERSATIONS], + } + + for data in hass.data[DOMAIN][CONF_INTENTS].values(): + matchers = [] + for sentence in data[CONF_SENTENCES]: + matchers.append(create_matcher(sentence)) + + data[CONF_MATCHERS] = matchers + + hass.async_create_task(hass.config_entries.flow.async_init( + DOMAIN, context={'source': config_entries.SOURCE_IMPORT} + )) + + return True + + +async def async_setup_entry(hass, config): + """Set up a config entry.""" + from hangups.auth import GoogleAuthError + + try: + from .hangouts_bot import HangoutsBot + + bot = HangoutsBot( + hass, + config.data.get(CONF_REFRESH_TOKEN), + hass.data[DOMAIN][CONF_INTENTS], + hass.data[DOMAIN][CONF_ERROR_SUPPRESSED_CONVERSATIONS]) + hass.data[DOMAIN][CONF_BOT] = bot + except GoogleAuthError as exception: + _LOGGER.error("Hangouts failed to log in: %s", str(exception)) + return False + + dispatcher.async_dispatcher_connect( + hass, + EVENT_HANGOUTS_CONNECTED, + bot.async_handle_update_users_and_conversations) + + dispatcher.async_dispatcher_connect( + hass, + EVENT_HANGOUTS_CONVERSATIONS_CHANGED, + bot.async_update_conversation_commands) + dispatcher.async_dispatcher_connect( + hass, + EVENT_HANGOUTS_CONVERSATIONS_CHANGED, + bot.async_handle_update_error_suppressed_conversations) + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, + bot.async_handle_hass_stop) + + await bot.async_connect() + + hass.services.async_register(DOMAIN, SERVICE_SEND_MESSAGE, + bot.async_handle_send_message, + schema=MESSAGE_SCHEMA) + hass.services.async_register(DOMAIN, + SERVICE_UPDATE, + bot. + async_handle_update_users_and_conversations, + schema=vol.Schema({})) + + return True + + +async def async_unload_entry(hass, _): + """Unload a config entry.""" + bot = hass.data[DOMAIN].pop(CONF_BOT) + await bot.async_disconnect() + return True diff --git a/homeassistant/components/hangouts/config_flow.py b/homeassistant/components/hangouts/config_flow.py new file mode 100644 index 00000000000000..74eb14b050da34 --- /dev/null +++ b/homeassistant/components/hangouts/config_flow.py @@ -0,0 +1,107 @@ +"""Config flow to configure Google Hangouts.""" +import voluptuous as vol + +from homeassistant import config_entries, data_entry_flow +from homeassistant.const import CONF_EMAIL, CONF_PASSWORD +from homeassistant.core import callback + +from .const import CONF_2FA, CONF_REFRESH_TOKEN +from .const import DOMAIN as HANGOUTS_DOMAIN + + +@callback +def configured_hangouts(hass): + """Return the configures Google Hangouts Account.""" + entries = hass.config_entries.async_entries(HANGOUTS_DOMAIN) + if entries: + return entries[0] + return None + + +@config_entries.HANDLERS.register(HANGOUTS_DOMAIN) +class HangoutsFlowHandler(data_entry_flow.FlowHandler): + """Config flow Google Hangouts.""" + + VERSION = 1 + + def __init__(self): + """Initialize Google Hangouts config flow.""" + self._credentials = None + self._refresh_token = None + + async def async_step_user(self, user_input=None): + """Handle a flow start.""" + errors = {} + + if configured_hangouts(self.hass) is not None: + return self.async_abort(reason="already_configured") + + if user_input is not None: + from hangups import get_auth + from .hangups_utils import (HangoutsCredentials, + HangoutsRefreshToken, + GoogleAuthError, Google2FAError) + self._credentials = HangoutsCredentials(user_input[CONF_EMAIL], + user_input[CONF_PASSWORD]) + self._refresh_token = HangoutsRefreshToken(None) + try: + await self.hass.async_add_executor_job(get_auth, + self._credentials, + self._refresh_token) + + return await self.async_step_final() + except GoogleAuthError as err: + if isinstance(err, Google2FAError): + return await self.async_step_2fa() + msg = str(err) + if msg == 'Unknown verification code input': + errors['base'] = 'invalid_2fa_method' + else: + errors['base'] = 'invalid_login' + + return self.async_show_form( + step_id='user', + data_schema=vol.Schema({ + vol.Required(CONF_EMAIL): str, + vol.Required(CONF_PASSWORD): str + }), + errors=errors + ) + + async def async_step_2fa(self, user_input=None): + """Handle the 2fa step, if needed.""" + errors = {} + + if user_input is not None: + from hangups import get_auth + from .hangups_utils import GoogleAuthError + self._credentials.set_verification_code(user_input[CONF_2FA]) + try: + await self.hass.async_add_executor_job(get_auth, + self._credentials, + self._refresh_token) + + return await self.async_step_final() + except GoogleAuthError: + errors['base'] = 'invalid_2fa' + + return self.async_show_form( + step_id=CONF_2FA, + data_schema=vol.Schema({ + vol.Required(CONF_2FA): str, + }), + errors=errors + ) + + async def async_step_final(self): + """Handle the final step, create the config entry.""" + return self.async_create_entry( + title=self._credentials.get_email(), + data={ + CONF_EMAIL: self._credentials.get_email(), + CONF_REFRESH_TOKEN: self._refresh_token.get() + }) + + async def async_step_import(self, _): + """Handle a flow import.""" + return await self.async_step_user() diff --git a/homeassistant/components/hangouts/const.py b/homeassistant/components/hangouts/const.py new file mode 100644 index 00000000000000..3b96edf93a29a6 --- /dev/null +++ b/homeassistant/components/hangouts/const.py @@ -0,0 +1,68 @@ +"""Constants for Google Hangouts Component.""" +import logging + +import voluptuous as vol + +from homeassistant.components.notify import ATTR_MESSAGE, ATTR_TARGET +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger('homeassistant.components.hangouts') + + +DOMAIN = 'hangouts' + +CONF_2FA = '2fa' +CONF_REFRESH_TOKEN = 'refresh_token' +CONF_BOT = 'bot' + +CONF_CONVERSATIONS = 'conversations' +CONF_DEFAULT_CONVERSATIONS = 'default_conversations' +CONF_ERROR_SUPPRESSED_CONVERSATIONS = 'error_suppressed_conversations' + +CONF_INTENTS = 'intents' +CONF_INTENT_TYPE = 'intent_type' +CONF_SENTENCES = 'sentences' +CONF_MATCHERS = 'matchers' + +EVENT_HANGOUTS_CONNECTED = 'hangouts_connected' +EVENT_HANGOUTS_DISCONNECTED = 'hangouts_disconnected' +EVENT_HANGOUTS_USERS_CHANGED = 'hangouts_users_changed' +EVENT_HANGOUTS_CONVERSATIONS_CHANGED = 'hangouts_conversations_changed' +EVENT_HANGOUTS_MESSAGE_RECEIVED = 'hangouts_message_received' + +CONF_CONVERSATION_ID = 'id' +CONF_CONVERSATION_NAME = 'name' + +SERVICE_SEND_MESSAGE = 'send_message' +SERVICE_UPDATE = 'update' + + +TARGETS_SCHEMA = vol.All( + vol.Schema({ + vol.Exclusive(CONF_CONVERSATION_ID, 'id or name'): cv.string, + vol.Exclusive(CONF_CONVERSATION_NAME, 'id or name'): cv.string + }), + cv.has_at_least_one_key(CONF_CONVERSATION_ID, CONF_CONVERSATION_NAME) +) +MESSAGE_SEGMENT_SCHEMA = vol.Schema({ + vol.Required('text'): cv.string, + vol.Optional('is_bold'): cv.boolean, + vol.Optional('is_italic'): cv.boolean, + vol.Optional('is_strikethrough'): cv.boolean, + vol.Optional('is_underline'): cv.boolean, + vol.Optional('parse_str'): cv.boolean, + vol.Optional('link_target'): cv.string +}) + +MESSAGE_SCHEMA = vol.Schema({ + vol.Required(ATTR_TARGET): [TARGETS_SCHEMA], + vol.Required(ATTR_MESSAGE): [MESSAGE_SEGMENT_SCHEMA] +}) + +INTENT_SCHEMA = vol.All( + # Basic Schema + vol.Schema({ + vol.Required(CONF_SENTENCES): vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_CONVERSATIONS): [TARGETS_SCHEMA] + }), +) diff --git a/homeassistant/components/hangouts/hangouts_bot.py b/homeassistant/components/hangouts/hangouts_bot.py new file mode 100644 index 00000000000000..15f4156d374486 --- /dev/null +++ b/homeassistant/components/hangouts/hangouts_bot.py @@ -0,0 +1,249 @@ +"""The Hangouts Bot.""" +import logging + +from homeassistant.helpers import dispatcher, intent + +from .const import ( + ATTR_MESSAGE, ATTR_TARGET, CONF_CONVERSATIONS, DOMAIN, + EVENT_HANGOUTS_CONNECTED, EVENT_HANGOUTS_CONVERSATIONS_CHANGED, + EVENT_HANGOUTS_DISCONNECTED, EVENT_HANGOUTS_MESSAGE_RECEIVED, + CONF_MATCHERS, CONF_CONVERSATION_ID, + CONF_CONVERSATION_NAME) + +_LOGGER = logging.getLogger(__name__) + + +class HangoutsBot: + """The Hangouts Bot.""" + + def __init__(self, hass, refresh_token, intents, error_suppressed_convs): + """Set up the client.""" + self.hass = hass + self._connected = False + + self._refresh_token = refresh_token + + self._intents = intents + self._conversation_intents = None + + self._client = None + self._user_list = None + self._conversation_list = None + self._error_suppressed_convs = error_suppressed_convs + self._error_suppressed_conv_ids = None + + dispatcher.async_dispatcher_connect( + self.hass, EVENT_HANGOUTS_MESSAGE_RECEIVED, + self._async_handle_conversation_message) + + def _resolve_conversation_id(self, obj): + if CONF_CONVERSATION_ID in obj: + return obj[CONF_CONVERSATION_ID] + if CONF_CONVERSATION_NAME in obj: + conv = self._resolve_conversation_name(obj[CONF_CONVERSATION_NAME]) + if conv is not None: + return conv.id_ + return None + + def _resolve_conversation_name(self, name): + for conv in self._conversation_list.get_all(): + if conv.name == name: + return conv + return None + + def async_update_conversation_commands(self, _): + """Refresh the commands for every conversation.""" + self._conversation_intents = {} + + for intent_type, data in self._intents.items(): + if data.get(CONF_CONVERSATIONS): + conversations = [] + for conversation in data.get(CONF_CONVERSATIONS): + conv_id = self._resolve_conversation_id(conversation) + if conv_id is not None: + conversations.append(conv_id) + data['_' + CONF_CONVERSATIONS] = conversations + else: + data['_' + CONF_CONVERSATIONS] = \ + [conv.id_ for conv in self._conversation_list.get_all()] + + for conv_id in data['_' + CONF_CONVERSATIONS]: + if conv_id not in self._conversation_intents: + self._conversation_intents[conv_id] = {} + + self._conversation_intents[conv_id][intent_type] = data + + try: + self._conversation_list.on_event.remove_observer( + self._async_handle_conversation_event) + except ValueError: + pass + self._conversation_list.on_event.add_observer( + self._async_handle_conversation_event) + + def async_handle_update_error_suppressed_conversations(self, _): + """Resolve the list of error suppressed conversations.""" + self._error_suppressed_conv_ids = [] + for conversation in self._error_suppressed_convs: + conv_id = self._resolve_conversation_id(conversation) + if conv_id is not None: + self._error_suppressed_conv_ids.append(conv_id) + + async def _async_handle_conversation_event(self, event): + from hangups import ChatMessageEvent + if isinstance(event, ChatMessageEvent): + dispatcher.async_dispatcher_send(self.hass, + EVENT_HANGOUTS_MESSAGE_RECEIVED, + event.conversation_id, + event.user_id, event) + + async def _async_handle_conversation_message(self, + conv_id, user_id, event): + """Handle a message sent to a conversation.""" + user = self._user_list.get_user(user_id) + if user.is_self: + return + message = event.text + + _LOGGER.debug("Handling message '%s' from %s", + message, user.full_name) + + intents = self._conversation_intents.get(conv_id) + if intents is not None: + is_error = False + try: + intent_result = await self._async_process(intents, message) + except (intent.UnknownIntent, intent.IntentHandleError) as err: + is_error = True + intent_result = intent.IntentResponse() + intent_result.async_set_speech(str(err)) + + if intent_result is None: + is_error = True + intent_result = intent.IntentResponse() + intent_result.async_set_speech( + "Sorry, I didn't understand that") + + message = intent_result.as_dict().get('speech', {})\ + .get('plain', {}).get('speech') + + if (message is not None) and not ( + is_error and conv_id in self._error_suppressed_conv_ids): + await self._async_send_message( + [{'text': message, 'parse_str': True}], + [{CONF_CONVERSATION_ID: conv_id}]) + + async def _async_process(self, intents, text): + """Detect a matching intent.""" + for intent_type, data in intents.items(): + for matcher in data.get(CONF_MATCHERS, []): + match = matcher.match(text) + + if not match: + continue + + response = await self.hass.helpers.intent.async_handle( + DOMAIN, intent_type, + {key: {'value': value} for key, value + in match.groupdict().items()}, text) + return response + + async def async_connect(self): + """Login to the Google Hangouts.""" + from .hangups_utils import HangoutsRefreshToken, HangoutsCredentials + + from hangups import Client + from hangups import get_auth + session = await self.hass.async_add_executor_job( + get_auth, HangoutsCredentials(None, None, None), + HangoutsRefreshToken(self._refresh_token)) + + self._client = Client(session) + self._client.on_connect.add_observer(self._on_connect) + self._client.on_disconnect.add_observer(self._on_disconnect) + + self.hass.loop.create_task(self._client.connect()) + + def _on_connect(self): + _LOGGER.debug('Connected!') + self._connected = True + dispatcher.async_dispatcher_send(self.hass, EVENT_HANGOUTS_CONNECTED) + + def _on_disconnect(self): + """Handle disconnecting.""" + _LOGGER.debug('Connection lost!') + self._connected = False + dispatcher.async_dispatcher_send(self.hass, + EVENT_HANGOUTS_DISCONNECTED) + + async def async_disconnect(self): + """Disconnect the client if it is connected.""" + if self._connected: + await self._client.disconnect() + + async def async_handle_hass_stop(self, _): + """Run once when Home Assistant stops.""" + await self.async_disconnect() + + async def _async_send_message(self, message, targets): + conversations = [] + for target in targets: + conversation = None + if CONF_CONVERSATION_ID in target: + conversation = self._conversation_list.get( + target[CONF_CONVERSATION_ID]) + elif CONF_CONVERSATION_NAME in target: + conversation = self._resolve_conversation_name( + target[CONF_CONVERSATION_NAME]) + if conversation is not None: + conversations.append(conversation) + + if not conversations: + return False + + from hangups import ChatMessageSegment, hangouts_pb2 + messages = [] + for segment in message: + if 'parse_str' in segment and segment['parse_str']: + messages.extend(ChatMessageSegment.from_str(segment['text'])) + else: + if 'parse_str' in segment: + del segment['parse_str'] + messages.append(ChatMessageSegment(**segment)) + messages.append(ChatMessageSegment('', + segment_type=hangouts_pb2. + SEGMENT_TYPE_LINE_BREAK)) + + if not messages: + return False + for conv in conversations: + await conv.send_message(messages) + + async def _async_list_conversations(self): + import hangups + self._user_list, self._conversation_list = \ + (await hangups.build_user_conversation_list(self._client)) + conversations = {} + for i, conv in enumerate(self._conversation_list.get_all()): + users_in_conversation = [] + for user in conv.users: + users_in_conversation.append(user.full_name) + conversations[str(i)] = {CONF_CONVERSATION_ID: str(conv.id_), + CONF_CONVERSATION_NAME: conv.name, + 'users': users_in_conversation} + + self.hass.states.async_set("{}.conversations".format(DOMAIN), + len(self._conversation_list.get_all()), + attributes=conversations) + dispatcher.async_dispatcher_send(self.hass, + EVENT_HANGOUTS_CONVERSATIONS_CHANGED, + conversations) + + async def async_handle_send_message(self, service): + """Handle the send_message service.""" + await self._async_send_message(service.data[ATTR_MESSAGE], + service.data[ATTR_TARGET]) + + async def async_handle_update_users_and_conversations(self, _=None): + """Handle the update_users_and_conversations service.""" + await self._async_list_conversations() diff --git a/homeassistant/components/hangouts/hangups_utils.py b/homeassistant/components/hangouts/hangups_utils.py new file mode 100644 index 00000000000000..9aff7730201ac9 --- /dev/null +++ b/homeassistant/components/hangouts/hangups_utils.py @@ -0,0 +1,81 @@ +"""Utils needed for Google Hangouts.""" + +from hangups import CredentialsPrompt, GoogleAuthError, RefreshTokenCache + + +class Google2FAError(GoogleAuthError): + """A Google authentication request failed.""" + + +class HangoutsCredentials(CredentialsPrompt): + """Google account credentials. + + This implementation gets the user data as params. + """ + + def __init__(self, email, password, pin=None): + """Google account credentials. + + :param email: Google account email address. + :param password: Google account password. + :param pin: Google account verification code. + """ + self._email = email + self._password = password + self._pin = pin + + def get_email(self): + """Return email. + + :return: Google account email address. + """ + return self._email + + def get_password(self): + """Return password. + + :return: Google account password. + """ + return self._password + + def get_verification_code(self): + """Return the verification code. + + :return: Google account verification code. + """ + if self._pin is None: + raise Google2FAError() + return self._pin + + def set_verification_code(self, pin): + """Set the verification code. + + :param pin: Google account verification code. + """ + self._pin = pin + + +class HangoutsRefreshToken(RefreshTokenCache): + """Memory-based cache for refresh token.""" + + def __init__(self, token): + """Memory-based cache for refresh token. + + :param token: Initial refresh token. + """ + super().__init__("") + self._token = token + + def get(self): + """Get cached refresh token. + + :return: Cached refresh token. + """ + return self._token + + def set(self, refresh_token): + """Cache a refresh token. + + :param refresh_token: Refresh token to cache. + """ + self._token = refresh_token diff --git a/homeassistant/components/hangouts/services.yaml b/homeassistant/components/hangouts/services.yaml new file mode 100644 index 00000000000000..5d314bc2479900 --- /dev/null +++ b/homeassistant/components/hangouts/services.yaml @@ -0,0 +1,12 @@ +update: + description: Updates the list of users and conversations. + +send_message: + description: Send a notification to a specific target. + fields: + target: + description: List of targets with id or name. [Required] + example: '[{"id": "UgxrXzVrARmjx_C6AZx4AaABAagBo-6UCw"}, {"name": "Test Conversation"}]' + message: + description: List of message segments, only the "text" field is required in every segment. [Required] + example: '[{"text":"test", "is_bold": false, "is_italic": false, "is_strikethrough": false, "is_underline": false, "parse_str": false, "link_target": "http://google.com"}, ...]' \ No newline at end of file diff --git a/homeassistant/components/hangouts/strings.json b/homeassistant/components/hangouts/strings.json new file mode 100644 index 00000000000000..7e54586b810085 --- /dev/null +++ b/homeassistant/components/hangouts/strings.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts is already configured", + "unknown": "Unknown error occurred." + }, + "error": { + "invalid_login": "Invalid Login, please try again.", + "invalid_2fa": "Invalid 2 Factor Authorization, please try again.", + "invalid_2fa_method": "Invalid 2FA Method (Verify on Phone)." + }, + "step": { + "user": { + "data": { + "email": "E-Mail Address", + "password": "Password" + }, + "description": "", + "title": "Google Hangouts Login" + }, + "2fa": { + "data": { + "2fa": "2FA Pin" + }, + "description": "", + "title": "2-Factor-Authorization" + } + }, + "title": "Google Hangouts" + } +} diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index ad2f8b4ac6d053..eac02855b0bc6c 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -57,7 +57,7 @@ async def async_setup(hass, config): - """Setup the HomeKit component.""" + """Set up the HomeKit component.""" _LOGGER.debug('Begin setup HomeKit') conf = config[DOMAIN] @@ -196,7 +196,7 @@ def __init__(self, hass, name, port, ip_address, entity_filter, self.driver = None def setup(self): - """Setup bridge and accessory driver.""" + """Set up bridge and accessory driver.""" from .accessories import HomeBridge, HomeDriver self.hass.bus.async_listen_once( diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index a7e895f49e2a2a..adf5273b639dae 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -27,17 +27,17 @@ def debounce(func): - """Decorator function. Debounce callbacks form HomeKit.""" + """Decorate function to debounce callbacks from HomeKit.""" @ha_callback def call_later_listener(self, *args): - """Callback listener called from call_later.""" + """Handle call_later callback.""" debounce_params = self.debounce.pop(func.__name__, None) if debounce_params: self.hass.async_add_job(func, self, *debounce_params[1:]) @wraps(func) def wrapper(self, *args): - """Wrapper starts async timer.""" + """Start async timer.""" debounce_params = self.debounce.pop(func.__name__, None) if debounce_params: debounce_params[0]() # remove listener @@ -88,7 +88,7 @@ def __init__(self, hass, driver, name, entity_id, aid, config, CHAR_STATUS_LOW_BATTERY, value=0) async def run(self): - """Method called by accessory after driver is started. + """Handle accessory driver started event. Run inside the HAP-python event loop. """ @@ -100,7 +100,7 @@ async def run(self): @ha_callback def update_state_callback(self, entity_id=None, old_state=None, new_state=None): - """Callback from state change listener.""" + """Handle state change listener callback.""" _LOGGER.debug('New_state: %s', new_state) if new_state is None: return @@ -131,7 +131,7 @@ def update_battery(self, new_state): hk_charging) def update_state(self, new_state): - """Method called on state change to update HomeKit value. + """Handle state change to update HomeKit value. Overridden by accessory types. """ diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index 23a907d43f738c..9d60530edd7617 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -109,7 +109,7 @@ def show_setup_message(hass, pincode): """Display persistent notification with setup information.""" pin = pincode.decode() _LOGGER.info('Pincode: %s', pin) - message = 'To setup Home Assistant in the Home App, enter the ' \ + message = 'To set up Home Assistant in the Home App, enter the ' \ 'following code:\n### {}'.format(pin) hass.components.persistent_notification.create( message, 'HomeKit Setup', HOMEKIT_NOTIFY_ID) diff --git a/homeassistant/components/homematic/__init__.py b/homeassistant/components/homematic/__init__.py index f737e2ad7d2371..53c8e26701615e 100644 --- a/homeassistant/components/homematic/__init__.py +++ b/homeassistant/components/homematic/__init__.py @@ -20,7 +20,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.loader import bind_hass -REQUIREMENTS = ['pyhomematic==0.1.46'] +REQUIREMENTS = ['pyhomematic==0.1.47'] _LOGGER = logging.getLogger(__name__) @@ -47,6 +47,9 @@ ATTR_MESSAGE = 'message' ATTR_MODE = 'mode' ATTR_TIME = 'time' +ATTR_UNIQUE_ID = 'unique_id' +ATTR_PARAMSET_KEY = 'paramset_key' +ATTR_PARAMSET = 'paramset' EVENT_KEYPRESS = 'homematic.keypress' EVENT_IMPULSE = 'homematic.impulse' @@ -57,6 +60,7 @@ SERVICE_SET_VARIABLE_VALUE = 'set_variable_value' SERVICE_SET_DEVICE_VALUE = 'set_device_value' SERVICE_SET_INSTALL_MODE = 'set_install_mode' +SERVICE_PUT_PARAMSET = 'put_paramset' HM_DEVICE_TYPES = { DISCOVER_SWITCHES: [ @@ -77,7 +81,7 @@ DISCOVER_CLIMATE: [ 'Thermostat', 'ThermostatWall', 'MAXThermostat', 'ThermostatWall2', 'MAXWallThermostat', 'IPThermostat', 'IPThermostatWall', - 'ThermostatGroup'], + 'ThermostatGroup', 'IPThermostatWall230V'], DISCOVER_BINARY_SENSORS: [ 'ShutterContact', 'Smoke', 'SmokeV2', 'Motion', 'MotionV2', 'MotionIP', 'RemoteMotion', 'WeatherSensor', 'TiltSensor', @@ -102,7 +106,7 @@ 'LOW_BAT': ['battery', {0: 'High', 1: 'Low'}], 'ERROR': ['sabotage', {0: 'No', 1: 'Yes'}], 'SABOTAGE': ['sabotage', {0: 'No', 1: 'Yes'}], - 'RSSI_DEVICE': ['rssi', {}], + 'RSSI_PEER': ['rssi', {}], 'VALVE_STATE': ['valve', {}], 'BATTERY_STATE': ['battery', {}], 'CONTROL_MODE': ['mode', { @@ -173,6 +177,7 @@ vol.Required(ATTR_INTERFACE): cv.string, vol.Optional(ATTR_CHANNEL, default=1): vol.Coerce(int), vol.Optional(ATTR_PARAM): cv.string, + vol.Optional(ATTR_UNIQUE_ID): cv.string, }) CONFIG_SCHEMA = vol.Schema({ @@ -230,6 +235,13 @@ vol.Optional(ATTR_ADDRESS): vol.All(cv.string, vol.Upper), }) +SCHEMA_SERVICE_PUT_PARAMSET = vol.Schema({ + vol.Required(ATTR_INTERFACE): cv.string, + vol.Required(ATTR_ADDRESS): vol.All(cv.string, vol.Upper), + vol.Required(ATTR_PARAMSET_KEY): vol.All(cv.string, vol.Upper), + vol.Required(ATTR_PARAMSET): dict, +}) + @bind_hass def virtualkey(hass, address, channel, param, interface=None): @@ -269,6 +281,19 @@ def set_device_value(hass, address, channel, param, value, interface=None): hass.services.call(DOMAIN, SERVICE_SET_DEVICE_VALUE, data) +@bind_hass +def put_paramset(hass, interface, address, paramset_key, paramset): + """Call putParamset XML-RPC method of supplied interface.""" + data = { + ATTR_INTERFACE: interface, + ATTR_ADDRESS: address, + ATTR_PARAMSET_KEY: paramset_key, + ATTR_PARAMSET: paramset, + } + + hass.services.call(DOMAIN, SERVICE_PUT_PARAMSET, data) + + @bind_hass def set_install_mode(hass, interface, mode=None, time=None, address=None): """Call setInstallMode XML-RPC method of supplied interface.""" @@ -437,6 +462,26 @@ def _service_handle_install_mode(service): DOMAIN, SERVICE_SET_INSTALL_MODE, _service_handle_install_mode, schema=SCHEMA_SERVICE_SET_INSTALL_MODE) + def _service_put_paramset(service): + """Service to call the putParamset method on a HomeMatic connection.""" + interface = service.data.get(ATTR_INTERFACE) + address = service.data.get(ATTR_ADDRESS) + paramset_key = service.data.get(ATTR_PARAMSET_KEY) + # When passing in the paramset from a YAML file we get an OrderedDict + # here instead of a dict, so add this explicit cast. + # The service schema makes sure that this cast works. + paramset = dict(service.data.get(ATTR_PARAMSET)) + + _LOGGER.debug( + "Calling putParamset: %s, %s, %s, %s", + interface, address, paramset_key, paramset + ) + homematic.putParamset(interface, address, paramset_key, paramset) + + hass.services.register( + DOMAIN, SERVICE_PUT_PARAMSET, _service_put_paramset, + schema=SCHEMA_SERVICE_PUT_PARAMSET) + return True @@ -530,16 +575,21 @@ def _get_devices(hass, discovery_type, keys, interface): _LOGGER.debug("%s: Handling %s: %s: %s", discovery_type, key, param, channels) for channel in channels: - name = _create_ha_name( + name = _create_ha_id( name=device.NAME, channel=channel, param=param, count=len(channels) ) + unique_id = _create_ha_id( + name=key, channel=channel, param=param, + count=len(channels) + ) device_dict = { CONF_PLATFORM: "homematic", ATTR_ADDRESS: key, ATTR_INTERFACE: interface, ATTR_NAME: name, - ATTR_CHANNEL: channel + ATTR_CHANNEL: channel, + ATTR_UNIQUE_ID: unique_id } if param is not None: device_dict[ATTR_PARAM] = param @@ -554,7 +604,7 @@ def _get_devices(hass, discovery_type, keys, interface): return device_arr -def _create_ha_name(name, channel, param, count): +def _create_ha_id(name, channel, param, count): """Generate a unique entity id.""" # HMDevice is a simple device if count == 1 and param is None: @@ -725,6 +775,7 @@ def __init__(self, config): self._interface = config.get(ATTR_INTERFACE) self._channel = config.get(ATTR_CHANNEL) self._state = config.get(ATTR_PARAM) + self._unique_id = config.get(ATTR_UNIQUE_ID) self._data = {} self._homematic = None self._hmdevice = None @@ -740,6 +791,11 @@ def async_added_to_hass(self): """Load data init callbacks.""" yield from self.hass.async_add_job(self.link_homematic) + @property + def unique_id(self): + """Return unique ID. HomeMatic entity IDs are unique by default.""" + return self._unique_id.replace(" ", "_") + @property def should_poll(self): """Return false. HomeMatic states are pushed by the XML-RPC Server.""" @@ -813,7 +869,7 @@ def _hm_event_callback(self, device, caller, attribute, value): # Availability has changed if attribute == 'UNREACH': - self._available = bool(value) + self._available = not bool(value) has_changed = True elif not self.available: self._available = False diff --git a/homeassistant/components/homematic/services.yaml b/homeassistant/components/homematic/services.yaml index c2946b51842c2d..044bcfa46adc73 100644 --- a/homeassistant/components/homematic/services.yaml +++ b/homeassistant/components/homematic/services.yaml @@ -66,3 +66,20 @@ set_install_mode: address: description: (Optional) Address of homematic device or BidCoS-RF to learn example: LEQ3948571 + +put_paramset: + description: Call to putParamset in the RPC XML interface + fields: + interface: + description: The interfaces name from the config + example: wireless + address: + description: Address of Homematic device + example: LEQ3948571 + paramset_key: + description: The paramset_key argument to putParamset + example: MASTER + paramset: + description: A paramset dictionary + example: '{"WEEK_PROGRAM_POINTER": 1}' + diff --git a/homeassistant/components/homematicip_cloud/.translations/ca.json b/homeassistant/components/homematicip_cloud/.translations/ca.json index 9d40bc2d24170d..aab974ba13783b 100644 --- a/homeassistant/components/homematicip_cloud/.translations/ca.json +++ b/homeassistant/components/homematicip_cloud/.translations/ca.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "El punt d'acc\u00e9s ja est\u00e0 configurat", "conection_aborted": "No s'ha pogut connectar al servidor HMIP", + "connection_aborted": "No s'ha pogut connectar al servidor HMIP", "unknown": "S'ha produ\u00eft un error desconegut." }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/cs.json b/homeassistant/components/homematicip_cloud/.translations/cs.json index 59f232edea486a..4030450e51c9fe 100644 --- a/homeassistant/components/homematicip_cloud/.translations/cs.json +++ b/homeassistant/components/homematicip_cloud/.translations/cs.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "P\u0159\u00edstupov\u00fd bod je ji\u017e nakonfigurov\u00e1n", "conection_aborted": "Nelze se p\u0159ipojit k serveru HMIP", + "connection_aborted": "Nelze se p\u0159ipojit k HMIP serveru", "unknown": "Do\u0161lo k nezn\u00e1m\u00e9 chyb\u011b" }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/da.json b/homeassistant/components/homematicip_cloud/.translations/da.json new file mode 100644 index 00000000000000..b617130945a35c --- /dev/null +++ b/homeassistant/components/homematicip_cloud/.translations/da.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "invalid_pin": "Ugyldig PIN, pr\u00f8v igen." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/de.json b/homeassistant/components/homematicip_cloud/.translations/de.json index 61a9bd6eb404bc..fdccac0d2294a6 100644 --- a/homeassistant/components/homematicip_cloud/.translations/de.json +++ b/homeassistant/components/homematicip_cloud/.translations/de.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Der Accesspoint ist bereits konfiguriert", "conection_aborted": "Keine Verbindung zum HMIP-Server m\u00f6glich", + "connection_aborted": "Konnte nicht mit HMIP Server verbinden", "unknown": "Ein unbekannter Fehler ist aufgetreten." }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/en.json b/homeassistant/components/homematicip_cloud/.translations/en.json index 0cf99cd297582f..6fcfcddd75d6e6 100644 --- a/homeassistant/components/homematicip_cloud/.translations/en.json +++ b/homeassistant/components/homematicip_cloud/.translations/en.json @@ -1,8 +1,9 @@ { "config": { "abort": { - "already_configured": "Accesspoint is already configured", + "already_configured": "Access point is already configured", "conection_aborted": "Could not connect to HMIP server", + "connection_aborted": "Could not connect to HMIP server", "unknown": "Unknown error occurred." }, "error": { @@ -14,15 +15,15 @@ "step": { "init": { "data": { - "hapid": "Accesspoint ID (SGTIN)", + "hapid": "Access point ID (SGTIN)", "name": "Name (optional, used as name prefix for all devices)", "pin": "Pin Code (optional)" }, - "title": "Pick HomematicIP Accesspoint" + "title": "Pick HomematicIP Access point" }, "link": { - "description": "Press the blue button on the accesspoint and the submit button to register HomematicIP with Home Assistant.\n\n![Location of button on bridge](/static/images/config_flows/config_homematicip_cloud.png)", - "title": "Link Accesspoint" + "description": "Press the blue button on the access point and the submit button to register HomematicIP with Home Assistant.\n\n![Location of button on bridge](/static/images/config_flows/config_homematicip_cloud.png)", + "title": "Link Access point" } }, "title": "HomematicIP Cloud" diff --git a/homeassistant/components/homematicip_cloud/.translations/es.json b/homeassistant/components/homematicip_cloud/.translations/es.json new file mode 100644 index 00000000000000..3f16c45382b62c --- /dev/null +++ b/homeassistant/components/homematicip_cloud/.translations/es.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "unknown": "Se ha producido un error desconocido." + }, + "error": { + "invalid_pin": "PIN no v\u00e1lido, por favor int\u00e9ntalo de nuevo.", + "press_the_button": "Por favor, pulsa el bot\u00f3n azul" + }, + "step": { + "init": { + "data": { + "name": "Nombre (opcional, utilizado como prefijo para todos los dispositivos)", + "pin": "C\u00f3digo PIN (opcional)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/he.json b/homeassistant/components/homematicip_cloud/.translations/he.json new file mode 100644 index 00000000000000..bdf1e436badf15 --- /dev/null +++ b/homeassistant/components/homematicip_cloud/.translations/he.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "\u05e0\u05e7\u05d5\u05d3\u05ea \u05d4\u05d2\u05d9\u05e9\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8\u05ea", + "conection_aborted": "\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05e9\u05e8\u05ea HMIP", + "unknown": "\u05d0\u05d9\u05e8\u05e2\u05d4 \u05e9\u05d2\u05d9\u05d0\u05d4 \u05dc\u05d0 \u05d9\u05d3\u05d5\u05e2\u05d4." + }, + "error": { + "invalid_pin": "PIN \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1.", + "press_the_button": "\u05dc\u05d7\u05e5 \u05e2\u05dc \u05d4\u05db\u05e4\u05ea\u05d5\u05e8 \u05d4\u05db\u05d7\u05d5\u05dc.", + "register_failed": "\u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05e0\u05db\u05e9\u05dc, \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1.", + "timeout_button": "\u05e2\u05d1\u05e8 \u05d4\u05d6\u05de\u05df \u05d4\u05e7\u05e6\u05d5\u05d1 \u05dc\u05dc\u05d7\u05d9\u05e6\u05d4 \u05e2\u05dc \u05d4\u05db\u05e4\u05ea\u05d5\u05e8 \u05d4\u05db\u05d7\u05d5\u05dc, \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1" + }, + "step": { + "init": { + "data": { + "hapid": "\u05de\u05d6\u05d4\u05d4 \u05e0\u05e7\u05d5\u05d3\u05ea \u05d2\u05d9\u05e9\u05d4 (SGTIN)", + "name": "\u05e9\u05dd (\u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9, \u05de\u05e9\u05de\u05e9 \u05db\u05e7\u05d9\u05d3\u05d5\u05de\u05ea \u05e2\u05d1\u05d5\u05e8 \u05db\u05dc \u05d4\u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd)", + "pin": "\u05e7\u05d5\u05d3 PIN (\u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9)" + }, + "title": "\u05d1\u05d7\u05e8 \u05e0\u05e7\u05d5\u05d3\u05ea \u05d2\u05d9\u05e9\u05d4 HomematicIP" + }, + "link": { + "description": "\u05dc\u05d7\u05e5 \u05e2\u05dc \u05d4\u05db\u05e4\u05ea\u05d5\u05e8 \u05d4\u05db\u05d7\u05d5\u05dc \u05d1\u05e0\u05e7\u05d5\u05d3\u05ea \u05d2\u05d9\u05e9\u05d4 \u05d5\u05e2\u05dc \u05db\u05e4\u05ea\u05d5\u05e8 \u05d4\u05e9\u05dc\u05d9\u05d7\u05d4 \u05db\u05d3\u05d9 \u05dc\u05d7\u05d1\u05e8 \u05d0\u05ea HomematicIP \u05e2\u05ddHome Assistant.\n\n![\u05de\u05d9\u05e7\u05d5\u05dd \u05d4\u05db\u05e4\u05ea\u05d5\u05e8 \u05d1\u05de\u05d2\u05e9\u05e8](/static/images/config_flows/config_homematicip_cloud.png)", + "title": "\u05d7\u05d1\u05e8 \u05e0\u05e7\u05d5\u05d3\u05ea \u05d2\u05d9\u05e9\u05d4" + } + }, + "title": "\u05e2\u05e0\u05df HomematicIP" + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/it.json b/homeassistant/components/homematicip_cloud/.translations/it.json new file mode 100644 index 00000000000000..2566eb25570753 --- /dev/null +++ b/homeassistant/components/homematicip_cloud/.translations/it.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "init": { + "data": { + "pin": "Codice Pin (opzionale)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/ko.json b/homeassistant/components/homematicip_cloud/.translations/ko.json index e135873067eb86..617b65ff623987 100644 --- a/homeassistant/components/homematicip_cloud/.translations/ko.json +++ b/homeassistant/components/homematicip_cloud/.translations/ko.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\uc561\uc138\uc2a4 \ud3ec\uc778\ud2b8\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "conection_aborted": "HMIP \uc11c\ubc84\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "connection_aborted": "HMIP \uc11c\ubc84\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", "unknown": "\uc54c \uc218\uc5c6\ub294 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/lb.json b/homeassistant/components/homematicip_cloud/.translations/lb.json index 3dd3f1a5dca388..a21767fc7d9cbb 100644 --- a/homeassistant/components/homematicip_cloud/.translations/lb.json +++ b/homeassistant/components/homematicip_cloud/.translations/lb.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Acesspoint ass schon konfigur\u00e9iert", "conection_aborted": "Konnt sech net mam HMIP Server verbannen", + "connection_aborted": "Konnt sech net mam HMIP Server verbannen", "unknown": "Onbekannten Feeler opgetrueden" }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/nl.json b/homeassistant/components/homematicip_cloud/.translations/nl.json index 0559dae4bfd66c..23305a7e5844d3 100644 --- a/homeassistant/components/homematicip_cloud/.translations/nl.json +++ b/homeassistant/components/homematicip_cloud/.translations/nl.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Accesspoint is reeds geconfigureerd", "conection_aborted": "Kon geen verbinding maken met de HMIP-server", + "connection_aborted": "Kon geen verbinding maken met de HMIP-server", "unknown": "Er is een onbekende fout opgetreden." }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/no.json b/homeassistant/components/homematicip_cloud/.translations/no.json index 650c921af31e1b..a310a918f64326 100644 --- a/homeassistant/components/homematicip_cloud/.translations/no.json +++ b/homeassistant/components/homematicip_cloud/.translations/no.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Tilgangspunktet er allerede konfigurert", "conection_aborted": "Kunne ikke koble til HMIP serveren", + "connection_aborted": "Kunne ikke koble til HMIP serveren", "unknown": "Ukjent feil oppstod." }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/pl.json b/homeassistant/components/homematicip_cloud/.translations/pl.json index c2ec6be4bd465a..3fcbe7e69d1375 100644 --- a/homeassistant/components/homematicip_cloud/.translations/pl.json +++ b/homeassistant/components/homematicip_cloud/.translations/pl.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Punkt dost\u0119pu jest ju\u017c skonfigurowany", "conection_aborted": "Nie mo\u017cna po\u0142\u0105czy\u0107 si\u0119 z serwerem HMIP", + "connection_aborted": "Nie mo\u017cna po\u0142\u0105czy\u0107 si\u0119 z serwerem HMIP", "unknown": "Wyst\u0105pi\u0142 nieznany b\u0142\u0105d" }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/pt-BR.json b/homeassistant/components/homematicip_cloud/.translations/pt-BR.json index 6e5af1c26cc97a..d4ecbe5010725d 100644 --- a/homeassistant/components/homematicip_cloud/.translations/pt-BR.json +++ b/homeassistant/components/homematicip_cloud/.translations/pt-BR.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "O Accesspoint j\u00e1 est\u00e1 configurado", "conection_aborted": "N\u00e3o foi poss\u00edvel conectar ao servidor HMIP", + "connection_aborted": "N\u00e3o foi poss\u00edvel conectar ao servidor HMIP", "unknown": "Ocorreu um erro desconhecido." }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/ru.json b/homeassistant/components/homematicip_cloud/.translations/ru.json index 77c6469f64c67c..ed42daf19cd3c2 100644 --- a/homeassistant/components/homematicip_cloud/.translations/ru.json +++ b/homeassistant/components/homematicip_cloud/.translations/ru.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u0422\u043e\u0447\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430", "conection_aborted": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 HMIP", + "connection_aborted": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 HMIP", "unknown": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430" }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/sl.json b/homeassistant/components/homematicip_cloud/.translations/sl.json index d9749480c0d59b..4c4a00e31e03b7 100644 --- a/homeassistant/components/homematicip_cloud/.translations/sl.json +++ b/homeassistant/components/homematicip_cloud/.translations/sl.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Dostopna to\u010dka je \u017ee konfigurirana", "conection_aborted": "Povezave s stre\u017enikom HMIP ni bila mogo\u010da", + "connection_aborted": "Povezava s stre\u017enikom HMIP ni bila mogo\u010da", "unknown": "Pri\u0161lo je do neznane napake" }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/zh-Hans.json b/homeassistant/components/homematicip_cloud/.translations/zh-Hans.json index 38970e4a97cad5..930b649bceb128 100644 --- a/homeassistant/components/homematicip_cloud/.translations/zh-Hans.json +++ b/homeassistant/components/homematicip_cloud/.translations/zh-Hans.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u63a5\u5165\u70b9\u5df2\u7ecf\u914d\u7f6e\u5b8c\u6210", "conection_aborted": "\u65e0\u6cd5\u8fde\u63a5\u5230 HMIP \u670d\u52a1\u5668", + "connection_aborted": "\u65e0\u6cd5\u8fde\u63a5\u5230 HMIP \u670d\u52a1\u5668", "unknown": "\u53d1\u751f\u672a\u77e5\u9519\u8bef\u3002" }, "error": { diff --git a/homeassistant/components/homematicip_cloud/.translations/zh-Hant.json b/homeassistant/components/homematicip_cloud/.translations/zh-Hant.json index d8c6cff9b0cc65..9340070d9a39cd 100644 --- a/homeassistant/components/homematicip_cloud/.translations/zh-Hant.json +++ b/homeassistant/components/homematicip_cloud/.translations/zh-Hant.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Accesspoint \u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "conection_aborted": "\u7121\u6cd5\u9023\u7dda\u81f3 HMIP \u4f3a\u670d\u5668", + "connection_aborted": "\u7121\u6cd5\u9023\u7dda\u81f3 HMIP \u4f3a\u670d\u5668", "unknown": "\u767c\u751f\u672a\u77e5\u932f\u8aa4\u3002" }, "error": { diff --git a/homeassistant/components/homematicip_cloud/__init__.py b/homeassistant/components/homematicip_cloud/__init__.py index f2cc8f443ac801..05c5c970d2ed8e 100644 --- a/homeassistant/components/homematicip_cloud/__init__.py +++ b/homeassistant/components/homematicip_cloud/__init__.py @@ -1,24 +1,23 @@ """ -Support for HomematicIP components. +Support for HomematicIP Cloud components. For more details about this component, please refer to the documentation at https://home-assistant.io/components/homematicip_cloud/ """ - import logging import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant import config_entries +from homeassistant.const import CONF_NAME +import homeassistant.helpers.config_validation as cv -from .const import ( - DOMAIN, HMIPC_HAPID, HMIPC_AUTHTOKEN, HMIPC_NAME, - CONF_ACCESSPOINT, CONF_AUTHTOKEN, CONF_NAME) -# Loading the config flow file will register the flow from .config_flow import configured_haps -from .hap import HomematicipHAP, HomematicipAuth # noqa: F401 +from .const import ( + CONF_ACCESSPOINT, CONF_AUTHTOKEN, DOMAIN, HMIPC_AUTHTOKEN, HMIPC_HAPID, + HMIPC_NAME) from .device import HomematicipGenericDevice # noqa: F401 +from .hap import HomematicipAuth, HomematicipHAP # noqa: F401 REQUIREMENTS = ['homematicip==0.9.8'] @@ -34,7 +33,7 @@ async def async_setup(hass, config): - """Set up the HomematicIP component.""" + """Set up the HomematicIP Cloud component.""" hass.data[DOMAIN] = {} accesspoints = config.get(DOMAIN, []) @@ -54,7 +53,7 @@ async def async_setup(hass, config): async def async_setup_entry(hass, entry): - """Set up an accsspoint from a config entry.""" + """Set up an access point from a config entry.""" hap = HomematicipHAP(hass, entry) hapid = entry.data[HMIPC_HAPID].replace('-', '').upper() hass.data[DOMAIN][hapid] = hap diff --git a/homeassistant/components/homematicip_cloud/config_flow.py b/homeassistant/components/homematicip_cloud/config_flow.py index 78970031d11c27..d5045cf151b1e8 100644 --- a/homeassistant/components/homematicip_cloud/config_flow.py +++ b/homeassistant/components/homematicip_cloud/config_flow.py @@ -1,25 +1,25 @@ -"""Config flow to configure HomematicIP Cloud.""" +"""Config flow to configure the HomematicIP Cloud component.""" import voluptuous as vol from homeassistant import config_entries, data_entry_flow from homeassistant.core import callback -from .const import ( - DOMAIN as HMIPC_DOMAIN, _LOGGER, - HMIPC_HAPID, HMIPC_AUTHTOKEN, HMIPC_PIN, HMIPC_NAME) +from .const import DOMAIN as HMIPC_DOMAIN +from .const import HMIPC_AUTHTOKEN, HMIPC_HAPID, HMIPC_NAME, HMIPC_PIN +from .const import _LOGGER from .hap import HomematicipAuth @callback def configured_haps(hass): - """Return a set of the configured accesspoints.""" + """Return a set of the configured access points.""" return set(entry.data[HMIPC_HAPID] for entry in hass.config_entries.async_entries(HMIPC_DOMAIN)) @config_entries.HANDLERS.register(HMIPC_DOMAIN) class HomematicipCloudFlowHandler(data_entry_flow.FlowHandler): - """Config flow HomematicIP Cloud.""" + """Config flow for the HomematicIP Cloud component.""" VERSION = 1 @@ -44,28 +44,28 @@ async def async_step_init(self, user_input=None): self.auth = HomematicipAuth(self.hass, user_input) connected = await self.auth.async_setup() if connected: - _LOGGER.info("Connection established") + _LOGGER.info("Connection to HomematicIP Cloud established") return await self.async_step_link() return self.async_show_form( step_id='init', data_schema=vol.Schema({ vol.Required(HMIPC_HAPID): str, - vol.Optional(HMIPC_PIN): str, vol.Optional(HMIPC_NAME): str, + vol.Optional(HMIPC_PIN): str, }), errors=errors ) async def async_step_link(self, user_input=None): - """Attempt to link with the HomematicIP Cloud accesspoint.""" + """Attempt to link with the HomematicIP Cloud access point.""" errors = {} pressed = await self.auth.async_checkbutton() if pressed: authtoken = await self.auth.async_register() if authtoken: - _LOGGER.info("Write config entry") + _LOGGER.info("Write config entry for HomematicIP Cloud") return self.async_create_entry( title=self.auth.config.get(HMIPC_HAPID), data={ @@ -73,13 +73,13 @@ async def async_step_link(self, user_input=None): HMIPC_AUTHTOKEN: authtoken, HMIPC_NAME: self.auth.config.get(HMIPC_NAME) }) - return self.async_abort(reason='conection_aborted') + return self.async_abort(reason='connection_aborted') errors['base'] = 'press_the_button' return self.async_show_form(step_id='link', errors=errors) async def async_step_import(self, import_info): - """Import a new bridge as a config entry.""" + """Import a new access point as a config entry.""" hapid = import_info[HMIPC_HAPID] authtoken = import_info[HMIPC_AUTHTOKEN] name = import_info[HMIPC_NAME] @@ -88,13 +88,13 @@ async def async_step_import(self, import_info): if hapid in configured_haps(self.hass): return self.async_abort(reason='already_configured') - _LOGGER.info('Imported authentication for %s', hapid) + _LOGGER.info("Imported authentication for %s", hapid) return self.async_create_entry( title=hapid, data={ - HMIPC_HAPID: hapid, HMIPC_AUTHTOKEN: authtoken, - HMIPC_NAME: name + HMIPC_HAPID: hapid, + HMIPC_NAME: name, } ) diff --git a/homeassistant/components/homematicip_cloud/const.py b/homeassistant/components/homematicip_cloud/const.py index 54b05c464b546c..ba9c37b83d7e09 100644 --- a/homeassistant/components/homematicip_cloud/const.py +++ b/homeassistant/components/homematicip_cloud/const.py @@ -14,7 +14,6 @@ 'switch', ] -CONF_NAME = 'name' CONF_ACCESSPOINT = 'accesspoint' CONF_AUTHTOKEN = 'authtoken' diff --git a/homeassistant/components/homematicip_cloud/device.py b/homeassistant/components/homematicip_cloud/device.py index bb21e1df3d5f57..9c335befda4607 100644 --- a/homeassistant/components/homematicip_cloud/device.py +++ b/homeassistant/components/homematicip_cloud/device.py @@ -1,25 +1,25 @@ -"""GenericDevice for the HomematicIP Cloud component.""" +"""Generic device for the HomematicIP Cloud component.""" import logging from homeassistant.helpers.entity import Entity _LOGGER = logging.getLogger(__name__) -ATTR_HOME_ID = 'home_id' -ATTR_HOME_NAME = 'home_name' +ATTR_CONNECTED = 'connected' ATTR_DEVICE_ID = 'device_id' ATTR_DEVICE_LABEL = 'device_label' -ATTR_STATUS_UPDATE = 'status_update' +ATTR_DEVICE_RSSI = 'device_rssi' +ATTR_DUTY_CYCLE = 'duty_cycle' ATTR_FIRMWARE_STATE = 'firmware_state' -ATTR_UNREACHABLE = 'unreachable' +ATTR_GROUP_TYPE = 'group_type' +ATTR_HOME_ID = 'home_id' +ATTR_HOME_NAME = 'home_name' ATTR_LOW_BATTERY = 'low_battery' ATTR_MODEL_TYPE = 'model_type' -ATTR_GROUP_TYPE = 'group_type' -ATTR_DEVICE_RSSI = 'device_rssi' -ATTR_DUTY_CYCLE = 'duty_cycle' -ATTR_CONNECTED = 'connected' -ATTR_SABOTAGE = 'sabotage' ATTR_OPERATION_LOCK = 'operation_lock' +ATTR_SABOTAGE = 'sabotage' +ATTR_STATUS_UPDATE = 'status_update' +ATTR_UNREACHABLE = 'unreachable' class HomematicipGenericDevice(Entity): @@ -30,8 +30,7 @@ def __init__(self, home, device, post=None): self._home = home self._device = device self.post = post - _LOGGER.info('Setting up %s (%s)', self.name, - self._device.modelType) + _LOGGER.info("Setting up %s (%s)", self.name, self._device.modelType) async def async_added_to_hass(self): """Register callbacks.""" @@ -39,16 +38,16 @@ async def async_added_to_hass(self): def _device_changed(self, json, **kwargs): """Handle device state changes.""" - _LOGGER.debug('Event %s (%s)', self.name, self._device.modelType) + _LOGGER.debug("Event %s (%s)", self.name, self._device.modelType) self.async_schedule_update_ha_state() @property def name(self): """Return the name of the generic device.""" name = self._device.label - if (self._home.name is not None and self._home.name != ''): + if self._home.name is not None and self._home.name != '': name = "{} {}".format(self._home.name, name) - if (self.post is not None and self.post != ''): + if self.post is not None and self.post != '': name = "{} {}".format(name, self.post) return name diff --git a/homeassistant/components/homematicip_cloud/errors.py b/homeassistant/components/homematicip_cloud/errors.py index cb2925d1a70450..1102cde6fbecc2 100644 --- a/homeassistant/components/homematicip_cloud/errors.py +++ b/homeassistant/components/homematicip_cloud/errors.py @@ -1,21 +1,21 @@ -"""Errors for the HomematicIP component.""" +"""Errors for the HomematicIP Cloud component.""" from homeassistant.exceptions import HomeAssistantError class HmipcException(HomeAssistantError): - """Base class for HomematicIP exceptions.""" + """Base class for HomematicIP Cloud exceptions.""" class HmipcConnectionError(HmipcException): - """Unable to connect to the HomematicIP cloud server.""" + """Unable to connect to the HomematicIP Cloud server.""" class HmipcConnectionWait(HmipcException): - """Wait for registration to the HomematicIP cloud server.""" + """Wait for registration to the HomematicIP Cloud server.""" class HmipcRegistrationFailed(HmipcException): - """Registration on HomematicIP cloud failed.""" + """Registration on HomematicIP Cloud failed.""" class HmipcPressButton(HmipcException): diff --git a/homeassistant/components/homematicip_cloud/hap.py b/homeassistant/components/homematicip_cloud/hap.py index 9715a5fc02400c..6fddc7c001e425 100644 --- a/homeassistant/components/homematicip_cloud/hap.py +++ b/homeassistant/components/homematicip_cloud/hap.py @@ -1,14 +1,13 @@ -"""Accesspoint for the HomematicIP Cloud component.""" +"""Access point for the HomematicIP Cloud component.""" import asyncio import logging from homeassistant import config_entries -from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.core import callback +from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import ( - HMIPC_HAPID, HMIPC_AUTHTOKEN, HMIPC_PIN, HMIPC_NAME, - COMPONENTS) + COMPONENTS, HMIPC_AUTHTOKEN, HMIPC_HAPID, HMIPC_NAME, HMIPC_PIN) from .errors import HmipcConnectionError _LOGGER = logging.getLogger(__name__) @@ -74,10 +73,10 @@ async def get_auth(self, hass, hapid, pin): class HomematicipHAP: - """Manages HomematicIP http and websocket connection.""" + """Manages HomematicIP HTTP and WebSocket connection.""" def __init__(self, hass, config_entry): - """Initialize HomematicIP cloud connection.""" + """Initialize HomematicIP Cloud connection.""" self.hass = hass self.config_entry = config_entry self.home = None @@ -100,7 +99,7 @@ async def async_setup(self, tries=0): except HmipcConnectionError: retry_delay = 2 ** min(tries + 1, 6) _LOGGER.error("Error connecting to HomematicIP with HAP %s. " - "Retrying in %d seconds.", + "Retrying in %d seconds", self.config_entry.data.get(HMIPC_HAPID), retry_delay) async def retry_setup(_now): @@ -113,7 +112,7 @@ async def retry_setup(_now): return False - _LOGGER.info('Connected to HomematicIP with HAP %s.', + _LOGGER.info("Connected to HomematicIP with HAP %s", self.config_entry.data.get(HMIPC_HAPID)) for component in COMPONENTS: @@ -127,7 +126,7 @@ async def retry_setup(_now): def async_update(self, *args, **kwargs): """Async update the home device. - Triggered when the hmip HOME_CHANGED event has fired. + Triggered when the HMIP HOME_CHANGED event has fired. There are several occasions for this event to happen. We are only interested to check whether the access point is still connected. If not, device state changes cannot @@ -147,7 +146,7 @@ def async_update(self, *args, **kwargs): job.add_done_callback(self.get_state_finished) async def get_state(self): - """Update hmip state and tell hass.""" + """Update HMIP state and tell Home Assistant.""" await self.home.get_current_state() self.update_all() @@ -161,11 +160,11 @@ def get_state_finished(self, future): # Somehow connection could not recover. Will disconnect and # so reconnect loop is taking over. _LOGGER.error( - "updating state after himp access point reconnect failed.") + "Updating state after HMIP access point reconnect failed") self.hass.async_add_job(self.home.disable_events()) def set_all_to_unavailable(self): - """Set all devices to unavailable and tell Hass.""" + """Set all devices to unavailable and tell Home Assistant.""" for device in self.home.devices: device.unreach = True self.update_all() @@ -190,7 +189,7 @@ async def _handle_connection(self): return async def async_connect(self): - """Start websocket connection.""" + """Start WebSocket connection.""" from homematicip.base.base_connection import HmipConnectionError tries = 0 @@ -210,7 +209,7 @@ async def async_connect(self): tries += 1 retry_delay = 2 ** min(tries + 1, 6) _LOGGER.error("Error connecting to HomematicIP with HAP %s. " - "Retrying in %d seconds.", + "Retrying in %d seconds", self.config_entry.data.get(HMIPC_HAPID), retry_delay) try: self._retry_task = self.hass.async_add_job(asyncio.sleep( @@ -227,7 +226,7 @@ async def async_reset(self): if self._retry_task is not None: self._retry_task.cancel() self.home.disable_events() - _LOGGER.info("Closed connection to HomematicIP cloud server.") + _LOGGER.info("Closed connection to HomematicIP cloud server") for component in COMPONENTS: await self.hass.config_entries.async_forward_entry_unload( self.config_entry, component) diff --git a/homeassistant/components/homematicip_cloud/strings.json b/homeassistant/components/homematicip_cloud/strings.json index 887a3a5780b0eb..f2d38a1dc7b837 100644 --- a/homeassistant/components/homematicip_cloud/strings.json +++ b/homeassistant/components/homematicip_cloud/strings.json @@ -3,16 +3,16 @@ "title": "HomematicIP Cloud", "step": { "init": { - "title": "Pick HomematicIP Accesspoint", + "title": "Pick HomematicIP Access point", "data": { - "hapid": "Accesspoint ID (SGTIN)", + "hapid": "Access point ID (SGTIN)", "pin": "Pin Code (optional)", "name": "Name (optional, used as name prefix for all devices)" } }, "link": { - "title": "Link Accesspoint", - "description": "Press the blue button on the accesspoint and the submit button to register HomematicIP with Home Assistant.\n\n![Location of button on bridge](/static/images/config_flows/config_homematicip_cloud.png)" + "title": "Link Access point", + "description": "Press the blue button on the access point and the submit button to register HomematicIP with Home Assistant.\n\n![Location of button on bridge](/static/images/config_flows/config_homematicip_cloud.png)" } }, "error": { @@ -23,8 +23,8 @@ }, "abort": { "unknown": "Unknown error occurred.", - "conection_aborted": "Could not connect to HMIP server", - "already_configured": "Accesspoint is already configured" + "connection_aborted": "Could not connect to HMIP server", + "already_configured": "Access point is already configured" } } } diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index 9ba977f92f5c42..1b22f8e62d4319 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -200,18 +200,10 @@ def __init__(self, hass, api_password, if is_ban_enabled: setup_bans(hass, app, login_threshold) - if hass.auth.active: - if hass.auth.support_legacy: - _LOGGER.warning("Experimental auth api enabled and " - "legacy_api_password support enabled. Please " - "use access_token instead api_password, " - "although you can still use legacy " - "api_password") - else: - _LOGGER.warning("Experimental auth api enabled. Please use " - "access_token instead api_password.") - elif api_password is None: - _LOGGER.warning("You have been advised to set http.api_password.") + if hass.auth.active and hass.auth.support_legacy: + _LOGGER.warning( + "legacy_api_password support has been enabled. If you don't " + "require it, remove the 'api_password' from your http config.") setup_auth(app, trusted_networks, hass.auth.active, support_legacy=hass.auth.support_legacy, @@ -228,10 +220,12 @@ def __init__(self, hass, api_password, self.ssl_key = ssl_key self.server_host = server_host self.server_port = server_port + self.trusted_networks = trusted_networks self.is_ban_enabled = is_ban_enabled self.ssl_profile = ssl_profile self._handler = None - self.server = None + self.runner = None + self.site = None def register_view(self, view): """Register a view with the WSGI server. @@ -307,7 +301,7 @@ async def serve_file(request): self.app.router.add_route('GET', url_pattern, serve_file) async def start(self): - """Start the WSGI server.""" + """Start the aiohttp server.""" # We misunderstood the startup signal. You're not allowed to change # anything during startup. Temp workaround. # pylint: disable=protected-access @@ -320,7 +314,9 @@ async def start(self): context = ssl_util.server_context_intermediate() else: context = ssl_util.server_context_modern() - context.load_cert_chain(self.ssl_certificate, self.ssl_key) + await self.hass.async_add_executor_job( + context.load_cert_chain, self.ssl_certificate, + self.ssl_key) except OSError as error: _LOGGER.error("Could not read SSL certificate from %s: %s", self.ssl_certificate, error) @@ -328,7 +324,9 @@ async def start(self): if self.ssl_peer_certificate: context.verify_mode = ssl.CERT_REQUIRED - context.load_verify_locations(cafile=self.ssl_peer_certificate) + await self.hass.async_add_executor_job( + context.load_verify_locations, + self.ssl_peer_certificate) else: context = None @@ -339,21 +337,17 @@ async def start(self): # To work around this we now prevent the router from getting frozen self.app._router.freeze = lambda: None - self._handler = self.app.make_handler(loop=self.hass.loop) - + self.runner = web.AppRunner(self.app) + await self.runner.setup() + self.site = web.TCPSite(self.runner, self.server_host, + self.server_port, ssl_context=context) try: - self.server = await self.hass.loop.create_server( - self._handler, self.server_host, self.server_port, ssl=context) + await self.site.start() except OSError as error: _LOGGER.error("Failed to create HTTP server at port %d: %s", self.server_port, error) async def stop(self): - """Stop the WSGI server.""" - if self.server: - self.server.close() - await self.server.wait_closed() - await self.app.shutdown() - if self._handler: - await self._handler.shutdown(10) - await self.app.cleanup() + """Stop the aiohttp server.""" + await self.site.stop() + await self.runner.cleanup() diff --git a/homeassistant/components/http/auth.py b/homeassistant/components/http/auth.py index d01d1b50c5acc9..a18b4de7a10788 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -30,8 +30,10 @@ async def auth_middleware(request, handler): if use_auth and (HTTP_HEADER_HA_AUTH in request.headers or DATA_API_PASSWORD in request.query): if request.path not in old_auth_warning: - _LOGGER.warning('Please change to use bearer token access %s', - request.path) + _LOGGER.log( + logging.INFO if support_legacy else logging.WARNING, + 'You need to use a bearer token to access %s from %s', + request.path, request[KEY_REAL_IP]) old_auth_warning.add(request.path) legacy_auth = (not use_auth or support_legacy) and api_password diff --git a/homeassistant/components/http/ban.py b/homeassistant/components/http/ban.py index ab582066a22a72..015c386e836367 100644 --- a/homeassistant/components/http/ban.py +++ b/homeassistant/components/http/ban.py @@ -71,7 +71,7 @@ async def ban_middleware(request, handler): def log_invalid_auth(func): - """Decorator to handle invalid auth or failed login attempts.""" + """Decorate function to handle invalid auth or failed login attempts.""" async def handle_req(view, request, *args, **kwargs): """Try to log failed login attempts if response status >= 400.""" resp = await func(view, request, *args, **kwargs) diff --git a/homeassistant/components/http/cors.py b/homeassistant/components/http/cors.py index 555f302f8e1966..5698c6048e34d3 100644 --- a/homeassistant/components/http/cors.py +++ b/homeassistant/components/http/cors.py @@ -1,4 +1,4 @@ -"""Provide cors support for the HTTP component.""" +"""Provide CORS support for the HTTP component.""" from aiohttp.hdrs import ACCEPT, ORIGIN, CONTENT_TYPE @@ -17,7 +17,7 @@ @callback def setup_cors(app, origins): - """Setup cors.""" + """Set up CORS.""" import aiohttp_cors cors = aiohttp_cors.setup(app, defaults={ @@ -30,7 +30,7 @@ def setup_cors(app, origins): cors_added = set() def _allow_cors(route, config=None): - """Allow cors on a route.""" + """Allow CORS on a route.""" if hasattr(route, 'resource'): path = route.resource else: @@ -55,7 +55,7 @@ def _allow_cors(route, config=None): return async def cors_startup(app): - """Initialize cors when app starts up.""" + """Initialize CORS when app starts up.""" for route in list(app.router.routes()): _allow_cors(route) diff --git a/homeassistant/components/http/view.py b/homeassistant/components/http/view.py index 22ef34de54adad..b3b2587fc458e8 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -11,10 +11,10 @@ from aiohttp import web from aiohttp.web_exceptions import HTTPUnauthorized, HTTPInternalServerError -import homeassistant.remote as rem from homeassistant.components.http.ban import process_success_login from homeassistant.core import Context, is_callback from homeassistant.const import CONTENT_TYPE_JSON +from homeassistant.helpers.json import JSONEncoder from .const import KEY_AUTHENTICATED, KEY_REAL_IP @@ -44,7 +44,7 @@ def json(self, result, status_code=200, headers=None): """Return a JSON response.""" try: msg = json.dumps( - result, sort_keys=True, cls=rem.JSONEncoder).encode('UTF-8') + result, sort_keys=True, cls=JSONEncoder).encode('UTF-8') except TypeError as err: _LOGGER.error('Unable to serialize to JSON: %s\n%s', err, result) raise HTTPInternalServerError diff --git a/homeassistant/components/hue/.translations/da.json b/homeassistant/components/hue/.translations/da.json index 3e5e2b1d3d788e..19e60b073d380a 100644 --- a/homeassistant/components/hue/.translations/da.json +++ b/homeassistant/components/hue/.translations/da.json @@ -1,7 +1,16 @@ { "config": { "abort": { - "no_bridges": "Ingen Philips Hue bridge fundet" + "all_configured": "Alle Philips Hue brigdes er konfigureret", + "already_configured": "Bridgen er allerede konfigureret", + "cannot_connect": "Kunne ikke oprette forbindelse til bridgen", + "discover_timeout": "Ingen Philips Hue bridge fundet", + "no_bridges": "Ingen Philips Hue bridge fundet", + "unknown": "Ukendt fejl opstod" + }, + "error": { + "linking": "Ukendt sammenkoblings fejl opstod", + "register_failed": "Det lykkedes ikke at registrere, pr\u00f8v igen" }, "step": { "init": { @@ -11,6 +20,7 @@ "title": "V\u00e6lg Hue bridge" }, "link": { + "description": "Tryk p\u00e5 knappen p\u00e5 bridgen for at registrere Philips Hue med Home Assistant. \n\n ! [Placering af knap p\u00e5 bro] (/static/images/config_philips_hue.jpg)", "title": "Link Hub" } }, diff --git a/homeassistant/components/hue/.translations/he.json b/homeassistant/components/hue/.translations/he.json new file mode 100644 index 00000000000000..ddc91ae2266629 --- /dev/null +++ b/homeassistant/components/hue/.translations/he.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "all_configured": "\u05db\u05dc \u05d4\u05de\u05d2\u05e9\u05e8\u05d9\u05dd \u05e9\u05dc Philips Hue \u05de\u05d5\u05d2\u05d3\u05e8\u05d9\u05dd \u05db\u05d1\u05e8", + "already_configured": "\u05d4\u05de\u05d2\u05e9\u05e8 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8", + "cannot_connect": "\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05de\u05d2\u05e9\u05e8", + "discover_timeout": "\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05dc\u05d2\u05dc\u05d5\u05ea \u05de\u05d2\u05e9\u05e8\u05d9\u05dd", + "no_bridges": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05d2\u05e9\u05e8\u05d9 Philips Hue", + "unknown": "\u05d0\u05d9\u05e8\u05e2\u05d4 \u05e9\u05d2\u05d9\u05d0\u05d4 \u05dc\u05d0 \u05d9\u05d3\u05d5\u05e2\u05d4." + }, + "error": { + "linking": "\u05d0\u05d9\u05e8\u05e2\u05d4 \u05e9\u05d2\u05d9\u05d0\u05ea \u05e7\u05d9\u05e9\u05d5\u05e8 \u05dc\u05d0 \u05d9\u05d3\u05d5\u05e2\u05d4.", + "register_failed": "\u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05e0\u05db\u05e9\u05dc, \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1." + }, + "step": { + "init": { + "data": { + "host": "\u05de\u05d0\u05e8\u05d7" + }, + "title": "\u05d1\u05d7\u05e8 \u05de\u05d2\u05e9\u05e8" + }, + "link": { + "description": "\u05dc\u05d7\u05e5 \u05e2\u05dc \u05d4\u05db\u05e4\u05ea\u05d5\u05e8 \u05e2\u05dc \u05d4\u05de\u05d2\u05e9\u05e8 \u05db\u05d3\u05d9 \u05dc\u05d7\u05d1\u05e8 \u05d1\u05d9\u05df \u05d0\u05ea Philips Hue \u05e2\u05dd Home Assistant. \n\n![\u05de\u05d9\u05e7\u05d5\u05dd \u05d4\u05db\u05e4\u05ea\u05d5\u05e8 \u05d1\u05e8\u05db\u05d6\u05ea](/static/images/config_philips_hue.jpg)", + "title": "\u05e7\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e8\u05db\u05d6\u05ea" + } + }, + "title": "Philips Hue" + } +} \ No newline at end of file diff --git a/homeassistant/components/hue/.translations/pt-BR.json b/homeassistant/components/hue/.translations/pt-BR.json index 5c6e409245c7e9..b30764c92393ff 100644 --- a/homeassistant/components/hue/.translations/pt-BR.json +++ b/homeassistant/components/hue/.translations/pt-BR.json @@ -24,6 +24,6 @@ "title": "Hub de links" } }, - "title": "Philips Hue" + "title": "" } } \ No newline at end of file diff --git a/homeassistant/components/ihc/__init__.py b/homeassistant/components/ihc/__init__.py index 672964f765e12d..9b00f3bd7896da 100644 --- a/homeassistant/components/ihc/__init__.py +++ b/homeassistant/components/ihc/__init__.py @@ -123,7 +123,7 @@ def setup(hass, config): def autosetup_ihc_products(hass: HomeAssistantType, config, ihc_controller): - """Auto setup of IHC products from the ihc project file.""" + """Auto setup of IHC products from the IHC project file.""" project_xml = ihc_controller.get_project() if not project_xml: _LOGGER.error("Unable to read project from ICH controller") @@ -177,7 +177,7 @@ def get_discovery_info(component_setup, groups): def setup_service_functions(hass: HomeAssistantType, ihc_controller): - """Setup the IHC service functions.""" + """Set up the IHC service functions.""" def set_runtime_value_bool(call): """Set a IHC runtime bool value service function.""" ihc_id = call.data[ATTR_IHC_ID] diff --git a/homeassistant/components/ihc/ihcdevice.py b/homeassistant/components/ihc/ihcdevice.py index 2ccca366d901cd..93ab81850c9fe4 100644 --- a/homeassistant/components/ihc/ihcdevice.py +++ b/homeassistant/components/ihc/ihcdevice.py @@ -57,7 +57,7 @@ def device_state_attributes(self): } def on_ihc_change(self, ihc_id, value): - """Callback when IHC resource changes. + """Handle IHC resource change. Derived classes must overwrite this to do device specific stuff. """ diff --git a/homeassistant/components/ihc/services.yaml b/homeassistant/components/ihc/services.yaml index 7b6053eff898ae..a0cc6774fdf474 100644 --- a/homeassistant/components/ihc/services.yaml +++ b/homeassistant/components/ihc/services.yaml @@ -1,26 +1,26 @@ -# Describes the format for available ihc services +# Describes the format for available IHC services set_runtime_value_bool: - description: Set a boolean runtime value on the ihc controller + description: Set a boolean runtime value on the IHC controller fields: ihc_id: - description: The integer ihc resource id + description: The integer IHC resource id value: description: The boolean value to set set_runtime_value_int: - description: Set an integer runtime value on the ihc controller + description: Set an integer runtime value on the IHC controller fields: ihc_id: - description: The integer ihc resource id + description: The integer IHC resource id value: description: The integer value to set set_runtime_value_float: - description: Set a float runtime value on the ihc controller + description: Set a float runtime value on the IHC controller fields: ihc_id: - description: The integer ihc resource id + description: The integer IHC resource id value: description: The float value to set diff --git a/homeassistant/components/image_processing/demo.py b/homeassistant/components/image_processing/demo.py index e225113b5b175d..42ba7b4c05f3ea 100644 --- a/homeassistant/components/image_processing/demo.py +++ b/homeassistant/components/image_processing/demo.py @@ -12,9 +12,9 @@ ImageProcessingAlprEntity) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the demo image processing platform.""" - add_devices([ + add_entities([ DemoImageProcessingAlpr('camera.demo_camera', "Demo Alpr"), DemoImageProcessingFace( 'camera.demo_camera', "Demo Face") diff --git a/homeassistant/components/image_processing/dlib_face_detect.py b/homeassistant/components/image_processing/dlib_face_detect.py index d4a20da253c4d8..d3b4e14f4deaa6 100644 --- a/homeassistant/components/image_processing/dlib_face_detect.py +++ b/homeassistant/components/image_processing/dlib_face_detect.py @@ -20,7 +20,7 @@ ATTR_LOCATION = 'location' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dlib Face detection platform.""" entities = [] for camera in config[CONF_SOURCE]: @@ -28,7 +28,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): camera[CONF_ENTITY_ID], camera.get(CONF_NAME) )) - add_devices(entities) + add_entities(entities) class DlibFaceDetectEntity(ImageProcessingFaceEntity): diff --git a/homeassistant/components/image_processing/dlib_face_identify.py b/homeassistant/components/image_processing/dlib_face_identify.py index bf34eb4c2dab7a..d8c3f5f621fc80 100644 --- a/homeassistant/components/image_processing/dlib_face_identify.py +++ b/homeassistant/components/image_processing/dlib_face_identify.py @@ -27,7 +27,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dlib Face detection platform.""" entities = [] for camera in config[CONF_SOURCE]: @@ -35,7 +35,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): camera[CONF_ENTITY_ID], config[CONF_FACES], camera.get(CONF_NAME) )) - add_devices(entities) + add_entities(entities) class DlibFaceIdentifyEntity(ImageProcessingFaceEntity): diff --git a/homeassistant/components/image_processing/facebox.py b/homeassistant/components/image_processing/facebox.py index e5ce0b825d0794..2fbd1c5c81c4a0 100644 --- a/homeassistant/components/image_processing/facebox.py +++ b/homeassistant/components/image_processing/facebox.py @@ -152,7 +152,7 @@ def valid_file_path(file_path): return False -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the classifier.""" if DATA_FACEBOX not in hass.data: hass.data[DATA_FACEBOX] = [] @@ -173,7 +173,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): camera[CONF_ENTITY_ID], camera.get(CONF_NAME)) entities.append(facebox) hass.data[DATA_FACEBOX].append(facebox) - add_devices(entities) + add_entities(entities) def service_handle(service): """Handle for services.""" diff --git a/homeassistant/components/image_processing/microsoft_face_detect.py b/homeassistant/components/image_processing/microsoft_face_detect.py index 0b57dba8bcab4d..7e10d05c5b6476 100644 --- a/homeassistant/components/image_processing/microsoft_face_detect.py +++ b/homeassistant/components/image_processing/microsoft_face_detect.py @@ -46,7 +46,8 @@ def validate_attributes(list_attributes): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Microsoft Face detection platform.""" api = hass.data[DATA_MICROSOFT_FACE] attributes = config[CONF_ATTRIBUTES] @@ -57,7 +58,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): camera[CONF_ENTITY_ID], api, attributes, camera.get(CONF_NAME) )) - async_add_devices(entities) + async_add_entities(entities) class MicrosoftFaceDetectEntity(ImageProcessingFaceEntity): diff --git a/homeassistant/components/image_processing/microsoft_face_identify.py b/homeassistant/components/image_processing/microsoft_face_identify.py index 9479a804a44161..fae11a3dfa9e7a 100644 --- a/homeassistant/components/image_processing/microsoft_face_identify.py +++ b/homeassistant/components/image_processing/microsoft_face_identify.py @@ -30,7 +30,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Microsoft Face identify platform.""" api = hass.data[DATA_MICROSOFT_FACE] face_group = config[CONF_GROUP] @@ -43,7 +44,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): camera.get(CONF_NAME) )) - async_add_devices(entities) + async_add_entities(entities) class MicrosoftFaceIdentifyEntity(ImageProcessingFaceEntity): diff --git a/homeassistant/components/image_processing/openalpr_cloud.py b/homeassistant/components/image_processing/openalpr_cloud.py index dbf36dcd86ebe0..3daaeb6fa017ff 100644 --- a/homeassistant/components/image_processing/openalpr_cloud.py +++ b/homeassistant/components/image_processing/openalpr_cloud.py @@ -49,7 +49,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the OpenALPR cloud API platform.""" confidence = config[CONF_CONFIDENCE] params = { @@ -65,7 +66,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): camera[CONF_ENTITY_ID], params, confidence, camera.get(CONF_NAME) )) - async_add_devices(entities) + async_add_entities(entities) class OpenAlprCloudEntity(ImageProcessingAlprEntity): diff --git a/homeassistant/components/image_processing/openalpr_local.py b/homeassistant/components/image_processing/openalpr_local.py index 227e32696280df..901533d1da4303 100644 --- a/homeassistant/components/image_processing/openalpr_local.py +++ b/homeassistant/components/image_processing/openalpr_local.py @@ -56,7 +56,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the OpenALPR local platform.""" command = [config[CONF_ALPR_BIN], '-c', config[CONF_REGION], '-'] confidence = config[CONF_CONFIDENCE] @@ -67,7 +68,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): camera[CONF_ENTITY_ID], command, confidence, camera.get(CONF_NAME) )) - async_add_devices(entities) + async_add_entities(entities) class ImageProcessingAlprEntity(ImageProcessingEntity): diff --git a/homeassistant/components/image_processing/opencv.py b/homeassistant/components/image_processing/opencv.py index 00ae01f11231a4..1e5d38b638e3b2 100644 --- a/homeassistant/components/image_processing/opencv.py +++ b/homeassistant/components/image_processing/opencv.py @@ -16,7 +16,7 @@ from homeassistant.core import split_entity_id import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['numpy==1.15.0'] +REQUIREMENTS = ['numpy==1.15.1'] _LOGGER = logging.getLogger(__name__) @@ -80,7 +80,7 @@ def _get_default_classifier(dest_path): fil.write(chunk) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the OpenCV image processing platform.""" try: # Verify that the OpenCV python package is pre-installed @@ -105,7 +105,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): hass, camera[CONF_ENTITY_ID], camera.get(CONF_NAME), config[CONF_CLASSIFIER])) - add_devices(entities) + add_entities(entities) class OpenCVImageProcessor(ImageProcessingEntity): diff --git a/homeassistant/components/image_processing/seven_segments.py b/homeassistant/components/image_processing/seven_segments.py index b49739bcec321e..fb6f41b4a499a1 100644 --- a/homeassistant/components/image_processing/seven_segments.py +++ b/homeassistant/components/image_processing/seven_segments.py @@ -45,7 +45,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Seven segments OCR platform.""" entities = [] for camera in config[CONF_SOURCE]: @@ -53,7 +54,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): hass, camera[CONF_ENTITY_ID], config, camera.get(CONF_NAME) )) - async_add_devices(entities) + async_add_entities(entities) class ImageProcessingSsocr(ImageProcessingEntity): diff --git a/homeassistant/components/input_boolean.py b/homeassistant/components/input_boolean.py index 9c8435614a221a..b9c4dcc685eaf4 100644 --- a/homeassistant/components/input_boolean.py +++ b/homeassistant/components/input_boolean.py @@ -4,7 +4,6 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/input_boolean/ """ -import asyncio import logging import voluptuous as vol @@ -84,30 +83,20 @@ async def async_setup(hass, config): if not entities: return False - async def async_handler_service(service): - """Handle a calls to the input boolean services.""" - target_inputs = component.async_extract_from_service(service) - - if service.service == SERVICE_TURN_ON: - attr = 'async_turn_on' - elif service.service == SERVICE_TURN_OFF: - attr = 'async_turn_off' - else: - attr = 'async_toggle' - - tasks = [getattr(input_b, attr)() for input_b in target_inputs] - if tasks: - await asyncio.wait(tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_TURN_OFF, async_handler_service, - schema=SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_TURN_ON, async_handler_service, - schema=SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_TOGGLE, async_handler_service, - schema=SERVICE_SCHEMA) + component.async_register_entity_service( + SERVICE_TURN_ON, SERVICE_SCHEMA, + 'async_turn_on' + ) + + component.async_register_entity_service( + SERVICE_TURN_OFF, SERVICE_SCHEMA, + 'async_turn_off' + ) + + component.async_register_entity_service( + SERVICE_TOGGLE, SERVICE_SCHEMA, + 'async_toggle' + ) await component.async_add_entities(entities) return True diff --git a/homeassistant/components/input_datetime.py b/homeassistant/components/input_datetime.py index a77b67792f5658..df35ae53ba9fc3 100644 --- a/homeassistant/components/input_datetime.py +++ b/homeassistant/components/input_datetime.py @@ -4,14 +4,12 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/input_datetime/ """ -import asyncio import logging import datetime import voluptuous as vol -from homeassistant.const import ( - ATTR_ENTITY_ID, CONF_ICON, CONF_NAME, STATE_UNKNOWN) +from homeassistant.const import ATTR_ENTITY_ID, CONF_ICON, CONF_NAME import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent @@ -39,6 +37,15 @@ vol.Optional(ATTR_TIME): cv.time, }) + +def has_date_or_time(conf): + """Check at least date or time is true.""" + if conf[CONF_HAS_DATE] or conf[CONF_HAS_TIME]: + return conf + + raise vol.Invalid('Entity needs at least a date or a time') + + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ cv.slug: vol.All({ @@ -47,23 +54,11 @@ vol.Optional(CONF_HAS_TIME, default=False): cv.boolean, vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_INITIAL): cv.string, - }, cv.has_at_least_one_key_value((CONF_HAS_DATE, True), - (CONF_HAS_TIME, True)))}) + }, has_date_or_time)}) }, extra=vol.ALLOW_EXTRA) -@asyncio.coroutine -def async_set_datetime(hass, entity_id, dt_value): - """Set date and / or time of input_datetime.""" - yield from hass.services.async_call(DOMAIN, SERVICE_SET_DATETIME, { - ATTR_ENTITY_ID: entity_id, - ATTR_DATE: dt_value.date(), - ATTR_TIME: dt_value.time() - }) - - -@asyncio.coroutine -def async_setup(hass, config): +async def async_setup(hass, config): """Set up an input datetime.""" component = EntityComponent(_LOGGER, DOMAIN, hass) @@ -81,32 +76,24 @@ def async_setup(hass, config): if not entities: return False - @asyncio.coroutine - def async_set_datetime_service(call): + async def async_set_datetime_service(entity, call): """Handle a call to the input datetime 'set datetime' service.""" - target_inputs = component.async_extract_from_service(call) - - tasks = [] - for input_datetime in target_inputs: - time = call.data.get(ATTR_TIME) - date = call.data.get(ATTR_DATE) - if (input_datetime.has_date() and not date) or \ - (input_datetime.has_time() and not time): - _LOGGER.error("Invalid service data for " - "input_datetime.set_datetime: %s", - str(call.data)) - continue - - tasks.append(input_datetime.async_set_datetime(date, time)) - - if tasks: - yield from asyncio.wait(tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_DATETIME, async_set_datetime_service, - schema=SERVICE_SET_DATETIME_SCHEMA) - - yield from component.async_add_entities(entities) + time = call.data.get(ATTR_TIME) + date = call.data.get(ATTR_DATE) + if (entity.has_date and not date) or (entity.has_time and not time): + _LOGGER.error("Invalid service data for %s " + "input_datetime.set_datetime: %s", + entity.entity_id, str(call.data)) + return + + entity.async_set_datetime(date, time) + + component.async_register_entity_service( + SERVICE_SET_DATETIME, SERVICE_SET_DATETIME_SCHEMA, + async_set_datetime_service + ) + + await component.async_add_entities(entities) return True @@ -117,14 +104,13 @@ def __init__(self, object_id, name, has_date, has_time, icon, initial): """Initialize a select input.""" self.entity_id = ENTITY_ID_FORMAT.format(object_id) self._name = name - self._has_date = has_date - self._has_time = has_time + self.has_date = has_date + self.has_time = has_time self._icon = icon self._initial = initial self._current_datetime = None - @asyncio.coroutine - def async_added_to_hass(self): + async def async_added_to_hass(self): """Run when entity about to be added.""" restore_val = None @@ -134,27 +120,18 @@ def async_added_to_hass(self): # Priority 2: Old state if restore_val is None: - old_state = yield from async_get_last_state(self.hass, - self.entity_id) + old_state = await async_get_last_state(self.hass, self.entity_id) if old_state is not None: restore_val = old_state.state if restore_val is not None: - if not self._has_date: + if not self.has_date: self._current_datetime = dt_util.parse_time(restore_val) - elif not self._has_time: + elif not self.has_time: self._current_datetime = dt_util.parse_date(restore_val) else: self._current_datetime = dt_util.parse_datetime(restore_val) - def has_date(self): - """Return whether the input datetime carries a date.""" - return self._has_date - - def has_time(self): - """Return whether the input datetime carries a time.""" - return self._has_time - @property def should_poll(self): """If entity should be polled.""" @@ -173,55 +150,50 @@ def icon(self): @property def state(self): """Return the state of the component.""" - if self._current_datetime is None: - return STATE_UNKNOWN - return self._current_datetime @property def state_attributes(self): """Return the state attributes.""" attrs = { - 'has_date': self._has_date, - 'has_time': self._has_time, + 'has_date': self.has_date, + 'has_time': self.has_time, } if self._current_datetime is None: return attrs - if self._has_date and self._current_datetime is not None: + if self.has_date and self._current_datetime is not None: attrs['year'] = self._current_datetime.year attrs['month'] = self._current_datetime.month attrs['day'] = self._current_datetime.day - if self._has_time and self._current_datetime is not None: + if self.has_time and self._current_datetime is not None: attrs['hour'] = self._current_datetime.hour attrs['minute'] = self._current_datetime.minute attrs['second'] = self._current_datetime.second - if self._current_datetime is not None: - if not self._has_date: - attrs['timestamp'] = self._current_datetime.hour * 3600 + \ - self._current_datetime.minute * 60 + \ - self._current_datetime.second - elif not self._has_time: - extended = datetime.datetime.combine(self._current_datetime, - datetime.time(0, 0)) - attrs['timestamp'] = extended.timestamp() - else: - attrs['timestamp'] = self._current_datetime.timestamp() + if not self.has_date: + attrs['timestamp'] = self._current_datetime.hour * 3600 + \ + self._current_datetime.minute * 60 + \ + self._current_datetime.second + elif not self.has_time: + extended = datetime.datetime.combine(self._current_datetime, + datetime.time(0, 0)) + attrs['timestamp'] = extended.timestamp() + else: + attrs['timestamp'] = self._current_datetime.timestamp() return attrs - @asyncio.coroutine def async_set_datetime(self, date_val, time_val): """Set a new date / time.""" - if self._has_date and self._has_time and date_val and time_val: + if self.has_date and self.has_time and date_val and time_val: self._current_datetime = datetime.datetime.combine(date_val, time_val) - elif self._has_date and not self._has_time and date_val: + elif self.has_date and not self.has_time and date_val: self._current_datetime = date_val - if self._has_time and not self._has_date and time_val: + if self.has_time and not self.has_date and time_val: self._current_datetime = time_val - yield from self.async_update_ha_state() + self.async_schedule_update_ha_state() diff --git a/homeassistant/components/input_number.py b/homeassistant/components/input_number.py index e18169fca73169..2f25ca143b8efb 100644 --- a/homeassistant/components/input_number.py +++ b/homeassistant/components/input_number.py @@ -82,19 +82,6 @@ def _cv_input_number(cfg): }, required=True, extra=vol.ALLOW_EXTRA) -SERVICE_TO_METHOD = { - SERVICE_SET_VALUE: { - 'method': 'async_set_value', - 'schema': SERVICE_SET_VALUE_SCHEMA}, - SERVICE_INCREMENT: { - 'method': 'async_increment', - 'schema': SERVICE_DEFAULT_SCHEMA}, - SERVICE_DECREMENT: { - 'method': 'async_decrement', - 'schema': SERVICE_DEFAULT_SCHEMA}, -} - - @bind_hass def set_value(hass, entity_id, value): """Set input_number to value.""" @@ -144,28 +131,20 @@ def async_setup(hass, config): if not entities: return False - @asyncio.coroutine - def async_handle_service(service): - """Handle calls to input_number services.""" - target_inputs = component.async_extract_from_service(service) - method = SERVICE_TO_METHOD.get(service.service) - params = service.data.copy() - params.pop(ATTR_ENTITY_ID, None) - - # call method - update_tasks = [] - for target_input in target_inputs: - yield from getattr(target_input, method['method'])(**params) - if not target_input.should_poll: - continue - update_tasks.append(target_input.async_update_ha_state(True)) - - if update_tasks: - yield from asyncio.wait(update_tasks, loop=hass.loop) - - for service, data in SERVICE_TO_METHOD.items(): - hass.services.async_register( - DOMAIN, service, async_handle_service, schema=data['schema']) + component.async_register_entity_service( + SERVICE_SET_VALUE, SERVICE_SET_VALUE_SCHEMA, + 'async_set_value' + ) + + component.async_register_entity_service( + SERVICE_INCREMENT, SERVICE_DEFAULT_SCHEMA, + 'async_increment' + ) + + component.async_register_entity_service( + SERVICE_DECREMENT, SERVICE_DEFAULT_SCHEMA, + 'async_decrement' + ) yield from component.async_add_entities(entities) return True diff --git a/homeassistant/components/input_select.py b/homeassistant/components/input_select.py index f16b029c1d71c0..04e9b04787c643 100644 --- a/homeassistant/components/input_select.py +++ b/homeassistant/components/input_select.py @@ -129,61 +129,25 @@ def async_setup(hass, config): if not entities: return False - @asyncio.coroutine - def async_select_option_service(call): - """Handle a calls to the input select option service.""" - target_inputs = component.async_extract_from_service(call) - - tasks = [input_select.async_select_option(call.data[ATTR_OPTION]) - for input_select in target_inputs] - if tasks: - yield from asyncio.wait(tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SELECT_OPTION, async_select_option_service, - schema=SERVICE_SELECT_OPTION_SCHEMA) - - @asyncio.coroutine - def async_select_next_service(call): - """Handle a calls to the input select next service.""" - target_inputs = component.async_extract_from_service(call) - - tasks = [input_select.async_offset_index(1) - for input_select in target_inputs] - if tasks: - yield from asyncio.wait(tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SELECT_NEXT, async_select_next_service, - schema=SERVICE_SELECT_NEXT_SCHEMA) - - @asyncio.coroutine - def async_select_previous_service(call): - """Handle a calls to the input select previous service.""" - target_inputs = component.async_extract_from_service(call) - - tasks = [input_select.async_offset_index(-1) - for input_select in target_inputs] - if tasks: - yield from asyncio.wait(tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SELECT_PREVIOUS, async_select_previous_service, - schema=SERVICE_SELECT_PREVIOUS_SCHEMA) - - @asyncio.coroutine - def async_set_options_service(call): - """Handle a calls to the set options service.""" - target_inputs = component.async_extract_from_service(call) - - tasks = [input_select.async_set_options(call.data[ATTR_OPTIONS]) - for input_select in target_inputs] - if tasks: - yield from asyncio.wait(tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_OPTIONS, async_set_options_service, - schema=SERVICE_SET_OPTIONS_SCHEMA) + component.async_register_entity_service( + SERVICE_SELECT_OPTION, SERVICE_SELECT_OPTION_SCHEMA, + 'async_select_option' + ) + + component.async_register_entity_service( + SERVICE_SELECT_NEXT, SERVICE_SELECT_NEXT_SCHEMA, + lambda entity, call: entity.async_offset_index(1) + ) + + component.async_register_entity_service( + SERVICE_SELECT_PREVIOUS, SERVICE_SELECT_PREVIOUS_SCHEMA, + lambda entity, call: entity.async_offset_index(-1) + ) + + component.async_register_entity_service( + SERVICE_SET_OPTIONS, SERVICE_SET_OPTIONS_SCHEMA, + 'async_set_options' + ) yield from component.async_add_entities(entities) return True diff --git a/homeassistant/components/input_text.py b/homeassistant/components/input_text.py index 6433a01fb6d424..2cb4f58a13096e 100644 --- a/homeassistant/components/input_text.py +++ b/homeassistant/components/input_text.py @@ -107,19 +107,10 @@ def async_setup(hass, config): if not entities: return False - @asyncio.coroutine - def async_set_value_service(call): - """Handle a calls to the input box services.""" - target_inputs = component.async_extract_from_service(call) - - tasks = [input_text.async_set_value(call.data[ATTR_VALUE]) - for input_text in target_inputs] - if tasks: - yield from asyncio.wait(tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_SET_VALUE, async_set_value_service, - schema=SERVICE_SET_VALUE_SCHEMA) + component.async_register_entity_service( + SERVICE_SET_VALUE, SERVICE_SET_VALUE_SCHEMA, + 'async_set_value' + ) yield from component.async_add_entities(entities) return True diff --git a/homeassistant/components/insteon_plm/__init__.py b/homeassistant/components/insteon/__init__.py similarity index 73% rename from homeassistant/components/insteon_plm/__init__.py rename to homeassistant/components/insteon/__init__.py index 055015b74f5de2..212cdbac3b8bc1 100644 --- a/homeassistant/components/insteon_plm/__init__.py +++ b/homeassistant/components/insteon/__init__.py @@ -1,8 +1,8 @@ """ -Support for INSTEON PowerLinc Modem. +Support for INSTEON Modems (PLM and Hub). For more details about this component, please refer to the documentation at -https://home-assistant.io/components/insteon_plm/ +https://home-assistant.io/components/insteon/ """ import asyncio import collections @@ -12,18 +12,24 @@ from homeassistant.core import callback from homeassistant.const import (CONF_PORT, EVENT_HOMEASSISTANT_STOP, CONF_PLATFORM, - CONF_ENTITY_ID) + CONF_ENTITY_ID, + CONF_HOST) import homeassistant.helpers.config_validation as cv from homeassistant.helpers import discovery from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['insteonplm==0.11.7'] +REQUIREMENTS = ['insteonplm==0.12.3'] _LOGGER = logging.getLogger(__name__) -DOMAIN = 'insteon_plm' +DOMAIN = 'insteon' +CONF_IP_PORT = 'ip_port' +CONF_HUB_USERNAME = 'username' +CONF_HUB_PASSWORD = 'password' CONF_OVERRIDE = 'device_override' +CONF_PLM_HUB_MSG = ('Must configure either a PLM port or a Hub host, username ' + 'and password') CONF_ADDRESS = 'address' CONF_CAT = 'cat' CONF_SUBCAT = 'subcat' @@ -56,8 +62,8 @@ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] BUTTON_PRESSED_STATE_NAME = 'onLevelButton' -EVENT_BUTTON_ON = 'insteon_plm.button_on' -EVENT_BUTTON_OFF = 'insteon_plm.button_off' +EVENT_BUTTON_ON = 'insteon.button_on' +EVENT_BUTTON_OFF = 'insteon.button_off' EVENT_CONF_BUTTON = 'button' CONF_DEVICE_OVERRIDE_SCHEMA = vol.All( @@ -79,17 +85,34 @@ })) CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Required(CONF_PORT): cv.string, - vol.Optional(CONF_OVERRIDE): vol.All( - cv.ensure_list_csv, [CONF_DEVICE_OVERRIDE_SCHEMA]), - vol.Optional(CONF_X10_ALL_UNITS_OFF): vol.In(HOUSECODES), - vol.Optional(CONF_X10_ALL_LIGHTS_ON): vol.In(HOUSECODES), - vol.Optional(CONF_X10_ALL_LIGHTS_OFF): vol.In(HOUSECODES), - vol.Optional(CONF_X10): vol.All( - cv.ensure_list_csv, [CONF_X10_SCHEMA]) - }) -}, extra=vol.ALLOW_EXTRA) + DOMAIN: vol.All( + vol.Schema( + {vol.Exclusive(CONF_PORT, 'plm_or_hub', + msg=CONF_PLM_HUB_MSG): cv.isdevice, + vol.Exclusive(CONF_HOST, 'plm_or_hub', + msg=CONF_PLM_HUB_MSG): cv.string, + vol.Optional(CONF_IP_PORT, default=25105): int, + vol.Optional(CONF_HUB_USERNAME): cv.string, + vol.Optional(CONF_HUB_PASSWORD): cv.string, + vol.Optional(CONF_OVERRIDE): vol.All( + cv.ensure_list_csv, [CONF_DEVICE_OVERRIDE_SCHEMA]), + vol.Optional(CONF_X10_ALL_UNITS_OFF): vol.In(HOUSECODES), + vol.Optional(CONF_X10_ALL_LIGHTS_ON): vol.In(HOUSECODES), + vol.Optional(CONF_X10_ALL_LIGHTS_OFF): vol.In(HOUSECODES), + vol.Optional(CONF_X10): vol.All(cv.ensure_list_csv, + [CONF_X10_SCHEMA]) + }, extra=vol.ALLOW_EXTRA, required=True), + cv.has_at_least_one_key(CONF_PORT, CONF_HOST), + vol.Schema( + {vol.Inclusive(CONF_HOST, 'hub', + msg=CONF_PLM_HUB_MSG): cv.string, + vol.Inclusive(CONF_HUB_USERNAME, 'hub', + msg=CONF_PLM_HUB_MSG): cv.string, + vol.Inclusive(CONF_HUB_PASSWORD, 'hub', + msg=CONF_PLM_HUB_MSG): cv.string, + }, extra=vol.ALLOW_EXTRA, required=True)) + }, extra=vol.ALLOW_EXTRA) + ADD_ALL_LINK_SCHEMA = vol.Schema({ vol.Required(SRV_ALL_LINK_GROUP): vol.Range(min=0, max=255), @@ -116,14 +139,18 @@ @asyncio.coroutine def async_setup(hass, config): - """Set up the connection to the PLM.""" + """Set up the connection to the modem.""" import insteonplm ipdb = IPDB() - plm = None + insteon_modem = None conf = config[DOMAIN] port = conf.get(CONF_PORT) + host = conf.get(CONF_HOST) + ip_port = conf.get(CONF_IP_PORT) + username = conf.get(CONF_HUB_USERNAME) + password = conf.get(CONF_HUB_PASSWORD) overrides = conf.get(CONF_OVERRIDE, []) x10_devices = conf.get(CONF_X10, []) x10_all_units_off_housecode = conf.get(CONF_X10_ALL_UNITS_OFF) @@ -131,7 +158,7 @@ def async_setup(hass, config): x10_all_lights_off_housecode = conf.get(CONF_X10_ALL_LIGHTS_OFF) @callback - def async_plm_new_device(device): + def async_new_insteon_device(device): """Detect device from transport to be delegated to platform.""" for state_key in device.states: platform_info = ipdb[device.states[state_key]] @@ -143,7 +170,7 @@ def async_plm_new_device(device): _fire_button_on_off_event) else: - _LOGGER.info("New INSTEON PLM device: %s (%s) %s", + _LOGGER.info("New INSTEON device: %s (%s) %s", device.address, device.states[state_key].name, platform) @@ -160,12 +187,12 @@ def add_all_link(service): group = service.data.get(SRV_ALL_LINK_GROUP) mode = service.data.get(SRV_ALL_LINK_MODE) link_mode = 1 if mode.lower() == SRV_CONTROLLER else 0 - plm.start_all_linking(link_mode, group) + insteon_modem.start_all_linking(link_mode, group) def del_all_link(service): """Delete an INSTEON All-Link between two devices.""" group = service.data.get(SRV_ALL_LINK_GROUP) - plm.start_all_linking(255, group) + insteon_modem.start_all_linking(255, group) def load_aldb(service): """Load the device All-Link database.""" @@ -194,22 +221,22 @@ def print_im_aldb(service): """Print the All-Link Database for a device.""" # For now this sends logs to the log file. # Furture direction is to create an INSTEON control panel. - print_aldb_to_log(plm.aldb) + print_aldb_to_log(insteon_modem.aldb) def x10_all_units_off(service): """Send the X10 All Units Off command.""" housecode = service.data.get(SRV_HOUSECODE) - plm.x10_all_units_off(housecode) + insteon_modem.x10_all_units_off(housecode) def x10_all_lights_off(service): """Send the X10 All Lights Off command.""" housecode = service.data.get(SRV_HOUSECODE) - plm.x10_all_lights_off(housecode) + insteon_modem.x10_all_lights_off(housecode) def x10_all_lights_on(service): """Send the X10 All Lights On command.""" housecode = service.data.get(SRV_HOUSECODE) - plm.x10_all_lights_on(housecode) + insteon_modem.x10_all_lights_on(housecode) def _register_services(): hass.services.register(DOMAIN, SRV_ADD_ALL_LINK, add_all_link, @@ -231,11 +258,11 @@ def _register_services(): hass.services.register(DOMAIN, SRV_X10_ALL_LIGHTS_ON, x10_all_lights_on, schema=X10_HOUSECODE_SCHEMA) - _LOGGER.debug("Insteon_plm Services registered") + _LOGGER.debug("Insteon Services registered") def _fire_button_on_off_event(address, group, val): # Firing an event when a button is pressed. - device = plm.devices[address.hex] + device = insteon_modem.devices[address.hex] state_name = device.states[group].name button = ("" if state_name == BUTTON_PRESSED_STATE_NAME else state_name[-1].lower()) @@ -250,13 +277,23 @@ def _fire_button_on_off_event(address, group, val): event, address.hex, button) hass.bus.fire(event, schema) - _LOGGER.info("Looking for PLM on %s", port) - conn = yield from insteonplm.Connection.create( - device=port, - loop=hass.loop, - workdir=hass.config.config_dir) - - plm = conn.protocol + if host: + _LOGGER.info('Connecting to Insteon Hub on %s', host) + conn = yield from insteonplm.Connection.create( + host=host, + port=ip_port, + username=username, + password=password, + loop=hass.loop, + workdir=hass.config.config_dir) + else: + _LOGGER.info("Looking for Insteon PLM on %s", port) + conn = yield from insteonplm.Connection.create( + device=port, + loop=hass.loop, + workdir=hass.config.config_dir) + + insteon_modem = conn.protocol for device_override in overrides: # @@ -265,32 +302,32 @@ def _fire_button_on_off_event(address, group, val): address = device_override.get('address') for prop in device_override: if prop in [CONF_CAT, CONF_SUBCAT]: - plm.devices.add_override(address, prop, - device_override[prop]) + insteon_modem.devices.add_override(address, prop, + device_override[prop]) elif prop in [CONF_FIRMWARE, CONF_PRODUCT_KEY]: - plm.devices.add_override(address, CONF_PRODUCT_KEY, - device_override[prop]) + insteon_modem.devices.add_override(address, CONF_PRODUCT_KEY, + device_override[prop]) hass.data[DOMAIN] = {} - hass.data[DOMAIN]['plm'] = plm + hass.data[DOMAIN]['modem'] = insteon_modem hass.data[DOMAIN]['entities'] = {} hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, conn.close) - plm.devices.add_device_callback(async_plm_new_device) + insteon_modem.devices.add_device_callback(async_new_insteon_device) if x10_all_units_off_housecode: - device = plm.add_x10_device(x10_all_units_off_housecode, - 20, - 'allunitsoff') + device = insteon_modem.add_x10_device(x10_all_units_off_housecode, + 20, + 'allunitsoff') if x10_all_lights_on_housecode: - device = plm.add_x10_device(x10_all_lights_on_housecode, - 21, - 'alllightson') + device = insteon_modem.add_x10_device(x10_all_lights_on_housecode, + 21, + 'alllightson') if x10_all_lights_off_housecode: - device = plm.add_x10_device(x10_all_lights_off_housecode, - 22, - 'alllightsoff') + device = insteon_modem.add_x10_device(x10_all_lights_off_housecode, + 22, + 'alllightsoff') for device in x10_devices: housecode = device.get(CONF_HOUSECODE) unitcode = device.get(CONF_UNITCODE) @@ -300,11 +337,11 @@ def _fire_button_on_off_event(address, group, val): x10_type = 'dimmable' elif device.get(CONF_PLATFORM) == 'binary_sensor': x10_type = 'sensor' - _LOGGER.debug("Adding X10 device to insteonplm: %s %d %s", + _LOGGER.debug("Adding X10 device to Insteon: %s %d %s", housecode, unitcode, x10_type) - device = plm.add_x10_device(housecode, - unitcode, - x10_type) + device = insteon_modem.add_x10_device(housecode, + unitcode, + x10_type) if device and hasattr(device.states[0x01], 'steps'): device.states[0x01].steps = steps @@ -324,11 +361,14 @@ def __init__(self): from insteonplm.states.onOff import (OnOffSwitch, OnOffSwitch_OutletTop, OnOffSwitch_OutletBottom, - OpenClosedRelay) + OpenClosedRelay, + OnOffKeypadA, + OnOffKeypad) from insteonplm.states.dimmable import (DimmableSwitch, DimmableSwitch_Fan, - DimmableRemote) + DimmableRemote, + DimmableKeypadA) from insteonplm.states.sensor import (VariableSensor, OnOffSensor, @@ -347,6 +387,8 @@ def __init__(self): State(OnOffSwitch_OutletBottom, 'switch'), State(OpenClosedRelay, 'switch'), State(OnOffSwitch, 'switch'), + State(OnOffKeypadA, 'switch'), + State(OnOffKeypad, 'switch'), State(LeakSensorDryWet, 'binary_sensor'), State(IoLincSensor, 'binary_sensor'), @@ -357,6 +399,7 @@ def __init__(self): State(DimmableSwitch_Fan, 'fan'), State(DimmableSwitch, 'light'), State(DimmableRemote, 'on_off_events'), + State(DimmableKeypadA, 'light'), State(X10DimmableSwitch, 'light'), State(X10OnOffSwitch, 'switch'), @@ -382,11 +425,11 @@ def __getitem__(self, key): return None -class InsteonPLMEntity(Entity): +class InsteonEntity(Entity): """INSTEON abstract base entity.""" def __init__(self, device, state_key): - """Initialize the INSTEON PLM binary sensor.""" + """Initialize the INSTEON binary sensor.""" self._insteon_device_state = device.states[state_key] self._insteon_device = device self._insteon_device.aldb.add_loaded_callback(self._aldb_loaded) @@ -429,11 +472,17 @@ def device_state_attributes(self): @callback def async_entity_update(self, deviceid, statename, val): """Receive notification from transport that new data exists.""" + _LOGGER.debug('Received update for device %s group %d statename %s', + self.address, self.group, + self._insteon_device_state.name) self.async_schedule_update_ha_state() @asyncio.coroutine def async_added_to_hass(self): """Register INSTEON update events.""" + _LOGGER.debug('Tracking updates for device %s group %d statename %s', + self.address, self.group, + self._insteon_device_state.name) self._insteon_device_state.register_updates( self.async_entity_update) self.hass.data[DOMAIN]['entities'][self.entity_id] = self @@ -460,7 +509,7 @@ def print_aldb_to_log(aldb): _LOGGER.info('ALDB load status is %s', aldb.status.name) if aldb.status not in [ALDBStatus.LOADED, ALDBStatus.PARTIAL]: _LOGGER.warning('Device All-Link database not loaded') - _LOGGER.warning('Use service insteon_plm.load_aldb first') + _LOGGER.warning('Use service insteon.load_aldb first') return _LOGGER.info('RecID In Use Mode HWM Group Address Data 1 Data 2 Data 3') diff --git a/homeassistant/components/insteon_plm/services.yaml b/homeassistant/components/insteon/services.yaml similarity index 100% rename from homeassistant/components/insteon_plm/services.yaml rename to homeassistant/components/insteon/services.yaml diff --git a/homeassistant/components/insteon_local.py b/homeassistant/components/insteon_local.py index a18d4e0aa14736..003714d0f944e5 100644 --- a/homeassistant/components/insteon_local.py +++ b/homeassistant/components/insteon_local.py @@ -5,82 +5,24 @@ https://home-assistant.io/components/insteon_local/ """ import logging -import os - -import requests -import voluptuous as vol - -from homeassistant.const import ( - CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_TIMEOUT, CONF_USERNAME) -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.discovery import load_platform - -REQUIREMENTS = ['insteonlocal==0.53'] _LOGGER = logging.getLogger(__name__) -DEFAULT_PORT = 25105 -DEFAULT_TIMEOUT = 10 -DOMAIN = 'insteon_local' - -INSTEON_CACHE = '.insteon_local_cache' - -INSTEON_PLATFORMS = [ - 'light', - 'switch', - 'fan', -] - -CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Required(CONF_HOST): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Required(CONF_USERNAME): cv.string, - vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, - vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, - }) -}, extra=vol.ALLOW_EXTRA) - def setup(hass, config): - """Set up the local Insteon hub.""" - from insteonlocal.Hub import Hub - - conf = config[DOMAIN] - username = conf.get(CONF_USERNAME) - password = conf.get(CONF_PASSWORD) - host = conf.get(CONF_HOST) - port = conf.get(CONF_PORT) - timeout = conf.get(CONF_TIMEOUT) - - try: - if not os.path.exists(hass.config.path(INSTEON_CACHE)): - os.makedirs(hass.config.path(INSTEON_CACHE)) - - insteonhub = Hub(host, username, password, port, timeout, _LOGGER, - hass.config.path(INSTEON_CACHE)) - - # Check for successful connection - insteonhub.get_buffer_status() - except requests.exceptions.ConnectTimeout: - _LOGGER.error("Could not connect", exc_info=True) - return False - except requests.exceptions.ConnectionError: - _LOGGER.error("Could not connect", exc_info=True) - return False - except requests.exceptions.RequestException: - if insteonhub.http_code == 401: - _LOGGER.error("Bad username or password for Insteon_local hub") - else: - _LOGGER.error("Error on Insteon_local hub check", exc_info=True) - return False - - linked = insteonhub.get_linked() - - hass.data['insteon_local'] = insteonhub - - for insteon_platform in INSTEON_PLATFORMS: - load_platform(hass, insteon_platform, DOMAIN, {'linked': linked}, - config) - - return True + """Set up the insteon_local component. + + This component is deprecated as of release 0.77 and should be removed in + release 0.90. + """ + _LOGGER.warning('The insteon_local component has been replaced by ' + 'the insteon component') + _LOGGER.warning('Please see https://home-assistant.io/components/insteon') + + hass.components.persistent_notification.create( + 'insteon_local has been replaced by the insteon component.
' + 'Please see https://home-assistant.io/components/insteon', + title='insteon_local Component Deactivated', + notification_id='insteon_local') + + return False diff --git a/homeassistant/components/insteon_plm.py b/homeassistant/components/insteon_plm.py new file mode 100644 index 00000000000000..b89e5679a63f86 --- /dev/null +++ b/homeassistant/components/insteon_plm.py @@ -0,0 +1,30 @@ +""" +Support for INSTEON PowerLinc Modem. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/insteon_plm/ +""" +import asyncio +import logging + +_LOGGER = logging.getLogger(__name__) + + +@asyncio.coroutine +def async_setup(hass, config): + """Set up the insteon_plm component. + + This component is deprecated as of release 0.77 and should be removed in + release 0.90. + """ + _LOGGER.warning('The insteon_plm component has been replaced by ' + 'the insteon component') + _LOGGER.warning('Please see https://home-assistant.io/components/insteon') + + hass.components.persistent_notification.create( + 'insteon_plm has been replaced by the insteon component.
' + 'Please see https://home-assistant.io/components/insteon', + title='insteon_plm Component Deactivated', + notification_id='insteon_plm') + + return False diff --git a/homeassistant/components/iota.py b/homeassistant/components/iota.py index ada70f8a9ebdea..717213da9acca1 100644 --- a/homeassistant/components/iota.py +++ b/homeassistant/components/iota.py @@ -57,7 +57,7 @@ class IotaDevice(Entity): """Representation of a IOTA device.""" def __init__(self, name, seed, iri, is_testnet=False): - """Initialisation of the IOTA device.""" + """Initialise the IOTA device.""" self._name = name self._seed = seed self.iri = iri diff --git a/homeassistant/components/knx.py b/homeassistant/components/knx.py index 5b3af3029b4f4a..b15963fa6bd656 100644 --- a/homeassistant/components/knx.py +++ b/homeassistant/components/knx.py @@ -334,7 +334,7 @@ def async_register(self): self.hass, self.entity_id, self._async_entity_changed) async def _async_entity_changed(self, entity_id, old_state, new_state): - """Callback after entity changed.""" + """Handle entity change.""" if new_state is None: return await self.device.set(float(new_state.state)) diff --git a/homeassistant/components/konnected.py b/homeassistant/components/konnected.py index a3e9ff86ed012a..3df285863139ab 100644 --- a/homeassistant/components/konnected.py +++ b/homeassistant/components/konnected.py @@ -16,7 +16,7 @@ from homeassistant.components.discovery import SERVICE_KONNECTED from homeassistant.components.http import HomeAssistantView from homeassistant.const import ( - HTTP_BAD_REQUEST, HTTP_INTERNAL_SERVER_ERROR, HTTP_UNAUTHORIZED, + HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_UNAUTHORIZED, CONF_DEVICES, CONF_BINARY_SENSORS, CONF_SWITCHES, CONF_HOST, CONF_PORT, CONF_ID, CONF_NAME, CONF_TYPE, CONF_PIN, CONF_ZONE, CONF_ACCESS_TOKEN, ATTR_ENTITY_ID, ATTR_STATE) @@ -32,6 +32,10 @@ CONF_ACTIVATION = 'activation' CONF_API_HOST = 'api_host' +CONF_MOMENTARY = 'momentary' +CONF_PAUSE = 'pause' +CONF_REPEAT = 'repeat' + STATE_LOW = 'low' STATE_HIGH = 'high' @@ -53,7 +57,13 @@ vol.Exclusive(CONF_ZONE, 'a_pin'): vol.Any(*ZONE_TO_PIN), vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_ACTIVATION, default=STATE_HIGH): - vol.All(vol.Lower, vol.Any(STATE_HIGH, STATE_LOW)) + vol.All(vol.Lower, vol.Any(STATE_HIGH, STATE_LOW)), + vol.Optional(CONF_MOMENTARY): + vol.All(vol.Coerce(int), vol.Range(min=10)), + vol.Optional(CONF_PAUSE): + vol.All(vol.Coerce(int), vol.Range(min=10)), + vol.Optional(CONF_REPEAT): + vol.All(vol.Coerce(int), vol.Range(min=-1)), }), cv.has_at_least_one_key(CONF_PIN, CONF_ZONE) ) @@ -64,7 +74,7 @@ vol.Required(CONF_ACCESS_TOKEN): cv.string, vol.Optional(CONF_API_HOST): vol.Url(), vol.Required(CONF_DEVICES): [{ - vol.Required(CONF_ID): cv.string, + vol.Required(CONF_ID): cv.matches_regex("[0-9a-f]{12}"), vol.Optional(CONF_BINARY_SENSORS): vol.All( cv.ensure_list, [_BINARY_SENSOR_SCHEMA]), vol.Optional(CONF_SWITCHES): vol.All( @@ -97,12 +107,18 @@ async def async_setup(hass, config): def device_discovered(service, info): """Call when a Konnected device has been discovered.""" - _LOGGER.debug("Discovered a new Konnected device: %s", info) host = info.get(CONF_HOST) port = info.get(CONF_PORT) + discovered = DiscoveredDevice(hass, host, port) + if discovered.is_configured: + discovered.setup() + else: + _LOGGER.warning("Konnected device %s was discovered on the network" + " but not specified in configuration.yaml", + discovered.device_id) - device = KonnectedDevice(hass, host, port, cfg) - device.setup() + for device in cfg.get(CONF_DEVICES): + ConfiguredDevice(hass, device).save_data() discovery.async_listen( hass, @@ -114,121 +130,120 @@ def device_discovered(service, info): return True -class KonnectedDevice: - """A representation of a single Konnected device.""" +class ConfiguredDevice: + """A representation of a configured Konnected device.""" - def __init__(self, hass, host, port, config): + def __init__(self, hass, config): """Initialize the Konnected device.""" self.hass = hass - self.host = host - self.port = port - self.user_config = config - - import konnected - self.client = konnected.Client(host, str(port)) - self.status = self.client.get_status() - _LOGGER.info('Initialized Konnected device %s', self.device_id) - - def setup(self): - """Set up a newly discovered Konnected device.""" - user_config = self.config() - if user_config: - _LOGGER.debug('Configuring Konnected device %s', self.device_id) - self.save_data() - self.sync_device_config() - discovery.load_platform( - self.hass, 'binary_sensor', - DOMAIN, {'device_id': self.device_id}) - discovery.load_platform( - self.hass, 'switch', DOMAIN, - {'device_id': self.device_id}) + self.config = config @property def device_id(self): """Device id is the MAC address as string with punctuation removed.""" - return self.status['mac'].replace(':', '') - - def config(self): - """Return an object representing the user defined configuration.""" - device_id = self.device_id - valid_keys = [device_id, device_id.upper(), - device_id[6:], device_id.upper()[6:]] - configured_devices = self.user_config[CONF_DEVICES] - return next((device for device in - configured_devices if device[CONF_ID] in valid_keys), - None) + return self.config.get(CONF_ID) def save_data(self): """Save the device configuration to `hass.data`.""" sensors = {} - for entity in self.config().get(CONF_BINARY_SENSORS) or []: + for entity in self.config.get(CONF_BINARY_SENSORS) or []: if CONF_ZONE in entity: pin = ZONE_TO_PIN[entity[CONF_ZONE]] else: pin = entity[CONF_PIN] - sensor_status = next((sensor for sensor in - self.status.get('sensors') if - sensor.get(CONF_PIN) == pin), {}) - if sensor_status.get(ATTR_STATE): - initial_state = bool(int(sensor_status.get(ATTR_STATE))) - else: - initial_state = None - sensors[pin] = { CONF_TYPE: entity[CONF_TYPE], CONF_NAME: entity.get(CONF_NAME, 'Konnected {} Zone {}'.format( self.device_id[6:], PIN_TO_ZONE[pin])), - ATTR_STATE: initial_state + ATTR_STATE: None } _LOGGER.debug('Set up sensor %s (initial state: %s)', sensors[pin].get('name'), sensors[pin].get(ATTR_STATE)) - actuators = {} - for entity in self.config().get(CONF_SWITCHES) or []: + actuators = [] + for entity in self.config.get(CONF_SWITCHES) or []: if 'zone' in entity: pin = ZONE_TO_PIN[entity['zone']] else: pin = entity['pin'] - actuator_status = next((actuator for actuator in - self.status.get('actuators') if - actuator.get('pin') == pin), {}) - if actuator_status.get(ATTR_STATE): - initial_state = bool(int(actuator_status.get(ATTR_STATE))) - else: - initial_state = None - - actuators[pin] = { + act = { + CONF_PIN: pin, CONF_NAME: entity.get( CONF_NAME, 'Konnected {} Actuator {}'.format( self.device_id[6:], PIN_TO_ZONE[pin])), - ATTR_STATE: initial_state, + ATTR_STATE: None, CONF_ACTIVATION: entity[CONF_ACTIVATION], - } - _LOGGER.debug('Set up actuator %s (initial state: %s)', - actuators[pin].get(CONF_NAME), - actuators[pin].get(ATTR_STATE)) + CONF_MOMENTARY: entity.get(CONF_MOMENTARY), + CONF_PAUSE: entity.get(CONF_PAUSE), + CONF_REPEAT: entity.get(CONF_REPEAT)} + actuators.append(act) + _LOGGER.debug('Set up actuator %s', act) device_data = { - 'client': self.client, CONF_BINARY_SENSORS: sensors, CONF_SWITCHES: actuators, - CONF_HOST: self.host, - CONF_PORT: self.port, } if CONF_DEVICES not in self.hass.data[DOMAIN]: self.hass.data[DOMAIN][CONF_DEVICES] = {} - _LOGGER.debug('Storing data in hass.data[konnected]: %s', device_data) + _LOGGER.debug('Storing data in hass.data[%s][%s][%s]: %s', + DOMAIN, CONF_DEVICES, self.device_id, device_data) self.hass.data[DOMAIN][CONF_DEVICES][self.device_id] = device_data + discovery.load_platform( + self.hass, 'binary_sensor', + DOMAIN, {'device_id': self.device_id}) + discovery.load_platform( + self.hass, 'switch', DOMAIN, + {'device_id': self.device_id}) + + +class DiscoveredDevice: + """A representation of a discovered Konnected device.""" + + def __init__(self, hass, host, port): + """Initialize the Konnected device.""" + self.hass = hass + self.host = host + self.port = port + + import konnected + self.client = konnected.Client(host, str(port)) + self.status = self.client.get_status() + + def setup(self): + """Set up a newly discovered Konnected device.""" + _LOGGER.info('Discovered Konnected device %s. Open http://%s:%s in a ' + 'web browser to view device status.', + self.device_id, self.host, self.port) + self.save_data() + self.update_initial_states() + self.sync_device_config() + + def save_data(self): + """Save the discovery information to `hass.data`.""" + self.stored_configuration['client'] = self.client + self.stored_configuration['host'] = self.host + self.stored_configuration['port'] = self.port + + @property + def device_id(self): + """Device id is the MAC address as string with punctuation removed.""" + return self.status['mac'].replace(':', '') + + @property + def is_configured(self): + """Return true if device_id is specified in the configuration.""" + return bool(self.hass.data[DOMAIN][CONF_DEVICES].get(self.device_id)) + @property def stored_configuration(self): """Return the configuration stored in `hass.data` for this device.""" - return self.hass.data[DOMAIN][CONF_DEVICES][self.device_id] + return self.hass.data[DOMAIN][CONF_DEVICES].get(self.device_id) def sensor_configuration(self): """Return the configuration map for syncing sensors.""" @@ -237,11 +252,22 @@ def sensor_configuration(self): def actuator_configuration(self): """Return the configuration map for syncing actuators.""" - return [{'pin': p, + return [{'pin': data.get(CONF_PIN), 'trigger': (0 if data.get(CONF_ACTIVATION) in [0, STATE_LOW] else 1)} - for p, data in - self.stored_configuration[CONF_SWITCHES].items()] + for data in self.stored_configuration[CONF_SWITCHES]] + + def update_initial_states(self): + """Update the initial state of each sensor from status poll.""" + for sensor in self.status.get('sensors'): + entity_id = self.stored_configuration[CONF_BINARY_SENSORS]. \ + get(sensor.get(CONF_PIN), {}). \ + get(ATTR_ENTITY_ID) + + async_dispatcher_send( + self.hass, + SIGNAL_SENSOR_UPDATE.format(entity_id), + bool(sensor.get(ATTR_STATE))) def sync_device_config(self): """Sync the new pin configuration to the Konnected device.""" @@ -274,7 +300,7 @@ def sync_device_config(self): if (desired_sensor_configuration != current_sensor_configuration) or \ (current_actuator_config != desired_actuator_config) or \ (current_api_endpoint != desired_api_endpoint): - _LOGGER.debug('pushing settings to device %s', self.device_id) + _LOGGER.info('pushing settings to device %s', self.device_id) self.client.put_settings( desired_sensor_configuration, desired_actuator_config, @@ -320,8 +346,7 @@ async def put(self, request: Request, device_id, if device is None: return self.json_message('unregistered device', status_code=HTTP_BAD_REQUEST) - pin_data = device[CONF_BINARY_SENSORS].get(pin_num) or \ - device[CONF_SWITCHES].get(pin_num) + pin_data = device[CONF_BINARY_SENSORS].get(pin_num) if pin_data is None: return self.json_message('unregistered sensor/actuator', @@ -330,7 +355,7 @@ async def put(self, request: Request, device_id, entity_id = pin_data.get(ATTR_ENTITY_ID) if entity_id is None: return self.json_message('uninitialized sensor/actuator', - status_code=HTTP_INTERNAL_SERVER_ERROR) + status_code=HTTP_NOT_FOUND) async_dispatcher_send( hass, SIGNAL_SENSOR_UPDATE.format(entity_id), state) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 8b4b213771160c..bc7f136322b6e4 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -332,8 +332,8 @@ async def async_setup(hass, config): if not profiles_valid: return False - async def async_handle_light_service(service): - """Handle a turn light on or off service call.""" + async def async_handle_light_on_service(service): + """Handle a turn light on service call.""" # Get the validated data params = service.data.copy() @@ -345,39 +345,38 @@ async def async_handle_light_service(service): update_tasks = [] for light in target_lights: - if service.service == SERVICE_TURN_ON: - pars = params - if not pars: - pars = params.copy() - pars[ATTR_PROFILE] = Profiles.get_default(light.entity_id) - preprocess_turn_on_alternatives(pars) - await light.async_turn_on(**pars) - elif service.service == SERVICE_TURN_OFF: - await light.async_turn_off(**params) - else: - await light.async_toggle(**params) + light.async_set_context(service.context) + + pars = params + if not pars: + pars = params.copy() + pars[ATTR_PROFILE] = Profiles.get_default(light.entity_id) + preprocess_turn_on_alternatives(pars) + await light.async_turn_on(**pars) if not light.should_poll: continue update_tasks.append( - light.async_update_ha_state(True, service.context)) + light.async_update_ha_state(True)) if update_tasks: await asyncio.wait(update_tasks, loop=hass.loop) # Listen for light on and light off service calls. hass.services.async_register( - DOMAIN, SERVICE_TURN_ON, async_handle_light_service, + DOMAIN, SERVICE_TURN_ON, async_handle_light_on_service, schema=LIGHT_TURN_ON_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_TURN_OFF, async_handle_light_service, - schema=LIGHT_TURN_OFF_SCHEMA) + component.async_register_entity_service( + SERVICE_TURN_OFF, LIGHT_TURN_OFF_SCHEMA, + 'async_turn_off' + ) - hass.services.async_register( - DOMAIN, SERVICE_TOGGLE, async_handle_light_service, - schema=LIGHT_TOGGLE_SCHEMA) + component.async_register_entity_service( + SERVICE_TOGGLE, LIGHT_TOGGLE_SCHEMA, + 'async_toggle' + ) hass.helpers.intent.async_register(SetIntentHandler()) @@ -385,7 +384,7 @@ async def async_handle_light_service(service): async def async_setup_entry(hass, entry): - """Setup a config entry.""" + """Set up a config entry.""" return await hass.data[DOMAIN].async_setup_entry(entry) diff --git a/homeassistant/components/light/abode.py b/homeassistant/components/light/abode.py index 431f5d12ff0d56..69314b63a4bcc1 100644 --- a/homeassistant/components/light/abode.py +++ b/homeassistant/components/light/abode.py @@ -18,7 +18,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Abode light devices.""" import abodepy.helpers.constants as CONST @@ -37,7 +37,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data.devices.extend(devices) - add_devices(devices) + add_entities(devices) class AbodeLight(AbodeDevice, Light): diff --git a/homeassistant/components/light/ads.py b/homeassistant/components/light/ads.py index 41709a4692b6db..65569f6b2d5fb3 100644 --- a/homeassistant/components/light/ads.py +++ b/homeassistant/components/light/ads.py @@ -26,7 +26,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the light platform for ADS.""" ads_hub = hass.data.get(DATA_ADS) @@ -34,8 +34,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ads_var_brightness = config.get(CONF_ADS_VAR_BRIGHTNESS) name = config.get(CONF_NAME) - add_devices([AdsLight(ads_hub, ads_var_enable, ads_var_brightness, - name)], True) + add_entities([AdsLight(ads_hub, ads_var_enable, ads_var_brightness, + name)], True) class AdsLight(Light): diff --git a/homeassistant/components/light/avion.py b/homeassistant/components/light/avion.py index be608ea477668b..d6e6776ea413d1 100644 --- a/homeassistant/components/light/avion.py +++ b/homeassistant/components/light/avion.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an Avion switch.""" # pylint: disable=no-member import avion @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): device['address'] = address lights.append(AvionLight(device)) - add_devices(lights) + add_entities(lights) class AvionLight(Light): diff --git a/homeassistant/components/light/blinksticklight.py b/homeassistant/components/light/blinksticklight.py index bca587074b01c4..e145005a5a7c7d 100644 --- a/homeassistant/components/light/blinksticklight.py +++ b/homeassistant/components/light/blinksticklight.py @@ -31,7 +31,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Blinkstick device specified by serial number.""" from blinkstick import blinkstick @@ -40,7 +40,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): stick = blinkstick.find_by_serial(serial) - add_devices([BlinkStickLight(stick, name)], True) + add_entities([BlinkStickLight(stick, name)], True) class BlinkStickLight(Light): diff --git a/homeassistant/components/light/blinkt.py b/homeassistant/components/light/blinkt.py index 7035320945a0ef..d8f819492a5eca 100644 --- a/homeassistant/components/light/blinkt.py +++ b/homeassistant/components/light/blinkt.py @@ -28,7 +28,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Blinkt Light platform.""" # pylint: disable=no-member import blinkt @@ -38,7 +38,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name = config.get(CONF_NAME) - add_devices([ + add_entities([ BlinktLight(blinkt, name, index) for index in range(blinkt.NUM_PIXELS) ]) diff --git a/homeassistant/components/light/deconz.py b/homeassistant/components/light/deconz.py index 20160edf8066f3..412cf8693e59ae 100644 --- a/homeassistant/components/light/deconz.py +++ b/homeassistant/components/light/deconz.py @@ -6,26 +6,27 @@ """ from homeassistant.components.deconz.const import ( CONF_ALLOW_DECONZ_GROUPS, DOMAIN as DATA_DECONZ, - DATA_DECONZ_ID, DATA_DECONZ_UNSUB, SWITCH_TYPES) + DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DECONZ_DOMAIN, SWITCH_TYPES) from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_FLASH, ATTR_HS_COLOR, ATTR_TRANSITION, EFFECT_COLORLOOP, FLASH_LONG, FLASH_SHORT, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, SUPPORT_FLASH, SUPPORT_TRANSITION, Light) from homeassistant.core import callback +from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE from homeassistant.helpers.dispatcher import async_dispatcher_connect import homeassistant.util.color as color_util DEPENDENCIES = ['deconz'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Old way of setting up deCONZ lights and group.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the deCONZ lights and groups from a config entry.""" @callback def async_add_light(lights): @@ -34,7 +35,7 @@ def async_add_light(lights): for light in lights: if light.type not in SWITCH_TYPES: entities.append(DeconzLight(light)) - async_add_devices(entities, True) + async_add_entities(entities, True) hass.data[DATA_DECONZ_UNSUB].append( async_dispatcher_connect(hass, 'deconz_new_light', async_add_light)) @@ -47,7 +48,7 @@ def async_add_group(groups): for group in groups: if group.lights and allow_group: entities.append(DeconzLight(group)) - async_add_devices(entities, True) + async_add_entities(entities, True) hass.data[DATA_DECONZ_UNSUB].append( async_dispatcher_connect(hass, 'deconz_new_group', async_add_group)) @@ -155,7 +156,7 @@ async def async_turn_on(self, **kwargs): data['bri'] = kwargs[ATTR_BRIGHTNESS] if ATTR_TRANSITION in kwargs: - data['transitiontime'] = int(kwargs[ATTR_TRANSITION]) * 10 + data['transitiontime'] = int(kwargs[ATTR_TRANSITION] * 10) if ATTR_FLASH in kwargs: if kwargs[ATTR_FLASH] == FLASH_SHORT: @@ -179,7 +180,7 @@ async def async_turn_off(self, **kwargs): if ATTR_TRANSITION in kwargs: data['bri'] = 0 - data['transitiontime'] = int(kwargs[ATTR_TRANSITION]) * 10 + data['transitiontime'] = int(kwargs[ATTR_TRANSITION] * 10) if ATTR_FLASH in kwargs: if kwargs[ATTR_FLASH] == FLASH_SHORT: @@ -199,3 +200,19 @@ def device_state_attributes(self): if self._light.type == 'LightGroup': attributes['all_on'] = self._light.all_on return attributes + + @property + def device_info(self): + """Return a device description for device registry.""" + if (self._light.uniqueid is None or + self._light.uniqueid.count(':') != 7): + return None + serial = self._light.uniqueid.split('-', 1)[0] + return { + 'connections': {(CONNECTION_ZIGBEE, serial)}, + 'identifiers': {(DECONZ_DOMAIN, serial)}, + 'manufacturer': self._light.manufacturer, + 'model': self._light.modelid, + 'name': self._light.name, + 'sw_version': self._light.swversion, + } diff --git a/homeassistant/components/light/decora.py b/homeassistant/components/light/decora.py index 85d9180c59bcae..5de8e03aea5ee8 100644 --- a/homeassistant/components/light/decora.py +++ b/homeassistant/components/light/decora.py @@ -56,7 +56,7 @@ def wrapper_retry(device, *args, **kwargs): return wrapper_retry -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an Decora switch.""" lights = [] for address, device_config in config[CONF_DEVICES].items(): @@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): light = DecoraLight(device) lights.append(light) - add_devices(lights) + add_entities(lights) class DecoraLight(Light): diff --git a/homeassistant/components/light/decora_wifi.py b/homeassistant/components/light/decora_wifi.py index 17003d51610c13..da7ccfb2db2585 100644 --- a/homeassistant/components/light/decora_wifi.py +++ b/homeassistant/components/light/decora_wifi.py @@ -34,7 +34,7 @@ NOTIFICATION_TITLE = 'myLeviton Decora Setup' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Decora WiFi platform.""" # pylint: disable=import-error, no-name-in-module from decora_wifi import DecoraWiFiSession @@ -65,7 +65,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for switch in residence.get_iot_switches(): all_switches.append(switch) - add_devices(DecoraWifiLight(sw) for sw in all_switches) + add_entities(DecoraWifiLight(sw) for sw in all_switches) except ValueError: _LOGGER.error('Failed to communicate with myLeviton Service.') diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/light/demo.py index ba27cbd3ac5367..980d849174463b 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -24,9 +24,9 @@ SUPPORT_COLOR | SUPPORT_WHITE_VALUE) -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up the demo light platform.""" - add_devices_callback([ + add_entities_callback([ DemoLight(1, "Bed Light", False, True, effect_list=LIGHT_EFFECT_LIST, effect=LIGHT_EFFECT_LIST[0]), DemoLight(2, "Ceiling Lights", True, True, diff --git a/homeassistant/components/light/enocean.py b/homeassistant/components/light/enocean.py index beb9094b1cb02d..ebe2c409796799 100644 --- a/homeassistant/components/light/enocean.py +++ b/homeassistant/components/light/enocean.py @@ -32,13 +32,13 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the EnOcean light platform.""" sender_id = config.get(CONF_SENDER_ID) devname = config.get(CONF_NAME) dev_id = config.get(CONF_ID) - add_devices([EnOceanLight(sender_id, devname, dev_id)]) + add_entities([EnOceanLight(sender_id, devname, dev_id)]) class EnOceanLight(enocean.EnOceanDevice, Light): diff --git a/homeassistant/components/light/eufy.py b/homeassistant/components/light/eufy.py index 2e7370cb336f1b..7a44a58cd81d09 100644 --- a/homeassistant/components/light/eufy.py +++ b/homeassistant/components/light/eufy.py @@ -24,11 +24,11 @@ EUFY_MIN_KELVIN = 2700 -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Eufy bulbs.""" if discovery_info is None: return - add_devices([EufyLight(discovery_info)], True) + add_entities([EufyLight(discovery_info)], True) class EufyLight(Light): diff --git a/homeassistant/components/light/flux_led.py b/homeassistant/components/light/flux_led.py index 2b53fb650540df..f389d34cd5dbb7 100644 --- a/homeassistant/components/light/flux_led.py +++ b/homeassistant/components/light/flux_led.py @@ -99,7 +99,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Flux lights.""" import flux_led lights = [] @@ -116,7 +116,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): light_ips.append(ipaddr) if not config.get(CONF_AUTOMATIC_ADD, False): - add_devices(lights, True) + add_entities(lights, True) return # Find the bulbs on the LAN @@ -132,7 +132,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): light = FluxLight(device) lights.append(light) - add_devices(lights, True) + add_entities(lights, True) class FluxLight(Light): diff --git a/homeassistant/components/light/futurenow.py b/homeassistant/components/light/futurenow.py index 1777376881e331..8b0a809b667bb5 100644 --- a/homeassistant/components/light/futurenow.py +++ b/homeassistant/components/light/futurenow.py @@ -38,7 +38,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the light platform for each FutureNow unit.""" lights = [] for channel, device_config in config[CONF_DEVICES].items(): @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): device['port'] = config[CONF_PORT] lights.append(FutureNowLight(device)) - add_devices(lights, True) + add_entities(lights, True) def to_futurenow_level(level): diff --git a/homeassistant/components/light/greenwave.py b/homeassistant/components/light/greenwave.py index 52a70532005dcf..0c484a0e3f49d9 100644 --- a/homeassistant/components/light/greenwave.py +++ b/homeassistant/components/light/greenwave.py @@ -30,7 +30,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Greenwave Reality Platform.""" import greenwavereality as greenwave import os @@ -51,8 +51,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): else: token = None bulbs = greenwave.grab_bulbs(host, token) - add_devices(GreenwaveLight(device, host, token, GatewayData(host, token)) - for device in bulbs.values()) + add_entities(GreenwaveLight(device, host, token, GatewayData(host, token)) + for device in bulbs.values()) class GreenwaveLight(Light): diff --git a/homeassistant/components/light/group.py b/homeassistant/components/light/group.py index b2fdd36abe7edc..bf54d3ecf29586 100644 --- a/homeassistant/components/light/group.py +++ b/homeassistant/components/light/group.py @@ -41,10 +41,11 @@ async def async_setup_platform(hass: HomeAssistantType, config: ConfigType, - async_add_devices, discovery_info=None) -> None: + async_add_entities, + discovery_info=None) -> None: """Initialize light.group platform.""" - async_add_devices([LightGroup(config.get(CONF_NAME), - config[CONF_ENTITIES])]) + async_add_entities([LightGroup(config.get(CONF_NAME), + config[CONF_ENTITIES])]) class LightGroup(light.Light): @@ -80,7 +81,7 @@ def async_state_changed_listener(entity_id: str, old_state: State, await self.async_update() async def async_will_remove_from_hass(self): - """Callback when removed from HASS.""" + """Handle removal from HASS.""" if self._async_unsub_state_changed is not None: self._async_unsub_state_changed() self._async_unsub_state_changed = None diff --git a/homeassistant/components/light/hive.py b/homeassistant/components/light/hive.py index 1fd9e8aaacae70..eada16bbab9a21 100644 --- a/homeassistant/components/light/hive.py +++ b/homeassistant/components/light/hive.py @@ -15,13 +15,13 @@ DEPENDENCIES = ['hive'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Hive light devices.""" if discovery_info is None: return session = hass.data.get(DATA_HIVE) - add_devices([HiveDeviceLight(session, discovery_info)]) + add_entities([HiveDeviceLight(session, discovery_info)]) class HiveDeviceLight(Light): diff --git a/homeassistant/components/light/homekit_controller.py b/homeassistant/components/light/homekit_controller.py index 8d77cb0523668e..dd24e3cfb2e5ef 100644 --- a/homeassistant/components/light/homekit_controller.py +++ b/homeassistant/components/light/homekit_controller.py @@ -17,11 +17,11 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Homekit lighting.""" if discovery_info is not None: accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] - add_devices([HomeKitLight(accessory, discovery_info)], True) + add_entities([HomeKitLight(accessory, discovery_info)], True) class HomeKitLight(HomeKitEntity, Light): diff --git a/homeassistant/components/light/homematic.py b/homeassistant/components/light/homematic.py index a3db1ff30ff5cb..9a7baa713a3acc 100644 --- a/homeassistant/components/light/homematic.py +++ b/homeassistant/components/light/homematic.py @@ -5,9 +5,10 @@ https://home-assistant.io/components/light.homematic/ """ import logging + +from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) -from homeassistant.components.homematic import HMDevice, ATTR_DISCOVER_DEVICES from homeassistant.const import STATE_UNKNOWN _LOGGER = logging.getLogger(__name__) @@ -17,7 +18,7 @@ SUPPORT_HOMEMATIC = SUPPORT_BRIGHTNESS -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Homematic light platform.""" if discovery_info is None: return @@ -27,7 +28,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): new_device = HMLight(conf) devices.append(new_device) - add_devices(devices) + add_entities(devices) class HMLight(HMDevice, Light): diff --git a/homeassistant/components/light/homematicip_cloud.py b/homeassistant/components/light/homematicip_cloud.py index 617a7209a865fc..764ead62169e9b 100644 --- a/homeassistant/components/light/homematicip_cloud.py +++ b/homeassistant/components/light/homematicip_cloud.py @@ -1,37 +1,35 @@ """ -Support for HomematicIP light. +Support for HomematicIP Cloud lights. -For more details about this component, please refer to the documentation at +For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.homematicip_cloud/ """ - import logging -from homeassistant.components.light import ( - Light, ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS) from homeassistant.components.homematicip_cloud import ( - HomematicipGenericDevice, DOMAIN as HMIPC_DOMAIN, - HMIPC_HAPID) + HMIPC_HAPID, HomematicipGenericDevice) +from homeassistant.components.homematicip_cloud import DOMAIN as HMIPC_DOMAIN +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) DEPENDENCIES = ['homematicip_cloud'] _LOGGER = logging.getLogger(__name__) +ATTR_ENERGY_COUNTER = 'energy_counter_kwh' ATTR_POWER_CONSUMPTION = 'power_consumption' -ATTR_ENERGIE_COUNTER = 'energie_counter_kwh' ATTR_PROFILE_MODE = 'profile_mode' -async def async_setup_platform(hass, config, async_add_devices, - discovery_info=None): - """Old way of setting up HomematicIP lights.""" +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Old way of setting up HomematicIP Cloud lights.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): - """Set up the HomematicIP lights from a config entry.""" - from homematicip.aio.device import ( - AsyncBrandSwitchMeasuring, AsyncDimmer) +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up the HomematicIP Cloud lights from a config entry.""" + from homematicip.aio.device import AsyncBrandSwitchMeasuring, AsyncDimmer home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home devices = [] @@ -42,11 +40,11 @@ async def async_setup_entry(hass, config_entry, async_add_devices): devices.append(HomematicipDimmer(home, device)) if devices: - async_add_devices(devices) + async_add_entities(devices) class HomematicipLight(HomematicipGenericDevice, Light): - """MomematicIP light device.""" + """Representation of a HomematicIP Cloud light device.""" def __init__(self, home, device): """Initialize the light device.""" @@ -67,7 +65,7 @@ async def async_turn_off(self, **kwargs): class HomematicipLightMeasuring(HomematicipLight): - """MomematicIP measuring light device.""" + """Representation of a HomematicIP Cloud measuring light device.""" @property def device_state_attributes(self): @@ -79,13 +77,13 @@ def device_state_attributes(self): round(self._device.currentPowerConsumption, 2) }) attr.update({ - ATTR_ENERGIE_COUNTER: round(self._device.energyCounter, 2) + ATTR_ENERGY_COUNTER: round(self._device.energyCounter, 2) }) return attr class HomematicipDimmer(HomematicipGenericDevice, Light): - """MomematicIP dimmer light device.""" + """Representation of HomematicIP Cloud dimmer light device.""" def __init__(self, home, device): """Initialize the dimmer light device.""" @@ -109,8 +107,7 @@ def supported_features(self): async def async_turn_on(self, **kwargs): """Turn the light on.""" if ATTR_BRIGHTNESS in kwargs: - await self._device.set_dim_level( - kwargs[ATTR_BRIGHTNESS]/255.0) + await self._device.set_dim_level(kwargs[ATTR_BRIGHTNESS]/255.0) else: await self._device.set_dim_level(1) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index 0da59b6f100c39..2a51423a7a815a 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -47,7 +47,7 @@ GROUP_MIN_API_VERSION = (1, 13, 0) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Old way of setting up Hue lights. @@ -57,7 +57,7 @@ async def async_setup_platform(hass, config, async_add_devices, pass -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the Hue lights from a config entry.""" bridge = hass.data[hue.DOMAIN][config_entry.data['host']] cur_lights = {} @@ -137,13 +137,13 @@ async def update_bridge(): """ tasks = [] tasks.append(async_update_items( - hass, bridge, async_add_devices, request_update, + hass, bridge, async_add_entities, request_update, False, cur_lights, light_progress )) if allow_groups: tasks.append(async_update_items( - hass, bridge, async_add_devices, request_update, + hass, bridge, async_add_entities, request_update, True, cur_groups, group_progress )) @@ -152,7 +152,7 @@ async def update_bridge(): await update_bridge() -async def async_update_items(hass, bridge, async_add_devices, +async def async_update_items(hass, bridge, async_add_entities, request_bridge_update, is_group, current, progress_waiting): """Update either groups or lights from the bridge.""" @@ -195,7 +195,7 @@ async def async_update_items(hass, bridge, async_add_devices, current[item_id].async_schedule_update_ha_state() if new_lights: - async_add_devices(new_lights) + async_add_entities(new_lights) class HueLight(Light): diff --git a/homeassistant/components/light/hyperion.py b/homeassistant/components/light/hyperion.py index cbac8cf4e201ea..16be7d45825116 100644 --- a/homeassistant/components/light/hyperion.py +++ b/homeassistant/components/light/hyperion.py @@ -59,7 +59,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a Hyperion server remote.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -72,7 +72,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): default_color, hdmi_priority, effect_list) if device.setup(): - add_devices([device]) + add_entities([device]) return True return False diff --git a/homeassistant/components/light/iglo.py b/homeassistant/components/light/iglo.py index f40dc2ce84eea6..9dca5f8e5f5180 100644 --- a/homeassistant/components/light/iglo.py +++ b/homeassistant/components/light/iglo.py @@ -31,12 +31,12 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the iGlo lights.""" host = config.get(CONF_HOST) name = config.get(CONF_NAME) port = config.get(CONF_PORT) - add_devices([IGloLamp(name, host, port)], True) + add_entities([IGloLamp(name, host, port)], True) class IGloLamp(Light): diff --git a/homeassistant/components/light/ihc.py b/homeassistant/components/light/ihc.py index 5a7e85d50dc5b8..da90a53c84804e 100644 --- a/homeassistant/components/light/ihc.py +++ b/homeassistant/components/light/ihc.py @@ -28,8 +28,8 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the ihc lights platform.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the IHC lights platform.""" ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER] info = hass.data[IHC_DATA][IHC_INFO] devices = [] @@ -50,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): device = IhcLight(ihc_controller, name, ihc_id, info, dimmable) devices.append(device) - add_devices(devices) + add_entities(devices) class IhcLight(IHCDevice, Light): @@ -109,7 +109,7 @@ def turn_off(self, **kwargs) -> None: self.ihc_controller.set_runtime_value_bool(self.ihc_id, False) def on_ihc_change(self, ihc_id, value): - """Callback from Ihc notifications.""" + """Handle IHC notifications.""" if isinstance(value, bool): self._dimmable = False self._state = value != 0 diff --git a/homeassistant/components/light/insteon_plm.py b/homeassistant/components/light/insteon.py similarity index 73% rename from homeassistant/components/light/insteon_plm.py rename to homeassistant/components/light/insteon.py index 8a3b463c2bd08f..82f455c821e739 100644 --- a/homeassistant/components/light/insteon_plm.py +++ b/homeassistant/components/light/insteon.py @@ -2,40 +2,41 @@ Support for Insteon lights via PowerLinc Modem. For more details about this component, please refer to the documentation at -https://home-assistant.io/components/light.insteon_plm/ +https://home-assistant.io/components/light.insteon/ """ import asyncio import logging -from homeassistant.components.insteon_plm import InsteonPLMEntity +from homeassistant.components.insteon import InsteonEntity from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['insteon_plm'] +DEPENDENCIES = ['insteon'] MAX_BRIGHTNESS = 255 @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): - """Set up the Insteon PLM device.""" - plm = hass.data['insteon_plm'].get('plm') +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): + """Set up the Insteon component.""" + insteon_modem = hass.data['insteon'].get('modem') address = discovery_info['address'] - device = plm.devices[address] + device = insteon_modem.devices[address] state_key = discovery_info['state_key'] _LOGGER.debug('Adding device %s entity %s to Light platform', device.address.hex, device.states[state_key].name) - new_entity = InsteonPLMDimmerDevice(device, state_key) + new_entity = InsteonDimmerDevice(device, state_key) - async_add_devices([new_entity]) + async_add_entities([new_entity]) -class InsteonPLMDimmerDevice(InsteonPLMEntity, Light): +class InsteonDimmerDevice(InsteonEntity, Light): """A Class for an Insteon device.""" @property diff --git a/homeassistant/components/light/insteon_local.py b/homeassistant/components/light/insteon_local.py deleted file mode 100644 index e2bc54de517632..00000000000000 --- a/homeassistant/components/light/insteon_local.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -Support for Insteon dimmers via local hub control. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/light.insteon_local/ -""" -import logging -from datetime import timedelta - -from homeassistant.components.light import ( - ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) -from homeassistant import util - -_CONFIGURING = {} -_LOGGER = logging.getLogger(__name__) - -DEPENDENCIES = ['insteon_local'] -DOMAIN = 'light' - -MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100) -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) - -SUPPORT_INSTEON_LOCAL = SUPPORT_BRIGHTNESS - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up the Insteon local light platform.""" - insteonhub = hass.data['insteon_local'] - if discovery_info is None: - return - - linked = discovery_info['linked'] - device_list = [] - for device_id in linked: - if linked[device_id]['cat_type'] == 'dimmer': - device = insteonhub.dimmer(device_id) - device_list.append( - InsteonLocalDimmerDevice(device) - ) - - add_devices(device_list) - - -class InsteonLocalDimmerDevice(Light): - """An abstract Class for an Insteon node.""" - - def __init__(self, node): - """Initialize the device.""" - self.node = node - self._value = 0 - - @property - def name(self): - """Return the name of the node.""" - return self.node.device_id - - @property - def unique_id(self): - """Return the ID of this Insteon node.""" - return self.node.device_id - - @property - def brightness(self): - """Return the brightness of this light between 0..255.""" - return self._value - - @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) - def update(self): - """Update state of the light.""" - resp = self.node.status(0) - - while 'error' in resp and resp['error'] is True: - resp = self.node.status(0) - - if 'cmd2' in resp: - self._value = int(resp['cmd2'], 16) - - @property - def is_on(self): - """Return the boolean response if the node is on.""" - return self._value != 0 - - @property - def supported_features(self): - """Flag supported features.""" - return SUPPORT_INSTEON_LOCAL - - def turn_on(self, **kwargs): - """Turn device on.""" - brightness = 100 - if ATTR_BRIGHTNESS in kwargs: - brightness = int(kwargs[ATTR_BRIGHTNESS]) / 255 * 100 - - self.node.change_level(brightness) - - def turn_off(self, **kwargs): - """Turn device off.""" - self.node.off() diff --git a/homeassistant/components/light/isy994.py b/homeassistant/components/light/isy994.py index ce358d0a974e5e..06507eaeca6af9 100644 --- a/homeassistant/components/light/isy994.py +++ b/homeassistant/components/light/isy994.py @@ -16,13 +16,13 @@ def setup_platform(hass, config: ConfigType, - add_devices: Callable[[list], None], discovery_info=None): + add_entities: Callable[[list], None], discovery_info=None): """Set up the ISY994 light platform.""" devices = [] for node in hass.data[ISY994_NODES][DOMAIN]: devices.append(ISYLightDevice(node)) - add_devices(devices) + add_entities(devices) class ISYLightDevice(ISYDevice, Light): diff --git a/homeassistant/components/light/knx.py b/homeassistant/components/light/knx.py index 8fa2b56d1d2d1e..778d2fac59c153 100644 --- a/homeassistant/components/light/knx.py +++ b/homeassistant/components/light/knx.py @@ -37,27 +37,27 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up lights for KNX platform.""" if discovery_info is not None: - async_add_devices_discovery(hass, discovery_info, async_add_devices) + async_add_entities_discovery(hass, discovery_info, async_add_entities) else: - async_add_devices_config(hass, config, async_add_devices) + async_add_entities_config(hass, config, async_add_entities) @callback -def async_add_devices_discovery(hass, discovery_info, async_add_devices): +def async_add_entities_discovery(hass, discovery_info, async_add_entities): """Set up lights for KNX platform configured via xknx.yaml.""" entities = [] for device_name in discovery_info[ATTR_DISCOVER_DEVICES]: device = hass.data[DATA_KNX].xknx.devices[device_name] entities.append(KNXLight(hass, device)) - async_add_devices(entities) + async_add_entities(entities) @callback -def async_add_devices_config(hass, config, async_add_devices): +def async_add_entities_config(hass, config, async_add_entities): """Set up light for KNX platform configured within platform.""" import xknx light = xknx.devices.Light( @@ -71,7 +71,7 @@ def async_add_devices_config(hass, config, async_add_devices): group_address_color=config.get(CONF_COLOR_ADDRESS), group_address_color_state=config.get(CONF_COLOR_STATE_ADDRESS)) hass.data[DATA_KNX].xknx.devices.add(light) - async_add_devices([KNXLight(hass, light)]) + async_add_entities([KNXLight(hass, light)]) class KNXLight(Light): diff --git a/homeassistant/components/light/lifx.py b/homeassistant/components/light/lifx.py index 3738fd8f00413b..cf5d6fef70425e 100644 --- a/homeassistant/components/light/lifx.py +++ b/homeassistant/components/light/lifx.py @@ -136,7 +136,7 @@ def aiolifx_effects(): async def async_setup_platform(hass, config, - async_add_devices, + async_add_entities, discovery_info=None): """Set up the LIFX platform.""" if sys.platform == 'win32': @@ -145,7 +145,7 @@ async def async_setup_platform(hass, server_addr = config.get(CONF_SERVER) - lifx_manager = LIFXManager(hass, async_add_devices) + lifx_manager = LIFXManager(hass, async_add_entities) lifx_discovery = aiolifx().LifxDiscovery( hass.loop, lifx_manager, @@ -207,11 +207,11 @@ def merge_hsbk(base, change): class LIFXManager: """Representation of all known LIFX entities.""" - def __init__(self, hass, async_add_devices): + def __init__(self, hass, async_add_entities): """Initialize the light.""" self.entities = {} self.hass = hass - self.async_add_devices = async_add_devices + self.async_add_entities = async_add_entities self.effects_conductor = aiolifx_effects().Conductor(loop=hass.loop) self.register_set_state() @@ -335,7 +335,7 @@ async def register_new_device(self, device): _LOGGER.debug("%s register READY", entity.who) self.entities[device.mac_addr] = entity - self.async_add_devices([entity], True) + self.async_add_entities([entity], True) @callback def unregister(self, device): @@ -389,7 +389,7 @@ class LIFXLight(Light): def __init__(self, device, effects_conductor): """Initialize the light.""" - self.device = device + self.light = device self.effects_conductor = effects_conductor self.registered = True self.postponed_update = None @@ -403,28 +403,28 @@ def available(self): @property def unique_id(self): """Return a unique ID.""" - return self.device.mac_addr + return self.light.mac_addr @property def name(self): """Return the name of the device.""" - return self.device.label + return self.light.label @property def who(self): """Return a string identifying the device.""" - return "%s (%s)" % (self.device.ip_addr, self.name) + return "%s (%s)" % (self.light.ip_addr, self.name) @property def min_mireds(self): """Return the coldest color_temp that this light supports.""" - kelvin = lifx_features(self.device)['max_kelvin'] + kelvin = lifx_features(self.light)['max_kelvin'] return math.floor(color_util.color_temperature_kelvin_to_mired(kelvin)) @property def max_mireds(self): """Return the warmest color_temp that this light supports.""" - kelvin = lifx_features(self.device)['min_kelvin'] + kelvin = lifx_features(self.light)['min_kelvin'] return math.ceil(color_util.color_temperature_kelvin_to_mired(kelvin)) @property @@ -432,7 +432,7 @@ def supported_features(self): """Flag supported features.""" support = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_EFFECT - device_features = lifx_features(self.device) + device_features = lifx_features(self.light) if device_features['min_kelvin'] != device_features['max_kelvin']: support |= SUPPORT_COLOR_TEMP @@ -441,12 +441,12 @@ def supported_features(self): @property def brightness(self): """Return the brightness of this light between 0..255.""" - return convert_16_to_8(self.device.color[2]) + return convert_16_to_8(self.light.color[2]) @property def color_temp(self): """Return the color temperature.""" - _, sat, _, kelvin = self.device.color + _, sat, _, kelvin = self.light.color if sat: return None return color_util.color_temperature_kelvin_to_mired(kelvin) @@ -454,12 +454,12 @@ def color_temp(self): @property def is_on(self): """Return true if device is on.""" - return self.device.power_level != 0 + return self.light.power_level != 0 @property def effect(self): """Return the name of the currently running effect.""" - effect = self.effects_conductor.effect(self.device) + effect = self.effects_conductor.effect(self.light) if effect: return 'lifx_effect_' + effect.name return None @@ -497,7 +497,7 @@ async def async_turn_off(self, **kwargs): async def set_state(self, **kwargs): """Set a color on the light and turn it on/off.""" async with self.lock: - bulb = self.device + bulb = self.light await self.effects_conductor.stop([bulb]) @@ -545,12 +545,12 @@ async def set_state(self, **kwargs): async def set_power(self, ack, pwr, duration=0): """Send a power change to the device.""" - await ack(partial(self.device.set_power, pwr, duration=duration)) + await ack(partial(self.light.set_power, pwr, duration=duration)) async def set_color(self, ack, hsbk, kwargs, duration=0): """Send a color change to the device.""" - hsbk = merge_hsbk(self.device.color, hsbk) - await ack(partial(self.device.set_color, hsbk, duration=duration)) + hsbk = merge_hsbk(self.light.color, hsbk) + await ack(partial(self.light.set_color, hsbk, duration=duration)) async def default_effect(self, **kwargs): """Start an effect with default parameters.""" @@ -563,7 +563,7 @@ async def default_effect(self, **kwargs): async def async_update(self): """Update bulb status.""" if self.available and not self.lock.locked(): - await AwaitAioLIFX().wait(self.device.get_color) + await AwaitAioLIFX().wait(self.light.get_color) class LIFXWhite(LIFXLight): @@ -600,7 +600,7 @@ def effect_list(self): @property def hs_color(self): """Return the hs value.""" - hue, sat, _, _ = self.device.color + hue, sat, _, _ = self.light.color hue = hue / 65535 * 360 sat = sat / 65535 * 100 return (hue, sat) if sat else None @@ -611,7 +611,7 @@ class LIFXStrip(LIFXColor): async def set_color(self, ack, hsbk, kwargs, duration=0): """Send a color change to the device.""" - bulb = self.device + bulb = self.light num_zones = len(bulb.color_zones) zones = kwargs.get(ATTR_ZONES) @@ -659,7 +659,7 @@ async def update_color_zones(self): while self.available and zone < top: # Each get_color_zones can update 8 zones at once resp = await AwaitAioLIFX().wait(partial( - self.device.get_color_zones, + self.light.get_color_zones, start_index=zone)) if resp: zone += 8 diff --git a/homeassistant/components/light/lifx_legacy.py b/homeassistant/components/light/lifx_legacy.py index 3ad75a1cea4c57..6c5f68937f88a5 100644 --- a/homeassistant/components/light/lifx_legacy.py +++ b/homeassistant/components/light/lifx_legacy.py @@ -45,12 +45,12 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the LIFX platform.""" server_addr = config.get(CONF_SERVER) broadcast_addr = config.get(CONF_BROADCAST) - lifx_library = LIFX(add_devices, server_addr, broadcast_addr) + lifx_library = LIFX(add_entities, server_addr, broadcast_addr) # Register our poll service track_time_change(hass, lifx_library.poll, second=[10, 40]) @@ -61,14 +61,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class LIFX: """Representation of a LIFX light.""" - def __init__(self, add_devices_callback, server_addr=None, + def __init__(self, add_entities_callback, server_addr=None, broadcast_addr=None): """Initialize the light.""" import liffylights self._devices = [] - self._add_devices_callback = add_devices_callback + self._add_entities_callback = add_entities_callback self._liffylights = liffylights.LiffyLights( self.on_device, self.on_power, self.on_color, server_addr, @@ -93,7 +93,7 @@ def on_device(self, ipaddr, name, power, hue, sat, bri, kel): bulb = LIFXLight( self._liffylights, ipaddr, name, power, hue, sat, bri, kel) self._devices.append(bulb) - self._add_devices_callback([bulb]) + self._add_entities_callback([bulb]) else: _LOGGER.debug("update bulb %s %s %d %d %d %d %d", ipaddr, name, power, hue, sat, bri, kel) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index 2263a865758060..9400932802aba5 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -104,7 +104,7 @@ def rewrite_legacy(config): return {'bridges': new_bridges} -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the LimitlessLED lights.""" from limitlessled.bridge import Bridge @@ -126,7 +126,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): lights.append(LimitlessLEDGroup(group, { 'fade': group_conf[CONF_FADE] })) - add_devices(lights) + add_entities(lights) def state(new_state): @@ -190,7 +190,7 @@ def __init__(self, group, config): @asyncio.coroutine def async_added_to_hass(self): - """Called when entity is about to be added to hass.""" + """Handle entity about to be added to hass event.""" last_state = yield from async_get_last_state(self.hass, self.entity_id) if last_state: self._is_on = (last_state.state == STATE_ON) diff --git a/homeassistant/components/light/litejet.py b/homeassistant/components/light/litejet.py index b8491b6f0f53c1..8662afc668ebfe 100644 --- a/homeassistant/components/light/litejet.py +++ b/homeassistant/components/light/litejet.py @@ -17,7 +17,7 @@ ATTR_NUMBER = 'number' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up lights for the LiteJet platform.""" litejet_ = hass.data['litejet_system'] @@ -26,7 +26,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name = litejet_.get_load_name(i) if not litejet.is_ignored(hass, name): devices.append(LiteJetLight(hass, litejet_, i, name)) - add_devices(devices, True) + add_entities(devices, True) class LiteJetLight(Light): diff --git a/homeassistant/components/light/lutron.py b/homeassistant/components/light/lutron.py index 24744110c6fd98..6c4047e2314043 100644 --- a/homeassistant/components/light/lutron.py +++ b/homeassistant/components/light/lutron.py @@ -16,14 +16,14 @@ DEPENDENCIES = ['lutron'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Lutron lights.""" devs = [] for (area_name, device) in hass.data[LUTRON_DEVICES]['light']: dev = LutronLight(area_name, device, hass.data[LUTRON_CONTROLLER]) devs.append(dev) - add_devices(devs, True) + add_entities(devs, True) return True diff --git a/homeassistant/components/light/lutron_caseta.py b/homeassistant/components/light/lutron_caseta.py index 29186b8fcd2283..f345748683b8dc 100644 --- a/homeassistant/components/light/lutron_caseta.py +++ b/homeassistant/components/light/lutron_caseta.py @@ -20,7 +20,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Lutron Caseta lights.""" devs = [] bridge = hass.data[LUTRON_CASETA_SMARTBRIDGE] @@ -29,7 +30,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): dev = LutronCasetaLight(light_device, bridge) devs.append(dev) - async_add_devices(devs, True) + async_add_entities(devs, True) class LutronCasetaLight(LutronCasetaDevice, Light): diff --git a/homeassistant/components/light/lw12wifi.py b/homeassistant/components/light/lw12wifi.py index f81d8368f98136..71716b4130db01 100644 --- a/homeassistant/components/light/lw12wifi.py +++ b/homeassistant/components/light/lw12wifi.py @@ -36,8 +36,8 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup LW-12 WiFi LED Controller platform.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up LW-12 WiFi LED Controller platform.""" import lw12 # Assign configuration variables. @@ -46,14 +46,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): port = config.get(CONF_PORT) # Add devices lw12_light = lw12.LW12Controller(host, port) - add_devices([LW12WiFi(name, lw12_light)]) + add_entities([LW12WiFi(name, lw12_light)]) class LW12WiFi(Light): """LW-12 WiFi LED Controller.""" def __init__(self, name, lw12_light): - """Initialisation of LW-12 WiFi LED Controller. + """Initialise LW-12 WiFi LED Controller. Args: name: Friendly name for this platform to use. diff --git a/homeassistant/components/light/mochad.py b/homeassistant/components/light/mochad.py index 576e244103f6e6..2e68c369ba6447 100644 --- a/homeassistant/components/light/mochad.py +++ b/homeassistant/components/light/mochad.py @@ -34,10 +34,10 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up X10 dimmers over a mochad controller.""" devs = config.get(CONF_DEVICES) - add_devices([MochadLight( + add_entities([MochadLight( hass, mochad.CONTROLLER.ctrl, dev) for dev in devs]) return True @@ -54,8 +54,8 @@ def __init__(self, hass, ctrl, dev): self._name = dev.get(CONF_NAME, 'x10_light_dev_{}'.format(self._address)) self._comm_type = dev.get(mochad.CONF_COMM_TYPE, 'pl') - self.device = device.Device(ctrl, self._address, - comm_type=self._comm_type) + self.light = device.Device(ctrl, self._address, + comm_type=self._comm_type) self._brightness = 0 self._state = self._get_device_status() self._brightness_levels = dev.get(CONF_BRIGHTNESS_LEVELS) - 1 @@ -68,7 +68,7 @@ def brightness(self): def _get_device_status(self): """Get the status of the light from mochad.""" with mochad.REQ_LOCK: - status = self.device.get_status().rstrip() + status = self.light.get_status().rstrip() return status == 'on' @property @@ -98,12 +98,12 @@ def _adjust_brightness(self, brightness): if self._brightness > brightness: bdelta = self._brightness - brightness mochad_brightness = self._calculate_brightness_value(bdelta) - self.device.send_cmd("dim {}".format(mochad_brightness)) + self.light.send_cmd("dim {}".format(mochad_brightness)) self._controller.read_data() elif self._brightness < brightness: bdelta = brightness - self._brightness mochad_brightness = self._calculate_brightness_value(bdelta) - self.device.send_cmd("bright {}".format(mochad_brightness)) + self.light.send_cmd("bright {}".format(mochad_brightness)) self._controller.read_data() def turn_on(self, **kwargs): @@ -112,10 +112,10 @@ def turn_on(self, **kwargs): with mochad.REQ_LOCK: if self._brightness_levels > 32: out_brightness = self._calculate_brightness_value(brightness) - self.device.send_cmd('xdim {}'.format(out_brightness)) + self.light.send_cmd('xdim {}'.format(out_brightness)) self._controller.read_data() else: - self.device.send_cmd("on") + self.light.send_cmd("on") self._controller.read_data() # There is no persistence for X10 modules so a fresh on command # will be full brightness @@ -128,7 +128,7 @@ def turn_on(self, **kwargs): def turn_off(self, **kwargs): """Send the command to turn the light on.""" with mochad.REQ_LOCK: - self.device.send_cmd('off') + self.light.send_cmd('off') self._controller.read_data() # There is no persistence for X10 modules so we need to prepare # to track a fresh on command will full brightness diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index 09fa094c1b2cbb..225f0f510adcb8 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -100,7 +100,7 @@ }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up a MQTT Light.""" if discovery_info is not None: @@ -109,7 +109,7 @@ async def async_setup_platform(hass, config, async_add_devices, config.setdefault( CONF_STATE_VALUE_TEMPLATE, config.get(CONF_VALUE_TEMPLATE)) - async_add_devices([MqttLight( + async_add_entities([MqttLight( config.get(CONF_NAME), config.get(CONF_EFFECT_LIST), { diff --git a/homeassistant/components/light/mqtt_json.py b/homeassistant/components/light/mqtt_json.py index d17c7dd73bf5a1..239c924ed2b6ac 100644 --- a/homeassistant/components/light/mqtt_json.py +++ b/homeassistant/components/light/mqtt_json.py @@ -81,11 +81,11 @@ async def async_setup_platform(hass: HomeAssistantType, config: ConfigType, - async_add_devices, discovery_info=None): + async_add_entities, discovery_info=None): """Set up a MQTT JSON Light.""" if discovery_info is not None: config = PLATFORM_SCHEMA(discovery_info) - async_add_devices([MqttJson( + async_add_entities([MqttJson( config.get(CONF_NAME), config.get(CONF_EFFECT_LIST), { diff --git a/homeassistant/components/light/mqtt_template.py b/homeassistant/components/light/mqtt_template.py index ffa73aca915246..72cfd6b678c257 100644 --- a/homeassistant/components/light/mqtt_template.py +++ b/homeassistant/components/light/mqtt_template.py @@ -66,13 +66,13 @@ }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up a MQTT Template light.""" if discovery_info is not None: config = PLATFORM_SCHEMA(discovery_info) - async_add_devices([MqttTemplate( + async_add_entities([MqttTemplate( hass, config.get(CONF_NAME), config.get(CONF_EFFECT_LIST), diff --git a/homeassistant/components/light/mysensors.py b/homeassistant/components/light/mysensors.py index 4139abd40fa287..23d602c5d400d3 100644 --- a/homeassistant/components/light/mysensors.py +++ b/homeassistant/components/light/mysensors.py @@ -16,7 +16,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the mysensors platform for lights.""" device_class_map = { 'S_DIMMER': MySensorsLightDimmer, @@ -25,7 +25,7 @@ async def async_setup_platform( } mysensors.setup_mysensors_platform( hass, DOMAIN, discovery_info, device_class_map, - async_add_devices=async_add_devices) + async_add_entities=async_add_entities) class MySensorsLight(mysensors.device.MySensorsEntity, Light): diff --git a/homeassistant/components/light/mystrom.py b/homeassistant/components/light/mystrom.py index 5d4cdcc17d4bdd..8060bef0fa8e4a 100644 --- a/homeassistant/components/light/mystrom.py +++ b/homeassistant/components/light/mystrom.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the myStrom Light platform.""" from pymystrom.bulb import MyStromBulb from pymystrom.exceptions import MyStromConnectionError @@ -58,7 +58,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): except MyStromConnectionError: _LOGGER.warning("No route to device: %s", host) - add_devices([MyStromLight(bulb, name)], True) + add_entities([MyStromLight(bulb, name)], True) class MyStromLight(Light): diff --git a/homeassistant/components/light/nanoleaf_aurora.py b/homeassistant/components/light/nanoleaf_aurora.py index 6a0d3c36e9f0d9..6d9c1a50f790f8 100644 --- a/homeassistant/components/light/nanoleaf_aurora.py +++ b/homeassistant/components/light/nanoleaf_aurora.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Nanoleaf Aurora device.""" import nanoleaf import nanoleaf.setup @@ -83,7 +83,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return hass.data[DATA_NANOLEAF_AURORA][host] = aurora_light - add_devices([AuroraLight(aurora_light, name)], True) + add_entities([AuroraLight(aurora_light, name)], True) class AuroraLight(Light): diff --git a/homeassistant/components/light/osramlightify.py b/homeassistant/components/light/osramlightify.py index 939d0fe6988842..244a233c5178ba 100644 --- a/homeassistant/components/light/osramlightify.py +++ b/homeassistant/components/light/osramlightify.py @@ -49,7 +49,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Osram Lightify lights.""" import lightify @@ -65,10 +65,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.exception(msg) return - setup_bridge(bridge, add_devices, add_nodes, add_groups) + setup_bridge(bridge, add_entities, add_nodes, add_groups) -def setup_bridge(bridge, add_devices, add_nodes, add_groups): +def setup_bridge(bridge, add_entities, add_nodes, add_groups): """Set up the Lightify bridge.""" lights = {} @@ -106,7 +106,7 @@ def update_lights(): lights[group_name].group = group if new_lights: - add_devices(new_lights) + add_entities(new_lights) update_lights() diff --git a/homeassistant/components/light/piglow.py b/homeassistant/components/light/piglow.py index 755cf9dca66919..56c72e01fdf7c2 100644 --- a/homeassistant/components/light/piglow.py +++ b/homeassistant/components/light/piglow.py @@ -29,7 +29,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Piglow Light platform.""" import piglow @@ -39,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name = config.get(CONF_NAME) - add_devices([PiglowLight(piglow, name)]) + add_entities([PiglowLight(piglow, name)]) class PiglowLight(Light): diff --git a/homeassistant/components/light/qwikswitch.py b/homeassistant/components/light/qwikswitch.py index 528f4f73c53d56..413358d9cee074 100644 --- a/homeassistant/components/light/qwikswitch.py +++ b/homeassistant/components/light/qwikswitch.py @@ -11,14 +11,14 @@ DEPENDENCIES = [QWIKSWITCH] -async def async_setup_platform(hass, _, add_devices, discovery_info=None): +async def async_setup_platform(hass, _, add_entities, discovery_info=None): """Add lights from the main Qwikswitch component.""" if discovery_info is None: return qsusb = hass.data[QWIKSWITCH] devs = [QSLight(qsid, qsusb) for qsid in discovery_info[QWIKSWITCH]] - add_devices(devs) + add_entities(devs) class QSLight(QSToggleEntity, Light): diff --git a/homeassistant/components/light/rflink.py b/homeassistant/components/light/rflink.py index a05822ed8d173d..b410fdceff7bcd 100644 --- a/homeassistant/components/light/rflink.py +++ b/homeassistant/components/light/rflink.py @@ -156,9 +156,10 @@ def devices_from_config(domain_config, hass=None): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Rflink light platform.""" - async_add_devices(devices_from_config(config, hass)) + async_add_entities(devices_from_config(config, hass)) @asyncio.coroutine def add_new_device(event): @@ -170,7 +171,7 @@ def add_new_device(event): device_config = config[CONF_DEVICE_DEFAULTS] device = entity_class(device_id, hass, **device_config) - async_add_devices([device]) + async_add_entities([device]) # Register entity to listen to incoming Rflink events hass.data[DATA_ENTITY_LOOKUP][ diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index 293783ee3ab1af..10288773486541 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -36,12 +36,12 @@ SUPPORT_RFXTRX = SUPPORT_BRIGHTNESS -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the RFXtrx platform.""" import RFXtrx as rfxtrxmod lights = rfxtrx.get_devices_from_config(config, RfxtrxLight) - add_devices(lights) + add_entities(lights) def light_update(event): """Handle light updates from the RFXtrx gateway.""" @@ -51,7 +51,7 @@ def light_update(event): new_device = rfxtrx.get_new_device(event, config, RfxtrxLight) if new_device: - add_devices([new_device]) + add_entities([new_device]) rfxtrx.apply_received_command(event) diff --git a/homeassistant/components/light/rpi_gpio_pwm.py b/homeassistant/components/light/rpi_gpio_pwm.py index 9385c4bfb804b8..5a0e0546b1f253 100644 --- a/homeassistant/components/light/rpi_gpio_pwm.py +++ b/homeassistant/components/light/rpi_gpio_pwm.py @@ -54,7 +54,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the PWM LED lights.""" from pwmled.led import SimpleLed from pwmled.led.rgb import RgbLed @@ -92,7 +92,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return leds.append(led) - add_devices(leds) + add_entities(leds) class PwmSimpleLed(Light): diff --git a/homeassistant/components/light/scsgate.py b/homeassistant/components/light/scsgate.py index 3d567afe09e0b8..4a18bc996724f9 100644 --- a/homeassistant/components/light/scsgate.py +++ b/homeassistant/components/light/scsgate.py @@ -23,7 +23,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SCSGate switches.""" devices = config.get(CONF_DEVICES) lights = [] @@ -42,7 +42,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): light = SCSGateLight(name=name, scs_id=scs_id, logger=logger) lights.append(light) - add_devices(lights) + add_entities(lights) scsgate.SCSGATE.add_devices_to_register(lights) diff --git a/homeassistant/components/light/sensehat.py b/homeassistant/components/light/sensehat.py index 6ab2592cedf912..86153fffef8cd9 100644 --- a/homeassistant/components/light/sensehat.py +++ b/homeassistant/components/light/sensehat.py @@ -28,14 +28,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Sense Hat Light platform.""" from sense_hat import SenseHat sensehat = SenseHat() name = config.get(CONF_NAME) - add_devices([SenseHatLight(sensehat, name)]) + add_entities([SenseHatLight(sensehat, name)]) class SenseHatLight(Light): diff --git a/homeassistant/components/light/sisyphus.py b/homeassistant/components/light/sisyphus.py index ded78716317161..75cc86a0154fcc 100644 --- a/homeassistant/components/light/sisyphus.py +++ b/homeassistant/components/light/sisyphus.py @@ -17,10 +17,10 @@ SUPPORTED_FEATURES = SUPPORT_BRIGHTNESS -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a single Sisyphus table.""" name = discovery_info[CONF_NAME] - add_devices( + add_entities( [SisyphusLight(name, hass.data[DATA_SISYPHUS][name])], update_before_add=True) diff --git a/homeassistant/components/light/skybell.py b/homeassistant/components/light/skybell.py index d32183f146831c..ecb240f2ef345e 100644 --- a/homeassistant/components/light/skybell.py +++ b/homeassistant/components/light/skybell.py @@ -19,7 +19,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the platform for a Skybell device.""" skybell = hass.data.get(SKYBELL_DOMAIN) @@ -27,7 +27,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for device in skybell.get_devices(): sensors.append(SkybellLight(device)) - add_devices(sensors, True) + add_entities(sensors, True) def _to_skybell_level(level): diff --git a/homeassistant/components/light/tellduslive.py b/homeassistant/components/light/tellduslive.py index 321cfd677b5686..07b5458fa4506c 100644 --- a/homeassistant/components/light/tellduslive.py +++ b/homeassistant/components/light/tellduslive.py @@ -15,11 +15,11 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tellstick Net lights.""" if discovery_info is None: return - add_devices(TelldusLiveLight(hass, light) for light in discovery_info) + add_entities(TelldusLiveLight(hass, light) for light in discovery_info) class TelldusLiveLight(TelldusLiveEntity, Light): diff --git a/homeassistant/components/light/tellstick.py b/homeassistant/components/light/tellstick.py index 44e5e40b3b79cc..cf9dd545e99b6c 100644 --- a/homeassistant/components/light/tellstick.py +++ b/homeassistant/components/light/tellstick.py @@ -15,7 +15,7 @@ SUPPORT_TELLSTICK = SUPPORT_BRIGHTNESS -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tellstick lights.""" if (discovery_info is None or discovery_info[ATTR_DISCOVER_DEVICES] is None): @@ -24,10 +24,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): signal_repetitions = discovery_info.get( ATTR_DISCOVER_CONFIG, DEFAULT_SIGNAL_REPETITIONS) - add_devices([TellstickLight(hass.data[DATA_TELLSTICK][tellcore_id], - signal_repetitions) - for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]], - True) + add_entities([TellstickLight(hass.data[DATA_TELLSTICK][tellcore_id], + signal_repetitions) + for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]], + True) class TellstickLight(TellstickDevice, Light): diff --git a/homeassistant/components/light/template.py b/homeassistant/components/light/template.py index ad77b734fbb3d8..9be6eb99acc826 100644 --- a/homeassistant/components/light/template.py +++ b/homeassistant/components/light/template.py @@ -50,7 +50,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Template Lights.""" lights = [] @@ -103,7 +104,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): _LOGGER.error("No lights added") return False - async_add_devices(lights) + async_add_entities(lights) return True diff --git a/homeassistant/components/light/tikteck.py b/homeassistant/components/light/tikteck.py index c21da57ea96f1a..64b4069d98e4b7 100644 --- a/homeassistant/components/light/tikteck.py +++ b/homeassistant/components/light/tikteck.py @@ -31,7 +31,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tikteck platform.""" lights = [] for address, device_config in config[CONF_DEVICES].items(): @@ -43,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if light.is_valid: lights.append(light) - add_devices(lights) + add_entities(lights) class TikteckLight(Light): diff --git a/homeassistant/components/light/tplink.py b/homeassistant/components/light/tplink.py index 9374c1418f0bfb..a1e46c07d7d0d3 100644 --- a/homeassistant/components/light/tplink.py +++ b/homeassistant/components/light/tplink.py @@ -35,12 +35,12 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Initialise pyLB100 SmartBulb.""" from pyHS100 import SmartBulb host = config.get(CONF_HOST) name = config.get(CONF_NAME) - add_devices([TPLinkSmartBulb(SmartBulb(host), name)], True) + add_entities([TPLinkSmartBulb(SmartBulb(host), name)], True) def brightness_to_percentage(byt): diff --git a/homeassistant/components/light/tradfri.py b/homeassistant/components/light/tradfri.py index c30745239ea05c..0d12d095bb68a3 100644 --- a/homeassistant/components/light/tradfri.py +++ b/homeassistant/components/light/tradfri.py @@ -32,7 +32,7 @@ async def async_setup_platform(hass, config, - async_add_devices, discovery_info=None): + async_add_entities, discovery_info=None): """Set up the IKEA Tradfri Light platform.""" if discovery_info is None: return @@ -46,7 +46,7 @@ async def async_setup_platform(hass, config, devices = await api(devices_commands) lights = [dev for dev in devices if dev.has_light_control] if lights: - async_add_devices( + async_add_entities( TradfriLight(light, api, gateway_id) for light in lights) allow_tradfri_groups = hass.data[KEY_TRADFRI_GROUPS][gateway_id] @@ -55,7 +55,7 @@ async def async_setup_platform(hass, config, groups_commands = await api(groups_command) groups = await api(groups_commands) if groups: - async_add_devices( + async_add_entities( TradfriGroup(group, api, gateway_id) for group in groups) diff --git a/homeassistant/components/light/tuya.py b/homeassistant/components/light/tuya.py index d7691cea0118d6..0dc2cacc1e016f 100644 --- a/homeassistant/components/light/tuya.py +++ b/homeassistant/components/light/tuya.py @@ -14,7 +14,7 @@ DEPENDENCIES = ['tuya'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tuya light platform.""" if discovery_info is None: return @@ -26,7 +26,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if device is None: continue devices.append(TuyaLight(device)) - add_devices(devices) + add_entities(devices) class TuyaLight(TuyaDevice, Light): diff --git a/homeassistant/components/light/vera.py b/homeassistant/components/light/vera.py index e62ffaecdff92f..702236ac748f86 100644 --- a/homeassistant/components/light/vera.py +++ b/homeassistant/components/light/vera.py @@ -18,9 +18,9 @@ DEPENDENCIES = ['vera'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vera lights.""" - add_devices( + add_entities( [VeraLight(device, hass.data[VERA_CONTROLLER]) for device in hass.data[VERA_DEVICES]['light']], True) diff --git a/homeassistant/components/light/wemo.py b/homeassistant/components/light/wemo.py index 4c912d60fb7135..72279fbe1a4db0 100644 --- a/homeassistant/components/light/wemo.py +++ b/homeassistant/components/light/wemo.py @@ -7,11 +7,13 @@ import asyncio import logging from datetime import timedelta +import requests from homeassistant import util from homeassistant.components.light import ( Light, ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, ATTR_TRANSITION, SUPPORT_BRIGHTNESS, SUPPORT_COLOR_TEMP, SUPPORT_COLOR, SUPPORT_TRANSITION) +from homeassistant.exceptions import PlatformNotReady import homeassistant.util.color as color_util DEPENDENCIES = ['wemo'] @@ -25,22 +27,28 @@ SUPPORT_TRANSITION) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up discovered WeMo switches.""" from pywemo import discovery if discovery_info is not None: location = discovery_info['ssdp_description'] mac = discovery_info['mac_address'] - device = discovery.device_from_description(location, mac) + + try: + device = discovery.device_from_description(location, mac) + except (requests.exceptions.ConnectionError, + requests.exceptions.Timeout) as err: + _LOGGER.error('Unable to access %s (%s)', location, err) + raise PlatformNotReady if device.model_name == 'Dimmer': - add_devices([WemoDimmer(device)]) + add_entities([WemoDimmer(device)]) else: - setup_bridge(device, add_devices) + setup_bridge(device, add_entities) -def setup_bridge(bridge, add_devices): +def setup_bridge(bridge, add_entities): """Set up a WeMo link.""" lights = {} @@ -57,7 +65,7 @@ def update_lights(): new_lights.append(lights[light_id]) if new_lights: - add_devices(new_lights) + add_entities(new_lights) update_lights() @@ -68,39 +76,39 @@ class WemoLight(Light): def __init__(self, device, update_lights): """Initialize the WeMo light.""" self.light_id = device.name - self.device = device + self.wemo = device self.update_lights = update_lights @property def unique_id(self): """Return the ID of this light.""" - return self.device.uniqueID + return self.wemo.uniqueID @property def name(self): """Return the name of the light.""" - return self.device.name + return self.wemo.name @property def brightness(self): """Return the brightness of this light between 0..255.""" - return self.device.state.get('level', 255) + return self.wemo.state.get('level', 255) @property def hs_color(self): """Return the hs color values of this light.""" - xy_color = self.device.state.get('color_xy') + xy_color = self.wemo.state.get('color_xy') return color_util.color_xy_to_hs(*xy_color) if xy_color else None @property def color_temp(self): """Return the color temperature of this light in mireds.""" - return self.device.state.get('temperature_mireds') + return self.wemo.state.get('temperature_mireds') @property def is_on(self): """Return true if device is on.""" - return self.device.state['onoff'] != 0 + return self.wemo.state['onoff'] != 0 @property def supported_features(self): @@ -110,7 +118,7 @@ def supported_features(self): @property def available(self): """Return if light is available.""" - return self.device.state['available'] + return self.wemo.state['available'] def turn_on(self, **kwargs): """Turn the light on.""" @@ -120,23 +128,23 @@ def turn_on(self, **kwargs): if hs_color is not None: xy_color = color_util.color_hs_to_xy(*hs_color) - self.device.set_color(xy_color, transition=transitiontime) + self.wemo.set_color(xy_color, transition=transitiontime) if ATTR_COLOR_TEMP in kwargs: colortemp = kwargs[ATTR_COLOR_TEMP] - self.device.set_temperature(mireds=colortemp, - transition=transitiontime) + self.wemo.set_temperature(mireds=colortemp, + transition=transitiontime) if ATTR_BRIGHTNESS in kwargs: brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightness or 255) - self.device.turn_on(level=brightness, transition=transitiontime) + self.wemo.turn_on(level=brightness, transition=transitiontime) else: - self.device.turn_on(transition=transitiontime) + self.wemo.turn_on(transition=transitiontime) def turn_off(self, **kwargs): """Turn the light off.""" transitiontime = int(kwargs.get(ATTR_TRANSITION, 0)) - self.device.turn_off(transition=transitiontime) + self.wemo.turn_off(transition=transitiontime) def update(self): """Synchronize state with bridge.""" diff --git a/homeassistant/components/light/wink.py b/homeassistant/components/light/wink.py index a2cc4fd7aeb5b8..ee8c2aca8b5422 100644 --- a/homeassistant/components/light/wink.py +++ b/homeassistant/components/light/wink.py @@ -17,18 +17,18 @@ DEPENDENCIES = ['wink'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink lights.""" import pywink for light in pywink.get_light_bulbs(): _id = light.object_id() + light.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkLight(light, hass)]) + add_entities([WinkLight(light, hass)]) for light in pywink.get_light_groups(): _id = light.object_id() + light.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkLight(light, hass)]) + add_entities([WinkLight(light, hass)]) class WinkLight(WinkDevice, Light): diff --git a/homeassistant/components/light/x10.py b/homeassistant/components/light/x10.py index e782e664801c81..ef2211a4469fc8 100644 --- a/homeassistant/components/light/x10.py +++ b/homeassistant/components/light/x10.py @@ -39,7 +39,7 @@ def get_unit_status(code): return int(output.decode('utf-8')[0]) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the x10 Light platform.""" try: x10_command('info') @@ -47,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error(err.output) return False - add_devices(X10Light(light) for light in config[CONF_DEVICES]) + add_entities(X10Light(light) for light in config[CONF_DEVICES]) class X10Light(Light): diff --git a/homeassistant/components/light/xiaomi_aqara.py b/homeassistant/components/light/xiaomi_aqara.py index 75c85a4bfcfb7d..8ad0f2522d22b4 100644 --- a/homeassistant/components/light/xiaomi_aqara.py +++ b/homeassistant/components/light/xiaomi_aqara.py @@ -12,7 +12,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Perform the setup for Xiaomi devices.""" devices = [] for (_, gateway) in hass.data[PY_XIAOMI_GATEWAY].gateways.items(): @@ -21,7 +21,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if model in ['gateway', 'gateway.v3']: devices.append(XiaomiGatewayLight(device, 'Gateway Light', gateway)) - add_devices(devices) + add_entities(devices) class XiaomiGatewayLight(XiaomiDevice, Light): diff --git a/homeassistant/components/light/xiaomi_miio.py b/homeassistant/components/light/xiaomi_miio.py index fbb8dd66f013d8..51c36fc2dd08f0 100644 --- a/homeassistant/components/light/xiaomi_miio.py +++ b/homeassistant/components/light/xiaomi_miio.py @@ -42,7 +42,7 @@ 'philips.light.candle2']), }) -REQUIREMENTS = ['python-miio==0.4.0', 'construct==2.9.41'] +REQUIREMENTS = ['python-miio==0.4.1', 'construct==2.9.41'] # The light does not accept cct values < 1 CCT_MIN = 1 @@ -100,7 +100,7 @@ } -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the light from config.""" from miio import Device, DeviceException @@ -164,7 +164,7 @@ async def async_setup_platform(hass, config, async_add_devices, 'and provide the following data: %s', model) return False - async_add_devices(devices, update_before_add=True) + async_add_entities(devices, update_before_add=True) async def async_service_handler(service): """Map services to methods on Xiaomi Philips Lights.""" diff --git a/homeassistant/components/light/yeelight.py b/homeassistant/components/light/yeelight.py index 791de291b4803d..a08ebe459b48f6 100644 --- a/homeassistant/components/light/yeelight.py +++ b/homeassistant/components/light/yeelight.py @@ -120,7 +120,7 @@ def _wrap(self, *args, **kwargs): return _wrap -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Yeelight bulbs.""" from yeelight.enums import PowerMode @@ -150,7 +150,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): lights.append(light) hass.data[DATA_KEY][host] = light - add_devices(lights, True) + add_entities(lights, True) def service_handler(service): """Dispatch service calls to target entities.""" diff --git a/homeassistant/components/light/yeelightsunflower.py b/homeassistant/components/light/yeelightsunflower.py index 96cce67b1bb316..2250a85c55c962 100644 --- a/homeassistant/components/light/yeelightsunflower.py +++ b/homeassistant/components/light/yeelightsunflower.py @@ -26,7 +26,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Yeelight Sunflower Light platform.""" import yeelightsunflower @@ -37,7 +37,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Could not connect to Yeelight Sunflower hub") return False - add_devices(SunflowerBulb(light) for light in hub.get_lights()) + add_entities(SunflowerBulb(light) for light in hub.get_lights()) class SunflowerBulb(Light): diff --git a/homeassistant/components/light/zengge.py b/homeassistant/components/light/zengge.py index 35d2bf2388cd3b..b283b8611dc640 100644 --- a/homeassistant/components/light/zengge.py +++ b/homeassistant/components/light/zengge.py @@ -30,7 +30,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Zengge platform.""" lights = [] for address, device_config in config[CONF_DEVICES].items(): @@ -41,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if light.is_valid: lights.append(light) - add_devices(lights, True) + add_entities(lights, True) class ZenggeLight(Light): diff --git a/homeassistant/components/light/zha.py b/homeassistant/components/light/zha.py index bd01a513e0b454..dc5c4977944d0d 100644 --- a/homeassistant/components/light/zha.py +++ b/homeassistant/components/light/zha.py @@ -20,7 +20,7 @@ UNSUPPORTED_ATTRIBUTE = 0x86 -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Zigbee Home Automation lights.""" discovery_info = zha.get_discovery_info(hass, discovery_info) @@ -42,7 +42,7 @@ async def async_setup_platform(hass, config, async_add_devices, if result.get('color_temperature') is not UNSUPPORTED_ATTRIBUTE: discovery_info['color_capabilities'] |= CAPABILITIES_COLOR_TEMP - async_add_devices([Light(**discovery_info)], update_before_add=True) + async_add_entities([Light(**discovery_info)], update_before_add=True) class Light(zha.Entity, light.Light): diff --git a/homeassistant/components/light/zigbee.py b/homeassistant/components/light/zigbee.py index f4406abf7bda2c..42dc95d11631d1 100644 --- a/homeassistant/components/light/zigbee.py +++ b/homeassistant/components/light/zigbee.py @@ -22,9 +22,9 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Create and add an entity based on the configuration.""" - add_devices([ZigBeeLight(hass, ZigBeeDigitalOutConfig(config))]) + add_entities([ZigBeeLight(hass, ZigBeeDigitalOutConfig(config))]) class ZigBeeLight(ZigBeeDigitalOut, Light): diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index f03d028a38f1ee..3c4ff7cdeddfa3 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -101,38 +101,18 @@ def async_setup(hass, config): yield from component.async_setup(config) - @asyncio.coroutine - def async_handle_lock_service(service): - """Handle calls to the lock services.""" - target_locks = component.async_extract_from_service(service) - - code = service.data.get(ATTR_CODE) - - update_tasks = [] - for entity in target_locks: - if service.service == SERVICE_LOCK: - yield from entity.async_lock(code=code) - elif service.service == SERVICE_OPEN: - yield from entity.async_open(code=code) - else: - yield from entity.async_unlock(code=code) - - if not entity.should_poll: - continue - update_tasks.append(entity.async_update_ha_state(True)) - - if update_tasks: - yield from asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_UNLOCK, async_handle_lock_service, - schema=LOCK_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_LOCK, async_handle_lock_service, - schema=LOCK_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_OPEN, async_handle_lock_service, - schema=LOCK_SERVICE_SCHEMA) + component.async_register_entity_service( + SERVICE_UNLOCK, LOCK_SERVICE_SCHEMA, + 'async_unlock' + ) + component.async_register_entity_service( + SERVICE_LOCK, LOCK_SERVICE_SCHEMA, + 'async_lock' + ) + component.async_register_entity_service( + SERVICE_OPEN, LOCK_SERVICE_SCHEMA, + 'async_open' + ) return True diff --git a/homeassistant/components/lock/abode.py b/homeassistant/components/lock/abode.py index 2d3423266360d5..a8777ccb503344 100644 --- a/homeassistant/components/lock/abode.py +++ b/homeassistant/components/lock/abode.py @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Abode lock devices.""" import abodepy.helpers.constants as CONST @@ -30,7 +30,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data.devices.extend(devices) - add_devices(devices) + add_entities(devices) class AbodeLock(AbodeDevice, LockDevice): diff --git a/homeassistant/components/lock/august.py b/homeassistant/components/lock/august.py index 9ca63cb493bc77..7aec3c78690231 100644 --- a/homeassistant/components/lock/august.py +++ b/homeassistant/components/lock/august.py @@ -15,7 +15,7 @@ SCAN_INTERVAL = timedelta(seconds=5) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up August locks.""" data = hass.data[DATA_AUGUST] devices = [] @@ -23,7 +23,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for lock in data.locks: devices.append(AugustLock(data, lock)) - add_devices(devices, True) + add_entities(devices, True) class AugustLock(LockDevice): diff --git a/homeassistant/components/lock/bmw_connected_drive.py b/homeassistant/components/lock/bmw_connected_drive.py index 52734b1259ca42..c84df54cfba970 100644 --- a/homeassistant/components/lock/bmw_connected_drive.py +++ b/homeassistant/components/lock/bmw_connected_drive.py @@ -16,17 +16,18 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the BMW Connected Drive lock.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the BMW Connected Drive lock.""" accounts = hass.data[BMW_DOMAIN] _LOGGER.debug('Found BMW accounts: %s', ', '.join([a.name for a in accounts])) devices = [] for account in accounts: - for vehicle in account.account.vehicles: - device = BMWLock(account, vehicle, 'lock', 'BMW lock') - devices.append(device) - add_devices(devices, True) + if not account.read_only: + for vehicle in account.account.vehicles: + device = BMWLock(account, vehicle, 'lock', 'BMW lock') + devices.append(device) + add_entities(devices, True) class BMWLock(LockDevice): diff --git a/homeassistant/components/lock/demo.py b/homeassistant/components/lock/demo.py index 8da53a9ef11fba..a0cc45991c822f 100644 --- a/homeassistant/components/lock/demo.py +++ b/homeassistant/components/lock/demo.py @@ -8,9 +8,9 @@ from homeassistant.const import (STATE_LOCKED, STATE_UNLOCKED) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Demo lock platform.""" - add_devices([ + add_entities([ DemoLock('Front Door', STATE_LOCKED), DemoLock('Kitchen Door', STATE_UNLOCKED), DemoLock('Openable Lock', STATE_LOCKED, True) diff --git a/homeassistant/components/lock/homematic.py b/homeassistant/components/lock/homematic.py index 0d70849e37e41a..9d9f2a28b4f718 100644 --- a/homeassistant/components/lock/homematic.py +++ b/homeassistant/components/lock/homematic.py @@ -5,17 +5,17 @@ https://home-assistant.io/components/lock.homematic/ """ import logging -from homeassistant.components.lock import LockDevice, SUPPORT_OPEN -from homeassistant.components.homematic import HMDevice, ATTR_DISCOVER_DEVICES -from homeassistant.const import STATE_UNKNOWN +from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice +from homeassistant.components.lock import SUPPORT_OPEN, LockDevice +from homeassistant.const import STATE_UNKNOWN _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['homematic'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Homematic lock platform.""" if discovery_info is None: return @@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for conf in discovery_info[ATTR_DISCOVER_DEVICES]: devices.append(HMLock(conf)) - add_devices(devices) + add_entities(devices) class HMLock(HMDevice, LockDevice): diff --git a/homeassistant/components/lock/isy994.py b/homeassistant/components/lock/isy994.py index 9bcf5a86d08ecc..9481e619a61907 100644 --- a/homeassistant/components/lock/isy994.py +++ b/homeassistant/components/lock/isy994.py @@ -22,7 +22,7 @@ def setup_platform(hass, config: ConfigType, - add_devices: Callable[[list], None], discovery_info=None): + add_entities: Callable[[list], None], discovery_info=None): """Set up the ISY994 lock platform.""" devices = [] for node in hass.data[ISY994_NODES][DOMAIN]: @@ -31,7 +31,7 @@ def setup_platform(hass, config: ConfigType, for name, status, actions in hass.data[ISY994_PROGRAMS][DOMAIN]: devices.append(ISYLockProgram(name, status, actions)) - add_devices(devices) + add_entities(devices) class ISYLockDevice(ISYDevice, LockDevice): diff --git a/homeassistant/components/lock/kiwi.py b/homeassistant/components/lock/kiwi.py index 78ea45525f284c..5d217796767d1c 100644 --- a/homeassistant/components/lock/kiwi.py +++ b/homeassistant/components/lock/kiwi.py @@ -32,7 +32,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the KIWI lock platform.""" from kiwiki import KiwiClient, KiwiException try: @@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # No locks found; abort setup routine. _LOGGER.info("No KIWI locks found in your account.") return - add_devices([KiwiLock(lock, kiwi) for lock in available_locks], True) + add_entities([KiwiLock(lock, kiwi) for lock in available_locks], True) class KiwiLock(LockDevice): diff --git a/homeassistant/components/lock/lockitron.py b/homeassistant/components/lock/lockitron.py index 6bf445ba477529..b190a5cd2cd826 100644 --- a/homeassistant/components/lock/lockitron.py +++ b/homeassistant/components/lock/lockitron.py @@ -26,15 +26,15 @@ API_ACTION_URL = BASE_URL + '/v2/locks/{}?access_token={}&state={}' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Lockitron platform.""" access_token = config.get(CONF_ACCESS_TOKEN) device_id = config.get(CONF_ID) response = requests.get( API_STATE_URL.format(device_id, access_token), timeout=5) if response.status_code == 200: - add_devices([Lockitron(response.json()['state'], access_token, - device_id)]) + add_entities([Lockitron(response.json()['state'], access_token, + device_id)]) else: _LOGGER.error( "Error retrieving lock status during init: %s", response.text) diff --git a/homeassistant/components/lock/mqtt.py b/homeassistant/components/lock/mqtt.py index 45029e679a5c7d..103864a6bfd732 100644 --- a/homeassistant/components/lock/mqtt.py +++ b/homeassistant/components/lock/mqtt.py @@ -42,7 +42,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the MQTT lock.""" if discovery_info is not None: config = PLATFORM_SCHEMA(discovery_info) @@ -51,7 +52,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): if value_template is not None: value_template.hass = hass - async_add_devices([MqttLock( + async_add_entities([MqttLock( config.get(CONF_NAME), config.get(CONF_STATE_TOPIC), config.get(CONF_COMMAND_TOPIC), diff --git a/homeassistant/components/lock/nello.py b/homeassistant/components/lock/nello.py index f67243415c50f8..4fd9faafcbe4e3 100644 --- a/homeassistant/components/lock/nello.py +++ b/homeassistant/components/lock/nello.py @@ -27,11 +27,11 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Nello lock platform.""" from pynello import Nello nello = Nello(config.get(CONF_USERNAME), config.get(CONF_PASSWORD)) - add_devices([NelloLock(lock) for lock in nello.locations], True) + add_entities([NelloLock(lock) for lock in nello.locations], True) class NelloLock(LockDevice): diff --git a/homeassistant/components/lock/nuki.py b/homeassistant/components/lock/nuki.py index 536c8f2abeb794..6cf58dda04ce43 100644 --- a/homeassistant/components/lock/nuki.py +++ b/homeassistant/components/lock/nuki.py @@ -50,11 +50,11 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Nuki lock platform.""" from pynuki import NukiBridge bridge = NukiBridge(config.get(CONF_HOST), config.get(CONF_TOKEN)) - add_devices([NukiLock(lock) for lock in bridge.locks]) + add_entities([NukiLock(lock) for lock in bridge.locks]) def service_handler(service): """Service handler for nuki services.""" diff --git a/homeassistant/components/lock/sesame.py b/homeassistant/components/lock/sesame.py index 8d9c05e3f26d71..44a6cfb265c1f9 100644 --- a/homeassistant/components/lock/sesame.py +++ b/homeassistant/components/lock/sesame.py @@ -26,16 +26,16 @@ def setup_platform( hass, config: ConfigType, - add_devices: Callable[[list], None], discovery_info=None): + add_entities: Callable[[list], None], discovery_info=None): """Set up the Sesame platform.""" import pysesame email = config.get(CONF_EMAIL) password = config.get(CONF_PASSWORD) - add_devices([SesameDevice(sesame) for sesame in - pysesame.get_sesames(email, password)], - update_before_add=True) + add_entities([SesameDevice(sesame) for sesame in + pysesame.get_sesames(email, password)], + update_before_add=True) class SesameDevice(LockDevice): diff --git a/homeassistant/components/lock/tesla.py b/homeassistant/components/lock/tesla.py index 4d24ed2000359c..2ffb996aec349d 100644 --- a/homeassistant/components/lock/tesla.py +++ b/homeassistant/components/lock/tesla.py @@ -16,11 +16,11 @@ DEPENDENCIES = ['tesla'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tesla lock platform.""" devices = [TeslaLock(device, hass.data[TESLA_DOMAIN]['controller']) for device in hass.data[TESLA_DOMAIN]['devices']['lock']] - add_devices(devices, True) + add_entities(devices, True) class TeslaLock(TeslaDevice, LockDevice): diff --git a/homeassistant/components/lock/vera.py b/homeassistant/components/lock/vera.py index e6e277cdee1417..21287b6328ead4 100644 --- a/homeassistant/components/lock/vera.py +++ b/homeassistant/components/lock/vera.py @@ -16,9 +16,9 @@ DEPENDENCIES = ['vera'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return Vera locks.""" - add_devices( + add_entities( [VeraLock(device, hass.data[VERA_CONTROLLER]) for device in hass.data[VERA_DEVICES]['lock']], True) diff --git a/homeassistant/components/lock/verisure.py b/homeassistant/components/lock/verisure.py index 4af19f52611eec..877c8a1ddf6c52 100644 --- a/homeassistant/components/lock/verisure.py +++ b/homeassistant/components/lock/verisure.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Verisure platform.""" locks = [] if int(hub.config.get(CONF_LOCKS, 1)): @@ -26,7 +26,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for device_label in hub.get( "$.doorLockStatusList[*].deviceLabel")]) - add_devices(locks) + add_entities(locks) class VerisureDoorlock(LockDevice): diff --git a/homeassistant/components/lock/volvooncall.py b/homeassistant/components/lock/volvooncall.py index b6e7383b138251..58fa83cef30a75 100644 --- a/homeassistant/components/lock/volvooncall.py +++ b/homeassistant/components/lock/volvooncall.py @@ -12,12 +12,12 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Volvo On Call lock.""" if discovery_info is None: return - add_devices([VolvoLock(hass, *discovery_info)]) + add_entities([VolvoLock(hass, *discovery_info)]) class VolvoLock(VolvoEntity, LockDevice): diff --git a/homeassistant/components/lock/wink.py b/homeassistant/components/lock/wink.py index 1c42e427a00c05..03de8fc5919820 100644 --- a/homeassistant/components/lock/wink.py +++ b/homeassistant/components/lock/wink.py @@ -66,14 +66,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink platform.""" import pywink for lock in pywink.get_locks(): _id = lock.object_id() + lock.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkLockDevice(lock, hass)]) + add_entities([WinkLockDevice(lock, hass)]) def service_handle(service): """Handle for services.""" diff --git a/homeassistant/components/lock/xiaomi_aqara.py b/homeassistant/components/lock/xiaomi_aqara.py index 9b084a2bc55a73..15415a73284776 100644 --- a/homeassistant/components/lock/xiaomi_aqara.py +++ b/homeassistant/components/lock/xiaomi_aqara.py @@ -24,7 +24,7 @@ UNLOCK_MAINTAIN_TIME = 5 -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Perform the setup for Xiaomi devices.""" devices = [] @@ -34,7 +34,7 @@ async def async_setup_platform(hass, config, async_add_devices, model = device['model'] if model == 'lock.aq1': devices.append(XiaomiAqaraLock(device, 'Lock', gateway)) - async_add_devices(devices) + async_add_entities(devices) class XiaomiAqaraLock(LockDevice, XiaomiDevice): diff --git a/homeassistant/components/lock/zwave.py b/homeassistant/components/lock/zwave.py index b7bc9f15e19953..5ee88f053b54bb 100644 --- a/homeassistant/components/lock/zwave.py +++ b/homeassistant/components/lock/zwave.py @@ -120,10 +120,11 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Z-Wave Lock platform.""" yield from zwave.async_setup_platform( - hass, config, async_add_devices, discovery_info) + hass, config, async_add_entities, discovery_info) network = hass.data[zwave.const.DATA_NETWORK] diff --git a/homeassistant/components/mailbox/asterisk_mbox.py b/homeassistant/components/mailbox/asterisk_mbox.py index 2e807058edfb14..47d59234d7d54a 100644 --- a/homeassistant/components/mailbox/asterisk_mbox.py +++ b/homeassistant/components/mailbox/asterisk_mbox.py @@ -21,7 +21,7 @@ @asyncio.coroutine -def async_get_handler(hass, config, async_add_devices, discovery_info=None): +def async_get_handler(hass, config, async_add_entities, discovery_info=None): """Set up the Asterix VM platform.""" return AsteriskMailbox(hass, DOMAIN) diff --git a/homeassistant/components/media_extractor.py b/homeassistant/components/media_extractor.py index 793d33e52fafb5..8f2abb9be19e72 100644 --- a/homeassistant/components/media_extractor.py +++ b/homeassistant/components/media_extractor.py @@ -14,7 +14,7 @@ SERVICE_PLAY_MEDIA) from homeassistant.helpers import config_validation as cv -REQUIREMENTS = ['youtube_dl==2018.08.04'] +REQUIREMENTS = ['youtube_dl==2018.08.22'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index c475291227ac62..7c49b095c667fb 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -151,42 +151,6 @@ vol.Required(ATTR_MEDIA_SHUFFLE): cv.boolean, }) -SERVICE_TO_METHOD = { - SERVICE_TURN_ON: {'method': 'async_turn_on'}, - SERVICE_TURN_OFF: {'method': 'async_turn_off'}, - SERVICE_TOGGLE: {'method': 'async_toggle'}, - SERVICE_VOLUME_UP: {'method': 'async_volume_up'}, - SERVICE_VOLUME_DOWN: {'method': 'async_volume_down'}, - SERVICE_MEDIA_PLAY_PAUSE: {'method': 'async_media_play_pause'}, - SERVICE_MEDIA_PLAY: {'method': 'async_media_play'}, - SERVICE_MEDIA_PAUSE: {'method': 'async_media_pause'}, - SERVICE_MEDIA_STOP: {'method': 'async_media_stop'}, - SERVICE_MEDIA_NEXT_TRACK: {'method': 'async_media_next_track'}, - SERVICE_MEDIA_PREVIOUS_TRACK: {'method': 'async_media_previous_track'}, - SERVICE_CLEAR_PLAYLIST: {'method': 'async_clear_playlist'}, - SERVICE_VOLUME_SET: { - 'method': 'async_set_volume_level', - 'schema': MEDIA_PLAYER_SET_VOLUME_SCHEMA}, - SERVICE_VOLUME_MUTE: { - 'method': 'async_mute_volume', - 'schema': MEDIA_PLAYER_MUTE_VOLUME_SCHEMA}, - SERVICE_MEDIA_SEEK: { - 'method': 'async_media_seek', - 'schema': MEDIA_PLAYER_MEDIA_SEEK_SCHEMA}, - SERVICE_SELECT_SOURCE: { - 'method': 'async_select_source', - 'schema': MEDIA_PLAYER_SELECT_SOURCE_SCHEMA}, - SERVICE_SELECT_SOUND_MODE: { - 'method': 'async_select_sound_mode', - 'schema': MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA}, - SERVICE_PLAY_MEDIA: { - 'method': 'async_play_media', - 'schema': MEDIA_PLAYER_PLAY_MEDIA_SCHEMA}, - SERVICE_SHUFFLE_SET: { - 'method': 'async_set_shuffle', - 'schema': MEDIA_PLAYER_SET_SHUFFLE_SCHEMA}, -} - ATTR_TO_PROPERTY = [ ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, @@ -409,56 +373,95 @@ async def async_setup(hass, config): await component.async_setup(config) - async def async_service_handler(service): - """Map services to methods on MediaPlayerDevice.""" - method = SERVICE_TO_METHOD.get(service.service) - if not method: - return - - params = {} - if service.service == SERVICE_VOLUME_SET: - params['volume'] = service.data.get(ATTR_MEDIA_VOLUME_LEVEL) - elif service.service == SERVICE_VOLUME_MUTE: - params['mute'] = service.data.get(ATTR_MEDIA_VOLUME_MUTED) - elif service.service == SERVICE_MEDIA_SEEK: - params['position'] = service.data.get(ATTR_MEDIA_SEEK_POSITION) - elif service.service == SERVICE_SELECT_SOURCE: - params['source'] = service.data.get(ATTR_INPUT_SOURCE) - elif service.service == SERVICE_SELECT_SOUND_MODE: - params['sound_mode'] = service.data.get(ATTR_SOUND_MODE) - elif service.service == SERVICE_PLAY_MEDIA: - params['media_type'] = \ - service.data.get(ATTR_MEDIA_CONTENT_TYPE) - params['media_id'] = service.data.get(ATTR_MEDIA_CONTENT_ID) - params[ATTR_MEDIA_ENQUEUE] = \ - service.data.get(ATTR_MEDIA_ENQUEUE) - elif service.service == SERVICE_SHUFFLE_SET: - params[ATTR_MEDIA_SHUFFLE] = \ - service.data.get(ATTR_MEDIA_SHUFFLE) - target_players = component.async_extract_from_service(service) - - update_tasks = [] - for player in target_players: - await getattr(player, method['method'])(**params) - if not player.should_poll: - continue - update_tasks.append(player.async_update_ha_state(True)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - for service in SERVICE_TO_METHOD: - schema = SERVICE_TO_METHOD[service].get( - 'schema', MEDIA_PLAYER_SCHEMA) - hass.services.async_register( - DOMAIN, service, async_service_handler, - schema=schema) + component.async_register_entity_service( + SERVICE_TURN_ON, MEDIA_PLAYER_SCHEMA, + 'async_turn_on' + ) + component.async_register_entity_service( + SERVICE_TURN_OFF, MEDIA_PLAYER_SCHEMA, + 'async_turn_off' + ) + component.async_register_entity_service( + SERVICE_TOGGLE, MEDIA_PLAYER_SCHEMA, + 'async_toggle' + ) + component.async_register_entity_service( + SERVICE_VOLUME_UP, MEDIA_PLAYER_SCHEMA, + 'async_volume_up' + ) + component.async_register_entity_service( + SERVICE_VOLUME_DOWN, MEDIA_PLAYER_SCHEMA, + 'async_volume_down' + ) + component.async_register_entity_service( + SERVICE_MEDIA_PLAY_PAUSE, MEDIA_PLAYER_SCHEMA, + 'async_media_play_pause' + ) + component.async_register_entity_service( + SERVICE_MEDIA_PLAY, MEDIA_PLAYER_SCHEMA, + 'async_media_play' + ) + component.async_register_entity_service( + SERVICE_MEDIA_PAUSE, MEDIA_PLAYER_SCHEMA, + 'async_media_pause' + ) + component.async_register_entity_service( + SERVICE_MEDIA_STOP, MEDIA_PLAYER_SCHEMA, + 'async_media_stop' + ) + component.async_register_entity_service( + SERVICE_MEDIA_NEXT_TRACK, MEDIA_PLAYER_SCHEMA, + 'async_media_next_track' + ) + component.async_register_entity_service( + SERVICE_MEDIA_PREVIOUS_TRACK, MEDIA_PLAYER_SCHEMA, + 'async_media_previous_track' + ) + component.async_register_entity_service( + SERVICE_CLEAR_PLAYLIST, MEDIA_PLAYER_SCHEMA, + 'async_clear_playlist' + ) + component.async_register_entity_service( + SERVICE_VOLUME_SET, MEDIA_PLAYER_SET_VOLUME_SCHEMA, + lambda entity, call: entity.async_set_volume_level( + volume=call.data[ATTR_MEDIA_VOLUME_LEVEL]) + ) + component.async_register_entity_service( + SERVICE_VOLUME_MUTE, MEDIA_PLAYER_MUTE_VOLUME_SCHEMA, + lambda entity, call: entity.async_mute_volume( + mute=call.data[ATTR_MEDIA_VOLUME_MUTED]) + ) + component.async_register_entity_service( + SERVICE_MEDIA_SEEK, MEDIA_PLAYER_MEDIA_SEEK_SCHEMA, + lambda entity, call: entity.async_media_seek( + position=call.data[ATTR_MEDIA_SEEK_POSITION]) + ) + component.async_register_entity_service( + SERVICE_SELECT_SOURCE, MEDIA_PLAYER_SELECT_SOURCE_SCHEMA, + 'async_select_source' + ) + component.async_register_entity_service( + SERVICE_SELECT_SOUND_MODE, MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA, + 'async_select_sound_mode' + ) + component.async_register_entity_service( + SERVICE_PLAY_MEDIA, MEDIA_PLAYER_PLAY_MEDIA_SCHEMA, + lambda entity, call: entity.async_play_media( + media_type=call.data[ATTR_MEDIA_CONTENT_TYPE], + media_id=call.data[ATTR_MEDIA_CONTENT_ID], + enqueue=call.data.get(ATTR_MEDIA_ENQUEUE) + ) + ) + component.async_register_entity_service( + SERVICE_SHUFFLE_SET, MEDIA_PLAYER_SET_SHUFFLE_SCHEMA, + 'async_set_shuffle' + ) return True async def async_setup_entry(hass, entry): - """Setup a config entry.""" + """Set up a config entry.""" return await hass.data[DOMAIN].async_setup_entry(entry) diff --git a/homeassistant/components/media_player/anthemav.py b/homeassistant/components/media_player/anthemav.py index a74629917b34af..359ee0a9254b32 100644 --- a/homeassistant/components/media_player/anthemav.py +++ b/homeassistant/components/media_player/anthemav.py @@ -36,7 +36,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up our socket to the AVR.""" import anthemav @@ -63,7 +64,7 @@ def async_anthemav_update_callback(message): _LOGGER.debug("dump_rawdata: %s", avr.protocol.dump_rawdata) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, device.avr.close) - async_add_devices([device]) + async_add_entities([device]) class AnthemAVR(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/apple_tv.py b/homeassistant/components/media_player/apple_tv.py index d4a7ad198072ab..360ccd0f522aac 100644 --- a/homeassistant/components/media_player/apple_tv.py +++ b/homeassistant/components/media_player/apple_tv.py @@ -31,7 +31,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Apple TV platform.""" if not discovery_info: return @@ -56,7 +57,7 @@ def on_hass_stop(event): if entity not in hass.data[DATA_ENTITIES]: hass.data[DATA_ENTITIES].append(entity) - async_add_devices([entity]) + async_add_entities([entity]) class AppleTvDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/aquostv.py b/homeassistant/components/media_player/aquostv.py index 93daf5b2f893d6..6e8cc7271212ad 100644 --- a/homeassistant/components/media_player/aquostv.py +++ b/homeassistant/components/media_player/aquostv.py @@ -59,7 +59,7 @@ 8: 'PC_IN'} -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Sharp Aquos TV platform.""" import sharp_aquos_rc @@ -77,13 +77,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): host = vals[0] remote = sharp_aquos_rc.TV(host, port, username, password, timeout=20) - add_devices([SharpAquosTVDevice(name, remote, power_on_enabled)]) + add_entities([SharpAquosTVDevice(name, remote, power_on_enabled)]) return True host = config.get(CONF_HOST) remote = sharp_aquos_rc.TV(host, port, username, password, 15, 1) - add_devices([SharpAquosTVDevice(name, remote, power_on_enabled)]) + add_entities([SharpAquosTVDevice(name, remote, power_on_enabled)]) return True diff --git a/homeassistant/components/media_player/blackbird.py b/homeassistant/components/media_player/blackbird.py index 3d8e1fde687f74..7869093138cb5c 100644 --- a/homeassistant/components/media_player/blackbird.py +++ b/homeassistant/components/media_player/blackbird.py @@ -61,7 +61,7 @@ })) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Monoprice Blackbird 4k 8x8 HDBaseT Matrix platform.""" if DATA_BLACKBIRD not in hass.data: hass.data[DATA_BLACKBIRD] = {} @@ -100,7 +100,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): hass.data[DATA_BLACKBIRD][unique_id] = device devices.append(device) - add_devices(devices, True) + add_entities(devices, True) def service_handle(service): """Handle for services.""" diff --git a/homeassistant/components/media_player/bluesound.py b/homeassistant/components/media_player/bluesound.py index 5631ec06cf1ab4..1fe939b34efa18 100644 --- a/homeassistant/components/media_player/bluesound.py +++ b/homeassistant/components/media_player/bluesound.py @@ -88,7 +88,7 @@ } -def _add_player(hass, async_add_devices, host, port=None, name=None): +def _add_player(hass, async_add_entities, host, port=None, name=None): """Add Bluesound players.""" if host in [x.host for x in hass.data[DATA_BLUESOUND]]: return @@ -111,7 +111,7 @@ def _stop_polling(): @callback def _add_player_cb(): """Add player after first sync fetch.""" - async_add_devices([player]) + async_add_entities([player]) _LOGGER.info("Added device with name: %s", player.name) if hass.is_running: @@ -132,13 +132,13 @@ def _add_player_cb(): async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Bluesound platforms.""" if DATA_BLUESOUND not in hass.data: hass.data[DATA_BLUESOUND] = [] if discovery_info: - _add_player(hass, async_add_devices, discovery_info.get(CONF_HOST), + _add_player(hass, async_add_entities, discovery_info.get(CONF_HOST), discovery_info.get(CONF_PORT, None)) return @@ -146,7 +146,7 @@ async def async_setup_platform( if hosts: for host in hosts: _add_player( - hass, async_add_devices, host.get(CONF_HOST), + hass, async_add_entities, host.get(CONF_HOST), host.get(CONF_PORT), host.get(CONF_NAME)) async def async_service_handler(service): diff --git a/homeassistant/components/media_player/braviatv.py b/homeassistant/components/media_player/braviatv.py index 07a379db45cde7..9f4496582ad2d2 100644 --- a/homeassistant/components/media_player/braviatv.py +++ b/homeassistant/components/media_player/braviatv.py @@ -58,7 +58,7 @@ def _get_mac_address(ip_address): return None -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Sony Bravia TV platform.""" host = config.get(CONF_HOST) @@ -74,19 +74,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None): pin = host_config['pin'] mac = host_config['mac'] name = config.get(CONF_NAME) - add_devices([BraviaTVDevice(host, mac, name, pin)]) + add_entities([BraviaTVDevice(host, mac, name, pin)]) return - setup_bravia(config, pin, hass, add_devices) + setup_bravia(config, pin, hass, add_entities) -def setup_bravia(config, pin, hass, add_devices): +def setup_bravia(config, pin, hass, add_entities): """Set up a Sony Bravia TV based on host parameter.""" host = config.get(CONF_HOST) name = config.get(CONF_NAME) if pin is None: - request_configuration(config, hass, add_devices) + request_configuration(config, hass, add_entities) return mac = _get_mac_address(host) @@ -104,10 +104,10 @@ def setup_bravia(config, pin, hass, add_devices): hass.config.path(BRAVIA_CONFIG_FILE), {host: {'pin': pin, 'host': host, 'mac': mac}}) - add_devices([BraviaTVDevice(host, mac, name, pin)]) + add_entities([BraviaTVDevice(host, mac, name, pin)]) -def request_configuration(config, hass, add_devices): +def request_configuration(config, hass, add_entities): """Request configuration steps from the user.""" host = config.get(CONF_HOST) name = config.get(CONF_NAME) @@ -128,9 +128,9 @@ def bravia_configuration_callback(data): braviarc = braviarc.BraviaRC(host) braviarc.connect(pin, CLIENTID_PREFIX, NICKNAME) if braviarc.is_connected(): - setup_bravia(config, pin, hass, add_devices) + setup_bravia(config, pin, hass, add_entities) else: - request_configuration(config, hass, add_devices) + request_configuration(config, hass, add_entities) _CONFIGURING[host] = configurator.request_config( name, bravia_configuration_callback, diff --git a/homeassistant/components/media_player/cast.py b/homeassistant/components/media_player/cast.py index 099b365c50b159..ae9589c7886a5b 100644 --- a/homeassistant/components/media_player/cast.py +++ b/homeassistant/components/media_player/cast.py @@ -146,7 +146,7 @@ def _setup_internal_discovery(hass: HomeAssistantType) -> None: import pychromecast def internal_callback(name): - """Called when zeroconf has discovered a new chromecast.""" + """Handle zeroconf discovery of a new chromecast.""" mdns = listener.services[name] _discover_chromecast(hass, ChromecastInfo(*mdns)) @@ -186,7 +186,7 @@ def _async_create_cast_device(hass: HomeAssistantType, async def async_setup_platform(hass: HomeAssistantType, config: ConfigType, - async_add_devices, discovery_info=None): + async_add_entities, discovery_info=None): """Set up thet Cast platform. Deprecated. @@ -195,22 +195,22 @@ async def async_setup_platform(hass: HomeAssistantType, config: ConfigType, 'Setting configuration for Cast via platform is deprecated. ' 'Configure via Cast component instead.') await _async_setup_platform( - hass, config, async_add_devices, discovery_info) + hass, config, async_add_entities, discovery_info) -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Cast from a config entry.""" config = hass.data[CAST_DOMAIN].get('media_player', {}) if not isinstance(config, list): config = [config] await asyncio.wait([ - _async_setup_platform(hass, cfg, async_add_devices, None) + _async_setup_platform(hass, cfg, async_add_entities, None) for cfg in config]) async def _async_setup_platform(hass: HomeAssistantType, config: ConfigType, - async_add_devices, discovery_info): + async_add_entities, discovery_info): """Set up the cast platform.""" import pychromecast @@ -229,14 +229,14 @@ async def _async_setup_platform(hass: HomeAssistantType, config: ConfigType, @callback def async_cast_discovered(discover: ChromecastInfo) -> None: - """Callback for when a new chromecast is discovered.""" + """Handle discovery of a new chromecast.""" if info is not None and info.host_port != discover.host_port: # Not our requested cast device. return cast_device = _async_create_cast_device(hass, discover) if cast_device is not None: - async_add_devices([cast_device]) + async_add_entities([cast_device]) async_dispatcher_connect(hass, SIGNAL_CAST_DISCOVERED, async_cast_discovered) @@ -277,17 +277,17 @@ def __init__(self, cast_device, chromecast): chromecast.register_connection_listener(self) def new_cast_status(self, cast_status): - """Called when a new CastStatus is received.""" + """Handle reception of a new CastStatus.""" if self._valid: self._cast_device.new_cast_status(cast_status) def new_media_status(self, media_status): - """Called when a new MediaStatus is received.""" + """Handle reception of a new MediaStatus.""" if self._valid: self._cast_device.new_media_status(media_status) def new_connection_status(self, connection_status): - """Called when a new ConnectionStatus is received.""" + """Handle reception of a new ConnectionStatus.""" if self._valid: self._cast_device.new_connection_status(connection_status) @@ -321,7 +321,7 @@ async def async_added_to_hass(self): """Create chromecast object when added to hass.""" @callback def async_cast_discovered(discover: ChromecastInfo): - """Callback for changing elected leaders / IP.""" + """Handle discovery of new Chromecast.""" if self._cast_info.uuid is None: # We can't handle empty UUIDs return diff --git a/homeassistant/components/media_player/channels.py b/homeassistant/components/media_player/channels.py index 6ccc60617031db..fcfa16b33acf55 100644 --- a/homeassistant/components/media_player/channels.py +++ b/homeassistant/components/media_player/channels.py @@ -55,8 +55,8 @@ REQUIREMENTS = ['pychannels==1.0.0'] -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the Channels platform.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the Channels platform.""" device = ChannelsPlayer( config.get('name'), config.get(CONF_HOST), @@ -66,11 +66,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if DATA_CHANNELS not in hass.data: hass.data[DATA_CHANNELS] = [] - add_devices([device], True) + add_entities([device], True) hass.data[DATA_CHANNELS].append(device) def service_handler(service): - """Handler for services.""" + """Handle service.""" entity_id = service.data.get(ATTR_ENTITY_ID) device = next((device for device in hass.data[DATA_CHANNELS] if diff --git a/homeassistant/components/media_player/clementine.py b/homeassistant/components/media_player/clementine.py index 1ee18576ab8c85..fab2bef73f0998 100644 --- a/homeassistant/components/media_player/clementine.py +++ b/homeassistant/components/media_player/clementine.py @@ -43,7 +43,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Clementine platform.""" from clementineremote import ClementineRemote host = config.get(CONF_HOST) @@ -52,7 +52,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): client = ClementineRemote(host, port, token, reconnect=True) - add_devices([ClementineDevice(client, config[CONF_NAME])]) + add_entities([ClementineDevice(client, config[CONF_NAME])]) class ClementineDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/cmus.py b/homeassistant/components/media_player/cmus.py index 978a1088aa6b7c..6f579fd9791a68 100644 --- a/homeassistant/components/media_player/cmus.py +++ b/homeassistant/components/media_player/cmus.py @@ -38,7 +38,7 @@ }) -def setup_platform(hass, config, add_devices, discover_info=None): +def setup_platform(hass, config, add_entities, discover_info=None): """Set up the CMUS platform.""" from pycmus import exceptions @@ -52,7 +52,7 @@ def setup_platform(hass, config, add_devices, discover_info=None): except exceptions.InvalidPassword: _LOGGER.error("The provided password was rejected by cmus") return False - add_devices([cmus_remote], True) + add_entities([cmus_remote], True) class CmusDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/demo.py b/homeassistant/components/media_player/demo.py index 9edf69cd9c69ef..06f2a3f1155e73 100644 --- a/homeassistant/components/media_player/demo.py +++ b/homeassistant/components/media_player/demo.py @@ -14,9 +14,9 @@ import homeassistant.util.dt as dt_util -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the media player demo platform.""" - add_devices([ + add_entities([ DemoYoutubePlayer( 'Living Room', 'eyU3bRy2x44', '♥♥ The Best Fireplace Video (3 hours)', 300), diff --git a/homeassistant/components/media_player/denon.py b/homeassistant/components/media_player/denon.py index d85bd51e7fb863..320211e700f5fc 100644 --- a/homeassistant/components/media_player/denon.py +++ b/homeassistant/components/media_player/denon.py @@ -49,12 +49,12 @@ # 'Favorites': 'FVP'} -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Denon platform.""" denon = DenonDevice(config.get(CONF_NAME), config.get(CONF_HOST)) if denon.update(): - add_devices([denon]) + add_entities([denon]) class DenonDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/media_player/denonavr.py index 604fb91451e1e5..14839590bee975 100644 --- a/homeassistant/components/media_player/denonavr.py +++ b/homeassistant/components/media_player/denonavr.py @@ -61,7 +61,7 @@ NewHost = namedtuple('NewHost', ['host', 'name']) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Denon platform.""" import denonavr @@ -124,7 +124,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Add all freshly discovered receivers if receivers: - add_devices(receivers) + add_entities(receivers) class DenonDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/directv.py b/homeassistant/components/media_player/directv.py index 89547892550bc0..e03474cdb38eab 100644 --- a/homeassistant/components/media_player/directv.py +++ b/homeassistant/components/media_player/directv.py @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the DirecTV platform.""" known_devices = hass.data.get(DATA_DIRECTV) if not known_devices: @@ -75,7 +75,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dtvs.append(DirecTvDevice(*host)) known_devices.append(host[-1]) - add_devices(dtvs) + add_entities(dtvs) hass.data[DATA_DIRECTV] = known_devices return True diff --git a/homeassistant/components/media_player/dlna_dmr.py b/homeassistant/components/media_player/dlna_dmr.py index c40e3ed0ca9d30..6c970ec197e488 100644 --- a/homeassistant/components/media_player/dlna_dmr.py +++ b/homeassistant/components/media_player/dlna_dmr.py @@ -35,7 +35,7 @@ DLNA_DMR_DATA = 'dlna_dmr' REQUIREMENTS = [ - 'async-upnp-client==0.12.3', + 'async-upnp-client==0.12.4', ] DEFAULT_NAME = 'DLNA Digital Media Renderer' @@ -118,7 +118,7 @@ async def async_stop_server(event): async def async_setup_platform(hass: HomeAssistant, config, - async_add_devices, + async_add_entities, discovery_info=None): """Set up DLNA DMR platform.""" if config.get(CONF_URL) is not None: @@ -165,7 +165,7 @@ async def async_setup_platform(hass: HomeAssistant, # create our own device device = DlnaDmrDevice(dlna_device, name) _LOGGER.debug("Adding device: %s", device) - async_add_devices([device], True) + async_add_entities([device], True) class DlnaDmrDevice(MediaPlayerDevice): @@ -180,7 +180,7 @@ def __init__(self, dmr_device, name=None): self._subscription_renew_time = None async def async_added_to_hass(self): - """Callback when added.""" + """Handle addition.""" self._device.on_event = self._on_event # register unsubscribe on stop diff --git a/homeassistant/components/media_player/dunehd.py b/homeassistant/components/media_player/dunehd.py index ed20ac25cf90eb..f582ceefe5fcb8 100644 --- a/homeassistant/components/media_player/dunehd.py +++ b/homeassistant/components/media_player/dunehd.py @@ -32,7 +32,7 @@ SUPPORT_PLAY -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the DuneHD media player platform.""" from pdunehd import DuneHDPlayer @@ -40,7 +40,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): host = config.get(CONF_HOST) name = config.get(CONF_NAME) - add_devices([DuneHDPlayerEntity(DuneHDPlayer(host), name, sources)], True) + add_entities([DuneHDPlayerEntity(DuneHDPlayer(host), name, sources)], True) class DuneHDPlayerEntity(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/emby.py b/homeassistant/components/media_player/emby.py index 1dfb19a33bee51..809db228d0262e 100644 --- a/homeassistant/components/media_player/emby.py +++ b/homeassistant/components/media_player/emby.py @@ -51,7 +51,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Emby platform.""" from pyemby import EmbyServer @@ -94,7 +95,7 @@ def device_update_callback(data): if new_devices: _LOGGER.debug("Adding new devices: %s", new_devices) - async_add_devices(new_devices, update_before_add=True) + async_add_entities(new_devices, update_before_add=True) @callback def device_removal_callback(data): @@ -192,8 +193,8 @@ def supports_remote_control(self): @property def name(self): """Return the name of the device.""" - return 'Emby - {} - {}'.format(self.device.client, self.device.name) \ - or DEVICE_DEFAULT_NAME + return ('Emby - {} - {}'.format(self.device.client, self.device.name) + or DEVICE_DEFAULT_NAME) @property def should_poll(self): diff --git a/homeassistant/components/media_player/epson.py b/homeassistant/components/media_player/epson.py index b22234a40940fa..23bbf685004543 100644 --- a/homeassistant/components/media_player/epson.py +++ b/homeassistant/components/media_player/epson.py @@ -40,7 +40,7 @@ _LOGGER = logging.getLogger(__name__) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Epson media player platform.""" if DATA_EPSON not in hass.data: @@ -52,7 +52,7 @@ async def async_setup_platform(hass, config, async_add_devices, name, host, config.get(CONF_PORT), config.get(CONF_SSL)) hass.data[DATA_EPSON].append(epson) - async_add_devices([epson], update_before_add=True) + async_add_entities([epson], update_before_add=True) async def async_service_handler(service): """Handle for services.""" diff --git a/homeassistant/components/media_player/firetv.py b/homeassistant/components/media_player/firetv.py index 979aec57c74a49..0594b603a0c18c 100644 --- a/homeassistant/components/media_player/firetv.py +++ b/homeassistant/components/media_player/firetv.py @@ -45,7 +45,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the FireTV platform.""" name = config.get(CONF_NAME) ssl = config.get(CONF_SSL) @@ -58,7 +58,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): response = requests.get( DEVICE_LIST_URL.format(proto, host, port)).json() if device_id in response[CONF_DEVICES].keys(): - add_devices([FireTVDevice(proto, host, port, device_id, name)]) + add_entities([FireTVDevice(proto, host, port, device_id, name)]) _LOGGER.info("Device %s accessible and ready for control", device_id) else: diff --git a/homeassistant/components/media_player/frontier_silicon.py b/homeassistant/components/media_player/frontier_silicon.py index ab594f47e14d62..6dc4e73b1c0430 100644 --- a/homeassistant/components/media_player/frontier_silicon.py +++ b/homeassistant/components/media_player/frontier_silicon.py @@ -20,7 +20,7 @@ CONF_HOST, CONF_PORT, CONF_PASSWORD) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['afsapi==0.0.3'] +REQUIREMENTS = ['afsapi==0.0.4'] _LOGGER = logging.getLogger(__name__) @@ -42,12 +42,13 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Frontier Silicon platform.""" import requests if discovery_info is not None: - async_add_devices( + async_add_entities( [AFSAPIDevice(discovery_info['ssdp_description'], DEFAULT_PASSWORD)], update_before_add=True) @@ -58,7 +59,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): password = config.get(CONF_PASSWORD) try: - async_add_devices( + async_add_entities( [AFSAPIDevice(DEVICE_URL.format(host, port), password)], update_before_add=True) _LOGGER.debug("FSAPI device %s:%s -> %s", host, port, password) diff --git a/homeassistant/components/media_player/gpmdp.py b/homeassistant/components/media_player/gpmdp.py index 4a0ec1fa87f4aa..200191ad77aa52 100644 --- a/homeassistant/components/media_player/gpmdp.py +++ b/homeassistant/components/media_player/gpmdp.py @@ -45,7 +45,7 @@ }) -def request_configuration(hass, config, url, add_devices_callback): +def request_configuration(hass, config, url, add_entities_callback): """Request configuration steps from the user.""" configurator = hass.components.configurator if 'gpmdp' in _CONFIGURING: @@ -84,7 +84,7 @@ def gpmdp_configuration_callback(callback_data): if code == 'CODE_REQUIRED': continue setup_gpmdp(hass, config, code, - add_devices_callback) + add_entities_callback) save_json(hass.config.path(GPMDP_CONFIG_FILE), {"CODE": code}) websocket.send(json.dumps({'namespace': 'connect', 'method': 'connect', @@ -102,7 +102,7 @@ def gpmdp_configuration_callback(callback_data): ) -def setup_gpmdp(hass, config, code, add_devices): +def setup_gpmdp(hass, config, code, add_entities): """Set up gpmdp.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) @@ -110,17 +110,17 @@ def setup_gpmdp(hass, config, code, add_devices): url = 'ws://{}:{}'.format(host, port) if not code: - request_configuration(hass, config, url, add_devices) + request_configuration(hass, config, url, add_entities) return if 'gpmdp' in _CONFIGURING: configurator = hass.components.configurator configurator.request_done(_CONFIGURING.pop('gpmdp')) - add_devices([GPMDP(name, url, code)], True) + add_entities([GPMDP(name, url, code)], True) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the GPMDP platform.""" codeconfig = load_json(hass.config.path(GPMDP_CONFIG_FILE)) if codeconfig: @@ -131,7 +131,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): code = None else: code = None - setup_gpmdp(hass, config, code, add_devices) + setup_gpmdp(hass, config, code, add_entities) class GPMDP(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/gstreamer.py b/homeassistant/components/media_player/gstreamer.py index 91cd8d19cc4a59..e2477f0a4cd2f0 100644 --- a/homeassistant/components/media_player/gstreamer.py +++ b/homeassistant/components/media_player/gstreamer.py @@ -34,7 +34,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Gstreamer platform.""" from gsp import GstreamerPlayer name = config.get(CONF_NAME) @@ -46,7 +46,7 @@ def _shutdown(call): player.quit() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown) - add_devices([GstreamerDevice(player, name)]) + add_entities([GstreamerDevice(player, name)]) class GstreamerDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/hdmi_cec.py b/homeassistant/components/media_player/hdmi_cec.py index f5b4cbd48546b8..9198e4dec88f5e 100644 --- a/homeassistant/components/media_player/hdmi_cec.py +++ b/homeassistant/components/media_player/hdmi_cec.py @@ -22,13 +22,13 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return HDMI devices as +switches.""" if ATTR_NEW in discovery_info: _LOGGER.info("Setting up HDMI devices %s", discovery_info[ATTR_NEW]) - add_devices(CecPlayerDevice(hass, hass.data.get(device), - hass.data.get(device).logical_address) for - device in discovery_info[ATTR_NEW]) + add_entities(CecPlayerDevice(hass, hass.data.get(device), + hass.data.get(device).logical_address) for + device in discovery_info[ATTR_NEW]) class CecPlayerDevice(CecDevice, MediaPlayerDevice): diff --git a/homeassistant/components/media_player/horizon.py b/homeassistant/components/media_player/horizon.py index 9be4143ef2bac4..4fa97cb5537f35 100644 --- a/homeassistant/components/media_player/horizon.py +++ b/homeassistant/components/media_player/horizon.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Horizon platform.""" from einder import Client, keys from einder.exceptions import AuthenticationError @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.info("Connection to %s at %s established", name, host) - add_devices([HorizonDevice(client, name, keys)], True) + add_entities([HorizonDevice(client, name, keys)], True) class HorizonDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index e5f7a2f9432d66..31df74dbeafd3d 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -157,16 +157,16 @@ def set_volume_airplay_device(self, device_id, level): return self._request('PUT', path, {'level': level}) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the iTunes platform.""" - add_devices([ + add_entities([ ItunesDevice( config.get(CONF_NAME), config.get(CONF_HOST), config.get(CONF_PORT), config.get(CONF_SSL), - add_devices + add_entities ) ]) @@ -174,13 +174,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class ItunesDevice(MediaPlayerDevice): """Representation of an iTunes API instance.""" - def __init__(self, name, host, port, use_ssl, add_devices): + def __init__(self, name, host, port, use_ssl, add_entities): """Initialize the iTunes device.""" self._name = name self._host = host self._port = port self._use_ssl = use_ssl - self._add_devices = add_devices + self._add_entities = add_entities self.client = Itunes(self._host, self._port, self._use_ssl) @@ -257,7 +257,7 @@ def update(self): new_devices.append(airplay_device) if new_devices: - self._add_devices(new_devices) + self._add_entities(new_devices) @property def is_volume_muted(self): diff --git a/homeassistant/components/media_player/kodi.py b/homeassistant/components/media_player/kodi.py index 08de2d00835303..b36512e7c65f00 100644 --- a/homeassistant/components/media_player/kodi.py +++ b/homeassistant/components/media_player/kodi.py @@ -155,7 +155,8 @@ def _check_deprecated_turn_off(hass, turn_off_action): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Kodi platform.""" if DATA_KODI not in hass.data: hass.data[DATA_KODI] = dict() @@ -206,7 +207,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): unique_id=unique_id) hass.data[DATA_KODI][ip_addr] = entity - async_add_devices([entity], update_before_add=True) + async_add_entities([entity], update_before_add=True) @asyncio.coroutine def async_service_handler(service): diff --git a/homeassistant/components/media_player/lg_netcast.py b/homeassistant/components/media_player/lg_netcast.py index 955ba7ccb3266b..cbc15af91b69e5 100644 --- a/homeassistant/components/media_player/lg_netcast.py +++ b/homeassistant/components/media_player/lg_netcast.py @@ -42,7 +42,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the LG TV platform.""" from pylgnetcast import LgNetCastClient host = config.get(CONF_HOST) @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): client = LgNetCastClient(host, access_token) - add_devices([LgTVDevice(client, name)], True) + add_entities([LgTVDevice(client, name)], True) class LgTVDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/liveboxplaytv.py b/homeassistant/components/media_player/liveboxplaytv.py index 1b5948c964a042..d5b1bcd78fe48a 100644 --- a/homeassistant/components/media_player/liveboxplaytv.py +++ b/homeassistant/components/media_player/liveboxplaytv.py @@ -45,7 +45,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Orange Livebox Play TV platform.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -59,7 +60,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): except IOError: _LOGGER.error("Failed to connect to Livebox Play TV at %s:%s. " "Please check your configuration", host, port) - async_add_devices(livebox_devices, True) + async_add_entities(livebox_devices, True) class LiveboxPlayTvDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/mediaroom.py b/homeassistant/components/media_player/mediaroom.py index 32f1bb6e0ae811..f0b5365cf8345a 100644 --- a/homeassistant/components/media_player/mediaroom.py +++ b/homeassistant/components/media_player/mediaroom.py @@ -49,7 +49,7 @@ ) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Mediaroom platform.""" known_hosts = hass.data.get(DATA_MEDIAROOM) @@ -57,10 +57,10 @@ async def async_setup_platform(hass, config, async_add_devices, known_hosts = hass.data[DATA_MEDIAROOM] = [] host = config.get(CONF_HOST, None) if host: - async_add_devices([MediaroomDevice(host=host, - device_id=None, - optimistic=config[CONF_OPTIMISTIC], - timeout=config[CONF_TIMEOUT])]) + async_add_entities([MediaroomDevice(host=host, + device_id=None, + optimistic=config[CONF_OPTIMISTIC], + timeout=config[CONF_TIMEOUT])]) hass.data[DATA_MEDIAROOM].append(host) _LOGGER.debug("Trying to discover Mediaroom STB") @@ -77,7 +77,7 @@ def callback_notify(notify): host=notify.ip_address, device_id=notify.device_uuid, optimistic=False ) - async_add_devices([new_stb]) + async_add_entities([new_stb]) if not config[CONF_OPTIMISTIC]: from pymediaroom import install_mediaroom_protocol @@ -103,7 +103,7 @@ class MediaroomDevice(MediaPlayerDevice): """Representation of a Mediaroom set-up-box on the network.""" def set_state(self, mediaroom_state): - """Helper method to map pymediaroom states to HA states.""" + """Map pymediaroom state to HA state.""" from pymediaroom import State state_map = { diff --git a/homeassistant/components/media_player/monoprice.py b/homeassistant/components/media_player/monoprice.py index a951356500f082..96b8dcbcf26c7c 100644 --- a/homeassistant/components/media_player/monoprice.py +++ b/homeassistant/components/media_player/monoprice.py @@ -55,7 +55,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Monoprice 6-zone amplifier platform.""" port = config.get(CONF_PORT) @@ -76,7 +76,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): hass.data[DATA_MONOPRICE].append(MonopriceZone( monoprice, sources, zone_id, extra[CONF_NAME])) - add_devices(hass.data[DATA_MONOPRICE], True) + add_entities(hass.data[DATA_MONOPRICE], True) def service_handle(service): """Handle for services.""" diff --git a/homeassistant/components/media_player/mpchc.py b/homeassistant/components/media_player/mpchc.py index 773825e0d57d63..840429ef3d440d 100644 --- a/homeassistant/components/media_player/mpchc.py +++ b/homeassistant/components/media_player/mpchc.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the MPC-HC platform.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) @@ -43,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): url = '{}:{}'.format(host, port) - add_devices([MpcHcDevice(name, url)], True) + add_entities([MpcHcDevice(name, url)], True) class MpcHcDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/mpd.py b/homeassistant/components/media_player/mpd.py index 4b3dfc2ccbb545..8953215f44e859 100644 --- a/homeassistant/components/media_player/mpd.py +++ b/homeassistant/components/media_player/mpd.py @@ -32,9 +32,8 @@ PLAYLIST_UPDATE_INTERVAL = timedelta(seconds=120) -SUPPORT_MPD = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_STEP | \ - SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_VOLUME_MUTE | \ - SUPPORT_PLAY_MEDIA | SUPPORT_PLAY | SUPPORT_SELECT_SOURCE | \ +SUPPORT_MPD = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | \ + SUPPORT_NEXT_TRACK | SUPPORT_PLAY_MEDIA | SUPPORT_PLAY | \ SUPPORT_CLEAR_PLAYLIST | SUPPORT_SHUFFLE_SET | SUPPORT_SEEK | \ SUPPORT_STOP | SUPPORT_TURN_OFF | SUPPORT_TURN_ON @@ -46,7 +45,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the MPD platform.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -54,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): password = config.get(CONF_PASSWORD) device = MpdDevice(host, port, password, name) - add_devices([device], True) + add_entities([device], True) class MpdDevice(MediaPlayerDevice): @@ -72,7 +71,7 @@ def __init__(self, server, port, password, name): self._status = None self._currentsong = None - self._playlists = [] + self._playlists = None self._currentplaylist = None self._is_connected = False self._muted = False @@ -202,12 +201,24 @@ def media_album_name(self): @property def volume_level(self): """Return the volume level.""" - return int(self._status['volume'])/100 + if 'volume' in self._status: + return int(self._status['volume'])/100 + return None @property def supported_features(self): """Flag media player features that are supported.""" - return SUPPORT_MPD + if self._status is None: + return None + + supported = SUPPORT_MPD + if 'volume' in self._status: + supported |= \ + SUPPORT_VOLUME_SET | SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE + if self._playlists is not None: + supported |= SUPPORT_SELECT_SOURCE + + return supported @property def source(self): @@ -226,27 +237,36 @@ def select_source(self, source): @Throttle(PLAYLIST_UPDATE_INTERVAL) def _update_playlists(self, **kwargs): """Update available MPD playlists.""" - self._playlists = [] - for playlist_data in self._client.listplaylists(): - self._playlists.append(playlist_data['playlist']) + import mpd + + try: + self._playlists = [] + for playlist_data in self._client.listplaylists(): + self._playlists.append(playlist_data['playlist']) + except mpd.CommandError as error: + self._playlists = None + _LOGGER.warning("Playlists could not be updated: %s:", error) def set_volume_level(self, volume): """Set volume of media player.""" - self._client.setvol(int(volume * 100)) + if 'volume' in self._status: + self._client.setvol(int(volume * 100)) def volume_up(self): """Service to send the MPD the command for volume up.""" - current_volume = int(self._status['volume']) + if 'volume' in self._status: + current_volume = int(self._status['volume']) - if current_volume <= 100: - self._client.setvol(current_volume + 5) + if current_volume <= 100: + self._client.setvol(current_volume + 5) def volume_down(self): """Service to send the MPD the command for volume down.""" - current_volume = int(self._status['volume']) + if 'volume' in self._status: + current_volume = int(self._status['volume']) - if current_volume >= 0: - self._client.setvol(current_volume - 5) + if current_volume >= 0: + self._client.setvol(current_volume - 5) def media_play(self): """Service to send the MPD the command for play/pause.""" @@ -270,12 +290,13 @@ def media_previous_track(self): def mute_volume(self, mute): """Mute. Emulated with set_volume_level.""" - if mute is True: - self._muted_volume = self.volume_level - self.set_volume_level(0) - elif mute is False: - self.set_volume_level(self._muted_volume) - self._muted = mute + if 'volume' in self._status: + if mute: + self._muted_volume = self.volume_level + self.set_volume_level(0) + else: + self.set_volume_level(self._muted_volume) + self._muted = mute def play_media(self, media_type, media_id, **kwargs): """Send the media player the command for playing a playlist.""" diff --git a/homeassistant/components/media_player/nad.py b/homeassistant/components/media_player/nad.py index 2f0c49b2583598..d30c5815d3f8b4 100644 --- a/homeassistant/components/media_player/nad.py +++ b/homeassistant/components/media_player/nad.py @@ -47,10 +47,10 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the NAD platform.""" from nad_receiver import NADReceiver - add_devices([NAD( + add_entities([NAD( config.get(CONF_NAME), NADReceiver(config.get(CONF_SERIAL_PORT)), config.get(CONF_MIN_VOLUME), diff --git a/homeassistant/components/media_player/nadtcp.py b/homeassistant/components/media_player/nadtcp.py index 06ec3c04cbe57e..dd3c5e26d7a9c5 100644 --- a/homeassistant/components/media_player/nadtcp.py +++ b/homeassistant/components/media_player/nadtcp.py @@ -40,10 +40,10 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the NAD platform.""" from nad_receiver import NADReceiverTCP - add_devices([NADtcp( + add_entities([NADtcp( NADReceiverTCP(config.get(CONF_HOST)), config.get(CONF_NAME), config.get(CONF_MIN_VOLUME), diff --git a/homeassistant/components/media_player/onkyo.py b/homeassistant/components/media_player/onkyo.py index 92443ca2b42d99..af9a6ef54ce0aa 100644 --- a/homeassistant/components/media_player/onkyo.py +++ b/homeassistant/components/media_player/onkyo.py @@ -80,7 +80,7 @@ def determine_zones(receiver): return out -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Onkyo platform.""" import eiscp from eiscp import eISCP @@ -122,7 +122,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if receiver.host not in KNOWN_HOSTS: hosts.append(OnkyoDevice(receiver, config.get(CONF_SOURCES))) KNOWN_HOSTS.append(receiver.host) - add_devices(hosts, True) + add_entities(hosts, True) class OnkyoDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/openhome.py b/homeassistant/components/media_player/openhome.py index 5d9c7bd14c578a..1ffe0ef82df4a9 100644 --- a/homeassistant/components/media_player/openhome.py +++ b/homeassistant/components/media_player/openhome.py @@ -25,7 +25,7 @@ DEVICES = [] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Openhome platform.""" from openhomedevice.Device import Device @@ -43,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): device = OpenhomeDevice(hass, device) - add_devices([device], True) + add_entities([device], True) DEVICES.append(device) return True diff --git a/homeassistant/components/media_player/panasonic_viera.py b/homeassistant/components/media_player/panasonic_viera.py index 549071fde8e5d4..937a72c80ff639 100644 --- a/homeassistant/components/media_player/panasonic_viera.py +++ b/homeassistant/components/media_player/panasonic_viera.py @@ -42,7 +42,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Panasonic Viera TV platform.""" from panasonic_viera import RemoteControl @@ -61,13 +61,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): else: uuid = None remote = RemoteControl(host, port) - add_devices([PanasonicVieraTVDevice(mac, name, remote, uuid)]) + add_entities([PanasonicVieraTVDevice(mac, name, remote, uuid)]) return True host = config.get(CONF_HOST) remote = RemoteControl(host, port) - add_devices([PanasonicVieraTVDevice(mac, name, remote)]) + add_entities([PanasonicVieraTVDevice(mac, name, remote)]) return True diff --git a/homeassistant/components/media_player/pandora.py b/homeassistant/components/media_player/pandora.py index c4d8b77809552b..5295bfc40eb17c 100644 --- a/homeassistant/components/media_player/pandora.py +++ b/homeassistant/components/media_player/pandora.py @@ -43,7 +43,7 @@ STATION_PATTERN = re.compile(r'Station\s"(.+?)"', re.MULTILINE) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Pandora media player platform.""" if not _pianobar_exists(): return False @@ -55,7 +55,7 @@ def _stop_pianobar(_event): pandora.turn_off() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _stop_pianobar) - add_devices([pandora]) + add_entities([pandora]) class PandoraMediaPlayer(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/philips_js.py b/homeassistant/components/media_player/philips_js.py index 06f054a03f7232..9c7418d4c808ab 100644 --- a/homeassistant/components/media_player/philips_js.py +++ b/homeassistant/components/media_player/philips_js.py @@ -48,7 +48,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Philips TV platform.""" import haphilipsjs @@ -60,7 +60,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): tvapi = haphilipsjs.PhilipsTV(host, api_version) on_script = Script(hass, turn_on_action) if turn_on_action else None - add_devices([PhilipsTV(tvapi, name, on_script)]) + add_entities([PhilipsTV(tvapi, name, on_script)]) class PhilipsTV(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/pioneer.py b/homeassistant/components/media_player/pioneer.py index 25424224c703ec..71ccf9a460dd6a 100644 --- a/homeassistant/components/media_player/pioneer.py +++ b/homeassistant/components/media_player/pioneer.py @@ -39,14 +39,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Pioneer platform.""" pioneer = PioneerDevice( config.get(CONF_NAME), config.get(CONF_HOST), config.get(CONF_PORT), config.get(CONF_TIMEOUT)) if pioneer.update(): - add_devices([pioneer]) + add_entities([pioneer]) class PioneerDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/pjlink.py b/homeassistant/components/media_player/pjlink.py index 5d3122256ea670..42884cdce096e4 100644 --- a/homeassistant/components/media_player/pjlink.py +++ b/homeassistant/components/media_player/pjlink.py @@ -37,7 +37,7 @@ SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the PJLink platform.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -55,7 +55,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): device = PjLinkDevice(host, port, name, encoding, password) hass_data[device_label] = device - add_devices([device], True) + add_entities([device], True) def format_input_source(input_source_name, input_source_number): diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index e3c6f453c35b92..46dacd98aadd5f 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -59,7 +59,7 @@ PLEX_DATA = "plex" -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up the Plex platform.""" if PLEX_DATA not in hass.data: hass.data[PLEX_DATA] = {} @@ -98,12 +98,12 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): setup_plexserver( host, token, has_ssl, verify_ssl, - hass, config, add_devices_callback + hass, config, add_entities_callback ) def setup_plexserver( - host, token, has_ssl, verify_ssl, hass, config, add_devices_callback): + host, token, has_ssl, verify_ssl, hass, config, add_entities_callback): """Set up a plexserver based on host parameter.""" import plexapi.server import plexapi.exceptions @@ -124,7 +124,7 @@ def setup_plexserver( plexapi.exceptions.NotFound) as error: _LOGGER.info(error) # No token or wrong token - request_configuration(host, hass, config, add_devices_callback) + request_configuration(host, hass, config, add_entities_callback) return # If we came here and configuring this host, mark as done @@ -214,7 +214,7 @@ def update_devices(): del plex_clients[clients_to_remove.pop()] if new_plex_clients: - add_devices_callback(new_plex_clients) + add_entities_callback(new_plex_clients) @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_sessions(): @@ -238,7 +238,7 @@ def update_sessions(): update_devices() -def request_configuration(host, hass, config, add_devices_callback): +def request_configuration(host, hass, config, add_entities_callback): """Request configuration steps from the user.""" configurator = hass.components.configurator # We got an error if this method is called while we are configuring @@ -254,7 +254,7 @@ def plex_configuration_callback(data): host, data.get('token'), cv.boolean(data.get('has_ssl')), cv.boolean(data.get('do_not_verify')), - hass, config, add_devices_callback + hass, config, add_entities_callback ) _CONFIGURING[host] = configurator.request_config( diff --git a/homeassistant/components/media_player/roku.py b/homeassistant/components/media_player/roku.py index 5f28660f4bdff2..fca7b29d2ecc73 100644 --- a/homeassistant/components/media_player/roku.py +++ b/homeassistant/components/media_player/roku.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Roku platform.""" hosts = [] @@ -73,7 +73,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID) - add_devices(rokus) + add_entities(rokus) class RokuDevice(MediaPlayerDevice): @@ -87,7 +87,7 @@ def __init__(self, host): self.ip_address = host self.channels = [] self.current_app = None - self.device_info = {} + self._device_info = {} self.update() @@ -96,7 +96,7 @@ def update(self): import requests.exceptions try: - self.device_info = self.roku.device_info + self._device_info = self.roku.device_info self.ip_address = self.roku.host self.channels = self.get_source_list() @@ -121,9 +121,9 @@ def should_poll(self): @property def name(self): """Return the name of the device.""" - if self.device_info.userdevicename: - return self.device_info.userdevicename - return "Roku {}".format(self.device_info.sernum) + if self._device_info.userdevicename: + return self._device_info.userdevicename + return "Roku {}".format(self._device_info.sernum) @property def state(self): @@ -149,7 +149,7 @@ def supported_features(self): @property def unique_id(self): """Return a unique, HASS-friendly identifier for this entity.""" - return self.device_info.sernum + return self._device_info.sernum @property def media_content_type(self): diff --git a/homeassistant/components/media_player/russound_rio.py b/homeassistant/components/media_player/russound_rio.py index e9f8ab5f199a7b..cf946945cf2d84 100644 --- a/homeassistant/components/media_player/russound_rio.py +++ b/homeassistant/components/media_player/russound_rio.py @@ -35,7 +35,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Russound RIO platform.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -65,7 +66,7 @@ def on_stop(event): hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_stop) - async_add_devices(devices) + async_add_entities(devices) class RussoundZoneDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/russound_rnet.py b/homeassistant/components/media_player/russound_rnet.py index 932872467bd7d2..8aef15e02afba3 100644 --- a/homeassistant/components/media_player/russound_rnet.py +++ b/homeassistant/components/media_player/russound_rnet.py @@ -42,7 +42,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Russound RNET platform.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -63,7 +63,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if russ.is_connected(): for zone_id, extra in config[CONF_ZONES].items(): - add_devices([RussoundRNETDevice( + add_entities([RussoundRNETDevice( hass, russ, sources, zone_id, extra)], True) else: _LOGGER.error('Not connected to %s:%s', host, port) diff --git a/homeassistant/components/media_player/samsungtv.py b/homeassistant/components/media_player/samsungtv.py index 55b3fb0ea4f5ba..72c3ab2c621711 100644 --- a/homeassistant/components/media_player/samsungtv.py +++ b/homeassistant/components/media_player/samsungtv.py @@ -50,7 +50,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Samsung TV platform.""" known_devices = hass.data.get(KNOWN_DEVICES_KEY) if known_devices is None: @@ -81,7 +81,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ip_addr = socket.gethostbyname(host) if ip_addr not in known_devices: known_devices.add(ip_addr) - add_devices([SamsungTVDevice(host, port, name, timeout, mac)]) + add_entities([SamsungTVDevice(host, port, name, timeout, mac)]) _LOGGER.info("Samsung TV %s:%d added as '%s'", host, port, name) else: _LOGGER.info("Ignoring duplicate Samsung TV %s:%d", host, port) diff --git a/homeassistant/components/media_player/sisyphus.py b/homeassistant/components/media_player/sisyphus.py index 9a94da158a19da..36f28769b128e3 100644 --- a/homeassistant/components/media_player/sisyphus.py +++ b/homeassistant/components/media_player/sisyphus.py @@ -39,11 +39,11 @@ # pylint: disable=unused-argument -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a media player entity for a Sisyphus table.""" name = discovery_info[CONF_NAME] host = discovery_info[CONF_HOST] - add_devices( + add_entities( [SisyphusPlayer(name, host, hass.data[DATA_SISYPHUS][name])], update_before_add=True) diff --git a/homeassistant/components/media_player/snapcast.py b/homeassistant/components/media_player/snapcast.py index a880d3c920d150..84864caeed154c 100644 --- a/homeassistant/components/media_player/snapcast.py +++ b/homeassistant/components/media_player/snapcast.py @@ -47,7 +47,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Snapcast platform.""" import snapcast.control from snapcast.control.server import CONTROL_PORT @@ -86,7 +87,7 @@ def _handle_service(service): clients = [SnapcastClientDevice(client, hpid) for client in server.clients] devices = groups + clients hass.data[DATA_KEY] = devices - async_add_devices(devices) + async_add_entities(devices) class SnapcastGroupDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/songpal.py b/homeassistant/components/media_player/songpal.py index 5d0962775f05c7..c1bfbbe59cd1d4 100644 --- a/homeassistant/components/media_player/songpal.py +++ b/homeassistant/components/media_player/songpal.py @@ -47,7 +47,7 @@ async def async_setup_platform(hass, config, - async_add_devices, discovery_info=None): + async_add_entities, discovery_info=None): """Set up the Songpal platform.""" from songpal import SongpalException if PLATFORM not in hass.data: @@ -72,7 +72,7 @@ async def async_setup_platform(hass, config, hass.data[PLATFORM][endpoint] = device - async_add_devices([device], True) + async_add_entities([device], True) async def async_service_handler(service): """Service handler.""" diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index 5375001f75c53f..c4309519e36dfa 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -33,6 +33,7 @@ # Quiet down soco logging to just actual problems. logging.getLogger('soco').setLevel(logging.WARNING) +logging.getLogger('soco.events').setLevel(logging.ERROR) logging.getLogger('soco.data_structures_entry').setLevel(logging.ERROR) _SOCO_SERVICES_LOGGER = logging.getLogger('soco.services') @@ -55,6 +56,7 @@ SOURCE_LINEIN = 'Line-in' SOURCE_TV = 'TV' +CONF_ADVERTISE_ADDR = 'advertise_addr' CONF_INTERFACE_ADDR = 'interface_addr' # Service call validation schemas @@ -73,6 +75,7 @@ UPNP_ERRORS_TO_IGNORE = ['701', '711'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_ADVERTISE_ADDR): cv.string, vol.Optional(CONF_INTERFACE_ADDR): cv.string, vol.Optional(CONF_HOSTS): vol.All(cv.ensure_list, [cv.string]), }) @@ -118,48 +121,37 @@ def __init__(self): self.topology_lock = threading.Lock() -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Sonos platform. Deprecated. """ _LOGGER.warning('Loading Sonos via platform config is deprecated.') - _setup_platform(hass, config, add_devices, discovery_info) + _setup_platform(hass, config, add_entities, discovery_info) -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Sonos from a config entry.""" - def add_devices(devices, update_before_add=False): + def add_entities(devices, update_before_add=False): """Sync version of async add devices.""" - hass.add_job(async_add_devices, devices, update_before_add) + hass.add_job(async_add_entities, devices, update_before_add) hass.add_job(_setup_platform, hass, hass.data[SONOS_DOMAIN].get('media_player', {}), - add_devices, None) + add_entities, None) -def _setup_platform(hass, config, add_devices, discovery_info): +def _setup_platform(hass, config, add_entities, discovery_info): """Set up the Sonos platform.""" import soco - import soco.events - import soco.exceptions - - orig_parse_event_xml = soco.events.parse_event_xml - - def safe_parse_event_xml(xml): - """Avoid SoCo 0.14 event thread dying from invalid xml.""" - try: - return orig_parse_event_xml(xml) - # pylint: disable=broad-except - except Exception as ex: - _LOGGER.debug("Dodged exception: %s %s", type(ex), str(ex)) - return {} - - soco.events.parse_event_xml = safe_parse_event_xml if DATA_SONOS not in hass.data: hass.data[DATA_SONOS] = SonosData() + advertise_addr = config.get(CONF_ADVERTISE_ADDR) + if advertise_addr: + soco.config.EVENT_ADVERTISE_IP = advertise_addr + players = [] if discovery_info: player = soco.SoCo(discovery_info.get('host')) @@ -194,7 +186,7 @@ def safe_parse_event_xml(xml): return hass.data[DATA_SONOS].uids.update(p.uid for p in players) - add_devices(SonosDevice(p) for p in players) + add_entities(SonosDevice(p) for p in players) _LOGGER.debug("Added %s Sonos speakers", len(players)) def service_handle(service): @@ -451,7 +443,7 @@ def _set_basic_information(self): def _set_favorites(self): """Set available favorites.""" - # SoCo 0.14 raises a generic Exception on invalid xml in favorites. + # SoCo 0.16 raises a generic Exception on invalid xml in favorites. # Filter those out now so our list is safe to use. # pylint: disable=broad-except try: @@ -863,9 +855,7 @@ def select_source(self, source): src = fav.pop() uri = src.reference.get_uri() if _is_radio_uri(uri): - # SoCo 0.14 fails to XML escape the title parameter - from xml.sax.saxutils import escape - self.soco.play_uri(uri, title=escape(source)) + self.soco.play_uri(uri, title=source) else: self.soco.clear_queue() self.soco.add_to_queue(src.reference) diff --git a/homeassistant/components/media_player/soundtouch.py b/homeassistant/components/media_player/soundtouch.py index 8f14031481ada4..4e26af9dcc2fd1 100644 --- a/homeassistant/components/media_player/soundtouch.py +++ b/homeassistant/components/media_player/soundtouch.py @@ -71,7 +71,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Bose Soundtouch platform.""" if DATA_SOUNDTOUCH not in hass.data: hass.data[DATA_SOUNDTOUCH] = [] @@ -92,7 +92,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): } soundtouch_device = SoundTouchDevice(None, remote_config) hass.data[DATA_SOUNDTOUCH].append(soundtouch_device) - add_devices([soundtouch_device]) + add_entities([soundtouch_device]) else: name = config.get(CONF_NAME) remote_config = { @@ -102,7 +102,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): } soundtouch_device = SoundTouchDevice(name, remote_config) hass.data[DATA_SOUNDTOUCH].append(soundtouch_device) - add_devices([soundtouch_device]) + add_entities([soundtouch_device]) def service_handle(service): """Handle the applying of a service.""" @@ -323,8 +323,8 @@ def create_zone(self, slaves): _LOGGER.warning("Unable to create zone without slaves") else: _LOGGER.info("Creating zone with master %s", - self.device.config.name) - self.device.create_zone([slave.device for slave in slaves]) + self._device.config.name) + self._device.create_zone([slave.device for slave in slaves]) def remove_zone_slave(self, slaves): """ @@ -341,8 +341,8 @@ def remove_zone_slave(self, slaves): _LOGGER.warning("Unable to find slaves to remove") else: _LOGGER.info("Removing slaves from zone with master %s", - self.device.config.name) - self.device.remove_zone_slave([slave.device for slave in slaves]) + self._device.config.name) + self._device.remove_zone_slave([slave.device for slave in slaves]) def add_zone_slave(self, slaves): """ @@ -357,5 +357,5 @@ def add_zone_slave(self, slaves): _LOGGER.warning("Unable to find slaves to add") else: _LOGGER.info("Adding slaves to zone with master %s", - self.device.config.name) - self.device.add_zone_slave([slave.device for slave in slaves]) + self._device.config.name) + self._device.add_zone_slave([slave.device for slave in slaves]) diff --git a/homeassistant/components/media_player/spotify.py b/homeassistant/components/media_player/spotify.py index 73ec8a175b1f17..9fc200c67fd9f2 100644 --- a/homeassistant/components/media_player/spotify.py +++ b/homeassistant/components/media_player/spotify.py @@ -57,7 +57,7 @@ SCAN_INTERVAL = timedelta(seconds=30) -def request_configuration(hass, config, add_devices, oauth): +def request_configuration(hass, config, add_entities, oauth): """Request Spotify authorization.""" configurator = hass.components.configurator hass.data[DOMAIN] = configurator.request_config( @@ -68,7 +68,7 @@ def request_configuration(hass, config, add_devices, oauth): submit_caption=CONFIGURATOR_SUBMIT_CAPTION) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Spotify platform.""" import spotipy.oauth2 callback_url = '{}{}'.format(hass.config.api.base_url, AUTH_CALLBACK_PATH) @@ -81,8 +81,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if not token_info: _LOGGER.info("no token; requesting authorization") hass.http.register_view(SpotifyAuthCallbackView( - config, add_devices, oauth)) - request_configuration(hass, config, add_devices, oauth) + config, add_entities, oauth)) + request_configuration(hass, config, add_entities, oauth) return if hass.data.get(DOMAIN): configurator = hass.components.configurator @@ -90,7 +90,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): del hass.data[DOMAIN] player = SpotifyMediaPlayer(oauth, config.get(CONF_NAME, DEFAULT_NAME), config[CONF_ALIASES]) - add_devices([player], True) + add_entities([player], True) class SpotifyAuthCallbackView(HomeAssistantView): @@ -100,10 +100,10 @@ class SpotifyAuthCallbackView(HomeAssistantView): url = AUTH_CALLBACK_PATH name = AUTH_CALLBACK_NAME - def __init__(self, config, add_devices, oauth): + def __init__(self, config, add_entities, oauth): """Initialize.""" self.config = config - self.add_devices = add_devices + self.add_entities = add_entities self.oauth = oauth @callback @@ -111,7 +111,8 @@ def get(self, request): """Receive authorization token.""" hass = request.app['hass'] self.oauth.get_access_token(request.query['code']) - hass.async_add_job(setup_platform, hass, self.config, self.add_devices) + hass.async_add_job( + setup_platform, hass, self.config, self.add_entities) class SpotifyMediaPlayer(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index 8eb4c85f6b27bf..a2732b5f84924f 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -65,7 +65,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the squeezebox platform.""" import socket @@ -108,7 +109,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): players = yield from lms.create_players() hass.data[DATA_SQUEEZEBOX].extend(players) - async_add_devices(players) + async_add_entities(players) @asyncio.coroutine def async_service_handler(service): diff --git a/homeassistant/components/media_player/ue_smart_radio.py b/homeassistant/components/media_player/ue_smart_radio.py index 2684a8194174ab..ae7617ead243ed 100644 --- a/homeassistant/components/media_player/ue_smart_radio.py +++ b/homeassistant/components/media_player/ue_smart_radio.py @@ -53,7 +53,7 @@ def send_request(payload, session): return request.json() -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Logitech UE Smart Radio platform.""" email = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) @@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): player_id = player_request["result"]["players_loop"][0]["playerid"] player_name = player_request["result"]["players_loop"][0]["name"] - add_devices([UERadioDevice(session, player_id, player_name)]) + add_entities([UERadioDevice(session, player_id, player_name)]) class UERadioDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/universal.py b/homeassistant/components/media_player/universal.py index 66d12190320f4b..1572e2df89b144 100644 --- a/homeassistant/components/media_player/universal.py +++ b/homeassistant/components/media_player/universal.py @@ -62,7 +62,7 @@ }, extra=vol.REMOVE_EXTRA) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the universal media players.""" player = UniversalMediaPlayer( @@ -74,7 +74,7 @@ async def async_setup_platform(hass, config, async_add_devices, config.get(CONF_STATE_TEMPLATE) ) - async_add_devices([player]) + async_add_entities([player]) class UniversalMediaPlayer(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/vizio.py b/homeassistant/components/media_player/vizio.py index 046aecbb92e244..673be3074de79d 100644 --- a/homeassistant/components/media_player/vizio.py +++ b/homeassistant/components/media_player/vizio.py @@ -53,7 +53,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the VizioTV media player platform.""" host = config.get(CONF_HOST) token = config.get(CONF_ACCESS_TOKEN) @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): device = VizioDevice(host, token, name, volume_step) if device.validate_setup() is False: - _LOGGER.error("Failed to setup Vizio TV platform, " + _LOGGER.error("Failed to set up Vizio TV platform, " "please check if host and API key are correct") return @@ -71,7 +71,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.warning("InsecureRequestWarning is disabled " "because of Vizio platform configuration") urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - add_devices([device], True) + add_entities([device], True) class VizioDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/vlc.py b/homeassistant/components/media_player/vlc.py index 45e1a91c510fda..075a533e372c89 100644 --- a/homeassistant/components/media_player/vlc.py +++ b/homeassistant/components/media_player/vlc.py @@ -34,10 +34,10 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the vlc platform.""" - add_devices([VlcDevice(config.get(CONF_NAME, DEFAULT_NAME), - config.get(CONF_ARGUMENTS))]) + add_entities([VlcDevice(config.get(CONF_NAME, DEFAULT_NAME), + config.get(CONF_ARGUMENTS))]) class VlcDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/volumio.py b/homeassistant/components/media_player/volumio.py index c4ddd38fc4f471..00f5d25362f7d5 100644 --- a/homeassistant/components/media_player/volumio.py +++ b/homeassistant/components/media_player/volumio.py @@ -51,7 +51,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Volumio platform.""" if DATA_VOLUMIO not in hass.data: hass.data[DATA_VOLUMIO] = dict() @@ -75,7 +76,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): entity = Volumio(name, host, port, hass) hass.data[DATA_VOLUMIO][ip_addr] = entity - async_add_devices([entity]) + async_add_entities([entity]) class Volumio(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/webostv.py b/homeassistant/components/media_player/webostv.py index 362095daee6605..fd6b1c6d96e336 100644 --- a/homeassistant/components/media_player/webostv.py +++ b/homeassistant/components/media_player/webostv.py @@ -61,7 +61,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the LG WebOS TV platform.""" if discovery_info is not None: host = urlparse(discovery_info[1]).hostname @@ -84,11 +84,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): config = hass.config.path(config.get(CONF_FILENAME)) setup_tv(host, name, customize, config, timeout, hass, - add_devices, turn_on_action) + add_entities, turn_on_action) def setup_tv(host, name, customize, config, timeout, hass, - add_devices, turn_on_action): + add_entities, turn_on_action): """Set up a LG WebOS TV based on host parameter.""" from pylgtv import WebOsClient from pylgtv import PyLGTVPairException @@ -113,7 +113,7 @@ def setup_tv(host, name, customize, config, timeout, hass, _LOGGER.warning("LG webOS TV %s needs to be paired", host) request_configuration( host, name, customize, config, timeout, hass, - add_devices, turn_on_action) + add_entities, turn_on_action) return # If we came here and configuring this host, mark as done. @@ -122,13 +122,13 @@ def setup_tv(host, name, customize, config, timeout, hass, configurator = hass.components.configurator configurator.request_done(request_id) - add_devices([LgWebOSDevice(host, name, customize, config, timeout, - hass, turn_on_action)], True) + add_entities([LgWebOSDevice(host, name, customize, config, timeout, + hass, turn_on_action)], True) def request_configuration( host, name, customize, config, timeout, hass, - add_devices, turn_on_action): + add_entities, turn_on_action): """Request configuration steps from the user.""" configurator = hass.components.configurator @@ -141,7 +141,7 @@ def request_configuration( def lgtv_configuration_callback(data): """Handle actions when configuration callback is called.""" setup_tv(host, name, customize, config, timeout, hass, - add_devices, turn_on_action) + add_entities, turn_on_action) _CONFIGURING[host] = configurator.request_config( name, lgtv_configuration_callback, diff --git a/homeassistant/components/media_player/xiaomi_tv.py b/homeassistant/components/media_player/xiaomi_tv.py index d44ac138e4171f..ad66ae855bd560 100644 --- a/homeassistant/components/media_player/xiaomi_tv.py +++ b/homeassistant/components/media_player/xiaomi_tv.py @@ -29,7 +29,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Xiaomi TV platform.""" from pymitv import Discover @@ -45,10 +45,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ) else: # Register TV with Home Assistant. - add_devices([XiaomiTV(host, name)]) + add_entities([XiaomiTV(host, name)]) else: # Otherwise, discover TVs on network. - add_devices(XiaomiTV(tv, DEFAULT_NAME) for tv in Discover().scan()) + add_entities(XiaomiTV(tv, DEFAULT_NAME) for tv in Discover().scan()) class XiaomiTV(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/yamaha.py b/homeassistant/components/media_player/yamaha.py index cf36345806745e..2ffe58b02af2b2 100644 --- a/homeassistant/components/media_player/yamaha.py +++ b/homeassistant/components/media_player/yamaha.py @@ -57,7 +57,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Yamaha platform.""" import rxv # Keep track of configured receivers so that we don't end up @@ -126,7 +126,7 @@ def service_handler(service): DOMAIN, SERVICE_ENABLE_OUTPUT, service_handler, schema=ENABLE_OUTPUT_SCHEMA) - add_devices(devices) + add_entities(devices) class YamahaDevice(MediaPlayerDevice): diff --git a/homeassistant/components/media_player/yamaha_musiccast.py b/homeassistant/components/media_player/yamaha_musiccast.py index b42a5ae474c2ec..135bf4d0aefe77 100644 --- a/homeassistant/components/media_player/yamaha_musiccast.py +++ b/homeassistant/components/media_player/yamaha_musiccast.py @@ -48,7 +48,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Yamaha MusicCast platform.""" import socket import pymusiccast @@ -93,7 +93,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.debug( "receiver: %s / Port: %d / Zone: %s", receiver, port, zone) - add_devices( + add_entities( [YamahaDevice(receiver, receiver.zones[zone])], True) else: diff --git a/homeassistant/components/media_player/ziggo_mediabox_xl.py b/homeassistant/components/media_player/ziggo_mediabox_xl.py index 1886cd751ea87f..376b9e7c426130 100644 --- a/homeassistant/components/media_player/ziggo_mediabox_xl.py +++ b/homeassistant/components/media_player/ziggo_mediabox_xl.py @@ -34,7 +34,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Ziggo Mediabox XL platform.""" from ziggo_mediabox_xl import ZiggoMediaboxXL @@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Can't connect to %s: %s", host, error) else: _LOGGER.info("Ignoring duplicate Ziggo Mediabox XL %s", host) - add_devices(hosts, True) + add_entities(hosts, True) class ZiggoMediaboxXLDevice(MediaPlayerDevice): diff --git a/homeassistant/components/mqtt_eventstream.py b/homeassistant/components/mqtt_eventstream.py index ea4463f5c2347e..0e01310115fde0 100644 --- a/homeassistant/components/mqtt_eventstream.py +++ b/homeassistant/components/mqtt_eventstream.py @@ -17,7 +17,7 @@ EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL) from homeassistant.core import EventOrigin, State import homeassistant.helpers.config_validation as cv -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder DOMAIN = 'mqtt_eventstream' DEPENDENCIES = ['mqtt'] diff --git a/homeassistant/components/mqtt_statestream.py b/homeassistant/components/mqtt_statestream.py index 205a638c574120..592e31cbff14bf 100644 --- a/homeassistant/components/mqtt_statestream.py +++ b/homeassistant/components/mqtt_statestream.py @@ -15,7 +15,7 @@ from homeassistant.components.mqtt import valid_publish_topic from homeassistant.helpers.entityfilter import generate_filter from homeassistant.helpers.event import async_track_state_change -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder import homeassistant.helpers.config_validation as cv CONF_BASE_TOPIC = 'base_topic' diff --git a/homeassistant/components/mysensors/__init__.py b/homeassistant/components/mysensors/__init__.py index e498539f2f9e78..725494cd197230 100644 --- a/homeassistant/components/mysensors/__init__.py +++ b/homeassistant/components/mysensors/__init__.py @@ -130,10 +130,10 @@ def _get_mysensors_name(gateway, node_id, child_id): @callback def setup_mysensors_platform( hass, domain, discovery_info, device_class, device_args=None, - async_add_devices=None): + async_add_entities=None): """Set up a MySensors platform.""" # Only act if called via MySensors by discovery event. - # Otherwise gateway is not setup. + # Otherwise gateway is not set up. if not discovery_info: return if device_args is None: @@ -161,6 +161,6 @@ def setup_mysensors_platform( new_devices.append(devices[dev_id]) if new_devices: _LOGGER.info("Adding new devices: %s", new_devices) - if async_add_devices is not None: - async_add_devices(new_devices, True) + if async_add_entities is not None: + async_add_entities(new_devices, True) return new_devices diff --git a/homeassistant/components/nest/.translations/da.json b/homeassistant/components/nest/.translations/da.json new file mode 100644 index 00000000000000..4410f83d2ca4da --- /dev/null +++ b/homeassistant/components/nest/.translations/da.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_setup": "Du kan kun konfigurere en enkelt Nest konto." + }, + "error": { + "invalid_code": "Ugyldig kode" + }, + "step": { + "init": { + "data": { + "flow_impl": "Udbyder" + }, + "title": "Godkendelses udbyder" + }, + "link": { + "data": { + "code": "PIN-kode" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nest/.translations/es.json b/homeassistant/components/nest/.translations/es.json new file mode 100644 index 00000000000000..ceca4464e060ff --- /dev/null +++ b/homeassistant/components/nest/.translations/es.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "link": { + "data": { + "code": "C\u00f3digo PIN" + } + } + }, + "title": "Nest" + } +} \ No newline at end of file diff --git a/homeassistant/components/nest/.translations/he.json b/homeassistant/components/nest/.translations/he.json new file mode 100644 index 00000000000000..7f777f42b6daa8 --- /dev/null +++ b/homeassistant/components/nest/.translations/he.json @@ -0,0 +1,33 @@ +{ + "config": { + "abort": { + "already_setup": "\u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d7\u05e9\u05d1\u05d5\u05df Nest \u05d9\u05d7\u05d9\u05d3.", + "authorize_url_fail": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05dc\u05d0 \u05d9\u05d3\u05d5\u05e2\u05d4 \u05d1\u05d9\u05e6\u05d9\u05e8\u05ea \u05e7\u05d9\u05e9\u05d5\u05e8 \u05d0\u05d9\u05de\u05d5\u05ea.", + "authorize_url_timeout": "\u05e2\u05d1\u05e8 \u05d4\u05d6\u05de\u05df \u05d4\u05e7\u05e6\u05d5\u05d1 \u05e2\u05d1\u05d5\u05e8 \u05d9\u05e6\u05d9\u05e8\u05ea \u05e7\u05d9\u05e9\u05d5\u05e8 \u05d0\u05d9\u05de\u05d5\u05ea", + "no_flows": "\u05e2\u05dc\u05d9\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea Nest \u05dc\u05e4\u05e0\u05d9 \u05e9\u05ea\u05d5\u05db\u05dc \u05dc\u05d0\u05de\u05ea \u05d0\u05ea\u05d5. [\u05d0\u05e0\u05d0 \u05e7\u05e8\u05d0 \u05d0\u05ea \u05d4\u05d4\u05d5\u05e8\u05d0\u05d5\u05ea] (https://www.home-assistant.io/components/nest/)." + }, + "error": { + "internal_error": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05e4\u05e0\u05d9\u05de\u05d9\u05ea \u05d1\u05d0\u05d9\u05de\u05d5\u05ea \u05d4\u05e7\u05d5\u05d3", + "invalid_code": "\u05e7\u05d5\u05d3 \u05dc\u05d0 \u05ea\u05e7\u05d9\u05df", + "timeout": "\u05e2\u05d1\u05e8 \u05d4\u05d6\u05de\u05df \u05d4\u05e7\u05e6\u05d5\u05d1 \u05dc\u05d0\u05d9\u05de\u05d5\u05ea \u05d4\u05e7\u05d5\u05d3", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05dc\u05d0 \u05d9\u05d3\u05d5\u05e2\u05d4 \u05d1\u05d0\u05d9\u05de\u05d5\u05ea \u05d4\u05e7\u05d5\u05d3" + }, + "step": { + "init": { + "data": { + "flow_impl": "\u05e1\u05e4\u05e7" + }, + "description": "\u05d1\u05d7\u05e8 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea \u05e1\u05e4\u05e7 \u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05e9\u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d0\u05de\u05ea \u05e2\u05dd Nest.", + "title": "\u05e1\u05e4\u05e7 \u05d0\u05d9\u05de\u05d5\u05ea" + }, + "link": { + "data": { + "code": "\u05e7\u05d5\u05d3 Pin" + }, + "description": "\u05db\u05d3\u05d9 \u05dc\u05e7\u05e9\u05e8 \u05d0\u05ea \u05d7\u05e9\u05d1\u05d5\u05df Nest \u05e9\u05dc\u05da, [\u05d0\u05de\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05e9\u05dc\u05da] ({url}). \n\n \u05dc\u05d0\u05d7\u05e8 \u05d4\u05d0\u05d9\u05e9\u05d5\u05e8, \u05d4\u05e2\u05ea\u05e7 \u05d0\u05ea \u05e7\u05d5\u05d3 \u05d4PIN \u05e9\u05e1\u05d5\u05e4\u05e7 \u05d5\u05d4\u05d3\u05d1\u05e7 \u05d0\u05d5\u05ea\u05d5 \u05dc\u05de\u05d8\u05d4.", + "title": "\u05e7\u05d9\u05e9\u05d5\u05e8 \u05d7\u05e9\u05d1\u05d5\u05df Nest" + } + }, + "title": "Nest" + } +} \ No newline at end of file diff --git a/homeassistant/components/nest/__init__.py b/homeassistant/components/nest/__init__.py index d25b94bbc17682..57111350396e1b 100644 --- a/homeassistant/components/nest/__init__.py +++ b/homeassistant/components/nest/__init__.py @@ -119,7 +119,7 @@ async def async_setup(hass, config): async def async_setup_entry(hass, entry): - """Setup Nest from a config entry.""" + """Set up Nest from a config entry.""" from nest import Nest nest = Nest(access_token=entry.data['tokens']['access_token']) diff --git a/homeassistant/components/notify/aws_lambda.py b/homeassistant/components/notify/aws_lambda.py index 46ac2f89d33cbf..8a3cb900f4b7c2 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/notify/aws_lambda.py @@ -15,7 +15,7 @@ from homeassistant.components.notify import ( ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) import homeassistant.helpers.config_validation as cv -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder REQUIREMENTS = ['boto3==1.4.7'] diff --git a/homeassistant/components/notify/hangouts.py b/homeassistant/components/notify/hangouts.py new file mode 100644 index 00000000000000..eb2880e8a4644a --- /dev/null +++ b/homeassistant/components/notify/hangouts.py @@ -0,0 +1,66 @@ +""" +Hangouts notification service. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/notify.hangouts/ +""" +import logging + +import voluptuous as vol + +from homeassistant.components.notify import (ATTR_TARGET, PLATFORM_SCHEMA, + NOTIFY_SERVICE_SCHEMA, + BaseNotificationService, + ATTR_MESSAGE) + +from homeassistant.components.hangouts.const \ + import (DOMAIN, SERVICE_SEND_MESSAGE, + TARGETS_SCHEMA, CONF_DEFAULT_CONVERSATIONS) + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = [DOMAIN] + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_DEFAULT_CONVERSATIONS): [TARGETS_SCHEMA] +}) + +NOTIFY_SERVICE_SCHEMA = NOTIFY_SERVICE_SCHEMA.extend({ + vol.Optional(ATTR_TARGET): [TARGETS_SCHEMA] +}) + + +def get_service(hass, config, discovery_info=None): + """Get the Hangouts notification service.""" + return HangoutsNotificationService(config.get(CONF_DEFAULT_CONVERSATIONS)) + + +class HangoutsNotificationService(BaseNotificationService): + """Send Notifications to Hangouts conversations.""" + + def __init__(self, default_conversations): + """Set up the notification service.""" + self._default_conversations = default_conversations + + def send_message(self, message="", **kwargs): + """Send the message to the Google Hangouts server.""" + target_conversations = None + if ATTR_TARGET in kwargs: + target_conversations = [] + for target in kwargs.get(ATTR_TARGET): + target_conversations.append({'id': target}) + else: + target_conversations = self._default_conversations + + messages = [] + if 'title' in kwargs: + messages.append({'text': kwargs['title'], 'is_bold': True}) + + messages.append({'text': message, 'parse_str': True}) + service_data = { + ATTR_TARGET: target_conversations, + ATTR_MESSAGE: messages + } + + return self.hass.services.call( + DOMAIN, SERVICE_SEND_MESSAGE, service_data=service_data) diff --git a/homeassistant/components/notify/html5.py b/homeassistant/components/notify/html5.py index 1ed5047200419d..fa93cc4ba4ddb6 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/notify/html5.py @@ -132,17 +132,6 @@ def _load_config(filename): return {} -class JSONBytesDecoder(json.JSONEncoder): - """JSONEncoder to decode bytes objects to unicode.""" - - # pylint: disable=method-hidden, arguments-differ - def default(self, obj): - """Decode object if it's a bytes object, else defer to base class.""" - if isinstance(obj, bytes): - return obj.decode() - return json.JSONEncoder.default(self, obj) - - class HTML5PushRegistrationView(HomeAssistantView): """Accepts push registrations from a browser.""" diff --git a/homeassistant/components/notify/pushsafer.py b/homeassistant/components/notify/pushsafer.py index 30068854f2ea94..443d56521c1e2b 100644 --- a/homeassistant/components/notify/pushsafer.py +++ b/homeassistant/components/notify/pushsafer.py @@ -32,6 +32,10 @@ ATTR_URL = 'url' ATTR_URLTITLE = 'urltitle' ATTR_TIME2LIVE = 'time2live' +ATTR_PRIORITY = 'priority' +ATTR_RETRY = 'retry' +ATTR_EXPIRE = 'expire' +ATTR_ANSWER = 'answer' ATTR_PICTURE1 = 'picture1' # Attributes contained in picture1 @@ -106,6 +110,10 @@ def send_message(self, message='', **kwargs): 'u': data.get(ATTR_URL, ""), 'ut': data.get(ATTR_URLTITLE, ""), 'l': data.get(ATTR_TIME2LIVE, ""), + 'pr': data.get(ATTR_PRIORITY, ""), + 're': data.get(ATTR_RETRY, ""), + 'ex': data.get(ATTR_EXPIRE, ""), + 'a': data.get(ATTR_ANSWER, ""), 'p': picture1_encoded } diff --git a/homeassistant/components/notify/sendgrid.py b/homeassistant/components/notify/sendgrid.py index 92b709af8ad10c..231a17455d1724 100644 --- a/homeassistant/components/notify/sendgrid.py +++ b/homeassistant/components/notify/sendgrid.py @@ -14,7 +14,7 @@ CONF_API_KEY, CONF_SENDER, CONF_RECIPIENT, CONTENT_TYPE_TEXT_PLAIN) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['sendgrid==5.4.1'] +REQUIREMENTS = ['sendgrid==5.6.0'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/openuv.py b/homeassistant/components/openuv.py index dd038611ae96dc..d696f0e5100276 100644 --- a/homeassistant/components/openuv.py +++ b/homeassistant/components/openuv.py @@ -37,6 +37,7 @@ TYPE_CURRENT_OZONE_LEVEL = 'current_ozone_level' TYPE_CURRENT_UV_INDEX = 'current_uv_index' +TYPE_CURRENT_UV_LEVEL = 'current_uv_level' TYPE_MAX_UV_INDEX = 'max_uv_index' TYPE_PROTECTION_WINDOW = 'uv_protection_window' TYPE_SAFE_EXPOSURE_TIME_1 = 'safe_exposure_time_type_1' @@ -59,6 +60,7 @@ TYPE_CURRENT_OZONE_LEVEL: ( 'Current Ozone Level', 'mdi:vector-triangle', 'du'), TYPE_CURRENT_UV_INDEX: ('Current UV Index', 'mdi:weather-sunny', 'index'), + TYPE_CURRENT_UV_LEVEL: ('Current UV Level', 'mdi:weather-sunny', None), TYPE_MAX_UV_INDEX: ('Max UV Index', 'mdi:weather-sunny', 'index'), TYPE_SAFE_EXPOSURE_TIME_1: ( 'Skin Type 1 Safe Exposure Time', 'mdi:timer', 'minutes'), diff --git a/homeassistant/components/panel_custom.py b/homeassistant/components/panel_custom.py index 0444e7a5b5305c..740a28a9dec4a0 100644 --- a/homeassistant/components/panel_custom.py +++ b/homeassistant/components/panel_custom.py @@ -22,8 +22,13 @@ CONF_CONFIG = 'config' CONF_WEBCOMPONENT_PATH = 'webcomponent_path' CONF_JS_URL = 'js_url' +CONF_MODULE_URL = 'module_url' CONF_EMBED_IFRAME = 'embed_iframe' CONF_TRUST_EXTERNAL_SCRIPT = 'trust_external_script' +CONF_URL_EXCLUSIVE_GROUP = 'url_exclusive_group' + +MSG_URL_CONFLICT = \ + 'Pass in only one of webcomponent_path, module_url or js_url' DEFAULT_EMBED_IFRAME = False DEFAULT_TRUST_EXTERNAL = False @@ -40,8 +45,12 @@ vol.Optional(CONF_SIDEBAR_ICON, default=DEFAULT_ICON): cv.icon, vol.Optional(CONF_URL_PATH): cv.string, vol.Optional(CONF_CONFIG): dict, - vol.Optional(CONF_WEBCOMPONENT_PATH): cv.isfile, - vol.Optional(CONF_JS_URL): cv.string, + vol.Exclusive(CONF_WEBCOMPONENT_PATH, CONF_URL_EXCLUSIVE_GROUP, + msg=MSG_URL_CONFLICT): cv.string, + vol.Exclusive(CONF_JS_URL, CONF_URL_EXCLUSIVE_GROUP, + msg=MSG_URL_CONFLICT): cv.string, + vol.Exclusive(CONF_MODULE_URL, CONF_URL_EXCLUSIVE_GROUP, + msg=MSG_URL_CONFLICT): cv.string, vol.Optional(CONF_EMBED_IFRAME, default=DEFAULT_EMBED_IFRAME): cv.boolean, vol.Optional(CONF_TRUST_EXTERNAL_SCRIPT, @@ -66,6 +75,8 @@ async def async_register_panel( html_url=None, # JS source of your panel js_url=None, + # JS module of your panel + module_url=None, # If your panel should be run inside an iframe embed_iframe=DEFAULT_EMBED_IFRAME, # Should user be asked for confirmation when loading external source @@ -73,10 +84,10 @@ async def async_register_panel( # Configuration to be passed to the panel config=None): """Register a new custom panel.""" - if js_url is None and html_url is None: - raise ValueError('Either js_url or html_url is required.') - elif js_url and html_url: - raise ValueError('Pass in either JS url or HTML url, not both.') + if js_url is None and html_url is None and module_url is None: + raise ValueError('Either js_url, module_url or html_url is required.') + elif (js_url and html_url) or (module_url and html_url): + raise ValueError('Pass in only one of JS url, Module url or HTML url.') if config is not None and not isinstance(config, dict): raise ValueError('Config needs to be a dictionary.') @@ -90,6 +101,9 @@ async def async_register_panel( if js_url is not None: custom_panel_config['js_url'] = js_url + if module_url is not None: + custom_panel_config['module_url'] = module_url + if html_url is not None: custom_panel_config['html_url'] = html_url @@ -136,6 +150,9 @@ async def async_setup(hass, config): if CONF_JS_URL in panel: kwargs['js_url'] = panel[CONF_JS_URL] + elif CONF_MODULE_URL in panel: + kwargs['module_url'] = panel[CONF_MODULE_URL] + elif not await hass.async_add_job(os.path.isfile, panel_path): _LOGGER.error('Unable to find webcomponent for %s: %s', name, panel_path) diff --git a/homeassistant/components/prometheus.py b/homeassistant/components/prometheus.py index da986f024a4440..5fa768b6983cf8 100644 --- a/homeassistant/components/prometheus.py +++ b/homeassistant/components/prometheus.py @@ -49,7 +49,9 @@ def setup(hass, config): conf = config[DOMAIN] entity_filter = conf[CONF_FILTER] namespace = conf.get(CONF_PROM_NAMESPACE) - metrics = PrometheusMetrics(prometheus_client, entity_filter, namespace) + climate_units = hass.config.units.temperature_unit + metrics = PrometheusMetrics(prometheus_client, entity_filter, namespace, + climate_units) hass.bus.listen(EVENT_STATE_CHANGED, metrics.handle_event) return True @@ -58,7 +60,8 @@ def setup(hass, config): class PrometheusMetrics: """Model all of the metrics which should be exposed to Prometheus.""" - def __init__(self, prometheus_client, entity_filter, namespace): + def __init__(self, prometheus_client, entity_filter, namespace, + climate_units): """Initialize Prometheus Metrics.""" self.prometheus_client = prometheus_client self._filter = entity_filter @@ -67,6 +70,7 @@ def __init__(self, prometheus_client, entity_filter, namespace): else: self.metrics_prefix = "" self._metrics = {} + self._climate_units = climate_units def handle_event(self, event): """Listen for new messages on the bus, and add them to Prometheus.""" @@ -173,8 +177,7 @@ def _handle_lock(self, state): def _handle_climate(self, state): temp = state.attributes.get(ATTR_TEMPERATURE) if temp: - unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) - if unit == TEMP_FAHRENHEIT: + if self._climate_units == TEMP_FAHRENHEIT: temp = fahrenheit_to_celsius(temp) metric = self._metric( 'temperature_c', self.prometheus_client.Gauge, @@ -183,7 +186,7 @@ def _handle_climate(self, state): current_temp = state.attributes.get(ATTR_CURRENT_TEMPERATURE) if current_temp: - if unit == TEMP_FAHRENHEIT: + if self._climate_units == TEMP_FAHRENHEIT: current_temp = fahrenheit_to_celsius(current_temp) metric = self._metric( 'current_temperature_c', self.prometheus_client.Gauge, diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index b8b777990f76b6..700dd57eacf340 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -11,7 +11,7 @@ import homeassistant.util.dt as dt_util from homeassistant.core import ( Context, Event, EventOrigin, State, split_entity_id) -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder # SQLAlchemy Schema # pylint: disable=invalid-name diff --git a/homeassistant/components/remote/__init__.py b/homeassistant/components/remote/__init__.py index ddae36b92a70ed..11ecb20f7aa02f 100644 --- a/homeassistant/components/remote/__init__.py +++ b/homeassistant/components/remote/__init__.py @@ -134,42 +134,25 @@ def async_setup(hass, config): _LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_REMOTES) yield from component.async_setup(config) - @asyncio.coroutine - def async_handle_remote_service(service): - """Handle calls to the remote services.""" - target_remotes = component.async_extract_from_service(service) - kwargs = service.data.copy() - - update_tasks = [] - for remote in target_remotes: - if service.service == SERVICE_TURN_ON: - yield from remote.async_turn_on(**kwargs) - elif service.service == SERVICE_TOGGLE: - yield from remote.async_toggle(**kwargs) - elif service.service == SERVICE_SEND_COMMAND: - yield from remote.async_send_command(**kwargs) - else: - yield from remote.async_turn_off(**kwargs) - - if not remote.should_poll: - continue - update_tasks.append(remote.async_update_ha_state(True)) - - if update_tasks: - yield from asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_TURN_OFF, async_handle_remote_service, - schema=REMOTE_SERVICE_ACTIVITY_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_TURN_ON, async_handle_remote_service, - schema=REMOTE_SERVICE_ACTIVITY_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_TOGGLE, async_handle_remote_service, - schema=REMOTE_SERVICE_ACTIVITY_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_SEND_COMMAND, async_handle_remote_service, - schema=REMOTE_SERVICE_SEND_COMMAND_SCHEMA) + component.async_register_entity_service( + SERVICE_TURN_OFF, REMOTE_SERVICE_ACTIVITY_SCHEMA, + 'async_turn_off' + ) + + component.async_register_entity_service( + SERVICE_TURN_ON, REMOTE_SERVICE_ACTIVITY_SCHEMA, + 'async_turn_on' + ) + + component.async_register_entity_service( + SERVICE_TOGGLE, REMOTE_SERVICE_ACTIVITY_SCHEMA, + 'async_toggle' + ) + + component.async_register_entity_service( + SERVICE_SEND_COMMAND, REMOTE_SERVICE_SEND_COMMAND_SCHEMA, + 'async_send_command' + ) return True diff --git a/homeassistant/components/remote/apple_tv.py b/homeassistant/components/remote/apple_tv.py index 7d11c931a656ef..d8eac11372cace 100644 --- a/homeassistant/components/remote/apple_tv.py +++ b/homeassistant/components/remote/apple_tv.py @@ -16,7 +16,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Apple TV remote platform.""" if not discovery_info: return @@ -25,7 +26,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): host = discovery_info[CONF_HOST] atv = hass.data[DATA_APPLE_TV][host][ATTR_ATV] power = hass.data[DATA_APPLE_TV][host][ATTR_POWER] - async_add_devices([AppleTVRemote(atv, power, name)]) + async_add_entities([AppleTVRemote(atv, power, name)]) class AppleTVRemote(remote.RemoteDevice): diff --git a/homeassistant/components/remote/demo.py b/homeassistant/components/remote/demo.py index d959d74574f3b9..f44061ac8f9eff 100644 --- a/homeassistant/components/remote/demo.py +++ b/homeassistant/components/remote/demo.py @@ -8,9 +8,9 @@ from homeassistant.const import DEVICE_DEFAULT_NAME -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up the demo remotes.""" - add_devices_callback([ + add_entities_callback([ DemoRemote('Remote One', False, None), DemoRemote('Remote Two', True, 'mdi:remote'), ]) diff --git a/homeassistant/components/remote/harmony.py b/homeassistant/components/remote/harmony.py index a63b73250357f5..5b7d0d1df7868d 100644 --- a/homeassistant/components/remote/harmony.py +++ b/homeassistant/components/remote/harmony.py @@ -44,7 +44,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Harmony platform.""" host = None activity = None @@ -96,7 +96,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): device = HarmonyRemote( name, address, port, activity, harmony_conf_file, delay_secs) DEVICES.append(device) - add_devices([device]) + add_entities([device]) register_services(hass) except (ValueError, AttributeError): raise PlatformNotReady diff --git a/homeassistant/components/remote/itach.py b/homeassistant/components/remote/itach.py index 829a038953cd81..e7f23dfcd13eaa 100644 --- a/homeassistant/components/remote/itach.py +++ b/homeassistant/components/remote/itach.py @@ -44,7 +44,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ITach connection and devices.""" import pyitachip2ir itachip2ir = pyitachip2ir.ITachIP2IR( @@ -71,7 +71,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): cmddatas += "{}\n{}\n".format(cmdname, cmddata) itachip2ir.addDevice(name, modaddr, connaddr, cmddatas) devices.append(ITachIP2IRRemote(itachip2ir, name)) - add_devices(devices, True) + add_entities(devices, True) return True diff --git a/homeassistant/components/remote/kira.py b/homeassistant/components/remote/kira.py index dc37eb760f7d7d..24fc54ee78c5b3 100644 --- a/homeassistant/components/remote/kira.py +++ b/homeassistant/components/remote/kira.py @@ -18,14 +18,14 @@ CONF_REMOTE = "remote" -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Kira platform.""" if discovery_info: name = discovery_info.get(CONF_NAME) device = discovery_info.get(CONF_DEVICE) kira = hass.data[DOMAIN][CONF_REMOTE][name] - add_devices([KiraRemote(device, kira)]) + add_entities([KiraRemote(device, kira)]) return True diff --git a/homeassistant/components/remote/xiaomi_miio.py b/homeassistant/components/remote/xiaomi_miio.py index eda09e3af64494..723f575ba349a2 100644 --- a/homeassistant/components/remote/xiaomi_miio.py +++ b/homeassistant/components/remote/xiaomi_miio.py @@ -22,7 +22,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util.dt import utcnow -REQUIREMENTS = ['python-miio==0.4.0', 'construct==2.9.41'] +REQUIREMENTS = ['python-miio==0.4.1', 'construct==2.9.41'] _LOGGER = logging.getLogger(__name__) @@ -62,7 +62,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Xiaomi IR Remote (Chuangmi IR) platform.""" from miio import ChuangmiIr, DeviceException @@ -106,7 +107,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): hass.data[DATA_KEY][host] = xiaomi_miio_remote - async_add_devices([xiaomi_miio_remote]) + async_add_entities([xiaomi_miio_remote]) @asyncio.coroutine def async_service_handler(service): @@ -254,7 +255,7 @@ def _send_command(self, payload): payload, ex) def send_command(self, command, **kwargs): - """Wrapper for _send_command.""" + """Send a command.""" num_repeats = kwargs.get(ATTR_NUM_REPEATS) delay = kwargs.get(ATTR_DELAY_SECS, DEFAULT_DELAY_SECS) diff --git a/homeassistant/components/sabnzbd.py b/homeassistant/components/sabnzbd.py index b9c75c87c1d1ed..380867a3285651 100644 --- a/homeassistant/components/sabnzbd.py +++ b/homeassistant/components/sabnzbd.py @@ -110,7 +110,7 @@ async def async_configure_sabnzbd(hass, config, use_ssl, name=DEFAULT_NAME, async def async_setup(hass, config): - """Setup the SABnzbd component.""" + """Set up the SABnzbd component.""" async def sabnzbd_discovered(service, info): """Handle service discovery.""" ssl = info.get('properties', {}).get('https', '0') == '1' @@ -129,7 +129,7 @@ async def sabnzbd_discovered(service, info): @callback def async_setup_sabnzbd(hass, sab_api, config, name): - """Setup SABnzbd sensors and services.""" + """Set up SABnzbd sensors and services.""" sab_api_data = SabnzbdApiData(sab_api, name, config.get(CONF_SENSORS, {})) if config.get(CONF_SENSORS): @@ -193,7 +193,7 @@ async def async_configuration_callback(data): return def success(): - """Setup was successful.""" + """Signal successful setup.""" conf = load_json(hass.config.path(CONFIG_FILE)) conf[host] = {CONF_API_KEY: api_key} save_json(hass.config.path(CONFIG_FILE), conf) diff --git a/homeassistant/components/scene/__init__.py b/homeassistant/components/scene/__init__.py index 7b76836555c73f..8771a84c1d64d1 100644 --- a/homeassistant/components/scene/__init__.py +++ b/homeassistant/components/scene/__init__.py @@ -95,7 +95,7 @@ async def async_handle_scene_service(service): async def async_setup_entry(hass, entry): - """Setup a config entry.""" + """Set up a config entry.""" return await hass.data[DOMAIN].async_setup_entry(entry) diff --git a/homeassistant/components/scene/deconz.py b/homeassistant/components/scene/deconz.py index 3eb73736717efa..dde78dadc49f76 100644 --- a/homeassistant/components/scene/deconz.py +++ b/homeassistant/components/scene/deconz.py @@ -11,20 +11,20 @@ DEPENDENCIES = ['deconz'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Old way of setting up deCONZ scenes.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up scenes for deCONZ component.""" scenes = hass.data[DATA_DECONZ].scenes entities = [] for scene in scenes.values(): entities.append(DeconzScene(scene)) - async_add_devices(entities) + async_add_entities(entities) class DeconzScene(Scene): diff --git a/homeassistant/components/scene/homeassistant.py b/homeassistant/components/scene/homeassistant.py index 57c56e8b2f6f87..7e1d670ca6995d 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/scene/homeassistant.py @@ -36,11 +36,12 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up home assistant scene entries.""" scene_config = config.get(STATES) - async_add_devices(HomeAssistantScene( + async_add_entities(HomeAssistantScene( hass, _process_config(scene)) for scene in scene_config) return True diff --git a/homeassistant/components/scene/hunterdouglas_powerview.py b/homeassistant/components/scene/hunterdouglas_powerview.py index 4f5ac5725a3d87..40534b68635a30 100644 --- a/homeassistant/components/scene/hunterdouglas_powerview.py +++ b/homeassistant/components/scene/hunterdouglas_powerview.py @@ -37,7 +37,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up home assistant scene entries.""" # from aiopvapi.hub import Hub from aiopvapi.scenes import Scenes @@ -60,7 +61,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): PvScene(_raw_scene, hub_address, hass.loop, websession), _rooms) for _raw_scene in _scenes[SCENE_DATA]) - async_add_devices(pvscenes) + async_add_entities(pvscenes) class PowerViewScene(Scene): diff --git a/homeassistant/components/scene/knx.py b/homeassistant/components/scene/knx.py index 901e25aea82015..cd333ba79b4465 100644 --- a/homeassistant/components/scene/knx.py +++ b/homeassistant/components/scene/knx.py @@ -26,27 +26,27 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the scenes for KNX platform.""" if discovery_info is not None: - async_add_devices_discovery(hass, discovery_info, async_add_devices) + async_add_entities_discovery(hass, discovery_info, async_add_entities) else: - async_add_devices_config(hass, config, async_add_devices) + async_add_entities_config(hass, config, async_add_entities) @callback -def async_add_devices_discovery(hass, discovery_info, async_add_devices): +def async_add_entities_discovery(hass, discovery_info, async_add_entities): """Set up scenes for KNX platform configured via xknx.yaml.""" entities = [] for device_name in discovery_info[ATTR_DISCOVER_DEVICES]: device = hass.data[DATA_KNX].xknx.devices[device_name] entities.append(KNXScene(device)) - async_add_devices(entities) + async_add_entities(entities) @callback -def async_add_devices_config(hass, config, async_add_devices): +def async_add_entities_config(hass, config, async_add_entities): """Set up scene for KNX platform configured within platform.""" import xknx scene = xknx.devices.Scene( @@ -55,7 +55,7 @@ def async_add_devices_config(hass, config, async_add_devices): group_address=config.get(CONF_ADDRESS), scene_number=config.get(CONF_SCENE_NUMBER)) hass.data[DATA_KNX].xknx.devices.add(scene) - async_add_devices([KNXScene(scene)]) + async_add_entities([KNXScene(scene)]) class KNXScene(Scene): diff --git a/homeassistant/components/scene/lifx_cloud.py b/homeassistant/components/scene/lifx_cloud.py index a9ec1ef679cb6f..3169acb3a318b1 100644 --- a/homeassistant/components/scene/lifx_cloud.py +++ b/homeassistant/components/scene/lifx_cloud.py @@ -30,7 +30,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the scenes stored in the LIFX Cloud.""" token = config.get(CONF_TOKEN) timeout = config.get(CONF_TIMEOUT) @@ -56,7 +57,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): devices = [] for scene in data: devices.append(LifxCloudScene(hass, headers, timeout, scene)) - async_add_devices(devices) + async_add_entities(devices) return True if status == 401: _LOGGER.error("Unauthorized (bad token?) on %s", url) diff --git a/homeassistant/components/scene/litejet.py b/homeassistant/components/scene/litejet.py index 87539e2dded96d..e12643fa651bf1 100644 --- a/homeassistant/components/scene/litejet.py +++ b/homeassistant/components/scene/litejet.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up scenes for the LiteJet platform.""" litejet_ = hass.data['litejet_system'] @@ -25,7 +25,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name = litejet_.get_scene_name(i) if not litejet.is_ignored(hass, name): devices.append(LiteJetScene(litejet_, i, name)) - add_devices(devices) + add_entities(devices) class LiteJetScene(Scene): diff --git a/homeassistant/components/scene/lutron_caseta.py b/homeassistant/components/scene/lutron_caseta.py index 0d9024d194e59a..0f9173663a9a21 100644 --- a/homeassistant/components/scene/lutron_caseta.py +++ b/homeassistant/components/scene/lutron_caseta.py @@ -16,7 +16,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Lutron Caseta lights.""" devs = [] bridge = hass.data[LUTRON_CASETA_SMARTBRIDGE] @@ -25,7 +26,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): dev = LutronCasetaScene(scenes[scene], bridge) devs.append(dev) - async_add_devices(devs, True) + async_add_entities(devs, True) class LutronCasetaScene(Scene): diff --git a/homeassistant/components/scene/tahoma.py b/homeassistant/components/scene/tahoma.py index 39206623901045..5846d97c7f910a 100644 --- a/homeassistant/components/scene/tahoma.py +++ b/homeassistant/components/scene/tahoma.py @@ -15,13 +15,13 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tahoma scenes.""" controller = hass.data[TAHOMA_DOMAIN]['controller'] scenes = [] for scene in hass.data[TAHOMA_DOMAIN]['scenes']: scenes.append(TahomaScene(scene, controller)) - add_devices(scenes, True) + add_entities(scenes, True) class TahomaScene(Scene): diff --git a/homeassistant/components/scene/tuya.py b/homeassistant/components/scene/tuya.py index 3990a7da206993..2e03e5dba9a288 100644 --- a/homeassistant/components/scene/tuya.py +++ b/homeassistant/components/scene/tuya.py @@ -12,7 +12,7 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tuya scenes.""" if discovery_info is None: return @@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if device is None: continue devices.append(TuyaScene(device)) - add_devices(devices) + add_entities(devices) class TuyaScene(TuyaDevice, Scene): diff --git a/homeassistant/components/scene/velux.py b/homeassistant/components/scene/velux.py index 63bb23b1086cbc..77ba30158e41f1 100644 --- a/homeassistant/components/scene/velux.py +++ b/homeassistant/components/scene/velux.py @@ -12,13 +12,13 @@ DEPENDENCIES = ['velux'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the scenes for velux platform.""" entities = [] for scene in hass.data[DATA_VELUX].pyvlx.scenes: entities.append(VeluxScene(scene)) - async_add_devices(entities) + async_add_entities(entities) class VeluxScene(Scene): diff --git a/homeassistant/components/scene/vera.py b/homeassistant/components/scene/vera.py index 4f580356fbb634..6cae1195f87395 100644 --- a/homeassistant/components/scene/vera.py +++ b/homeassistant/components/scene/vera.py @@ -16,9 +16,9 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vera scenes.""" - add_devices( + add_entities( [VeraScene(scene, hass.data[VERA_CONTROLLER]) for scene in hass.data[VERA_SCENES]], True) diff --git a/homeassistant/components/scene/wink.py b/homeassistant/components/scene/wink.py index 5bd053bdd39d21..62da668694b96e 100644 --- a/homeassistant/components/scene/wink.py +++ b/homeassistant/components/scene/wink.py @@ -15,14 +15,14 @@ DEPENDENCIES = ['wink'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink platform.""" import pywink for scene in pywink.get_scenes(): _id = scene.object_id() + scene.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkScene(scene, hass)]) + add_entities([WinkScene(scene, hass)]) class WinkScene(WinkDevice, Scene): diff --git a/homeassistant/components/sensor/.translations/moon.cs.json b/homeassistant/components/sensor/.translations/moon.cs.json new file mode 100644 index 00000000000000..ef1d5bf5f132fe --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.cs.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Prvn\u00ed \u010dtvr\u0165", + "full_moon": "\u00dapln\u011bk", + "last_quarter": "Posledn\u00ed \u010dtvr\u0165", + "new_moon": "Nov", + "waning_crescent": "Couvaj\u00edc\u00ed srpek", + "waning_gibbous": "Couvaj\u00edc\u00ed m\u011bs\u00edc", + "waxing_crescent": "Dor\u016fstaj\u00edc\u00ed srpek", + "waxing_gibbous": "Dor\u016fstaj\u00edc\u00ed m\u011bs\u00edc" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.es.json b/homeassistant/components/sensor/.translations/moon.es.json new file mode 100644 index 00000000000000..bbc03820b5b537 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.es.json @@ -0,0 +1,7 @@ +{ + "state": { + "first_quarter": "Primer cuarto", + "full_moon": "Luna llena", + "new_moon": "Luna nueva" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.he.json b/homeassistant/components/sensor/.translations/moon.he.json new file mode 100644 index 00000000000000..60999f83645044 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.he.json @@ -0,0 +1,8 @@ +{ + "state": { + "first_quarter": "\u05e8\u05d1\u05e2\u05d5\u05df \u05e8\u05d0\u05e9\u05d5\u05df", + "full_moon": "\u05d9\u05e8\u05d7 \u05de\u05dc\u05d0", + "last_quarter": "\u05e8\u05d1\u05e2\u05d5\u05df \u05d0\u05d7\u05e8\u05d5\u05df", + "new_moon": "\u05e8\u05d0\u05e9 \u05d7\u05d5\u05d3\u05e9" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.it.json b/homeassistant/components/sensor/.translations/moon.it.json new file mode 100644 index 00000000000000..fce5152b3f9096 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.it.json @@ -0,0 +1,8 @@ +{ + "state": { + "first_quarter": "Primo quarto", + "full_moon": "Luna piena", + "last_quarter": "Ultimo quarto", + "new_moon": "Nuova luna" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.lb.json b/homeassistant/components/sensor/.translations/moon.lb.json new file mode 100644 index 00000000000000..2aa7ea03db7c28 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.lb.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "\u00c9ischt V\u00e9ierel", + "full_moon": "Vollmound", + "last_quarter": "L\u00e4scht V\u00e9ierel", + "new_moon": "Neimound", + "waning_crescent": "Ofhuelende Mound", + "waning_gibbous": "Dr\u00ebtt V\u00e9ierel", + "waxing_crescent": "Zouhuelende Mound", + "waxing_gibbous": "Zweet V\u00e9ierel" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.pl.json b/homeassistant/components/sensor/.translations/moon.pl.json new file mode 100644 index 00000000000000..85dfe79bae4de8 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.pl.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "pierwsza kwadra", + "full_moon": "pe\u0142nia", + "last_quarter": "ostatnia kwadra", + "new_moon": "n\u00f3w", + "waning_crescent": "sierp ubywaj\u0105cy", + "waning_gibbous": "ubywaj\u0105cy garbaty", + "waxing_crescent": "sierp przybywaj\u0105cy", + "waxing_gibbous": "przybywaj\u0105cy garbaty" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.pt-BR.json b/homeassistant/components/sensor/.translations/moon.pt-BR.json new file mode 100644 index 00000000000000..af4cefff6e5a3c --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.pt-BR.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Quarto crescente", + "full_moon": "Cheia", + "last_quarter": "Quarto minguante", + "new_moon": "Nova", + "waning_crescent": "Minguante", + "waning_gibbous": "Minguante gibosa", + "waxing_crescent": "Crescente", + "waxing_gibbous": "Crescente gibosa" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.sv.json b/homeassistant/components/sensor/.translations/moon.sv.json new file mode 100644 index 00000000000000..ae69c1c9654939 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.sv.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "F\u00f6rsta kvartalet", + "full_moon": "Fullm\u00e5ne", + "last_quarter": "Sista kvartalet", + "new_moon": "Nym\u00e5ne", + "waning_crescent": "Avtagande halvm\u00e5ne", + "waning_gibbous": "Avtagande halvm\u00e5ne", + "waxing_crescent": "Tilltagande halvm\u00e5ne", + "waxing_gibbous": "Tilltagande halvm\u00e5ne" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.he.json b/homeassistant/components/sensor/.translations/season.he.json new file mode 100644 index 00000000000000..282c24f3ad964d --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.he.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\u05e1\u05ea\u05d9\u05d5", + "spring": "\u05d0\u05d1\u05d9\u05d1", + "summer": "\u05e7\u05d9\u05e5", + "winter": "\u05d7\u05d5\u05e8\u05e3" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 8550d175b63335..948f844cfd439d 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -43,7 +43,7 @@ async def async_setup(hass, config): async def async_setup_entry(hass, entry): - """Setup a config entry.""" + """Set up a config entry.""" return await hass.data[DOMAIN].async_setup_entry(entry) diff --git a/homeassistant/components/sensor/abode.py b/homeassistant/components/sensor/abode.py index 26247c7745474f..4695a5f0471ada 100644 --- a/homeassistant/components/sensor/abode.py +++ b/homeassistant/components/sensor/abode.py @@ -22,7 +22,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for an Abode device.""" import abodepy.helpers.constants as CONST @@ -38,7 +38,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data.devices.extend(devices) - add_devices(devices) + add_entities(devices) class AbodeSensor(AbodeDevice): diff --git a/homeassistant/components/sensor/ads.py b/homeassistant/components/sensor/ads.py index 659ac6d7e5dc56..5d5cbb379bfed2 100644 --- a/homeassistant/components/sensor/ads.py +++ b/homeassistant/components/sensor/ads.py @@ -32,7 +32,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an ADS sensor device.""" ads_hub = hass.data.get(ads.DATA_ADS) @@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): entity = AdsSensor( ads_hub, ads_var, ads_type, name, unit_of_measurement, factor) - add_devices([entity]) + add_entities([entity]) class AdsSensor(Entity): diff --git a/homeassistant/components/sensor/airvisual.py b/homeassistant/components/sensor/airvisual.py index 403722c7b6ad26..dcd89ccb78a72f 100644 --- a/homeassistant/components/sensor/airvisual.py +++ b/homeassistant/components/sensor/airvisual.py @@ -117,7 +117,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Configure the platform and add the sensors.""" from pyairvisual import Client @@ -161,7 +161,7 @@ async def async_setup_platform( AirVisualSensor( data, kind, name, icon, unit, locale, location_id)) - async_add_devices(sensors, True) + async_add_entities(sensors, True) class AirVisualSensor(Entity): diff --git a/homeassistant/components/sensor/alarmdecoder.py b/homeassistant/components/sensor/alarmdecoder.py index ce709eee94cf79..51e166bfce6832 100644 --- a/homeassistant/components/sensor/alarmdecoder.py +++ b/homeassistant/components/sensor/alarmdecoder.py @@ -15,13 +15,13 @@ DEPENDENCIES = ['alarmdecoder'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up for AlarmDecoder sensor devices.""" _LOGGER.debug("AlarmDecoderSensor: setup_platform") device = AlarmDecoderSensor(hass) - add_devices([device]) + add_entities([device]) class AlarmDecoderSensor(Entity): diff --git a/homeassistant/components/sensor/alpha_vantage.py b/homeassistant/components/sensor/alpha_vantage.py index a7e6f6d26221bb..c0b280d2d69c52 100644 --- a/homeassistant/components/sensor/alpha_vantage.py +++ b/homeassistant/components/sensor/alpha_vantage.py @@ -63,7 +63,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Alpha Vantage sensor.""" from alpha_vantage.timeseries import TimeSeries from alpha_vantage.foreignexchange import ForeignExchange @@ -107,7 +107,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.debug(str(error)) dev.append(AlphaVantageForeignExchange(forex, conversion)) - add_devices(dev, True) + add_entities(dev, True) _LOGGER.debug("Setup completed") diff --git a/homeassistant/components/sensor/amcrest.py b/homeassistant/components/sensor/amcrest.py index 99a4371f6a21ad..53a8c663f21162 100644 --- a/homeassistant/components/sensor/amcrest.py +++ b/homeassistant/components/sensor/amcrest.py @@ -20,7 +20,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up a sensor for an Amcrest IP Camera.""" if discovery_info is None: return @@ -34,7 +35,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): amcrest_sensors.append( AmcrestSensor(amcrest.name, amcrest.device, sensor_type)) - async_add_devices(amcrest_sensors, True) + async_add_entities(amcrest_sensors, True) return True diff --git a/homeassistant/components/sensor/android_ip_webcam.py b/homeassistant/components/sensor/android_ip_webcam.py index f25056d5a0fc7b..333bf12ec21019 100644 --- a/homeassistant/components/sensor/android_ip_webcam.py +++ b/homeassistant/components/sensor/android_ip_webcam.py @@ -15,7 +15,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the IP Webcam Sensor.""" if discovery_info is None: return @@ -30,7 +31,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for sensor in sensors: all_sensors.append(IPWebcamSensor(name, host, ipcam, sensor)) - async_add_devices(all_sensors, True) + async_add_entities(all_sensors, True) class IPWebcamSensor(AndroidIPCamEntity): diff --git a/homeassistant/components/sensor/api_streams.py b/homeassistant/components/sensor/api_streams.py index 0d193dee79bb88..1ecae1e753e25a 100644 --- a/homeassistant/components/sensor/api_streams.py +++ b/homeassistant/components/sensor/api_streams.py @@ -49,7 +49,8 @@ def handle(self, record): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the API stream platform.""" entity = APICount() handler = StreamHandler(entity) @@ -65,7 +66,7 @@ def remove_logger(event): hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, remove_logger) - async_add_devices([entity]) + async_add_entities([entity]) class APICount(Entity): diff --git a/homeassistant/components/sensor/arduino.py b/homeassistant/components/sensor/arduino.py index d4d8ea09d294ca..f46eebce1b2913 100644 --- a/homeassistant/components/sensor/arduino.py +++ b/homeassistant/components/sensor/arduino.py @@ -33,7 +33,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Arduino platform.""" if arduino.BOARD is None: _LOGGER.error("A connection has not been made to the Arduino board") @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors = [] for pinnum, pin in pins.items(): sensors.append(ArduinoSensor(pin.get(CONF_NAME), pinnum, CONF_TYPE)) - add_devices(sensors) + add_entities(sensors) class ArduinoSensor(Entity): diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/sensor/arest.py index 751f0f11171cdb..3d85a331f6f1df 100644 --- a/homeassistant/components/sensor/arest.py +++ b/homeassistant/components/sensor/arest.py @@ -44,7 +44,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the aREST sensor.""" resource = config.get(CONF_RESOURCE) var_conf = config.get(CONF_MONITORED_VARIABLES) @@ -102,7 +102,7 @@ def _render(value): pin=pinnum, unit_of_measurement=pin.get( CONF_UNIT_OF_MEASUREMENT), renderer=renderer)) - add_devices(dev, True) + add_entities(dev, True) class ArestSensor(Entity): diff --git a/homeassistant/components/sensor/arlo.py b/homeassistant/components/sensor/arlo.py index 6d764b1c9164b2..be940cc4f51039 100644 --- a/homeassistant/components/sensor/arlo.py +++ b/homeassistant/components/sensor/arlo.py @@ -43,7 +43,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an Arlo IP sensor.""" arlo = hass.data.get(DATA_ARLO) if not arlo: @@ -70,7 +70,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): SENSOR_TYPES[sensor_type][0], base_station.name) sensors.append(ArloSensor(name, base_station, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) class ArloSensor(Entity): diff --git a/homeassistant/components/sensor/arwn.py b/homeassistant/components/sensor/arwn.py index 6b0d3e569d78b0..580701490a6127 100644 --- a/homeassistant/components/sensor/arwn.py +++ b/homeassistant/components/sensor/arwn.py @@ -59,7 +59,8 @@ def _slug(name): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the ARWN platform.""" @callback def async_sensor_event_received(topic, payload, qos): @@ -97,7 +98,7 @@ def async_sensor_event_received(topic, payload, qos): store[sensor.name] = sensor _LOGGER.debug("Registering new sensor %(name)s => %(event)s", dict(name=sensor.name, event=event)) - async_add_devices((sensor,), True) + async_add_entities((sensor,), True) else: store[sensor.name].set_event(event) diff --git a/homeassistant/components/sensor/bbox.py b/homeassistant/components/sensor/bbox.py index d24621becc9794..c81160dc2ae847 100644 --- a/homeassistant/components/sensor/bbox.py +++ b/homeassistant/components/sensor/bbox.py @@ -48,7 +48,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Bbox sensor.""" # Create a data fetcher to support all of the configured sensors. Then make # the first call to init the data. @@ -65,7 +65,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in config[CONF_MONITORED_VARIABLES]: sensors.append(BboxSensor(bbox_data, variable, name)) - add_devices(sensors, True) + add_entities(sensors, True) class BboxSensor(Entity): diff --git a/homeassistant/components/sensor/bh1750.py b/homeassistant/components/sensor/bh1750.py index 6d34d4ea9f89c0..6230ae8a74d798 100644 --- a/homeassistant/components/sensor/bh1750.py +++ b/homeassistant/components/sensor/bh1750.py @@ -67,7 +67,8 @@ # pylint: disable=import-error @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the BH1750 sensor.""" import smbus from i2csense.bh1750 import BH1750 @@ -95,7 +96,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): _LOGGER.info("Setup of BH1750 light sensor at %s in mode %s is complete", i2c_address, operation_mode) - async_add_devices(dev, True) + async_add_entities(dev, True) class BH1750Sensor(Entity): diff --git a/homeassistant/components/sensor/bitcoin.py b/homeassistant/components/sensor/bitcoin.py index f51b7dcd5bde40..34855d19104e81 100644 --- a/homeassistant/components/sensor/bitcoin.py +++ b/homeassistant/components/sensor/bitcoin.py @@ -58,7 +58,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Bitcoin sensors.""" from blockchain import exchangerates @@ -73,7 +73,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in config[CONF_DISPLAY_OPTIONS]: dev.append(BitcoinSensor(data, variable, currency)) - add_devices(dev, True) + add_entities(dev, True) class BitcoinSensor(Entity): diff --git a/homeassistant/components/sensor/blink.py b/homeassistant/components/sensor/blink.py index db7ab7c2e9e8ae..97356b6fc613f9 100644 --- a/homeassistant/components/sensor/blink.py +++ b/homeassistant/components/sensor/blink.py @@ -21,7 +21,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a Blink sensor.""" if discovery_info is None: return @@ -35,7 +35,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devs.append(BlinkSensor(name, 'notifications', index, data)) index += 1 - add_devices(devs, True) + add_entities(devs, True) class BlinkSensor(Entity): diff --git a/homeassistant/components/sensor/blockchain.py b/homeassistant/components/sensor/blockchain.py index 2276f5fc1b77a4..e51db7edcad0d2 100644 --- a/homeassistant/components/sensor/blockchain.py +++ b/homeassistant/components/sensor/blockchain.py @@ -33,7 +33,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Blockchain.info sensors.""" from pyblockchain import validate_address @@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Bitcoin address is not valid: %s", address) return False - add_devices([BlockchainSensor(name, addresses)], True) + add_entities([BlockchainSensor(name, addresses)], True) class BlockchainSensor(Entity): diff --git a/homeassistant/components/sensor/bloomsky.py b/homeassistant/components/sensor/bloomsky.py index d33796d04ccc77..8926848102cb89 100644 --- a/homeassistant/components/sensor/bloomsky.py +++ b/homeassistant/components/sensor/bloomsky.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available BloomSky weather sensors.""" bloomsky = hass.components.bloomsky # Default needed in case of discovery @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for device in bloomsky.BLOOMSKY.devices.values(): for variable in sensors: - add_devices( + add_entities( [BloomSkySensor(bloomsky.BLOOMSKY, device, variable)], True) diff --git a/homeassistant/components/sensor/bme280.py b/homeassistant/components/sensor/bme280.py index 1685d34c0ecf52..676800c10691e4 100644 --- a/homeassistant/components/sensor/bme280.py +++ b/homeassistant/components/sensor/bme280.py @@ -82,7 +82,8 @@ # pylint: disable=import-error @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the BME280 sensor.""" import smbus from i2csense.bme280 import BME280 @@ -117,7 +118,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): except KeyError: pass - async_add_devices(dev, True) + async_add_entities(dev, True) class BME280Handler: diff --git a/homeassistant/components/sensor/bme680.py b/homeassistant/components/sensor/bme680.py index 2dbda26ac32312..65d486ada36064 100644 --- a/homeassistant/components/sensor/bme680.py +++ b/homeassistant/components/sensor/bme680.py @@ -98,7 +98,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the BME680 sensor.""" SENSOR_TYPES[SENSOR_TEMP][1] = hass.config.units.temperature_unit name = config.get(CONF_NAME) @@ -112,7 +113,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): dev.append(BME680Sensor( sensor_handler, variable, SENSOR_TYPES[variable][1], name)) - async_add_devices(dev) + async_add_entities(dev) return diff --git a/homeassistant/components/sensor/bmw_connected_drive.py b/homeassistant/components/sensor/bmw_connected_drive.py index e3331cdc763cbd..ff80100e21dd31 100644 --- a/homeassistant/components/sensor/bmw_connected_drive.py +++ b/homeassistant/components/sensor/bmw_connected_drive.py @@ -27,7 +27,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the BMW sensors.""" accounts = hass.data[BMW_DOMAIN] _LOGGER.debug('Found BMW accounts: %s', @@ -41,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices.append(device) device = BMWConnectedDriveSensor(account, vehicle, 'mileage') devices.append(device) - add_devices(devices, True) + add_entities(devices, True) class BMWConnectedDriveSensor(Entity): @@ -58,7 +58,10 @@ def __init__(self, account, vehicle, attribute: str): @property def should_poll(self) -> bool: - """Data update is triggered from BMWConnectedDriveEntity.""" + """Return False. + + Data update is triggered from BMWConnectedDriveEntity. + """ return False @property diff --git a/homeassistant/components/sensor/bom.py b/homeassistant/components/sensor/bom.py index eb63e1162541ad..11685c7ff68f5b 100644 --- a/homeassistant/components/sensor/bom.py +++ b/homeassistant/components/sensor/bom.py @@ -99,7 +99,7 @@ def validate_station(station): }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the BOM sensor.""" station = config.get(CONF_STATION) zone_id, wmo_id = config.get(CONF_ZONE_ID), config.get(CONF_WMO_ID) @@ -127,8 +127,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Received error from BOM Current: %s", err) return - add_devices([BOMCurrentSensor(bom_data, variable, config.get(CONF_NAME)) - for variable in config[CONF_MONITORED_CONDITIONS]]) + add_entities([BOMCurrentSensor(bom_data, variable, config.get(CONF_NAME)) + for variable in config[CONF_MONITORED_CONDITIONS]]) class BOMCurrentSensor(Entity): diff --git a/homeassistant/components/sensor/broadlink.py b/homeassistant/components/sensor/broadlink.py index 06d7f512c9feb1..21e5b0ee1d98f0 100644 --- a/homeassistant/components/sensor/broadlink.py +++ b/homeassistant/components/sensor/broadlink.py @@ -47,7 +47,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Broadlink device sensors.""" host = config.get(CONF_HOST) mac = config.get(CONF_MAC).encode().replace(b':', b'') @@ -59,7 +59,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev = [] for variable in config[CONF_MONITORED_CONDITIONS]: dev.append(BroadlinkSensor(name, broadlink_data, variable)) - add_devices(dev, True) + add_entities(dev, True) class BroadlinkSensor(Entity): diff --git a/homeassistant/components/sensor/buienradar.py b/homeassistant/components/sensor/buienradar.py index 992c27bbe2ea91..c7ca0c097ffd7c 100644 --- a/homeassistant/components/sensor/buienradar.py +++ b/homeassistant/components/sensor/buienradar.py @@ -141,7 +141,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Create the buienradar sensor.""" from homeassistant.components.weather.buienradar import DEFAULT_TIMEFRAME @@ -163,7 +164,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for sensor_type in config[CONF_MONITORED_CONDITIONS]: dev.append(BrSensor(sensor_type, config.get(CONF_NAME, 'br'), coordinates)) - async_add_devices(dev) + async_add_entities(dev) data = BrData(hass, coordinates, timeframe, dev) # schedule the first update in 1 minute from now: diff --git a/homeassistant/components/sensor/canary.py b/homeassistant/components/sensor/canary.py index 51fe1d4dd7a3fa..015c6b378e090b 100644 --- a/homeassistant/components/sensor/canary.py +++ b/homeassistant/components/sensor/canary.py @@ -30,7 +30,7 @@ STATE_AIR_QUALITY_VERY_ABNORMAL = "very_abnormal" -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Canary sensors.""" data = hass.data[DATA_CANARY] devices = [] @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices.append(CanarySensor(data, sensor_type, location, device)) - add_devices(devices, True) + add_entities(devices, True) class CanarySensor(Entity): diff --git a/homeassistant/components/sensor/cert_expiry.py b/homeassistant/components/sensor/cert_expiry.py index 1ccaf2f692532b..df48ebbf41cd91 100644 --- a/homeassistant/components/sensor/cert_expiry.py +++ b/homeassistant/components/sensor/cert_expiry.py @@ -1,5 +1,5 @@ """ -Counter for the days till a HTTPS (TLS) certificate will expire. +Counter for the days until an HTTPS (TLS) certificate will expire. For more details about this sensor please refer to the documentation at https://home-assistant.io/components/sensor.cert_expiry/ @@ -33,7 +33,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up certificate expiry sensor.""" def run_setup(event): """Wait until Home Assistant is fully initialized before creating. @@ -44,8 +44,8 @@ def run_setup(event): server_port = config.get(CONF_PORT) sensor_name = config.get(CONF_NAME) - add_devices([SSLCertificate(sensor_name, server_name, server_port)], - True) + add_entities([SSLCertificate(sensor_name, server_name, server_port)], + True) # To allow checking of the HA certificate we must first be running. hass.bus.listen_once(EVENT_HOMEASSISTANT_START, run_setup) diff --git a/homeassistant/components/sensor/citybikes.py b/homeassistant/components/sensor/citybikes.py index c9a69923135ccc..8003a77a4526e9 100644 --- a/homeassistant/components/sensor/citybikes.py +++ b/homeassistant/components/sensor/citybikes.py @@ -126,7 +126,7 @@ def async_citybikes_request(hass, uri, schema): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, +def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the CityBikes platform.""" if PLATFORM not in hass.data: @@ -167,7 +167,7 @@ def async_setup_platform(hass, config, async_add_devices, (station_id, station_uid)): devices.append(CityBikesStation(hass, network, station_id, name)) - async_add_devices(devices, True) + async_add_entities(devices, True) class CityBikesNetwork: diff --git a/homeassistant/components/sensor/coinbase.py b/homeassistant/components/sensor/coinbase.py index 32e1d8f211a136..40444dee93c076 100644 --- a/homeassistant/components/sensor/coinbase.py +++ b/homeassistant/components/sensor/coinbase.py @@ -21,7 +21,7 @@ ETH_ICON = 'mdi:currency-eth' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Coinbase sensors.""" if discovery_info is None: return @@ -35,7 +35,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): hass.data[DATA_COINBASE], discovery_info['exchange_currency'], discovery_info['native_currency']) - add_devices([sensor], True) + add_entities([sensor], True) class AccountSensor(Entity): diff --git a/homeassistant/components/sensor/coinmarketcap.py b/homeassistant/components/sensor/coinmarketcap.py index c4f38b1be02d85..18d3f0a3d009c5 100644 --- a/homeassistant/components/sensor/coinmarketcap.py +++ b/homeassistant/components/sensor/coinmarketcap.py @@ -56,7 +56,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the CoinMarketCap sensor.""" currency_id = config.get(CONF_CURRENCY_ID) display_currency = config.get(CONF_DISPLAY_CURRENCY).upper() @@ -71,7 +71,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): currency_id = DEFAULT_CURRENCY_ID display_currency = DEFAULT_DISPLAY_CURRENCY - add_devices([CoinMarketCapSensor( + add_entities([CoinMarketCapSensor( CoinMarketCapData( currency_id, display_currency), display_currency_decimals)], True) diff --git a/homeassistant/components/sensor/comed_hourly_pricing.py b/homeassistant/components/sensor/comed_hourly_pricing.py index c0c477ade0b926..3595bcaa227e5c 100644 --- a/homeassistant/components/sensor/comed_hourly_pricing.py +++ b/homeassistant/components/sensor/comed_hourly_pricing.py @@ -50,7 +50,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the ComEd Hourly Pricing sensor.""" websession = async_get_clientsession(hass) dev = [] @@ -60,7 +61,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): hass.loop, websession, variable[CONF_SENSOR_TYPE], variable[CONF_OFFSET], variable.get(CONF_NAME))) - async_add_devices(dev, True) + async_add_entities(dev, True) class ComedHourlyPricingSensor(Entity): diff --git a/homeassistant/components/sensor/comfoconnect.py b/homeassistant/components/sensor/comfoconnect.py index ad6b07fb3dacbb..b13c8d8d26326d 100644 --- a/homeassistant/components/sensor/comfoconnect.py +++ b/homeassistant/components/sensor/comfoconnect.py @@ -23,7 +23,7 @@ SENSOR_TYPES = {} -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ComfoConnect fan platform.""" from pycomfoconnect import ( SENSOR_TEMPERATURE_EXTRACT, SENSOR_HUMIDITY_EXTRACT, @@ -90,7 +90,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ) ) - add_devices(sensors, True) + add_entities(sensors, True) class ComfoConnectSensor(Entity): diff --git a/homeassistant/components/sensor/command_line.py b/homeassistant/components/sensor/command_line.py index 846604a9ff5884..e1d151410b1a93 100644 --- a/homeassistant/components/sensor/command_line.py +++ b/homeassistant/components/sensor/command_line.py @@ -43,7 +43,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Command Sensor.""" name = config.get(CONF_NAME) command = config.get(CONF_COMMAND) @@ -55,7 +55,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): json_attributes = config.get(CONF_JSON_ATTRIBUTES) data = CommandSensorData(hass, command, command_timeout) - add_devices([CommandSensor( + add_entities([CommandSensor( hass, data, name, unit, value_template, json_attributes)], True) diff --git a/homeassistant/components/sensor/cpuspeed.py b/homeassistant/components/sensor/cpuspeed.py index c6a7106663f699..e97972dae3bd0d 100644 --- a/homeassistant/components/sensor/cpuspeed.py +++ b/homeassistant/components/sensor/cpuspeed.py @@ -30,11 +30,11 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the CPU speed sensor.""" name = config.get(CONF_NAME) - add_devices([CpuSpeedSensor(name)], True) + add_entities([CpuSpeedSensor(name)], True) class CpuSpeedSensor(Entity): diff --git a/homeassistant/components/sensor/crimereports.py b/homeassistant/components/sensor/crimereports.py index adf7e3c0fa9719..2f1db42a12788a 100644 --- a/homeassistant/components/sensor/crimereports.py +++ b/homeassistant/components/sensor/crimereports.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Crime Reports platform.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) @@ -50,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): include = config.get(CONF_INCLUDE) exclude = config.get(CONF_EXCLUDE) - add_devices([CrimeReportsSensor( + add_entities([CrimeReportsSensor( hass, name, latitude, longitude, radius, include, exclude)], True) diff --git a/homeassistant/components/sensor/cups.py b/homeassistant/components/sensor/cups.py index 846b109afca9e5..b002d39352aa9b 100644 --- a/homeassistant/components/sensor/cups.py +++ b/homeassistant/components/sensor/cups.py @@ -50,7 +50,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the CUPS sensor.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -71,7 +71,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Printer is not present: %s", printer) continue - add_devices(dev, True) + add_entities(dev, True) class CupsSensor(Entity): diff --git a/homeassistant/components/sensor/currencylayer.py b/homeassistant/components/sensor/currencylayer.py index 4a7face0156733..67c9c7bbf19c51 100644 --- a/homeassistant/components/sensor/currencylayer.py +++ b/homeassistant/components/sensor/currencylayer.py @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Currencylayer sensor.""" base = config.get(CONF_BASE) api_key = config.get(CONF_API_KEY) @@ -54,7 +54,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(CurrencylayerSensor(rest, base, variable)) if 'error' in response.json(): return False - add_devices(sensors, True) + add_entities(sensors, True) class CurrencylayerSensor(Entity): diff --git a/homeassistant/components/sensor/daikin.py b/homeassistant/components/sensor/daikin.py index 2da5cb5cdf040e..3445eb531aa239 100644 --- a/homeassistant/components/sensor/daikin.py +++ b/homeassistant/components/sensor/daikin.py @@ -31,7 +31,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Daikin sensors.""" if discovery_info is not None: host = discovery_info.get('ip') @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for monitored_state in monitored_conditions: sensors.append(DaikinClimateSensor(api, monitored_state, units, name)) - add_devices(sensors, True) + add_entities(sensors, True) class DaikinClimateSensor(Entity): diff --git a/homeassistant/components/sensor/darksky.py b/homeassistant/components/sensor/darksky.py index b2bb7bb4da2354..7ce51454ee5f6b 100644 --- a/homeassistant/components/sensor/darksky.py +++ b/homeassistant/components/sensor/darksky.py @@ -170,7 +170,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dark Sky sensor.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) @@ -211,7 +211,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(DarkSkySensor( forecast_data, variable, name, forecast_day)) - add_devices(sensors, True) + add_entities(sensors, True) class DarkSkySensor(Entity): diff --git a/homeassistant/components/sensor/deconz.py b/homeassistant/components/sensor/deconz.py index 7c492fd496d263..8cb3915dc46e8c 100644 --- a/homeassistant/components/sensor/deconz.py +++ b/homeassistant/components/sensor/deconz.py @@ -6,10 +6,11 @@ """ from homeassistant.components.deconz.const import ( ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DATA_DECONZ, - DATA_DECONZ_ID, DATA_DECONZ_UNSUB) + DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DECONZ_DOMAIN) from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_VOLTAGE, DEVICE_CLASS_BATTERY) from homeassistant.core import callback +from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity from homeassistant.util import slugify @@ -21,13 +22,13 @@ ATTR_EVENT_ID = 'event_id' -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Old way of setting up deCONZ sensors.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the deCONZ sensors.""" @callback def async_add_sensor(sensors): @@ -43,7 +44,7 @@ def async_add_sensor(sensors): entities.append(DeconzBattery(sensor)) else: entities.append(DeconzSensor(sensor)) - async_add_devices(entities, True) + async_add_entities(entities, True) hass.data[DATA_DECONZ_UNSUB].append( async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_sensor)) @@ -134,6 +135,22 @@ def device_state_attributes(self): attr[ATTR_DAYLIGHT] = self._sensor.daylight return attr + @property + def device_info(self): + """Return a device description for device registry.""" + if (self._sensor.uniqueid is None or + self._sensor.uniqueid.count(':') != 7): + return None + serial = self._sensor.uniqueid.split('-', 1)[0] + return { + 'connections': {(CONNECTION_ZIGBEE, serial)}, + 'identifiers': {(DECONZ_DOMAIN, serial)}, + 'manufacturer': self._sensor.manufacturer, + 'model': self._sensor.modelid, + 'name': self._sensor.name, + 'sw_version': self._sensor.swversion, + } + class DeconzBattery(Entity): """Battery class for when a device is only represented as an event.""" @@ -192,3 +209,19 @@ def device_state_attributes(self): ATTR_EVENT_ID: slugify(self._device.name), } return attr + + @property + def device_info(self): + """Return a device description for device registry.""" + if (self._device.uniqueid is None or + self._device.uniqueid.count(':') != 7): + return None + serial = self._device.uniqueid.split('-', 1)[0] + return { + 'connections': {(CONNECTION_ZIGBEE, serial)}, + 'identifiers': {(DECONZ_DOMAIN, serial)}, + 'manufacturer': self._device.manufacturer, + 'model': self._device.modelid, + 'name': self._device.name, + 'sw_version': self._device.swversion, + } diff --git a/homeassistant/components/sensor/deluge.py b/homeassistant/components/sensor/deluge.py index b9109f6428c1df..f56b3ac4b97149 100644 --- a/homeassistant/components/sensor/deluge.py +++ b/homeassistant/components/sensor/deluge.py @@ -42,7 +42,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Deluge sensors.""" from deluge_client import DelugeRPCClient @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in config[CONF_MONITORED_VARIABLES]: dev.append(DelugeSensor(variable, deluge_api, name)) - add_devices(dev) + add_entities(dev) class DelugeSensor(Entity): diff --git a/homeassistant/components/sensor/demo.py b/homeassistant/components/sensor/demo.py index 15cc0ec46aebe4..7921181b742581 100644 --- a/homeassistant/components/sensor/demo.py +++ b/homeassistant/components/sensor/demo.py @@ -10,9 +10,9 @@ from homeassistant.helpers.entity import Entity -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Demo sensors.""" - add_devices([ + add_entities([ DemoSensor('Outside Temperature', 15.6, DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, 12), DemoSensor('Outside Humidity', 54, DEVICE_CLASS_HUMIDITY, '%', None), diff --git a/homeassistant/components/sensor/deutsche_bahn.py b/homeassistant/components/sensor/deutsche_bahn.py index 0e6ab164d4f73a..2cbf9a6d691029 100644 --- a/homeassistant/components/sensor/deutsche_bahn.py +++ b/homeassistant/components/sensor/deutsche_bahn.py @@ -34,13 +34,13 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Deutsche Bahn Sensor.""" start = config.get(CONF_START) destination = config.get(CONF_DESTINATION) only_direct = config.get(CONF_ONLY_DIRECT) - add_devices([DeutscheBahnSensor(start, destination, only_direct)], True) + add_entities([DeutscheBahnSensor(start, destination, only_direct)], True) class DeutscheBahnSensor(Entity): diff --git a/homeassistant/components/sensor/dht.py b/homeassistant/components/sensor/dht.py index e3aaf2f84840fd..0aae10fde64e3a 100644 --- a/homeassistant/components/sensor/dht.py +++ b/homeassistant/components/sensor/dht.py @@ -51,7 +51,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the DHT sensor.""" # pylint: disable=import-error import Adafruit_DHT @@ -83,7 +83,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): except KeyError: pass - add_devices(dev, True) + add_entities(dev, True) class DHTSensor(Entity): diff --git a/homeassistant/components/sensor/discogs.py b/homeassistant/components/sensor/discogs.py index 2920dc025d76c8..6e85c41ac6e009 100644 --- a/homeassistant/components/sensor/discogs.py +++ b/homeassistant/components/sensor/discogs.py @@ -37,7 +37,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Discogs sensor.""" import discogs_client @@ -51,7 +52,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): _LOGGER.error("API token is not valid") return - async_add_devices([DiscogsSensor(identity, name)], True) + async_add_entities([DiscogsSensor(identity, name)], True) class DiscogsSensor(Entity): diff --git a/homeassistant/components/sensor/dnsip.py b/homeassistant/components/sensor/dnsip.py index 7b792d179c5c22..ac681dc691a626 100644 --- a/homeassistant/components/sensor/dnsip.py +++ b/homeassistant/components/sensor/dnsip.py @@ -40,7 +40,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the DNS IP sensor.""" hostname = config.get(CONF_HOSTNAME) ipv6 = config.get(CONF_IPV6) @@ -49,7 +50,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): else: resolver = config.get(CONF_RESOLVER) - async_add_devices([WanIpSensor( + async_add_entities([WanIpSensor( hass, hostname, resolver, ipv6)], True) diff --git a/homeassistant/components/sensor/dovado.py b/homeassistant/components/sensor/dovado.py index 2a78d4ad864887..03c2ad601dfc45 100644 --- a/homeassistant/components/sensor/dovado.py +++ b/homeassistant/components/sensor/dovado.py @@ -54,9 +54,9 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dovado platform for sensors.""" - return Dovado().setup(hass, config, add_devices) + return Dovado().setup(hass, config, add_entities) class Dovado: @@ -67,7 +67,7 @@ def __init__(self): self.state = {} self._dovado = None - def setup(self, hass, config, add_devices): + def setup(self, hass, config, add_entities): """Set up the connection.""" import dovado self._dovado = dovado.Dovado( @@ -90,7 +90,7 @@ def send_sms(service): for sensor in SENSORS: if sensor in config.get(CONF_SENSORS, [sensor]): - add_devices([DovadoSensor(self, sensor)]) + add_entities([DovadoSensor(self, sensor)]) return True diff --git a/homeassistant/components/sensor/dsmr.py b/homeassistant/components/sensor/dsmr.py index 3a1bf1da39e1a5..13b13114150322 100644 --- a/homeassistant/components/sensor/dsmr.py +++ b/homeassistant/components/sensor/dsmr.py @@ -49,7 +49,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the DSMR sensor.""" # Suppress logging logging.getLogger('dsmr_parser').setLevel(logging.ERROR) @@ -160,7 +161,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): DerivativeDSMREntity('Hourly Gas Consumption', gas_obis), ] - async_add_devices(devices) + async_add_entities(devices) def update_entities_telegram(telegram): """Update entities with latest telegram and trigger state update.""" diff --git a/homeassistant/components/sensor/dte_energy_bridge.py b/homeassistant/components/sensor/dte_energy_bridge.py index c1687b6025b391..629b21e4944a1e 100644 --- a/homeassistant/components/sensor/dte_energy_bridge.py +++ b/homeassistant/components/sensor/dte_energy_bridge.py @@ -31,13 +31,13 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the DTE energy bridge sensor.""" name = config.get(CONF_NAME) ip_address = config.get(CONF_IP_ADDRESS) version = config.get(CONF_VERSION, 1) - add_devices([DteEnergyBridgeSensor(ip_address, name, version)], True) + add_entities([DteEnergyBridgeSensor(ip_address, name, version)], True) class DteEnergyBridgeSensor(Entity): diff --git a/homeassistant/components/sensor/dublin_bus_transport.py b/homeassistant/components/sensor/dublin_bus_transport.py index a443c78b2b1725..0fccf7da5a807c 100644 --- a/homeassistant/components/sensor/dublin_bus_transport.py +++ b/homeassistant/components/sensor/dublin_bus_transport.py @@ -56,14 +56,14 @@ def due_in_minutes(timestamp): return str(int(diff.total_seconds() / 60)) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dublin public transport sensor.""" name = config.get(CONF_NAME) stop = config.get(CONF_STOP_ID) route = config.get(CONF_ROUTE) data = PublicTransportData(stop, route) - add_devices([DublinPublicTransportSensor(data, stop, route, name)], True) + add_entities([DublinPublicTransportSensor(data, stop, route, name)], True) class DublinPublicTransportSensor(Entity): diff --git a/homeassistant/components/sensor/duke_energy.py b/homeassistant/components/sensor/duke_energy.py index 458a2929d0b6b3..41d3e5706de05d 100644 --- a/homeassistant/components/sensor/duke_energy.py +++ b/homeassistant/components/sensor/duke_energy.py @@ -27,8 +27,8 @@ LAST_BILL_DAYS_BILLED = "last_bills_days_billed" -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup all Duke Energy meters.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up all Duke Energy meters.""" from pydukeenergy.api import DukeEnergy, DukeEnergyException try: @@ -36,10 +36,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): config[CONF_PASSWORD], update_interval=120) except DukeEnergyException: - _LOGGER.error("Failed to setup Duke Energy") + _LOGGER.error("Failed to set up Duke Energy") return - add_devices([DukeEnergyMeter(meter) for meter in duke.get_meters()]) + add_entities([DukeEnergyMeter(meter) for meter in duke.get_meters()]) class DukeEnergyMeter(Entity): diff --git a/homeassistant/components/sensor/dwd_weather_warnings.py b/homeassistant/components/sensor/dwd_weather_warnings.py index 4f9664617a31d2..4b7e07d4f3a666 100644 --- a/homeassistant/components/sensor/dwd_weather_warnings.py +++ b/homeassistant/components/sensor/dwd_weather_warnings.py @@ -53,7 +53,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the DWD-Weather-Warnings sensor.""" name = config.get(CONF_NAME) region_name = config.get(CONF_REGION_NAME) @@ -63,7 +63,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors = [DwdWeatherWarningsSensor(api, name, condition) for condition in config[CONF_MONITORED_CONDITIONS]] - add_devices(sensors, True) + add_entities(sensors, True) class DwdWeatherWarningsSensor(Entity): diff --git a/homeassistant/components/sensor/dweet.py b/homeassistant/components/sensor/dweet.py index 065c88d8332b45..25bcaa18bab78f 100644 --- a/homeassistant/components/sensor/dweet.py +++ b/homeassistant/components/sensor/dweet.py @@ -34,7 +34,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dweet sensor.""" import dweepy @@ -57,7 +57,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dweet = DweetData(device) - add_devices([DweetSensor(hass, dweet, name, value_template, unit)], True) + add_entities([DweetSensor(hass, dweet, name, value_template, unit)], True) class DweetSensor(Entity): diff --git a/homeassistant/components/sensor/dyson.py b/homeassistant/components/sensor/dyson.py index 91629a18f68b78..0619e3f606997b 100644 --- a/homeassistant/components/sensor/dyson.py +++ b/homeassistant/components/sensor/dyson.py @@ -23,7 +23,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dyson Sensors.""" _LOGGER.debug("Creating new Dyson fans") devices = [] @@ -37,7 +37,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices.append(DysonHumiditySensor(hass, device)) devices.append(DysonTemperatureSensor(hass, device, unit)) devices.append(DysonAirQualitySensor(hass, device)) - add_devices(devices) + add_entities(devices) class DysonSensor(Entity): diff --git a/homeassistant/components/sensor/ebox.py b/homeassistant/components/sensor/ebox.py index 218968ecee8c09..24458e444dc7a9 100644 --- a/homeassistant/components/sensor/ebox.py +++ b/homeassistant/components/sensor/ebox.py @@ -64,7 +64,7 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the EBox sensor.""" username = config.get(CONF_USERNAME) @@ -86,7 +86,7 @@ async def async_setup_platform(hass, config, async_add_devices, for variable in config[CONF_MONITORED_VARIABLES]: sensors.append(EBoxSensor(ebox_data, variable, name)) - async_add_devices(sensors, True) + async_add_entities(sensors, True) class EBoxSensor(Entity): diff --git a/homeassistant/components/sensor/ecobee.py b/homeassistant/components/sensor/ecobee.py index a478f964f5a95d..ae22401a6182b7 100644 --- a/homeassistant/components/sensor/ecobee.py +++ b/homeassistant/components/sensor/ecobee.py @@ -19,7 +19,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Ecobee sensors.""" if discovery_info is None: return @@ -33,7 +33,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev.append(EcobeeSensor(sensor['name'], item['type'], index)) - add_devices(dev, True) + add_entities(dev, True) class EcobeeSensor(Entity): diff --git a/homeassistant/components/sensor/eddystone_temperature.py b/homeassistant/components/sensor/eddystone_temperature.py index 4c209d17d07d38..9e8dc33314a7fd 100644 --- a/homeassistant/components/sensor/eddystone_temperature.py +++ b/homeassistant/components/sensor/eddystone_temperature.py @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Validate configuration, create devices and start monitoring thread.""" bt_device_id = config.get("bt_device_id") @@ -70,7 +70,7 @@ def monitor_start(_service_or_event): _LOGGER.info("Starting scanner for Eddystone beacons") mon.start() - add_devices(devices) + add_entities(devices) mon.start() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, monitor_stop) hass.bus.listen_once(EVENT_HOMEASSISTANT_START, monitor_start) diff --git a/homeassistant/components/sensor/efergy.py b/homeassistant/components/sensor/efergy.py index b9fe294146315d..54666c74f964f5 100644 --- a/homeassistant/components/sensor/efergy.py +++ b/homeassistant/components/sensor/efergy.py @@ -56,7 +56,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Efergy sensor.""" app_token = config.get(CONF_APPTOKEN) utc_offset = str(config.get(CONF_UTC_OFFSET)) @@ -76,7 +76,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): variable[CONF_SENSOR_TYPE], app_token, utc_offset, variable[CONF_PERIOD], variable[CONF_CURRENCY])) - add_devices(dev, True) + add_entities(dev, True) class EfergySensor(Entity): diff --git a/homeassistant/components/sensor/eight_sleep.py b/homeassistant/components/sensor/eight_sleep.py index 5899ef267cb8ae..0fc793d31cae47 100644 --- a/homeassistant/components/sensor/eight_sleep.py +++ b/homeassistant/components/sensor/eight_sleep.py @@ -35,7 +35,7 @@ _LOGGER = logging.getLogger(__name__) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the eight sleep sensors.""" if discovery_info is None: @@ -60,7 +60,7 @@ async def async_setup_platform(hass, config, async_add_devices, else: all_sensors.append(EightUserSensor(name, eight, sensor, units)) - async_add_devices(all_sensors, True) + async_add_entities(all_sensors, True) class EightHeatSensor(EightSleepHeatEntity): diff --git a/homeassistant/components/sensor/eliqonline.py b/homeassistant/components/sensor/eliqonline.py index 6405c707536d88..a2b1a4071c1bea 100644 --- a/homeassistant/components/sensor/eliqonline.py +++ b/homeassistant/components/sensor/eliqonline.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ELIQ Online sensor.""" import eliqonline @@ -52,7 +52,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Could not access the ELIQ Online API: %s", error) return False - add_devices([EliqSensor(api, channel_id, name)], True) + add_entities([EliqSensor(api, channel_id, name)], True) class EliqSensor(Entity): diff --git a/homeassistant/components/sensor/emoncms.py b/homeassistant/components/sensor/emoncms.py index a62eaba7df8459..7546224d4c545a 100644 --- a/homeassistant/components/sensor/emoncms.py +++ b/homeassistant/components/sensor/emoncms.py @@ -61,7 +61,7 @@ def get_id(sensorid, feedtag, feedname, feedid, feeduserid): sensorid, feedtag, feedname, feedid, feeduserid) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Emoncms sensor.""" apikey = config.get(CONF_API_KEY) url = config.get(CONF_URL) @@ -102,7 +102,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(EmonCmsSensor(hass, data, name, value_template, unit_of_measurement, str(sensorid), elem)) - add_devices(sensors) + add_entities(sensors) class EmonCmsSensor(Entity): diff --git a/homeassistant/components/sensor/enocean.py b/homeassistant/components/sensor/enocean.py index 6b0207c248846e..02e6812d5d5d9e 100644 --- a/homeassistant/components/sensor/enocean.py +++ b/homeassistant/components/sensor/enocean.py @@ -25,12 +25,12 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an EnOcean sensor device.""" dev_id = config.get(CONF_ID) devname = config.get(CONF_NAME) - add_devices([EnOceanSensor(dev_id, devname)]) + add_entities([EnOceanSensor(dev_id, devname)]) class EnOceanSensor(enocean.EnOceanDevice, Entity): diff --git a/homeassistant/components/sensor/enphase_envoy.py b/homeassistant/components/sensor/enphase_envoy.py index 3c132fcf7df536..6afe887537c83a 100644 --- a/homeassistant/components/sensor/enphase_envoy.py +++ b/homeassistant/components/sensor/enphase_envoy.py @@ -37,15 +37,15 @@ vol.All(cv.ensure_list, [vol.In(list(SENSORS))])}) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Enphase Envoy sensor.""" ip_address = config[CONF_IP_ADDRESS] monitored_conditions = config[CONF_MONITORED_CONDITIONS] # Iterate through the list of sensors for condition in monitored_conditions: - add_devices([Envoy(ip_address, condition, SENSORS[condition][0], - SENSORS[condition][1])], True) + add_entities([Envoy(ip_address, condition, SENSORS[condition][0], + SENSORS[condition][1])], True) class Envoy(Entity): diff --git a/homeassistant/components/sensor/envirophat.py b/homeassistant/components/sensor/envirophat.py index bf4ee55c446ba4..1c90f5998e8daf 100644 --- a/homeassistant/components/sensor/envirophat.py +++ b/homeassistant/components/sensor/envirophat.py @@ -52,7 +52,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Sense HAT sensor platform.""" try: import envirophat @@ -66,7 +66,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in config[CONF_DISPLAY_OPTIONS]: dev.append(EnvirophatSensor(data, variable)) - add_devices(dev, True) + add_entities(dev, True) class EnvirophatSensor(Entity): diff --git a/homeassistant/components/sensor/envisalink.py b/homeassistant/components/sensor/envisalink.py index 24cb224570c06e..91f99e31b48218 100644 --- a/homeassistant/components/sensor/envisalink.py +++ b/homeassistant/components/sensor/envisalink.py @@ -20,7 +20,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Perform the setup for Envisalink sensor devices.""" configured_partitions = discovery_info['partitions'] @@ -35,7 +36,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): hass.data[DATA_EVL]) devices.append(device) - async_add_devices(devices) + async_add_entities(devices) class EnvisalinkSensor(EnvisalinkDevice, Entity): diff --git a/homeassistant/components/sensor/etherscan.py b/homeassistant/components/sensor/etherscan.py index 360ca4516ce6f5..24cf046cca0762 100644 --- a/homeassistant/components/sensor/etherscan.py +++ b/homeassistant/components/sensor/etherscan.py @@ -30,7 +30,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Etherscan.io sensors.""" address = config.get(CONF_ADDRESS) name = config.get(CONF_NAME) @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if not name: name = "ETH Balance" - add_devices([EtherscanSensor(name, address, token, token_address)], True) + add_entities([EtherscanSensor(name, address, token, token_address)], True) class EtherscanSensor(Entity): diff --git a/homeassistant/components/sensor/fail2ban.py b/homeassistant/components/sensor/fail2ban.py index bf868d49201a08..0f018af819dcec 100644 --- a/homeassistant/components/sensor/fail2ban.py +++ b/homeassistant/components/sensor/fail2ban.py @@ -40,7 +40,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the fail2ban sensor.""" name = config.get(CONF_NAME) jails = config.get(CONF_JAILS) @@ -52,7 +53,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for jail in jails: device_list.append(BanSensor(name, jail, log_parser)) - async_add_devices(device_list, True) + async_add_entities(device_list, True) class BanSensor(Entity): diff --git a/homeassistant/components/sensor/fastdotcom.py b/homeassistant/components/sensor/fastdotcom.py index 65474cd4bf66d3..6624265f60cec3 100644 --- a/homeassistant/components/sensor/fastdotcom.py +++ b/homeassistant/components/sensor/fastdotcom.py @@ -41,11 +41,11 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Fast.com sensor.""" data = SpeedtestData(hass, config) sensor = SpeedtestSensor(data) - add_devices([sensor]) + add_entities([sensor]) def update(call=None): """Update service for manual updates.""" diff --git a/homeassistant/components/sensor/fedex.py b/homeassistant/components/sensor/fedex.py index 991588f07f326c..7d5f47b3631865 100644 --- a/homeassistant/components/sensor/fedex.py +++ b/homeassistant/components/sensor/fedex.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Fedex platform.""" import fedexdeliverymanager @@ -57,7 +57,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.exception("Could not connect to Fedex Delivery Manager") return False - add_devices([FedexSensor(session, name, update_interval)], True) + add_entities([FedexSensor(session, name, update_interval)], True) class FedexSensor(Entity): diff --git a/homeassistant/components/sensor/fido.py b/homeassistant/components/sensor/fido.py index 4f724b5b851d6d..4c027b906a2555 100644 --- a/homeassistant/components/sensor/fido.py +++ b/homeassistant/components/sensor/fido.py @@ -71,7 +71,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Fido sensor.""" username = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) @@ -89,7 +90,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for variable in config[CONF_MONITORED_VARIABLES]: sensors.append(FidoSensor(fido_data, variable, name, number)) - async_add_devices(sensors, True) + async_add_entities(sensors, True) class FidoSensor(Entity): diff --git a/homeassistant/components/sensor/file.py b/homeassistant/components/sensor/file.py index cbdd4eef227728..1839b3566eed99 100644 --- a/homeassistant/components/sensor/file.py +++ b/homeassistant/components/sensor/file.py @@ -33,7 +33,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the file sensor.""" file_path = config.get(CONF_FILE_PATH) name = config.get(CONF_NAME) @@ -44,7 +45,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): value_template.hass = hass if hass.config.is_allowed_path(file_path): - async_add_devices( + async_add_entities( [FileSensor(name, file_path, unit, value_template)], True) else: _LOGGER.error("'%s' is not a whitelisted directory", file_path) diff --git a/homeassistant/components/sensor/filesize.py b/homeassistant/components/sensor/filesize.py index a5a65f9bb5e120..4df858fda23dba 100644 --- a/homeassistant/components/sensor/filesize.py +++ b/homeassistant/components/sensor/filesize.py @@ -26,7 +26,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the file size sensor.""" sensors = [] for path in config.get(CONF_FILE_PATHS): @@ -38,7 +38,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(Filesize(path)) if sensors: - add_devices(sensors, True) + add_entities(sensors, True) class Filesize(Entity): diff --git a/homeassistant/components/sensor/filter.py b/homeassistant/components/sensor/filter.py index 15059b08a179be..1c6e857b92be02 100644 --- a/homeassistant/components/sensor/filter.py +++ b/homeassistant/components/sensor/filter.py @@ -114,7 +114,7 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the template sensors.""" name = config.get(CONF_NAME) @@ -124,7 +124,7 @@ async def async_setup_platform(hass, config, async_add_devices, entity=entity_id, **_filter) for _filter in config[CONF_FILTERS]] - async_add_devices([SensorFilter(name, entity_id, filters)]) + async_add_entities([SensorFilter(name, entity_id, filters)]) class SensorFilter(Entity): diff --git a/homeassistant/components/sensor/fints.py b/homeassistant/components/sensor/fints.py index ef064e842282e4..1704c13b5aa424 100644 --- a/homeassistant/components/sensor/fints.py +++ b/homeassistant/components/sensor/fints.py @@ -50,7 +50,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the sensors. Login to the bank and get a list of existing accounts. Create a @@ -98,7 +98,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.debug('Creating holdings %s for bank %s', account.accountnumber, fints_name) - add_devices(accounts, True) + add_entities(accounts, True) class FinTsClient: @@ -108,7 +108,7 @@ class FinTsClient: """ def __init__(self, credentials: BankCredentials, name: str): - """Constructor for class FinTsClient.""" + """Initialize a FinTsClient.""" self._credentials = credentials self.name = name @@ -151,14 +151,14 @@ def detect_accounts(self): class FinTsAccount(Entity): - """Sensor for a FinTS balanc account. + """Sensor for a FinTS balance account. A balance account contains an amount of money (=balance). The amount may also be negative. """ def __init__(self, client: FinTsClient, account, name: str) -> None: - """Constructor for class FinTsAccount.""" + """Initialize a FinTs balance account.""" self._client = client # type: FinTsClient self._account = account self._name = name # type: str @@ -167,7 +167,10 @@ def __init__(self, client: FinTsClient, account, name: str) -> None: @property def should_poll(self) -> bool: - """Data needs to be polled from the bank servers.""" + """Return True. + + Data needs to be polled from the bank servers. + """ return True def update(self) -> None: @@ -218,7 +221,7 @@ class FinTsHoldingsAccount(Entity): """ def __init__(self, client: FinTsClient, account, name: str) -> None: - """Constructor for class FinTsHoldingsAccount.""" + """Initialize a FinTs holdings account.""" self._client = client # type: FinTsClient self._name = name # type: str self._account = account @@ -227,7 +230,10 @@ def __init__(self, client: FinTsClient, account, name: str) -> None: @property def should_poll(self) -> bool: - """Data needs to be polled from the bank servers.""" + """Return True. + + Data needs to be polled from the bank servers. + """ return True def update(self) -> None: diff --git a/homeassistant/components/sensor/fitbit.py b/homeassistant/components/sensor/fitbit.py index 87bd735a03df1d..f5b44d577a7cb8 100644 --- a/homeassistant/components/sensor/fitbit.py +++ b/homeassistant/components/sensor/fitbit.py @@ -151,7 +151,7 @@ }) -def request_app_setup(hass, config, add_devices, config_path, +def request_app_setup(hass, config, add_entities, config_path, discovery_info=None): """Assist user with configuring the Fitbit dev application.""" configurator = hass.components.configurator @@ -167,9 +167,9 @@ def fitbit_configuration_callback(callback_data): configurator.notify_errors(_CONFIGURING['fitbit'], error_msg) else: - setup_platform(hass, config, add_devices, discovery_info) + setup_platform(hass, config, add_entities, discovery_info) else: - setup_platform(hass, config, add_devices, discovery_info) + setup_platform(hass, config, add_entities, discovery_info) start_url = "{}{}".format(hass.config.api.base_url, FITBIT_AUTH_CALLBACK_PATH) @@ -215,19 +215,19 @@ def fitbit_configuration_callback(callback_data): ) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Fitbit sensor.""" config_path = hass.config.path(FITBIT_CONFIG_FILE) if os.path.isfile(config_path): config_file = load_json(config_path) if config_file == DEFAULT_CONFIG: request_app_setup( - hass, config, add_devices, config_path, discovery_info=None) + hass, config, add_entities, config_path, discovery_info=None) return False else: save_json(config_path, DEFAULT_CONFIG) request_app_setup( - hass, config, add_devices, config_path, discovery_info=None) + hass, config, add_entities, config_path, discovery_info=None) return False if "fitbit" in _CONFIGURING: @@ -276,7 +276,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev.append(FitbitSensor( authd_client, config_path, resource, hass.config.units.is_metric, clock_format)) - add_devices(dev, True) + add_entities(dev, True) else: oauth = fitbit.api.FitbitOauth2Client( @@ -293,7 +293,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): hass.http.register_redirect(FITBIT_AUTH_START, fitbit_auth_start_url) hass.http.register_view(FitbitAuthCallbackView( - config, add_devices, oauth)) + config, add_entities, oauth)) request_oauth_completion(hass) @@ -305,10 +305,10 @@ class FitbitAuthCallbackView(HomeAssistantView): url = FITBIT_AUTH_CALLBACK_PATH name = 'api:fitbit:callback' - def __init__(self, config, add_devices, oauth): + def __init__(self, config, add_entities, oauth): """Initialize the OAuth callback view.""" self.config = config - self.add_devices = add_devices + self.add_entities = add_entities self.oauth = oauth @callback @@ -368,7 +368,8 @@ def get(self, request): } save_json(hass.config.path(FITBIT_CONFIG_FILE), config_contents) - hass.async_add_job(setup_platform, hass, self.config, self.add_devices) + hass.async_add_job(setup_platform, hass, self.config, + self.add_entities) return html_response diff --git a/homeassistant/components/sensor/fixer.py b/homeassistant/components/sensor/fixer.py index 5a6f8da79b2246..1bdd9e71272399 100644 --- a/homeassistant/components/sensor/fixer.py +++ b/homeassistant/components/sensor/fixer.py @@ -38,7 +38,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Fixer.io sensor.""" from fixerio import Fixerio, exceptions @@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return data = ExchangeData(target, api_key) - add_devices([ExchangeRateSensor(data, name, target)], True) + add_entities([ExchangeRateSensor(data, name, target)], True) class ExchangeRateSensor(Entity): diff --git a/homeassistant/components/sensor/folder.py b/homeassistant/components/sensor/folder.py index 2b5f3dd4309230..8101bbd059aaf9 100644 --- a/homeassistant/components/sensor/folder.py +++ b/homeassistant/components/sensor/folder.py @@ -42,7 +42,7 @@ def get_size(files_list): return sum(size_list) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the folder sensor.""" path = config.get(CONF_FOLDER_PATHS) @@ -50,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("folder %s is not valid or allowed", path) else: folder = Folder(path, config.get(CONF_FILTER)) - add_devices([folder], True) + add_entities([folder], True) class Folder(Entity): diff --git a/homeassistant/components/sensor/foobot.py b/homeassistant/components/sensor/foobot.py index d247a90e93afca..62139c53c4b8fb 100644 --- a/homeassistant/components/sensor/foobot.py +++ b/homeassistant/components/sensor/foobot.py @@ -52,7 +52,7 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the devices associated with the account.""" from foobot_async import FoobotClient @@ -82,7 +82,7 @@ async def async_setup_platform(hass, config, async_add_devices, except FoobotClient.ClientError: _LOGGER.error('Failed to fetch data from foobot servers.') return - async_add_devices(dev, True) + async_add_entities(dev, True) class FoobotSensor(Entity): diff --git a/homeassistant/components/sensor/fritzbox_callmonitor.py b/homeassistant/components/sensor/fritzbox_callmonitor.py index 3da9c512ebdd72..c60d06da0395aa 100644 --- a/homeassistant/components/sensor/fritzbox_callmonitor.py +++ b/homeassistant/components/sensor/fritzbox_callmonitor.py @@ -56,7 +56,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Fritz!Box call monitor sensor platform.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) @@ -77,7 +77,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensor = FritzBoxCallSensor(name=name, phonebook=phonebook) - add_devices([sensor]) + add_entities([sensor]) monitor = FritzBoxCallMonitor(host=host, port=port, sensor=sensor) monitor.connect() diff --git a/homeassistant/components/sensor/fritzbox_netmonitor.py b/homeassistant/components/sensor/fritzbox_netmonitor.py index b980323abe1e48..356c1424012a0f 100644 --- a/homeassistant/components/sensor/fritzbox_netmonitor.py +++ b/homeassistant/components/sensor/fritzbox_netmonitor.py @@ -48,7 +48,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the FRITZ!Box monitor sensors.""" # pylint: disable=import-error import fritzconnection as fc @@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return 1 _LOGGER.info("Successfully connected to FRITZ!Box") - add_devices([FritzboxMonitorSensor(name, fstatus)], True) + add_entities([FritzboxMonitorSensor(name, fstatus)], True) class FritzboxMonitorSensor(Entity): diff --git a/homeassistant/components/sensor/gearbest.py b/homeassistant/components/sensor/gearbest.py index d71419ba79e69e..5521e9a644c1ec 100644 --- a/homeassistant/components/sensor/gearbest.py +++ b/homeassistant/components/sensor/gearbest.py @@ -43,7 +43,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Gearbest sensor.""" from gearbest_parser import CurrencyConverter currency = config.get(CONF_CURRENCY) @@ -68,7 +68,7 @@ def currency_update(event_time): currency_update, MIN_TIME_BETWEEN_CURRENCY_UPDATES) - add_devices(sensors, True) + add_entities(sensors, True) class GearbestSensor(Entity): diff --git a/homeassistant/components/sensor/geizhals.py b/homeassistant/components/sensor/geizhals.py index 06062b26b004a9..2c7325866acf9c 100644 --- a/homeassistant/components/sensor/geizhals.py +++ b/homeassistant/components/sensor/geizhals.py @@ -15,14 +15,16 @@ from homeassistant.helpers.entity import Entity from homeassistant.const import (CONF_DOMAIN, CONF_NAME) -REQUIREMENTS = ['beautifulsoup4==4.6.1'] +REQUIREMENTS = ['beautifulsoup4==4.6.3'] + _LOGGER = logging.getLogger(__name__) -CONF_PRODUCT_ID = 'product_id' CONF_DESCRIPTION = 'description' +CONF_PRODUCT_ID = 'product_id' CONF_REGEX = 'regex' ICON = 'mdi:coin' + MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=120) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ @@ -39,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Geizwatch sensor.""" name = config.get(CONF_NAME) description = config.get(CONF_DESCRIPTION) @@ -47,15 +49,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): domain = config.get(CONF_DOMAIN) regex = config.get(CONF_REGEX) - add_devices([Geizwatch(name, description, product_id, domain, regex)], - True) + add_entities([Geizwatch(name, description, product_id, domain, regex)], + True) class Geizwatch(Entity): """Implementation of Geizwatch.""" - def __init__(self, name, description, product_id, domain, - regex): + def __init__(self, name, description, product_id, domain, regex): """Initialize the sensor.""" self._name = name self.description = description diff --git a/homeassistant/components/sensor/geo_rss_events.py b/homeassistant/components/sensor/geo_rss_events.py index b79e6e69adf492..1ba0ce2e0654a6 100644 --- a/homeassistant/components/sensor/geo_rss_events.py +++ b/homeassistant/components/sensor/geo_rss_events.py @@ -54,7 +54,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the GeoRSS component.""" home_latitude = hass.config.latitude home_longitude = hass.config.longitude @@ -81,7 +81,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): device = GeoRssServiceSensor(category, data, name, unit_of_measurement) devices.append(device) - add_devices(devices, True) + add_entities(devices, True) class GeoRssServiceSensor(Entity): diff --git a/homeassistant/components/sensor/gitter.py b/homeassistant/components/sensor/gitter.py index 907af07a2dba24..97cd3f662d519a 100644 --- a/homeassistant/components/sensor/gitter.py +++ b/homeassistant/components/sensor/gitter.py @@ -33,7 +33,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Gitter sensor.""" from gitterpy.client import GitterClient from gitterpy.errors import GitterTokenError @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Token is not valid") return - add_devices([GitterSensor(gitter, room, name, username)], True) + add_entities([GitterSensor(gitter, room, name, username)], True) class GitterSensor(Entity): diff --git a/homeassistant/components/sensor/glances.py b/homeassistant/components/sensor/glances.py index a6dfd89e45a137..c2127827ebdc8b 100644 --- a/homeassistant/components/sensor/glances.py +++ b/homeassistant/components/sensor/glances.py @@ -4,25 +4,30 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.glances/ """ -import logging from datetime import timedelta +import logging -import requests import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - CONF_HOST, CONF_PORT, CONF_NAME, CONF_RESOURCES, TEMP_CELSIUS) + CONF_HOST, CONF_NAME, CONF_PORT, CONF_RESOURCES, TEMP_CELSIUS) +from homeassistant.exceptions import PlatformNotReady +from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle +REQUIREMENTS = ['glances_api==0.1.0'] + _LOGGER = logging.getLogger(__name__) -_RESOURCE = 'api/2/all' + +CONF_VERSION = 'version' DEFAULT_HOST = 'localhost' DEFAULT_NAME = 'Glances' DEFAULT_PORT = '61208' +DEFAULT_VERSION = 2 MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1) @@ -53,33 +58,43 @@ vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, vol.Optional(CONF_RESOURCES, default=['disk_use']): vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), + vol.Optional(CONF_VERSION, default=DEFAULT_VERSION): vol.In([2, 3]), }) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up the Glances sensor.""" +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the Glances sensors.""" + from glances_api import Glances + name = config.get(CONF_NAME) host = config.get(CONF_HOST) port = config.get(CONF_PORT) - url = 'http://{}:{}/{}'.format(host, port, _RESOURCE) + version = config.get(CONF_VERSION) var_conf = config.get(CONF_RESOURCES) - rest = GlancesData(url) - rest.update() + session = async_get_clientsession(hass) + glances = GlancesData( + Glances(hass.loop, session, host=host, port=port, version=version)) + + await glances.async_update() + + if glances.api.data is None: + raise PlatformNotReady dev = [] for resource in var_conf: - dev.append(GlancesSensor(rest, name, resource)) + dev.append(GlancesSensor(glances, name, resource)) - add_devices(dev, True) + async_add_entities(dev, True) class GlancesSensor(Entity): """Implementation of a Glances sensor.""" - def __init__(self, rest, name, sensor_type): + def __init__(self, glances, name, sensor_type): """Initialize the sensor.""" - self.rest = rest + self.glances = glances self._name = name self.type = sensor_type self._state = None @@ -103,17 +118,17 @@ def unit_of_measurement(self): @property def available(self): """Could the device be accessed during the last update call.""" - return self.rest.data is not None + return self.glances.available @property def state(self): """Return the state of the resources.""" return self._state - def update(self): + async def async_update(self): """Get the latest data from REST API.""" - self.rest.update() - value = self.rest.data + await self.glances.async_update() + value = self.glances.api.data if value is not None: if self.type == 'disk_use_percent': @@ -179,17 +194,19 @@ def update(self): class GlancesData: """The class for handling the data retrieval.""" - def __init__(self, resource): + def __init__(self, api): """Initialize the data object.""" - self._resource = resource - self.data = {} + self.api = api + self.available = True @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self): + async def async_update(self): """Get the latest data from the Glances REST API.""" + from glances_api.exceptions import GlancesApiError + try: - response = requests.get(self._resource, timeout=10) - self.data = response.json() - except requests.exceptions.ConnectionError: - _LOGGER.error("Connection error: %s", self._resource) - self.data = None + await self.api.get_data() + self.available = True + except GlancesApiError: + _LOGGER.error("Unable to fetch data from Glances") + self.available = False diff --git a/homeassistant/components/sensor/google_travel_time.py b/homeassistant/components/sensor/google_travel_time.py index d14a70ecc84cf9..a69b865f30bbee 100644 --- a/homeassistant/components/sensor/google_travel_time.py +++ b/homeassistant/components/sensor/google_travel_time.py @@ -79,7 +79,7 @@ def convert_time_to_utc(timestr): return dt_util.as_timestamp(combined) -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up the Google travel time platform.""" def run_setup(event): """Delay the setup until Home Assistant is fully initialized. @@ -112,7 +112,7 @@ def run_setup(event): hass, name, api_key, origin, destination, options) if sensor.valid_api_connection: - add_devices_callback([sensor]) + add_entities_callback([sensor]) # Wait until start event is sent to load this component. hass.bus.listen_once(EVENT_HOMEASSISTANT_START, run_setup) diff --git a/homeassistant/components/sensor/google_wifi.py b/homeassistant/components/sensor/google_wifi.py index cc5461ed548124..35db8f7c9e82ee 100644 --- a/homeassistant/components/sensor/google_wifi.py +++ b/homeassistant/components/sensor/google_wifi.py @@ -76,7 +76,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Google Wifi sensor.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) @@ -87,7 +87,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for condition in conditions: dev.append(GoogleWifiSensor(api, name, condition)) - add_devices(dev, True) + add_entities(dev, True) class GoogleWifiSensor(Entity): diff --git a/homeassistant/components/sensor/gpsd.py b/homeassistant/components/sensor/gpsd.py index f463d0fb8d1ad0..0504cf7a511725 100644 --- a/homeassistant/components/sensor/gpsd.py +++ b/homeassistant/components/sensor/gpsd.py @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the GPSD component.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Not able to connect to GPSD") return False - add_devices([GpsdSensor(hass, name, host, port)]) + add_entities([GpsdSensor(hass, name, host, port)]) class GpsdSensor(Entity): diff --git a/homeassistant/components/sensor/gtfs.py b/homeassistant/components/sensor/gtfs.py index 120fe8fdb225bd..633a50f15c1530 100644 --- a/homeassistant/components/sensor/gtfs.py +++ b/homeassistant/components/sensor/gtfs.py @@ -151,7 +151,7 @@ def get_next_departure(sched, start_station_id, end_station_id, offset): } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the GTFS sensor.""" gtfs_dir = hass.config.path(DEFAULT_PATH) data = config.get(CONF_DATA) @@ -179,7 +179,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if not gtfs.feeds: pygtfs.append_feed(gtfs, os.path.join(gtfs_dir, data)) - add_devices([GTFSDepartureSensor(gtfs, name, origin, destination, offset)]) + add_entities([ + GTFSDepartureSensor(gtfs, name, origin, destination, offset)]) class GTFSDepartureSensor(Entity): diff --git a/homeassistant/components/sensor/haveibeenpwned.py b/homeassistant/components/sensor/haveibeenpwned.py index bc79c4d0c1dac0..9428eaea00e7d9 100644 --- a/homeassistant/components/sensor/haveibeenpwned.py +++ b/homeassistant/components/sensor/haveibeenpwned.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the HaveIBeenPwned sensor.""" emails = config.get(CONF_EMAIL) data = HaveIBeenPwnedData(emails) @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for email in emails: devices.append(HaveIBeenPwnedSensor(data, hass, email)) - add_devices(devices) + add_entities(devices) # To make sure we get initial data for the sensors ignoring the normal # throttle of 15 minutes but using an update throttle of 5 seconds diff --git a/homeassistant/components/sensor/hddtemp.py b/homeassistant/components/sensor/hddtemp.py index f8afe9c7637023..52514a2de39729 100644 --- a/homeassistant/components/sensor/hddtemp.py +++ b/homeassistant/components/sensor/hddtemp.py @@ -37,7 +37,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the HDDTemp sensor.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) @@ -54,7 +54,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for disk in disks: dev.append(HddTempSensor(name, disk, hddtemp)) - add_devices(dev, True) + add_entities(dev, True) class HddTempSensor(Entity): diff --git a/homeassistant/components/sensor/history_stats.py b/homeassistant/components/sensor/history_stats.py index c3d0fe8f1b6eb8..c76d2cefca0d46 100644 --- a/homeassistant/components/sensor/history_stats.py +++ b/homeassistant/components/sensor/history_stats.py @@ -67,7 +67,7 @@ def exactly_two_period_keys(conf): # noinspection PyUnusedLocal -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the History Stats sensor.""" entity_id = config.get(CONF_ENTITY_ID) entity_state = config.get(CONF_STATE) @@ -81,8 +81,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if template is not None: template.hass = hass - add_devices([HistoryStatsSensor(hass, entity_id, entity_state, start, end, - duration, sensor_type, name)]) + add_entities([HistoryStatsSensor(hass, entity_id, entity_state, start, end, + duration, sensor_type, name)]) return True diff --git a/homeassistant/components/sensor/hive.py b/homeassistant/components/sensor/hive.py index 2d609070415883..3f900320801402 100644 --- a/homeassistant/components/sensor/hive.py +++ b/homeassistant/components/sensor/hive.py @@ -16,7 +16,7 @@ 'Hive_OutsideTemperature': 'mdi:thermometer'} -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Hive sensor devices.""" if discovery_info is None: return @@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if (discovery_info["HA_DeviceType"] == "Hub_OnlineStatus" or discovery_info["HA_DeviceType"] == "Hive_OutsideTemperature"): - add_devices([HiveSensorEntity(session, discovery_info)]) + add_entities([HiveSensorEntity(session, discovery_info)]) class HiveSensorEntity(Entity): diff --git a/homeassistant/components/sensor/homematic.py b/homeassistant/components/sensor/homematic.py index 60741a9f3c8475..8495286c143f16 100644 --- a/homeassistant/components/sensor/homematic.py +++ b/homeassistant/components/sensor/homematic.py @@ -5,8 +5,9 @@ https://home-assistant.io/components/sensor.homematic/ """ import logging + +from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice from homeassistant.const import STATE_UNKNOWN -from homeassistant.components.homematic import HMDevice, ATTR_DISCOVER_DEVICES _LOGGER = logging.getLogger(__name__) @@ -69,8 +70,8 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up the HomeMatic platform.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the HomeMatic sensor platform.""" if discovery_info is None: return @@ -79,11 +80,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): new_device = HMSensor(conf) devices.append(new_device) - add_devices(devices) + add_entities(devices) class HMSensor(HMDevice): - """Represents various HomeMatic sensors in Home Assistant.""" + """Representation of a HomeMatic sensor.""" @property def state(self): @@ -111,4 +112,4 @@ def _init_data_struct(self): if self._state: self._data.update({self._state: STATE_UNKNOWN}) else: - _LOGGER.critical("Can't initialize sensor %s", self._name) + _LOGGER.critical("Unable to initialize sensor: %s", self._name) diff --git a/homeassistant/components/sensor/homematicip_cloud.py b/homeassistant/components/sensor/homematicip_cloud.py index 7292e3b2f40488..2b8365b8f64cf7 100644 --- a/homeassistant/components/sensor/homematicip_cloud.py +++ b/homeassistant/components/sensor/homematicip_cloud.py @@ -1,18 +1,17 @@ """ -Support for HomematicIP sensors. +Support for HomematicIP Cloud sensors. -For more details about this component, please refer to the documentation at +For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.homematicip_cloud/ """ - import logging from homeassistant.components.homematicip_cloud import ( - HomematicipGenericDevice, DOMAIN as HMIPC_DOMAIN, - HMIPC_HAPID) + HMIPC_HAPID, HomematicipGenericDevice) +from homeassistant.components.homematicip_cloud import DOMAIN as HMIPC_DOMAIN from homeassistant.const import ( - TEMP_CELSIUS, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, - DEVICE_CLASS_ILLUMINANCE) + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, + TEMP_CELSIUS) _LOGGER = logging.getLogger(__name__) @@ -25,14 +24,14 @@ ATTR_HUMIDITY = 'humidity' -async def async_setup_platform(hass, config, async_add_devices, - discovery_info=None): - """Set up the HomematicIP sensors devices.""" +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the HomematicIP Cloud sensors devices.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): - """Set up the HomematicIP sensors from a config entry.""" +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up the HomematicIP Cloud sensors from a config entry.""" from homematicip.device import ( HeatingThermostat, TemperatureHumiditySensorWithoutDisplay, TemperatureHumiditySensorDisplay, MotionDetectorIndoor) @@ -50,11 +49,11 @@ async def async_setup_entry(hass, config_entry, async_add_devices): devices.append(HomematicipIlluminanceSensor(home, device)) if devices: - async_add_devices(devices) + async_add_entities(devices) class HomematicipAccesspointStatus(HomematicipGenericDevice): - """Representation of an HomeMaticIP access point.""" + """Representation of an HomeMaticIP Cloud access point.""" def __init__(self, home): """Initialize access point device.""" @@ -82,7 +81,7 @@ def unit_of_measurement(self): class HomematicipHeatingThermostat(HomematicipGenericDevice): - """MomematicIP heating thermostat representation.""" + """Represenation of a HomematicIP heating thermostat device.""" def __init__(self, home, device): """Initialize heating thermostat device.""" @@ -115,7 +114,7 @@ def unit_of_measurement(self): class HomematicipHumiditySensor(HomematicipGenericDevice): - """MomematicIP humidity device.""" + """Represenation of a HomematicIP Cloud humidity device.""" def __init__(self, home, device): """Initialize the thermometer device.""" @@ -138,7 +137,7 @@ def unit_of_measurement(self): class HomematicipTemperatureSensor(HomematicipGenericDevice): - """MomematicIP the thermometer device.""" + """Representation of a HomematicIP Cloud thermometer device.""" def __init__(self, home, device): """Initialize the thermometer device.""" @@ -161,7 +160,7 @@ def unit_of_measurement(self): class HomematicipIlluminanceSensor(HomematicipGenericDevice): - """MomematicIP the thermometer device.""" + """Represenation of a HomematicIP Illuminance device.""" def __init__(self, home, device): """Initialize the device.""" diff --git a/homeassistant/components/sensor/hp_ilo.py b/homeassistant/components/sensor/hp_ilo.py index 98ee83f8958355..ac2d5ccb109840 100644 --- a/homeassistant/components/sensor/hp_ilo.py +++ b/homeassistant/components/sensor/hp_ilo.py @@ -59,7 +59,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the HP ILO sensor.""" hostname = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -89,7 +89,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): CONF_UNIT_OF_MEASUREMENT)) devices.append(new_device) - add_devices(devices, True) + add_entities(devices, True) class HpIloSensor(Entity): diff --git a/homeassistant/components/sensor/htu21d.py b/homeassistant/components/sensor/htu21d.py index 870ca0ca160189..28ab933ff6c9e2 100644 --- a/homeassistant/components/sensor/htu21d.py +++ b/homeassistant/components/sensor/htu21d.py @@ -41,7 +41,8 @@ # pylint: disable=import-error @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the HTU21D sensor.""" import smbus from i2csense.htu21d import HTU21D @@ -63,7 +64,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): dev = [HTU21DSensor(sensor_handler, name, SENSOR_TEMPERATURE, temp_unit), HTU21DSensor(sensor_handler, name, SENSOR_HUMIDITY, '%')] - async_add_devices(dev) + async_add_entities(dev) class HTU21DHandler: diff --git a/homeassistant/components/sensor/hydrawise.py b/homeassistant/components/sensor/hydrawise.py index fea2780da0756e..bff99ddc50140a 100644 --- a/homeassistant/components/sensor/hydrawise.py +++ b/homeassistant/components/sensor/hydrawise.py @@ -24,7 +24,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for a Hydrawise device.""" hydrawise = hass.data[DATA_HYDRAWISE].data @@ -33,7 +33,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for zone in hydrawise.relays: sensors.append(HydrawiseSensor(zone, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) class HydrawiseSensor(HydrawiseEntity): diff --git a/homeassistant/components/sensor/hydroquebec.py b/homeassistant/components/sensor/hydroquebec.py index db75d51fbad5c0..44b96bab1e91b0 100644 --- a/homeassistant/components/sensor/hydroquebec.py +++ b/homeassistant/components/sensor/hydroquebec.py @@ -94,7 +94,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the HydroQuebec sensor.""" # Create a data fetcher to support all of the configured sensors. Then make # the first call to init the data. @@ -118,7 +119,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for variable in config[CONF_MONITORED_VARIABLES]: sensors.append(HydroQuebecSensor(hydroquebec_data, variable, name)) - async_add_devices(sensors, True) + async_add_entities(sensors, True) class HydroQuebecSensor(Entity): diff --git a/homeassistant/components/sensor/ihc.py b/homeassistant/components/sensor/ihc.py index 2dcf2c3f7bee84..f5140838a7a30a 100644 --- a/homeassistant/components/sensor/ihc.py +++ b/homeassistant/components/sensor/ihc.py @@ -30,8 +30,8 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the ihc sensor platform.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the IHC sensor platform.""" ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER] info = hass.data[IHC_DATA][IHC_INFO] devices = [] @@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensor = IHCSensor(ihc_controller, name, ihc_id, info, unit) devices.append(sensor) - add_devices(devices) + add_entities(devices) class IHCSensor(IHCDevice, Entity): @@ -77,6 +77,6 @@ def unit_of_measurement(self): return self._unit_of_measurement def on_ihc_change(self, ihc_id, value): - """Callback when ihc resource changes.""" + """Handle IHC resource change.""" self._state = value self.schedule_update_ha_state() diff --git a/homeassistant/components/sensor/imap.py b/homeassistant/components/sensor/imap.py index 647a40295ac684..2ea1fd576e6748 100644 --- a/homeassistant/components/sensor/imap.py +++ b/homeassistant/components/sensor/imap.py @@ -41,7 +41,7 @@ async def async_setup_platform(hass, config, - async_add_devices, + async_add_entities, discovery_info=None): """Set up the IMAP platform.""" sensor = ImapSensor(config.get(CONF_NAME), @@ -54,7 +54,7 @@ async def async_setup_platform(hass, raise PlatformNotReady hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, sensor.shutdown()) - async_add_devices([sensor], True) + async_add_entities([sensor], True) class ImapSensor(Entity): diff --git a/homeassistant/components/sensor/imap_email_content.py b/homeassistant/components/sensor/imap_email_content.py index a1a604df3e496b..ed535b69a1d034 100644 --- a/homeassistant/components/sensor/imap_email_content.py +++ b/homeassistant/components/sensor/imap_email_content.py @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Email sensor platform.""" reader = EmailReader( config.get(CONF_USERNAME), config.get(CONF_PASSWORD), @@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): config.get(CONF_SENDERS), value_template) if sensor.connected: - add_devices([sensor], True) + add_entities([sensor], True) else: return False diff --git a/homeassistant/components/sensor/influxdb.py b/homeassistant/components/sensor/influxdb.py index 8bfbaf49837f30..87e2bdb5c9cff2 100644 --- a/homeassistant/components/sensor/influxdb.py +++ b/homeassistant/components/sensor/influxdb.py @@ -63,7 +63,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the InfluxDB component.""" influx_conf = { 'host': config[CONF_HOST], @@ -81,7 +81,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if sensor.connected: dev.append(sensor) - add_devices(dev, True) + add_entities(dev, True) class InfluxSensor(Entity): diff --git a/homeassistant/components/sensor/insteon.py b/homeassistant/components/sensor/insteon.py new file mode 100644 index 00000000000000..5b8a6b9a977960 --- /dev/null +++ b/homeassistant/components/sensor/insteon.py @@ -0,0 +1,37 @@ +""" +Support for INSTEON dimmers via PowerLinc Modem. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/sensor.insteon/ +""" +import asyncio +import logging + +from homeassistant.components.insteon import InsteonEntity +from homeassistant.helpers.entity import Entity + +DEPENDENCIES = ['insteon'] + +_LOGGER = logging.getLogger(__name__) + + +@asyncio.coroutine +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): + """Set up the INSTEON device class for the hass platform.""" + insteon_modem = hass.data['insteon'].get('modem') + + address = discovery_info['address'] + device = insteon_modem.devices[address] + state_key = discovery_info['state_key'] + + _LOGGER.debug('Adding device %s entity %s to Sensor platform', + device.address.hex, device.states[state_key].name) + + new_entity = InsteonSensorDevice(device, state_key) + + async_add_entities([new_entity]) + + +class InsteonSensorDevice(InsteonEntity, Entity): + """A Class for an Insteon device.""" diff --git a/homeassistant/components/sensor/insteon_plm.py b/homeassistant/components/sensor/insteon_plm.py deleted file mode 100644 index 61f5877ed78159..00000000000000 --- a/homeassistant/components/sensor/insteon_plm.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -Support for INSTEON dimmers via PowerLinc Modem. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor.insteon_plm/ -""" -import asyncio -import logging - -from homeassistant.components.insteon_plm import InsteonPLMEntity -from homeassistant.helpers.entity import Entity - -DEPENDENCIES = ['insteon_plm'] - -_LOGGER = logging.getLogger(__name__) - - -@asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): - """Set up the INSTEON PLM device class for the hass platform.""" - plm = hass.data['insteon_plm'].get('plm') - - address = discovery_info['address'] - device = plm.devices[address] - state_key = discovery_info['state_key'] - - _LOGGER.debug('Adding device %s entity %s to Sensor platform', - device.address.hex, device.states[state_key].name) - - new_entity = InsteonPLMSensorDevice(device, state_key) - - async_add_devices([new_entity]) - - -class InsteonPLMSensorDevice(InsteonPLMEntity, Entity): - """A Class for an Insteon device.""" diff --git a/homeassistant/components/sensor/ios.py b/homeassistant/components/sensor/ios.py index 1fd556b17c0c7b..f775381c4ec6b2 100644 --- a/homeassistant/components/sensor/ios.py +++ b/homeassistant/components/sensor/ios.py @@ -19,7 +19,7 @@ DEFAULT_ICON_STATE = 'mdi:power-plug' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the iOS sensor.""" if discovery_info is None: return @@ -28,7 +28,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for sensor_type in ('level', 'state'): dev.append(IOSSensor(sensor_type, device_name, device)) - add_devices(dev, True) + add_entities(dev, True) class IOSSensor(Entity): diff --git a/homeassistant/components/sensor/iota.py b/homeassistant/components/sensor/iota.py index 2e3e58a18f3e43..961cd119d789eb 100644 --- a/homeassistant/components/sensor/iota.py +++ b/homeassistant/components/sensor/iota.py @@ -24,7 +24,7 @@ SCAN_INTERVAL = timedelta(minutes=3) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the IOTA sensor.""" # Add sensors for wallet balance iota_config = discovery_info @@ -34,7 +34,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Add sensor for node information sensors.append(IotaNodeSensor(iota_config=iota_config)) - add_devices(sensors) + add_entities(sensors) class IotaBalanceSensor(IotaDevice): diff --git a/homeassistant/components/sensor/iperf3.py b/homeassistant/components/sensor/iperf3.py index 8e030390f501b1..0eb6dfaa00c69f 100644 --- a/homeassistant/components/sensor/iperf3.py +++ b/homeassistant/components/sensor/iperf3.py @@ -64,7 +64,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Iperf3 sensor.""" if hass.data.get(IPERF3_DATA) is None: hass.data[IPERF3_DATA] = {} @@ -81,7 +81,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensor)) hass.data[IPERF3_DATA]['sensors'].extend(dev) - add_devices(dev) + add_entities(dev) def _service_handler(service): """Update service for manual updates.""" diff --git a/homeassistant/components/sensor/irish_rail_transport.py b/homeassistant/components/sensor/irish_rail_transport.py index 38fd910260ad6d..10f4004ae748a0 100644 --- a/homeassistant/components/sensor/irish_rail_transport.py +++ b/homeassistant/components/sensor/irish_rail_transport.py @@ -49,7 +49,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Irish Rail transport sensor.""" from pyirishrail.pyirishrail import IrishRailRTPI station = config.get(CONF_STATION) @@ -61,7 +61,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): irish_rail = IrishRailRTPI() data = IrishRailTransportData( irish_rail, station, direction, destination, stops_at) - add_devices([IrishRailTransportSensor( + add_entities([IrishRailTransportSensor( data, station, direction, destination, stops_at, name)], True) diff --git a/homeassistant/components/sensor/isy994.py b/homeassistant/components/sensor/isy994.py index 19dcfc87014009..eca7e88a17eb37 100644 --- a/homeassistant/components/sensor/isy994.py +++ b/homeassistant/components/sensor/isy994.py @@ -236,7 +236,7 @@ def setup_platform(hass, config: ConfigType, - add_devices: Callable[[list], None], discovery_info=None): + add_entities: Callable[[list], None], discovery_info=None): """Set up the ISY994 sensor platform.""" devices = [] @@ -247,7 +247,7 @@ def setup_platform(hass, config: ConfigType, for node in hass.data[ISY994_WEATHER]: devices.append(ISYWeatherDevice(node)) - add_devices(devices) + add_entities(devices) class ISYSensorDevice(ISYDevice): diff --git a/homeassistant/components/sensor/juicenet.py b/homeassistant/components/sensor/juicenet.py index 0d305ca23c719f..18725394a1f507 100644 --- a/homeassistant/components/sensor/juicenet.py +++ b/homeassistant/components/sensor/juicenet.py @@ -25,7 +25,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Juicenet sensor.""" api = hass.data[DOMAIN]['api'] @@ -34,7 +34,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in SENSOR_TYPES: dev.append(JuicenetSensorDevice(device, variable, hass)) - add_devices(dev) + add_entities(dev) class JuicenetSensorDevice(JuicenetDevice, Entity): diff --git a/homeassistant/components/sensor/kira.py b/homeassistant/components/sensor/kira.py index 19566100f99534..ced17281cc85f1 100644 --- a/homeassistant/components/sensor/kira.py +++ b/homeassistant/components/sensor/kira.py @@ -18,14 +18,14 @@ CONF_SENSOR = 'sensor' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a Kira sensor.""" if discovery_info is not None: name = discovery_info.get(CONF_NAME) device = discovery_info.get(CONF_DEVICE) kira = hass.data[DOMAIN][CONF_SENSOR][name] - add_devices([KiraReceiver(device, kira)]) + add_entities([KiraReceiver(device, kira)]) class KiraReceiver(Entity): diff --git a/homeassistant/components/sensor/knx.py b/homeassistant/components/sensor/knx.py index 925b16cb4c7d20..ec506189c1282a 100644 --- a/homeassistant/components/sensor/knx.py +++ b/homeassistant/components/sensor/knx.py @@ -27,27 +27,27 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up sensor(s) for KNX platform.""" if discovery_info is not None: - async_add_devices_discovery(hass, discovery_info, async_add_devices) + async_add_entities_discovery(hass, discovery_info, async_add_entities) else: - async_add_devices_config(hass, config, async_add_devices) + async_add_entities_config(hass, config, async_add_entities) @callback -def async_add_devices_discovery(hass, discovery_info, async_add_devices): +def async_add_entities_discovery(hass, discovery_info, async_add_entities): """Set up sensors for KNX platform configured via xknx.yaml.""" entities = [] for device_name in discovery_info[ATTR_DISCOVER_DEVICES]: device = hass.data[DATA_KNX].xknx.devices[device_name] entities.append(KNXSensor(hass, device)) - async_add_devices(entities) + async_add_entities(entities) @callback -def async_add_devices_config(hass, config, async_add_devices): +def async_add_entities_config(hass, config, async_add_entities): """Set up sensor for KNX platform configured within platform.""" import xknx sensor = xknx.devices.Sensor( @@ -56,7 +56,7 @@ def async_add_devices_config(hass, config, async_add_devices): group_address=config.get(CONF_ADDRESS), value_type=config.get(CONF_TYPE)) hass.data[DATA_KNX].xknx.devices.add(sensor) - async_add_devices([KNXSensor(hass, sensor)]) + async_add_entities([KNXSensor(hass, sensor)]) class KNXSensor(Entity): diff --git a/homeassistant/components/sensor/kwb.py b/homeassistant/components/sensor/kwb.py index f307b0f6102fd5..20e5bc7f4ac554 100644 --- a/homeassistant/components/sensor/kwb.py +++ b/homeassistant/components/sensor/kwb.py @@ -48,7 +48,7 @@ ) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the KWB component.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -77,7 +77,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, lambda event: easyfire.stop_thread()) - add_devices(sensors) + add_entities(sensors) class KWBSensor(Entity): diff --git a/homeassistant/components/sensor/lacrosse.py b/homeassistant/components/sensor/lacrosse.py index 034f0be49f6d63..a2dbaa8f324405 100644 --- a/homeassistant/components/sensor/lacrosse.py +++ b/homeassistant/components/sensor/lacrosse.py @@ -56,7 +56,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the LaCrosse sensors.""" import pylacrosse from serial import SerialException @@ -103,7 +103,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ) ) - add_devices(sensors) + add_entities(sensors) class LaCrosseSensor(Entity): diff --git a/homeassistant/components/sensor/lastfm.py b/homeassistant/components/sensor/lastfm.py index 45eddee9f7e174..338adc7d501573 100644 --- a/homeassistant/components/sensor/lastfm.py +++ b/homeassistant/components/sensor/lastfm.py @@ -29,12 +29,12 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Last.fm platform.""" import pylast as lastfm network = lastfm.LastFMNetwork(api_key=config.get(CONF_API_KEY)) - add_devices( + add_entities( [LastfmSensor( username, network) for username in config.get(CONF_USERS)], True) diff --git a/homeassistant/components/sensor/linux_battery.py b/homeassistant/components/sensor/linux_battery.py index e7b8bf600a44d8..69fc145a4a5ef6 100644 --- a/homeassistant/components/sensor/linux_battery.py +++ b/homeassistant/components/sensor/linux_battery.py @@ -54,7 +54,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Linux Battery sensor.""" name = config.get(CONF_NAME) battery_id = config.get(CONF_BATTERY) @@ -69,7 +69,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("No battery found") return False - add_devices([LinuxBatterySensor(name, battery_id, system)], True) + add_entities([LinuxBatterySensor(name, battery_id, system)], True) class LinuxBatterySensor(Entity): diff --git a/homeassistant/components/sensor/london_air.py b/homeassistant/components/sensor/london_air.py index bbb5993b0644cf..6c96bb48e9715b 100644 --- a/homeassistant/components/sensor/london_air.py +++ b/homeassistant/components/sensor/london_air.py @@ -60,7 +60,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the London Air sensor.""" data = APIData() data.update() @@ -68,7 +68,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for name in config.get(CONF_LOCATIONS): sensors.append(AirSensor(name, data)) - add_devices(sensors, True) + add_entities(sensors, True) class APIData: diff --git a/homeassistant/components/sensor/london_underground.py b/homeassistant/components/sensor/london_underground.py index 4619eda061123a..a0617469cbaad6 100644 --- a/homeassistant/components/sensor/london_underground.py +++ b/homeassistant/components/sensor/london_underground.py @@ -43,7 +43,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tube sensor.""" data = TubeData() data.update() @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for line in config.get(CONF_LINE): sensors.append(LondonTubeSensor(line, data)) - add_devices(sensors, True) + add_entities(sensors, True) class LondonTubeSensor(Entity): diff --git a/homeassistant/components/sensor/loopenergy.py b/homeassistant/components/sensor/loopenergy.py index d888a6c634d655..0f2362ca33cc7e 100644 --- a/homeassistant/components/sensor/loopenergy.py +++ b/homeassistant/components/sensor/loopenergy.py @@ -56,7 +56,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Loop Energy sensors.""" import pyloopenergy @@ -84,7 +84,7 @@ def stop_loopenergy(event): if gas_config.get(CONF_GAS_SERIAL): sensors.append(LoopEnergyGas(controller)) - add_devices(sensors) + add_entities(sensors) class LoopEnergyDevice(Entity): diff --git a/homeassistant/components/sensor/luftdaten.py b/homeassistant/components/sensor/luftdaten.py index c9bc7205ce6305..445ccb7214e69e 100644 --- a/homeassistant/components/sensor/luftdaten.py +++ b/homeassistant/components/sensor/luftdaten.py @@ -59,7 +59,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Luftdaten sensor.""" from luftdaten import Luftdaten @@ -84,7 +84,7 @@ async def async_setup_platform( devices.append( LuftdatenSensor(luftdaten, name, variable, sensor_id, show_on_map)) - async_add_devices(devices) + async_add_entities(devices) class LuftdatenSensor(Entity): diff --git a/homeassistant/components/sensor/lyft.py b/homeassistant/components/sensor/lyft.py index 57e5f1c6b02c88..671308871e593c 100644 --- a/homeassistant/components/sensor/lyft.py +++ b/homeassistant/components/sensor/lyft.py @@ -42,7 +42,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Lyft sensor.""" from lyft_rides.auth import ClientCredentialGrant from lyft_rides.errors import APIError @@ -74,7 +74,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if product.get('estimate') is not None: dev.append(LyftSensor( 'price', timeandpriceest, product_id, product)) - add_devices(dev, True) + add_entities(dev, True) class LyftSensor(Entity): diff --git a/homeassistant/components/sensor/magicseaweed.py b/homeassistant/components/sensor/magicseaweed.py index 02c61024e30c5f..59f38553d79c7d 100644 --- a/homeassistant/components/sensor/magicseaweed.py +++ b/homeassistant/components/sensor/magicseaweed.py @@ -56,7 +56,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Magicseaweed sensor.""" name = config.get(CONF_NAME) spot_id = config[CONF_SPOT_ID] @@ -88,7 +88,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for hour in hours: sensors.append(MagicSeaweedSensor( forecast_data, variable, name, units, hour)) - add_devices(sensors, True) + add_entities(sensors, True) class MagicSeaweedSensor(Entity): diff --git a/homeassistant/components/sensor/melissa.py b/homeassistant/components/sensor/melissa.py index f67722b0198355..df4800cbbd05ec 100644 --- a/homeassistant/components/sensor/melissa.py +++ b/homeassistant/components/sensor/melissa.py @@ -15,8 +15,8 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the melissa sensor platform.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the melissa sensor platform.""" sensors = [] api = hass.data[DATA_MELISSA] devices = api.fetch_devices().values() @@ -25,7 +25,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if device['type'] == 'melissa': sensors.append(MelissaTemperatureSensor(device, api)) sensors.append(MelissaHumiditySensor(device, api)) - add_devices(sensors) + add_entities(sensors) class MelissaSensor(Entity): diff --git a/homeassistant/components/sensor/metoffice.py b/homeassistant/components/sensor/metoffice.py index ec3d3f47ba7f30..8cebecb71247be 100644 --- a/homeassistant/components/sensor/metoffice.py +++ b/homeassistant/components/sensor/metoffice.py @@ -86,7 +86,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Met Office sensor platform.""" import datapoint as dp @@ -121,7 +121,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in config[CONF_MONITORED_CONDITIONS]: sensors.append(MetOfficeCurrentSensor(site, data, variable, name)) - add_devices(sensors, True) + add_entities(sensors, True) class MetOfficeCurrentSensor(Entity): diff --git a/homeassistant/components/sensor/mfi.py b/homeassistant/components/sensor/mfi.py index f575768b505df0..44e5dbda84f410 100644 --- a/homeassistant/components/sensor/mfi.py +++ b/homeassistant/components/sensor/mfi.py @@ -49,7 +49,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up mFi sensors.""" host = config.get(CONF_HOST) username = config.get(CONF_USERNAME) @@ -68,10 +68,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Unable to connect to mFi: %s", str(ex)) return False - add_devices(MfiSensor(port, hass) - for device in client.get_devices() - for port in device.ports.values() - if port.model in SENSOR_MODELS) + add_entities(MfiSensor(port, hass) + for device in client.get_devices() + for port in device.ports.values() + if port.model in SENSOR_MODELS) class MfiSensor(Entity): diff --git a/homeassistant/components/sensor/mhz19.py b/homeassistant/components/sensor/mhz19.py index 60f6598ab21064..dd8a0395088048 100644 --- a/homeassistant/components/sensor/mhz19.py +++ b/homeassistant/components/sensor/mhz19.py @@ -42,7 +42,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available CO2 sensors.""" from pmsensor import co2sensor @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev.append( MHZ19Sensor(data, variable, SENSOR_TYPES[variable][1], name)) - add_devices(dev, True) + add_entities(dev, True) return True diff --git a/homeassistant/components/sensor/miflora.py b/homeassistant/components/sensor/miflora.py index 6f50a57b3ab034..ced17512089527 100644 --- a/homeassistant/components/sensor/miflora.py +++ b/homeassistant/components/sensor/miflora.py @@ -58,7 +58,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the MiFlora sensor.""" from miflora import miflora_poller try: @@ -92,7 +92,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devs.append(MiFloraSensor( poller, parameter, name, unit, force_update, median)) - add_devices(devs) + add_entities(devs) class MiFloraSensor(Entity): diff --git a/homeassistant/components/sensor/min_max.py b/homeassistant/components/sensor/min_max.py index f3a30724732726..7956dd97b5eceb 100644 --- a/homeassistant/components/sensor/min_max.py +++ b/homeassistant/components/sensor/min_max.py @@ -55,14 +55,15 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the min/max/mean sensor.""" entity_ids = config.get(CONF_ENTITY_IDS) name = config.get(CONF_NAME) sensor_type = config.get(CONF_TYPE) round_digits = config.get(CONF_ROUND_DIGITS) - async_add_devices( + async_add_entities( [MinMaxSensor(hass, entity_ids, name, sensor_type, round_digits)], True) return True diff --git a/homeassistant/components/sensor/mitemp_bt.py b/homeassistant/components/sensor/mitemp_bt.py index 249a69578db957..2ae5c29b04398e 100644 --- a/homeassistant/components/sensor/mitemp_bt.py +++ b/homeassistant/components/sensor/mitemp_bt.py @@ -56,7 +56,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the MiTempBt sensor.""" from mitemp_bt import mitemp_bt_poller try: @@ -90,7 +90,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devs.append(MiTempBtSensor( poller, parameter, name, unit, force_update, median)) - add_devices(devs) + add_entities(devs) class MiTempBtSensor(Entity): diff --git a/homeassistant/components/sensor/modbus.py b/homeassistant/components/sensor/modbus.py index 5f404ccd5f7f32..833cb0c5a628e3 100644 --- a/homeassistant/components/sensor/modbus.py +++ b/homeassistant/components/sensor/modbus.py @@ -59,7 +59,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Modbus sensors.""" sensors = [] data_types = {DATA_TYPE_INT: {1: 'h', 2: 'i', 4: 'q'}} @@ -108,7 +108,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if not sensors: return False - add_devices(sensors) + add_entities(sensors) class ModbusRegisterSensor(Entity): diff --git a/homeassistant/components/sensor/modem_callerid.py b/homeassistant/components/sensor/modem_callerid.py index 58e8becd6bb603..2da7953c12a2b0 100644 --- a/homeassistant/components/sensor/modem_callerid.py +++ b/homeassistant/components/sensor/modem_callerid.py @@ -30,7 +30,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up modem caller ID sensor platform.""" from basicmodem.basicmodem import BasicModem as bm name = config.get(CONF_NAME) @@ -41,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error('Unable to initialize modem.') return - add_devices([ModemCalleridSensor(hass, name, port, modem)]) + add_entities([ModemCalleridSensor(hass, name, port, modem)]) class ModemCalleridSensor(Entity): diff --git a/homeassistant/components/sensor/mold_indicator.py b/homeassistant/components/sensor/mold_indicator.py index 319185923cd1fd..e5794ab1314924 100644 --- a/homeassistant/components/sensor/mold_indicator.py +++ b/homeassistant/components/sensor/mold_indicator.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up MoldIndicator sensor.""" name = config.get(CONF_NAME, DEFAULT_NAME) indoor_temp_sensor = config.get(CONF_INDOOR_TEMP) @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): indoor_humidity_sensor = config.get(CONF_INDOOR_HUMIDITY) calib_factor = config.get(CONF_CALIBRATION_FACTOR) - add_devices([MoldIndicator( + add_entities([MoldIndicator( hass, name, indoor_temp_sensor, outdoor_temp_sensor, indoor_humidity_sensor, calib_factor)], True) diff --git a/homeassistant/components/sensor/moon.py b/homeassistant/components/sensor/moon.py index 50f4f72078cbcb..a019d21fd784df 100644 --- a/homeassistant/components/sensor/moon.py +++ b/homeassistant/components/sensor/moon.py @@ -26,11 +26,11 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Moon sensor.""" name = config.get(CONF_NAME) - async_add_devices([MoonSensor(name)], True) + async_add_entities([MoonSensor(name)], True) class MoonSensor(Entity): diff --git a/homeassistant/components/sensor/mopar.py b/homeassistant/components/sensor/mopar.py index 81c48555cfca32..6b2b3776557ad1 100644 --- a/homeassistant/components/sensor/mopar.py +++ b/homeassistant/components/sensor/mopar.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Mopar platform.""" import motorparts cookie = hass.config.path(COOKIE_FILE) @@ -66,8 +66,8 @@ def _handle_service(service): schema=REMOTE_COMMAND_SCHEMA) data = MoparData(session) - add_devices([MoparSensor(data, index) - for index, _ in enumerate(data.vehicles)], True) + add_entities([MoparSensor(data, index) + for index, _ in enumerate(data.vehicles)], True) class MoparData: diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py index 0e29c55d39d16e..6cf2d55755d38d 100644 --- a/homeassistant/components/sensor/mqtt.py +++ b/homeassistant/components/sensor/mqtt.py @@ -51,7 +51,7 @@ async def async_setup_platform(hass: HomeAssistantType, config: ConfigType, - async_add_devices, discovery_info=None): + async_add_entities, discovery_info=None): """Set up MQTT Sensor.""" if discovery_info is not None: config = PLATFORM_SCHEMA(discovery_info) @@ -60,7 +60,7 @@ async def async_setup_platform(hass: HomeAssistantType, config: ConfigType, if value_template is not None: value_template.hass = hass - async_add_devices([MqttSensor( + async_add_entities([MqttSensor( config.get(CONF_NAME), config.get(CONF_STATE_TOPIC), config.get(CONF_QOS), diff --git a/homeassistant/components/sensor/mqtt_room.py b/homeassistant/components/sensor/mqtt_room.py index 2a61c1143eeec5..e12e8e033ac6ee 100644 --- a/homeassistant/components/sensor/mqtt_room.py +++ b/homeassistant/components/sensor/mqtt_room.py @@ -53,9 +53,10 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up MQTT room Sensor.""" - async_add_devices([MQTTRoomSensor( + async_add_entities([MQTTRoomSensor( config.get(CONF_NAME), config.get(CONF_STATE_TOPIC), config.get(CONF_DEVICE_ID), diff --git a/homeassistant/components/sensor/mvglive.py b/homeassistant/components/sensor/mvglive.py index e066bb5e0b9b4f..a7a4b592664d1e 100644 --- a/homeassistant/components/sensor/mvglive.py +++ b/homeassistant/components/sensor/mvglive.py @@ -59,7 +59,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the MVGLive sensor.""" sensors = [] for nextdeparture in config.get(CONF_NEXT_DEPARTURE): @@ -73,7 +73,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): nextdeparture.get(CONF_TIMEOFFSET), nextdeparture.get(CONF_NUMBER), nextdeparture.get(CONF_NAME))) - add_devices(sensors, True) + add_entities(sensors, True) class MVGLiveSensor(Entity): diff --git a/homeassistant/components/sensor/mychevy.py b/homeassistant/components/sensor/mychevy.py index ef7c7ba86083f3..fa3343d779159b 100644 --- a/homeassistant/components/sensor/mychevy.py +++ b/homeassistant/components/sensor/mychevy.py @@ -31,7 +31,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the MyChevy sensors.""" if discovery_info is None: return @@ -42,7 +42,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for car in hub.cars: sensors.append(EVSensor(hub, sconfig, car.vid)) - add_devices(sensors) + add_entities(sensors) class MyChevyStatus(Entity): diff --git a/homeassistant/components/sensor/mysensors.py b/homeassistant/components/sensor/mysensors.py index 2fbfc0e97a4169..160d4b4784b93e 100644 --- a/homeassistant/components/sensor/mysensors.py +++ b/homeassistant/components/sensor/mysensors.py @@ -35,11 +35,11 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the MySensors platform for sensors.""" mysensors.setup_mysensors_platform( hass, DOMAIN, discovery_info, MySensorsSensor, - async_add_devices=async_add_devices) + async_add_entities=async_add_entities) class MySensorsSensor(mysensors.device.MySensorsEntity): diff --git a/homeassistant/components/sensor/nederlandse_spoorwegen.py b/homeassistant/components/sensor/nederlandse_spoorwegen.py index 431a44c56e35c7..81cfada25f68f0 100644 --- a/homeassistant/components/sensor/nederlandse_spoorwegen.py +++ b/homeassistant/components/sensor/nederlandse_spoorwegen.py @@ -48,7 +48,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the departure sensor.""" import ns_api nsapi = ns_api.NSAPI( @@ -72,7 +72,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): nsapi, departure.get(CONF_NAME), departure.get(CONF_FROM), departure.get(CONF_TO), departure.get(CONF_VIA))) if sensors: - add_devices(sensors, True) + add_entities(sensors, True) def valid_stations(stations, given_stations): diff --git a/homeassistant/components/sensor/nest.py b/homeassistant/components/sensor/nest.py index d2e1501ad7e961..738bc53d8806ac 100644 --- a/homeassistant/components/sensor/nest.py +++ b/homeassistant/components/sensor/nest.py @@ -55,14 +55,14 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Nest Sensor. No longer used. """ -async def async_setup_entry(hass, entry, async_add_devices): +async def async_setup_entry(hass, entry, async_add_entities): """Set up a Nest sensor based on a config entry.""" nest = hass.data[DATA_NEST] @@ -119,7 +119,7 @@ def get_sensors(): return all_sensors - async_add_devices(await hass.async_add_job(get_sensors), True) + async_add_entities(await hass.async_add_job(get_sensors), True) class NestBasicSensor(NestSensorDevice): diff --git a/homeassistant/components/sensor/netatmo.py b/homeassistant/components/sensor/netatmo.py index 3e3f7ce9486ebd..5216913528a12b 100644 --- a/homeassistant/components/sensor/netatmo.py +++ b/homeassistant/components/sensor/netatmo.py @@ -64,7 +64,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available Netatmo weather sensors.""" netatmo = hass.components.netatmo data = NetAtmoData(netatmo.NETATMO_AUTH, config.get(CONF_STATION, None)) @@ -95,7 +95,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): except pyatmo.NoDevice: return None - add_devices(dev, True) + add_entities(dev, True) class NetAtmoSensor(Entity): diff --git a/homeassistant/components/sensor/netatmo_public.py b/homeassistant/components/sensor/netatmo_public.py new file mode 100644 index 00000000000000..d1c6e03d1b0432 --- /dev/null +++ b/homeassistant/components/sensor/netatmo_public.py @@ -0,0 +1,141 @@ +""" +Support for Sensors using public Netatmo data. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.netatmo_public/. +""" +from datetime import timedelta +import logging + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import (CONF_NAME, CONF_TYPE) +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['netatmo'] + +CONF_AREAS = 'areas' +CONF_LAT_NE = 'lat_ne' +CONF_LON_NE = 'lon_ne' +CONF_LAT_SW = 'lat_sw' +CONF_LON_SW = 'lon_sw' + +DEFAULT_NAME = 'Netatmo Public Data' +DEFAULT_TYPE = 'max' +SENSOR_TYPES = {'max', 'avg'} + +# NetAtmo Data is uploaded to server every 10 minutes +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=600) + + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_AREAS): vol.All(cv.ensure_list, [ + { + vol.Required(CONF_LAT_NE): cv.latitude, + vol.Required(CONF_LAT_SW): cv.latitude, + vol.Required(CONF_LON_NE): cv.longitude, + vol.Required(CONF_LON_SW): cv.longitude, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_TYPE, default=DEFAULT_TYPE): + vol.In(SENSOR_TYPES) + } + ]), +}) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the access to Netatmo binary sensor.""" + netatmo = hass.components.netatmo + + sensors = [] + areas = config.get(CONF_AREAS) + for area_conf in areas: + data = NetatmoPublicData(netatmo.NETATMO_AUTH, + lat_ne=area_conf.get(CONF_LAT_NE), + lon_ne=area_conf.get(CONF_LON_NE), + lat_sw=area_conf.get(CONF_LAT_SW), + lon_sw=area_conf.get(CONF_LON_SW), + calculation=area_conf.get(CONF_TYPE)) + sensors.append(NetatmoPublicSensor(area_conf.get(CONF_NAME), data)) + add_entities(sensors) + + +class NetatmoPublicSensor(Entity): + """Represent a single sensor in a Netatmo.""" + + def __init__(self, name, data): + """Initialize the sensor.""" + self.netatmo_data = data + self._name = name + self._state = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def icon(self): + """Icon to use in the frontend.""" + return 'mdi:weather-rainy' + + @property + def device_class(self): + """Return the device class of the sensor.""" + return None + + @property + def state(self): + """Return true if binary sensor is on.""" + return self._state + + @property + def unit_of_measurement(self): + """Return the unit of measurement of this entity.""" + return 'mm' + + def update(self): + """Get the latest data from NetAtmo API and updates the states.""" + self.netatmo_data.update() + self._state = self.netatmo_data.data + + +class NetatmoPublicData: + """Get the latest data from NetAtmo.""" + + def __init__(self, auth, lat_ne, lon_ne, lat_sw, lon_sw, calculation): + """Initialize the data object.""" + self.auth = auth + self.data = None + self.lat_ne = lat_ne + self.lon_ne = lon_ne + self.lat_sw = lat_sw + self.lon_sw = lon_sw + self.calculation = calculation + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Request an update from the Netatmo API.""" + import pyatmo + raindata = pyatmo.PublicData(self.auth, + LAT_NE=self.lat_ne, + LON_NE=self.lon_ne, + LAT_SW=self.lat_sw, + LON_SW=self.lon_sw, + required_data_type="rain") + + if raindata.CountStationInArea() == 0: + _LOGGER.warning('No Rain Station available in this area.') + return + + raindata_live = raindata.getLive() + + if self.calculation == 'avg': + self.data = sum(raindata_live.values()) / len(raindata_live) + else: + self.data = max(raindata_live.values()) diff --git a/homeassistant/components/sensor/netdata.py b/homeassistant/components/sensor/netdata.py index 488b16113999a3..79fb59b4f7b7bf 100644 --- a/homeassistant/components/sensor/netdata.py +++ b/homeassistant/components/sensor/netdata.py @@ -47,7 +47,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Netdata sensor.""" from netdata import Netdata @@ -82,7 +82,7 @@ async def async_setup_platform( dev.append(NetdataSensor( netdata, name, sensor, sensor_name, element, icon, unit)) - async_add_devices(dev, True) + async_add_entities(dev, True) class NetdataSensor(Entity): diff --git a/homeassistant/components/sensor/netgear_lte.py b/homeassistant/components/sensor/netgear_lte.py index dac1f81ad2361b..2ecf545aa4e164 100644 --- a/homeassistant/components/sensor/netgear_lte.py +++ b/homeassistant/components/sensor/netgear_lte.py @@ -27,7 +27,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info): + hass, config, async_add_entities, discovery_info): """Set up Netgear LTE sensor devices.""" modem_data = hass.data[DATA_KEY].get_modem_data(config) @@ -38,7 +38,7 @@ async def async_setup_platform( elif sensor_type == SENSOR_USAGE: sensors.append(UsageSensor(modem_data, sensor_type)) - async_add_devices(sensors, True) + async_add_entities(sensors, True) @attr.s diff --git a/homeassistant/components/sensor/neurio_energy.py b/homeassistant/components/sensor/neurio_energy.py index fd8b8d2aeecee3..addb7925bc2ea6 100644 --- a/homeassistant/components/sensor/neurio_energy.py +++ b/homeassistant/components/sensor/neurio_energy.py @@ -42,7 +42,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Neurio sensor.""" api_key = config.get(CONF_API_KEY) api_secret = config.get(CONF_API_SECRET) @@ -64,9 +64,9 @@ def update_active(): update_active() # Active power sensor - add_devices([NeurioEnergy(data, ACTIVE_NAME, ACTIVE_TYPE, update_active)]) + add_entities([NeurioEnergy(data, ACTIVE_NAME, ACTIVE_TYPE, update_active)]) # Daily power sensor - add_devices([NeurioEnergy(data, DAILY_NAME, DAILY_TYPE, update_daily)]) + add_entities([NeurioEnergy(data, DAILY_NAME, DAILY_TYPE, update_daily)]) class NeurioData: diff --git a/homeassistant/components/sensor/noaa_tides.py b/homeassistant/components/sensor/noaa_tides.py new file mode 100644 index 00000000000000..6a72fdf8f2afa3 --- /dev/null +++ b/homeassistant/components/sensor/noaa_tides.py @@ -0,0 +1,138 @@ +""" +Support for the NOAA Tides and Currents API. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.noaa_tides/ +""" +from datetime import datetime, timedelta +import logging + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + ATTR_ATTRIBUTION, CONF_NAME, CONF_TIME_ZONE, CONF_UNIT_SYSTEM) +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity + +REQUIREMENTS = ['py_noaa==0.3.0'] + +_LOGGER = logging.getLogger(__name__) + +CONF_STATION_ID = 'station_id' + +DEFAULT_ATTRIBUTION = "Data provided by NOAA" +DEFAULT_NAME = 'NOAA Tides' +DEFAULT_TIMEZONE = 'lst_ldt' + +SCAN_INTERVAL = timedelta(minutes=60) + +TIMEZONES = ['gmt', 'lst', 'lst_ldt'] +UNIT_SYSTEMS = ['english', 'metric'] + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_STATION_ID): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_TIME_ZONE, default=DEFAULT_TIMEZONE): vol.In(TIMEZONES), + vol.Optional(CONF_UNIT_SYSTEM): vol.In(UNIT_SYSTEMS), +}) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the NOAA Tides and Currents sensor.""" + station_id = config[CONF_STATION_ID] + name = config.get(CONF_NAME) + timezone = config.get(CONF_TIME_ZONE) + + if CONF_UNIT_SYSTEM in config: + unit_system = config[CONF_UNIT_SYSTEM] + elif hass.config.units.is_metric: + unit_system = UNIT_SYSTEMS[1] + else: + unit_system = UNIT_SYSTEMS[0] + + noaa_sensor = NOAATidesAndCurrentsSensor( + name, station_id, timezone, unit_system) + + noaa_sensor.update() + if noaa_sensor.data is None: + _LOGGER.error("Unable to setup NOAA Tides Sensor") + return + add_entities([noaa_sensor], True) + + +class NOAATidesAndCurrentsSensor(Entity): + """Representation of a NOAA Tides and Currents sensor.""" + + def __init__(self, name, station_id, timezone, unit_system): + """Initialize the sensor.""" + self._name = name + self._station_id = station_id + self._timezone = timezone + self._unit_system = unit_system + self.data = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def device_state_attributes(self): + """Return the state attributes of this device.""" + attr = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION} + if self.data is None: + return attr + if self.data['hi_lo'][1] == 'H': + attr['high_tide_time'] = \ + self.data.index[1].strftime('%Y-%m-%dT%H:%M') + attr['high_tide_height'] = self.data['predicted_wl'][1] + attr['low_tide_time'] = \ + self.data.index[2].strftime('%Y-%m-%dT%H:%M') + attr['low_tide_height'] = self.data['predicted_wl'][2] + elif self.data['hi_lo'][1] == 'L': + attr['low_tide_time'] = \ + self.data.index[1].strftime('%Y-%m-%dT%H:%M') + attr['low_tide_height'] = self.data['predicted_wl'][1] + attr['high_tide_time'] = \ + self.data.index[2].strftime('%Y-%m-%dT%H:%M') + attr['high_tide_height'] = self.data['predicted_wl'][2] + return attr + + @property + def state(self): + """Return the state of the device.""" + if self.data is None: + return None + api_time = self.data.index[0] + if self.data['hi_lo'][0] == 'H': + tidetime = api_time.strftime('%-I:%M %p') + return "High tide at {}".format(tidetime) + if self.data['hi_lo'][0] == 'L': + tidetime = api_time.strftime('%-I:%M %p') + return "Low tide at {}".format(tidetime) + return None + + def update(self): + """Get the latest data from NOAA Tides and Currents API.""" + from py_noaa import coops # pylint: disable=import-error + begin = datetime.now() + delta = timedelta(days=2) + end = begin + delta + try: + df_predictions = coops.get_data( + begin_date=begin.strftime("%Y%m%d %H:%M"), + end_date=end.strftime("%Y%m%d %H:%M"), + stationid=self._station_id, + product="predictions", + datum="MLLW", + interval="hilo", + units=self._unit_system, + time_zone=self._timezone) + self.data = df_predictions.head() + _LOGGER.debug("Data = %s", self.data) + _LOGGER.debug("Recent Tide data queried with start time set to %s", + begin.strftime("%m-%d-%Y %H:%M")) + except ValueError as err: + _LOGGER.error("Check NOAA Tides and Currents: %s", err.args) + self.data = None diff --git a/homeassistant/components/sensor/nsw_fuel_station.py b/homeassistant/components/sensor/nsw_fuel_station.py index 5f677d39888ea9..60ad83a82f207b 100644 --- a/homeassistant/components/sensor/nsw_fuel_station.py +++ b/homeassistant/components/sensor/nsw_fuel_station.py @@ -42,7 +42,7 @@ NOTIFICATION_TITLE = 'NSW Fuel Station Sensor Setup' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the NSW Fuel Station sensor.""" from nsw_fuel import FuelCheckClient @@ -66,7 +66,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): available_fuel_types = station_data.get_available_fuel_types() - add_devices([ + add_entities([ StationPriceSensor(station_data, fuel_type) for fuel_type in fuel_types if fuel_type in available_fuel_types diff --git a/homeassistant/components/sensor/nut.py b/homeassistant/components/sensor/nut.py index 7126bd89ef9f3e..79ad176e42e594 100644 --- a/homeassistant/components/sensor/nut.py +++ b/homeassistant/components/sensor/nut.py @@ -164,7 +164,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): data = PyNUTData(host, port, alias, username, password) if data.status is None: - _LOGGER.error("NUT Sensor has no data, unable to setup") + _LOGGER.error("NUT Sensor has no data, unable to set up") raise PlatformNotReady _LOGGER.debug('NUT Sensors Available: %s', data.status) diff --git a/homeassistant/components/sensor/nzbget.py b/homeassistant/components/sensor/nzbget.py index a6fee5a69e8f38..2ee8e37a57ae47 100644 --- a/homeassistant/components/sensor/nzbget.py +++ b/homeassistant/components/sensor/nzbget.py @@ -50,7 +50,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the NZBGet sensors.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -78,7 +78,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): client_name=name) devices.append(new_sensor) - add_devices(devices) + add_entities(devices) class NZBGetSensor(Entity): diff --git a/homeassistant/components/sensor/octoprint.py b/homeassistant/components/sensor/octoprint.py index 9e62846e4d3e8c..d42828c9f55c21 100644 --- a/homeassistant/components/sensor/octoprint.py +++ b/homeassistant/components/sensor/octoprint.py @@ -38,7 +38,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available OctoPrint sensors.""" octoprint_api = hass.data[DOMAIN]["api"] name = config.get(CONF_NAME) @@ -74,7 +74,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name, SENSOR_TYPES[octo_type][3], SENSOR_TYPES[octo_type][0], SENSOR_TYPES[octo_type][1]) devices.append(new_sensor) - add_devices(devices, True) + add_entities(devices, True) class OctoPrintSensor(Entity): diff --git a/homeassistant/components/sensor/ohmconnect.py b/homeassistant/components/sensor/ohmconnect.py index d323a21a521096..be73cbcf042e62 100644 --- a/homeassistant/components/sensor/ohmconnect.py +++ b/homeassistant/components/sensor/ohmconnect.py @@ -31,12 +31,12 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the OhmConnect sensor.""" name = config.get(CONF_NAME) ohmid = config.get(CONF_ID) - add_devices([OhmconnectSensor(name, ohmid)], True) + add_entities([OhmconnectSensor(name, ohmid)], True) class OhmconnectSensor(Entity): diff --git a/homeassistant/components/sensor/onewire.py b/homeassistant/components/sensor/onewire.py index 95ad5f1713d460..d39ab24230cb6d 100644 --- a/homeassistant/components/sensor/onewire.py +++ b/homeassistant/components/sensor/onewire.py @@ -47,7 +47,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the one wire Sensors.""" base_dir = config.get(CONF_MOUNT_DIR) devs = [] @@ -85,7 +85,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "Check the mount_dir parameter if it's defined") return - add_devices(devs, True) + add_entities(devs, True) class OneWire(Entity): diff --git a/homeassistant/components/sensor/openevse.py b/homeassistant/components/sensor/openevse.py index 9086f37f8b2eda..eabf1739c4f56a 100644 --- a/homeassistant/components/sensor/openevse.py +++ b/homeassistant/components/sensor/openevse.py @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the OpenEVSE sensor.""" import openevsewifi @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in monitored_variables: dev.append(OpenEVSESensor(variable, charger)) - add_devices(dev, True) + add_entities(dev, True) class OpenEVSESensor(Entity): diff --git a/homeassistant/components/sensor/openexchangerates.py b/homeassistant/components/sensor/openexchangerates.py index 5e8231bb124904..01c84c63034385 100644 --- a/homeassistant/components/sensor/openexchangerates.py +++ b/homeassistant/components/sensor/openexchangerates.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Open Exchange Rates sensor.""" name = config.get(CONF_NAME) api_key = config.get(CONF_API_KEY) @@ -55,7 +55,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return False rest.update() - add_devices([OpenexchangeratesSensor(rest, name, quote)], True) + add_entities([OpenexchangeratesSensor(rest, name, quote)], True) class OpenexchangeratesSensor(Entity): diff --git a/homeassistant/components/sensor/openhardwaremonitor.py b/homeassistant/components/sensor/openhardwaremonitor.py index 1b345c752ffffb..d429cad9d9759b 100644 --- a/homeassistant/components/sensor/openhardwaremonitor.py +++ b/homeassistant/components/sensor/openhardwaremonitor.py @@ -41,10 +41,10 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Open Hardware Monitor platform.""" data = OpenHardwareMonitorData(config, hass) - add_devices(data.devices, True) + add_entities(data.devices, True) class OpenHardwareMonitorDevice(Entity): diff --git a/homeassistant/components/sensor/opensky.py b/homeassistant/components/sensor/opensky.py index 9178b46c488f6f..5ee11af4e605a9 100644 --- a/homeassistant/components/sensor/opensky.py +++ b/homeassistant/components/sensor/opensky.py @@ -56,11 +56,11 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Open Sky platform.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) - add_devices([OpenSkySensor( + add_entities([OpenSkySensor( hass, config.get(CONF_NAME, DOMAIN), latitude, longitude, config.get(CONF_RADIUS), config.get(CONF_ALTITUDE))], True) diff --git a/homeassistant/components/sensor/openuv.py b/homeassistant/components/sensor/openuv.py index b30c2908c40b7c..aaa04590b3fe6e 100644 --- a/homeassistant/components/sensor/openuv.py +++ b/homeassistant/components/sensor/openuv.py @@ -11,10 +11,10 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.openuv import ( DATA_UV, DOMAIN, SENSORS, TOPIC_UPDATE, TYPE_CURRENT_OZONE_LEVEL, - TYPE_CURRENT_UV_INDEX, TYPE_MAX_UV_INDEX, TYPE_SAFE_EXPOSURE_TIME_1, - TYPE_SAFE_EXPOSURE_TIME_2, TYPE_SAFE_EXPOSURE_TIME_3, - TYPE_SAFE_EXPOSURE_TIME_4, TYPE_SAFE_EXPOSURE_TIME_5, - TYPE_SAFE_EXPOSURE_TIME_6, OpenUvEntity) + TYPE_CURRENT_UV_INDEX, TYPE_CURRENT_UV_LEVEL, TYPE_MAX_UV_INDEX, + TYPE_SAFE_EXPOSURE_TIME_1, TYPE_SAFE_EXPOSURE_TIME_2, + TYPE_SAFE_EXPOSURE_TIME_3, TYPE_SAFE_EXPOSURE_TIME_4, + TYPE_SAFE_EXPOSURE_TIME_5, TYPE_SAFE_EXPOSURE_TIME_6, OpenUvEntity) from homeassistant.util.dt import as_local, parse_datetime DEPENDENCIES = ['openuv'] @@ -31,9 +31,15 @@ TYPE_SAFE_EXPOSURE_TIME_6: 'st6' } +UV_LEVEL_EXTREME = "Extreme" +UV_LEVEL_VHIGH = "Very High" +UV_LEVEL_HIGH = "High" +UV_LEVEL_MODERATE = "Moderate" +UV_LEVEL_LOW = "Low" + async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the OpenUV binary sensor platform.""" if discovery_info is None: return @@ -45,7 +51,7 @@ async def async_setup_platform( name, icon, unit = SENSORS[sensor_type] sensors.append(OpenUvSensor(openuv, sensor_type, name, icon, unit)) - async_add_devices(sensors, True) + async_add_entities(sensors, True) class OpenUvSensor(OpenUvEntity): @@ -105,6 +111,17 @@ async def async_update(self): self._state = data['ozone'] elif self._sensor_type == TYPE_CURRENT_UV_INDEX: self._state = data['uv'] + elif self._sensor_type == TYPE_CURRENT_UV_LEVEL: + if data['uv'] >= 11: + self._state = UV_LEVEL_EXTREME + elif data['uv'] >= 8: + self._state = UV_LEVEL_VHIGH + elif data['uv'] >= 6: + self._state = UV_LEVEL_HIGH + elif data['uv'] >= 3: + self._state = UV_LEVEL_MODERATE + else: + self._state = UV_LEVEL_LOW elif self._sensor_type == TYPE_MAX_UV_INDEX: self._state = data['uv_max'] self._attrs.update({ diff --git a/homeassistant/components/sensor/openweathermap.py b/homeassistant/components/sensor/openweathermap.py index ba7fc4f90951af..2dbbb58174136a 100644 --- a/homeassistant/components/sensor/openweathermap.py +++ b/homeassistant/components/sensor/openweathermap.py @@ -51,7 +51,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the OpenWeatherMap sensor.""" from pyowm import OWM @@ -85,7 +85,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev.append(OpenWeatherMapSensor( name, data, 'forecast', SENSOR_TYPES['temperature'][1])) - add_devices(dev, True) + add_entities(dev, True) class OpenWeatherMapSensor(Entity): diff --git a/homeassistant/components/sensor/otp.py b/homeassistant/components/sensor/otp.py index 6ceed11a6b943d..2e3a13928e19dc 100644 --- a/homeassistant/components/sensor/otp.py +++ b/homeassistant/components/sensor/otp.py @@ -33,12 +33,13 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the OTP sensor.""" name = config.get(CONF_NAME) token = config.get(CONF_TOKEN) - async_add_devices([TOTPSensor(name, token)], True) + async_add_entities([TOTPSensor(name, token)], True) return True diff --git a/homeassistant/components/sensor/pi_hole.py b/homeassistant/components/sensor/pi_hole.py index 363ada725ba8db..a318618724e460 100644 --- a/homeassistant/components/sensor/pi_hole.py +++ b/homeassistant/components/sensor/pi_hole.py @@ -71,7 +71,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Pi-hole sensor.""" from hole import Hole @@ -94,7 +94,7 @@ async def async_setup_platform( sensors = [PiHoleSensor(pi_hole, name, condition) for condition in config[CONF_MONITORED_CONDITIONS]] - async_add_devices(sensors, True) + async_add_entities(sensors, True) class PiHoleSensor(Entity): diff --git a/homeassistant/components/sensor/pilight.py b/homeassistant/components/sensor/pilight.py index c30f1575049731..85843018c01a9b 100644 --- a/homeassistant/components/sensor/pilight.py +++ b/homeassistant/components/sensor/pilight.py @@ -30,9 +30,9 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Pilight Sensor.""" - add_devices([PilightSensor( + add_entities([PilightSensor( hass=hass, name=config.get(CONF_NAME), variable=config.get(CONF_VARIABLE), diff --git a/homeassistant/components/sensor/plex.py b/homeassistant/components/sensor/plex.py index 5aa156a0ac6df7..46766d75010b18 100644 --- a/homeassistant/components/sensor/plex.py +++ b/homeassistant/components/sensor/plex.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Plex sensor.""" name = config.get(CONF_NAME) plex_user = config.get(CONF_USERNAME) @@ -57,7 +57,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): import plexapi.exceptions try: - add_devices([PlexSensor( + add_entities([PlexSensor( name, plex_url, plex_user, plex_password, plex_server, plex_token)], True) except (plexapi.exceptions.BadRequest, plexapi.exceptions.Unauthorized, diff --git a/homeassistant/components/sensor/pocketcasts.py b/homeassistant/components/sensor/pocketcasts.py index 85fbf7b5a16afb..9d5b837bba96ea 100644 --- a/homeassistant/components/sensor/pocketcasts.py +++ b/homeassistant/components/sensor/pocketcasts.py @@ -31,7 +31,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the pocketcasts platform for sensors.""" import pocketcasts @@ -41,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): try: api = pocketcasts.Api(username, password) _LOGGER.debug("Found %d podcasts", len(api.my_podcasts())) - add_devices([PocketCastsSensor(api)], True) + add_entities([PocketCastsSensor(api)], True) except OSError as err: _LOGGER.error("Connection to server failed: %s", err) return False diff --git a/homeassistant/components/sensor/pollen.py b/homeassistant/components/sensor/pollen.py index 27750c9ac61b04..6df7047b353638 100644 --- a/homeassistant/components/sensor/pollen.py +++ b/homeassistant/components/sensor/pollen.py @@ -94,7 +94,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Configure the platform and add the sensors.""" from pypollencom import Client @@ -113,7 +113,7 @@ async def async_setup_platform( PollencomSensor( data, config[CONF_ZIP_CODE], kind, category, name, icon, unit)) - async_add_devices(sensors, True) + async_add_entities(sensors, True) def calculate_average_rating(indices): diff --git a/homeassistant/components/sensor/postnl.py b/homeassistant/components/sensor/postnl.py index 9b35c1fdc7e3cc..5c70ca035cf6b0 100644 --- a/homeassistant/components/sensor/postnl.py +++ b/homeassistant/components/sensor/postnl.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the PostNL sensor platform.""" from postnl_api import PostNL_API, UnauthorizedException @@ -50,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.exception("Can't connect to the PostNL webservice") return - add_devices([PostNLSensor(api, name)], True) + add_entities([PostNLSensor(api, name)], True) class PostNLSensor(Entity): diff --git a/homeassistant/components/sensor/pushbullet.py b/homeassistant/components/sensor/pushbullet.py index 415174ac273b95..9b26bdfbbe9c51 100644 --- a/homeassistant/components/sensor/pushbullet.py +++ b/homeassistant/components/sensor/pushbullet.py @@ -37,7 +37,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Pushbullet Sensor platform.""" from pushbullet import PushBullet from pushbullet import InvalidKeyError @@ -52,7 +52,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices = [] for sensor_type in config[CONF_MONITORED_CONDITIONS]: devices.append(PushBulletNotificationSensor(pbprovider, sensor_type)) - add_devices(devices) + add_entities(devices) class PushBulletNotificationSensor(Entity): diff --git a/homeassistant/components/sensor/pvoutput.py b/homeassistant/components/sensor/pvoutput.py index d4307d50228691..a12093099c4545 100644 --- a/homeassistant/components/sensor/pvoutput.py +++ b/homeassistant/components/sensor/pvoutput.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the PVOutput sensor.""" name = config.get(CONF_NAME) api_key = config.get(CONF_API_KEY) @@ -61,7 +61,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Unable to fetch data from PVOutput") return False - add_devices([PvoutputSensor(rest, name)], True) + add_entities([PvoutputSensor(rest, name)], True) class PvoutputSensor(Entity): diff --git a/homeassistant/components/sensor/pyload.py b/homeassistant/components/sensor/pyload.py index 4aa121e0895c76..78a191c16f43a7 100644 --- a/homeassistant/components/sensor/pyload.py +++ b/homeassistant/components/sensor/pyload.py @@ -43,7 +43,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the pyLoad sensors.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -70,7 +70,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): client_name=name) devices.append(new_sensor) - add_devices(devices, True) + add_entities(devices, True) class PyLoadSensor(Entity): diff --git a/homeassistant/components/sensor/qnap.py b/homeassistant/components/sensor/qnap.py index 8b25eb3de31af2..22f8d4c37c037e 100644 --- a/homeassistant/components/sensor/qnap.py +++ b/homeassistant/components/sensor/qnap.py @@ -102,7 +102,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the QNAP NAS sensor.""" api = QNAPStatsAPI(config) api.update() @@ -151,7 +151,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in config[CONF_MONITORED_CONDITIONS] if variable in _VOLUME_MON_COND] - add_devices(sensors) + add_entities(sensors) def round_nicely(number): diff --git a/homeassistant/components/sensor/qwikswitch.py b/homeassistant/components/sensor/qwikswitch.py index 1497b4ad5cc131..c8d33de4baff0b 100644 --- a/homeassistant/components/sensor/qwikswitch.py +++ b/homeassistant/components/sensor/qwikswitch.py @@ -14,7 +14,7 @@ _LOGGER = logging.getLogger(__name__) -async def async_setup_platform(hass, _, add_devices, discovery_info=None): +async def async_setup_platform(hass, _, add_entities, discovery_info=None): """Add sensor from the main Qwikswitch component.""" if discovery_info is None: return @@ -22,7 +22,7 @@ async def async_setup_platform(hass, _, add_devices, discovery_info=None): qsusb = hass.data[QWIKSWITCH] _LOGGER.debug("Setup qwikswitch.sensor %s, %s", qsusb, discovery_info) devs = [QSSensor(sensor) for sensor in discovery_info[QWIKSWITCH]] - add_devices(devs) + add_entities(devs) class QSSensor(QSEntity): diff --git a/homeassistant/components/sensor/radarr.py b/homeassistant/components/sensor/radarr.py index 8adaeb3c71b076..67695ae0e32b15 100644 --- a/homeassistant/components/sensor/radarr.py +++ b/homeassistant/components/sensor/radarr.py @@ -66,10 +66,10 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Radarr platform.""" conditions = config.get(CONF_MONITORED_CONDITIONS) - add_devices( + add_entities( [RadarrSensor(hass, config, sensor) for sensor in conditions], True) diff --git a/homeassistant/components/sensor/rainbird.py b/homeassistant/components/sensor/rainbird.py index 875e9c37bd3206..1af2b771014c21 100644 --- a/homeassistant/components/sensor/rainbird.py +++ b/homeassistant/components/sensor/rainbird.py @@ -29,7 +29,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a Rain Bird sensor.""" controller = hass.data[DATA_RAINBIRD] @@ -38,7 +38,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append( RainBirdSensor(controller, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) class RainBirdSensor(Entity): diff --git a/homeassistant/components/sensor/raincloud.py b/homeassistant/components/sensor/raincloud.py index c03aa0a2aecb62..15a346880f3367 100644 --- a/homeassistant/components/sensor/raincloud.py +++ b/homeassistant/components/sensor/raincloud.py @@ -25,7 +25,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for a raincloud device.""" raincloud = hass.data[DATA_RAINCLOUD].data @@ -40,7 +40,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for zone in raincloud.controller.faucet.zones: sensors.append(RainCloudSensor(zone, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) return True diff --git a/homeassistant/components/sensor/rainmachine.py b/homeassistant/components/sensor/rainmachine.py index f747a26df397b4..20e95f0e98f0e8 100644 --- a/homeassistant/components/sensor/rainmachine.py +++ b/homeassistant/components/sensor/rainmachine.py @@ -18,7 +18,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the RainMachine Switch platform.""" if discovery_info is None: return @@ -31,7 +31,7 @@ async def async_setup_platform( sensors.append( RainMachineSensor(rainmachine, sensor_type, name, icon, unit)) - async_add_devices(sensors, True) + async_add_entities(sensors, True) class RainMachineSensor(RainMachineEntity): diff --git a/homeassistant/components/sensor/random.py b/homeassistant/components/sensor/random.py index c3ff08a5781b86..4dec96bec2ef4a 100644 --- a/homeassistant/components/sensor/random.py +++ b/homeassistant/components/sensor/random.py @@ -34,14 +34,14 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Random number sensor.""" name = config.get(CONF_NAME) minimum = config.get(CONF_MINIMUM) maximum = config.get(CONF_MAXIMUM) unit = config.get(CONF_UNIT_OF_MEASUREMENT) - async_add_devices([RandomSensor(name, minimum, maximum, unit)], True) + async_add_entities([RandomSensor(name, minimum, maximum, unit)], True) class RandomSensor(Entity): diff --git a/homeassistant/components/sensor/rest.py b/homeassistant/components/sensor/rest.py index 8db48719a37cc6..53aab3f1ff798c 100644 --- a/homeassistant/components/sensor/rest.py +++ b/homeassistant/components/sensor/rest.py @@ -49,7 +49,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the RESTful sensor.""" name = config.get(CONF_NAME) resource = config.get(CONF_RESOURCE) @@ -77,7 +77,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): rest = RestData(method, resource, auth, headers, payload, verify_ssl) rest.update() - add_devices([RestSensor( + add_entities([RestSensor( hass, rest, name, unit, value_template, json_attrs, force_update )], True) diff --git a/homeassistant/components/sensor/rflink.py b/homeassistant/components/sensor/rflink.py index 80d77033bbb4c9..3952c815dca7d0 100644 --- a/homeassistant/components/sensor/rflink.py +++ b/homeassistant/components/sensor/rflink.py @@ -75,9 +75,10 @@ def devices_from_config(domain_config, hass=None): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Rflink platform.""" - async_add_devices(devices_from_config(config, hass)) + async_add_entities(devices_from_config(config, hass)) @asyncio.coroutine def add_new_device(event): @@ -87,7 +88,7 @@ def add_new_device(event): rflinksensor = partial(RflinkSensor, device_id, hass) device = rflinksensor(event[EVENT_KEY_SENSOR], event[EVENT_KEY_UNIT]) # Add device entity - async_add_devices([device]) + async_add_entities([device]) # Register entity to listen to incoming rflink events hass.data[DATA_ENTITY_LOOKUP][ diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index b410e7e860a853..c2eff8d7c5df0b 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -35,7 +35,7 @@ }, extra=vol.ALLOW_EXTRA) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the RFXtrx platform.""" from RFXtrx import SensorEvent sensors = [] @@ -60,7 +60,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(new_sensor) sub_sensors[_data_type] = new_sensor rfxtrx.RFX_DEVICES[device_id] = sub_sensors - add_devices(sensors) + add_entities(sensors) def sensor_update(event): """Handle sensor updates from the RFXtrx gateway.""" @@ -104,7 +104,7 @@ def sensor_update(event): sub_sensors = {} sub_sensors[new_sensor.data_type] = new_sensor rfxtrx.RFX_DEVICES[device_id] = sub_sensors - add_devices([new_sensor]) + add_entities([new_sensor]) if sensor_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: rfxtrx.RECEIVED_EVT_SUBSCRIBERS.append(sensor_update) diff --git a/homeassistant/components/sensor/ring.py b/homeassistant/components/sensor/ring.py index cae7690103d127..31c0360cc2350d 100644 --- a/homeassistant/components/sensor/ring.py +++ b/homeassistant/components/sensor/ring.py @@ -61,7 +61,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for a Ring device.""" ring = hass.data[DATA_RING] @@ -79,7 +79,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if 'stickup_cams' in SENSOR_TYPES[sensor_type][1]: sensors.append(RingSensor(hass, device, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) return True diff --git a/homeassistant/components/sensor/ripple.py b/homeassistant/components/sensor/ripple.py index d516706fdc0900..beb7bf2226980c 100644 --- a/homeassistant/components/sensor/ripple.py +++ b/homeassistant/components/sensor/ripple.py @@ -28,12 +28,12 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Ripple.com sensors.""" address = config.get(CONF_ADDRESS) name = config.get(CONF_NAME) - add_devices([RippleSensor(name, address)], True) + add_entities([RippleSensor(name, address)], True) class RippleSensor(Entity): diff --git a/homeassistant/components/sensor/sabnzbd.py b/homeassistant/components/sensor/sabnzbd.py index 185f83c9405406..7e94f9026a8c7b 100644 --- a/homeassistant/components/sensor/sabnzbd.py +++ b/homeassistant/components/sensor/sabnzbd.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the SABnzbd sensors.""" if discovery_info is None: @@ -25,8 +25,8 @@ async def async_setup_platform(hass, config, async_add_devices, sab_api_data = hass.data[DATA_SABNZBD] sensors = sab_api_data.sensors client_name = sab_api_data.name - async_add_devices([SabnzbdSensor(sensor, sab_api_data, client_name) - for sensor in sensors]) + async_add_entities([SabnzbdSensor(sensor, sab_api_data, client_name) + for sensor in sensors]) class SabnzbdSensor(Entity): diff --git a/homeassistant/components/sensor/scrape.py b/homeassistant/components/sensor/scrape.py index e7aace8ec6d24a..e702c52e06a4e8 100644 --- a/homeassistant/components/sensor/scrape.py +++ b/homeassistant/components/sensor/scrape.py @@ -19,12 +19,12 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['beautifulsoup4==4.6.1'] +REQUIREMENTS = ['beautifulsoup4==4.6.3'] _LOGGER = logging.getLogger(__name__) -CONF_SELECT = 'select' CONF_ATTR = 'attribute' +CONF_SELECT = 'select' DEFAULT_NAME = 'Web scrape' DEFAULT_VERIFY_SSL = True @@ -44,7 +44,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Web scrape sensor.""" name = config.get(CONF_NAME) resource = config.get(CONF_RESOURCE) @@ -74,7 +74,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Unable to fetch data from %s", resource) return False - add_devices([ + add_entities([ ScrapeSensor(rest, name, select, attr, value_template, unit)], True) diff --git a/homeassistant/components/sensor/season.py b/homeassistant/components/sensor/season.py index f06f6a896e7357..3b14c3854cdcf8 100644 --- a/homeassistant/components/sensor/season.py +++ b/homeassistant/components/sensor/season.py @@ -40,7 +40,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Display the current season.""" if None in (hass.config.latitude, hass.config.longitude): _LOGGER.error("Latitude or longitude not set in Home Assistant config") @@ -57,7 +57,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): hemisphere = EQUATOR _LOGGER.debug(_type) - add_devices([Season(hass, hemisphere, _type)]) + add_entities([Season(hass, hemisphere, _type)]) return True diff --git a/homeassistant/components/sensor/sense.py b/homeassistant/components/sensor/sense.py index 89e0d15bf488e7..3da022dc3b55a2 100644 --- a/homeassistant/components/sensor/sense.py +++ b/homeassistant/components/sensor/sense.py @@ -64,7 +64,7 @@ def __init__(self, name, sensor_type): }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Sense sensor.""" from sense_energy import Senseable @@ -96,7 +96,7 @@ def update_active(): devices.append(Sense(data, name, sensor_type, is_production, update_call)) - add_devices(devices) + add_entities(devices) class Sense(Entity): diff --git a/homeassistant/components/sensor/sensehat.py b/homeassistant/components/sensor/sensehat.py index f0e566f718f613..15c73d990e17d1 100644 --- a/homeassistant/components/sensor/sensehat.py +++ b/homeassistant/components/sensor/sensehat.py @@ -58,14 +58,14 @@ def get_average(temp_base): return temp_avg -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Sense HAT sensor platform.""" data = SenseHatData(config.get(CONF_IS_HAT_ATTACHED)) dev = [] for variable in config[CONF_DISPLAY_OPTIONS]: dev.append(SenseHatSensor(data, variable)) - add_devices(dev, True) + add_entities(dev, True) class SenseHatSensor(Entity): diff --git a/homeassistant/components/sensor/serial.py b/homeassistant/components/sensor/serial.py index 521dbce7df2aff..39b69e0a8c406a 100644 --- a/homeassistant/components/sensor/serial.py +++ b/homeassistant/components/sensor/serial.py @@ -36,7 +36,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Serial sensor platform.""" name = config.get(CONF_NAME) port = config.get(CONF_SERIAL_PORT) @@ -50,7 +51,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): hass.bus.async_listen_once( EVENT_HOMEASSISTANT_STOP, sensor.stop_serial_read()) - async_add_devices([sensor], True) + async_add_entities([sensor], True) class SerialSensor(Entity): diff --git a/homeassistant/components/sensor/serial_pm.py b/homeassistant/components/sensor/serial_pm.py index d2157066625a0c..46dfc9fae75279 100644 --- a/homeassistant/components/sensor/serial_pm.py +++ b/homeassistant/components/sensor/serial_pm.py @@ -27,7 +27,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available PM sensors.""" from pmsensor import serial_pm as pm @@ -54,7 +54,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name = 'PM{}'.format(pmname) dev.append(ParticulateMatterSensor(coll, name, pmname)) - add_devices(dev) + add_entities(dev) class ParticulateMatterSensor(Entity): diff --git a/homeassistant/components/sensor/shodan.py b/homeassistant/components/sensor/shodan.py index dfc49ce6639879..fd462d6811c54c 100644 --- a/homeassistant/components/sensor/shodan.py +++ b/homeassistant/components/sensor/shodan.py @@ -14,7 +14,7 @@ from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY, CONF_NAME from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['shodan==1.9.0'] +REQUIREMENTS = ['shodan==1.9.1'] _LOGGER = logging.getLogger(__name__) @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Shodan sensor.""" import shodan @@ -50,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.warning("Unable to connect to Shodan.io: %s", error) return False - add_devices([ShodanSensor(data, name)], True) + add_entities([ShodanSensor(data, name)], True) class ShodanSensor(Entity): diff --git a/homeassistant/components/sensor/sht31.py b/homeassistant/components/sensor/sht31.py index 2aeff8e73d8271..4b8498497715d6 100644 --- a/homeassistant/components/sensor/sht31.py +++ b/homeassistant/components/sensor/sht31.py @@ -46,8 +46,8 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the sensor platform.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the sensor platform.""" from Adafruit_SHT31 import SHT31 i2c_address = config.get(CONF_I2C_ADDRESS) @@ -72,7 +72,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name = "{} {}".format(config.get(CONF_NAME), sensor_type.capitalize()) devs.append(sensor_class(sensor_client, name)) - add_devices(devs) + add_entities(devs) class SHTClient: diff --git a/homeassistant/components/sensor/sigfox.py b/homeassistant/components/sensor/sigfox.py index 408435a966701e..5e2a56cadc36ee 100644 --- a/homeassistant/components/sensor/sigfox.py +++ b/homeassistant/components/sensor/sigfox.py @@ -32,7 +32,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the sigfox sensor.""" api_login = config[CONF_API_LOGIN] api_password = config[CONF_API_PASSWORD] @@ -47,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors = [] for device in devices: sensors.append(SigfoxDevice(device, auth, name)) - add_devices(sensors, True) + add_entities(sensors, True) def epoch_to_datetime(epoch_time): @@ -77,7 +77,7 @@ def check_credentials(self): _LOGGER.error( "Unable to login to Sigfox API, error code %s", str( response.status_code)) - raise ValueError('Sigfox component not setup') + raise ValueError('Sigfox component not set up') return True def get_device_types(self): diff --git a/homeassistant/components/sensor/simulated.py b/homeassistant/components/sensor/simulated.py index 419ca7c13fb175..8f2c3dd36e042a 100644 --- a/homeassistant/components/sensor/simulated.py +++ b/homeassistant/components/sensor/simulated.py @@ -54,7 +54,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the simulated sensor.""" name = config.get(CONF_NAME) unit = config.get(CONF_UNIT) @@ -68,7 +68,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensor = SimulatedSensor(name, unit, amp, mean, period, phase, fwhm, seed, relative_to_epoch) - add_devices([sensor], True) + add_entities([sensor], True) class SimulatedSensor(Entity): diff --git a/homeassistant/components/sensor/skybeacon.py b/homeassistant/components/sensor/skybeacon.py index 2731587ed710e3..441053a7e7e778 100644 --- a/homeassistant/components/sensor/skybeacon.py +++ b/homeassistant/components/sensor/skybeacon.py @@ -39,15 +39,15 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Skybeacon sensor.""" name = config.get(CONF_NAME) mac = config.get(CONF_MAC) _LOGGER.debug("Setting up...") mon = Monitor(hass, mac, name) - add_devices([SkybeaconTemp(name, mon)]) - add_devices([SkybeaconHumid(name, mon)]) + add_entities([SkybeaconTemp(name, mon)]) + add_entities([SkybeaconHumid(name, mon)]) def monitor_stop(_service_or_event): """Stop the monitor thread.""" diff --git a/homeassistant/components/sensor/skybell.py b/homeassistant/components/sensor/skybell.py index dc7295f463a4e4..de8c3a5694d3bc 100644 --- a/homeassistant/components/sensor/skybell.py +++ b/homeassistant/components/sensor/skybell.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the platform for a Skybell device.""" skybell = hass.data.get(SKYBELL_DOMAIN) @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for device in skybell.get_devices(): sensors.append(SkybellSensor(device, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) class SkybellSensor(SkybellDevice): diff --git a/homeassistant/components/sensor/sleepiq.py b/homeassistant/components/sensor/sleepiq.py index cf35a400e474c1..2c97d7eb1e12f7 100644 --- a/homeassistant/components/sensor/sleepiq.py +++ b/homeassistant/components/sensor/sleepiq.py @@ -10,7 +10,7 @@ ICON = 'mdi:hotel' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SleepIQ sensors.""" if discovery_info is None: return @@ -22,7 +22,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for bed_id, _ in data.beds.items(): for side in sleepiq.SIDES: dev.append(SleepNumberSensor(data, bed_id, side)) - add_devices(dev) + add_entities(dev) class SleepNumberSensor(sleepiq.SleepIQSensor): diff --git a/homeassistant/components/sensor/sma.py b/homeassistant/components/sensor/sma.py index 2be46da0bdb01c..945c3873bb637c 100644 --- a/homeassistant/components/sensor/sma.py +++ b/homeassistant/components/sensor/sma.py @@ -64,7 +64,8 @@ def _check_sensor_schema(conf): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up SMA WebConnect sensor.""" import pysma @@ -93,7 +94,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): sensor_defs = {name: val for name, val in sensor_defs.items() if name in used_sensors} - async_add_devices(hass_sensors) + async_add_entities(hass_sensors) # Init the SMA interface session = async_get_clientsession(hass) diff --git a/homeassistant/components/sensor/smappee.py b/homeassistant/components/sensor/smappee.py index 783c2aad4693ae..65f21815960786 100644 --- a/homeassistant/components/sensor/smappee.py +++ b/homeassistant/components/sensor/smappee.py @@ -49,7 +49,7 @@ SCAN_INTERVAL = timedelta(seconds=30) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Smappee sensor.""" smappee = hass.data[DATA_SMAPPEE] @@ -80,7 +80,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev.append(SmappeeSensor(smappee, None, sensor, SENSOR_TYPES[sensor])) - add_devices(dev, True) + add_entities(dev, True) class SmappeeSensor(Entity): diff --git a/homeassistant/components/sensor/snmp.py b/homeassistant/components/sensor/snmp.py index e6119ab80b6d31..f7d160ecf2f522 100644 --- a/homeassistant/components/sensor/snmp.py +++ b/homeassistant/components/sensor/snmp.py @@ -53,7 +53,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SNMP sensor.""" from pysnmp.hlapi import ( getCmd, CommunityData, SnmpEngine, UdpTransportTarget, ContextData, @@ -86,7 +86,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data = SnmpData( host, port, community, baseoid, version, accept_errors, default_value) - add_devices([SnmpSensor(data, name, unit, value_template)], True) + add_entities([SnmpSensor(data, name, unit, value_template)], True) class SnmpSensor(Entity): diff --git a/homeassistant/components/sensor/sochain.py b/homeassistant/components/sensor/sochain.py index 572d0f529218a6..9f8982f871f33e 100644 --- a/homeassistant/components/sensor/sochain.py +++ b/homeassistant/components/sensor/sochain.py @@ -36,7 +36,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the sochain sensors.""" from pysochain import ChainSo address = config.get(CONF_ADDRESS) @@ -46,7 +47,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): session = async_get_clientsession(hass) chainso = ChainSo(network, address, hass.loop, session) - async_add_devices([SochainSensor(name, network.upper(), chainso)], True) + async_add_entities([SochainSensor(name, network.upper(), chainso)], True) class SochainSensor(Entity): diff --git a/homeassistant/components/sensor/socialblade.py b/homeassistant/components/sensor/socialblade.py index 1e0084e1404613..9a73e9cdd68230 100644 --- a/homeassistant/components/sensor/socialblade.py +++ b/homeassistant/components/sensor/socialblade.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Social Blade sensor.""" social_blade = SocialBladeSensor( config[CHANNEL_ID], config[CONF_NAME]) @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if social_blade.valid_channel_id is False: return - add_devices([social_blade]) + add_entities([social_blade]) class SocialBladeSensor(Entity): diff --git a/homeassistant/components/sensor/sonarr.py b/homeassistant/components/sensor/sonarr.py index c2fd6c8066330f..b0e87992e39d66 100644 --- a/homeassistant/components/sensor/sonarr.py +++ b/homeassistant/components/sensor/sonarr.py @@ -67,10 +67,10 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Sonarr platform.""" conditions = config.get(CONF_MONITORED_CONDITIONS) - add_devices( + add_entities( [SonarrSensor(hass, config, sensor) for sensor in conditions], True) diff --git a/homeassistant/components/sensor/speedtest.py b/homeassistant/components/sensor/speedtest.py index 8c1ffc03786216..8da7374f231363 100644 --- a/homeassistant/components/sensor/speedtest.py +++ b/homeassistant/components/sensor/speedtest.py @@ -61,7 +61,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Speedtest sensor.""" data = SpeedtestData(hass, config) @@ -69,7 +69,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for sensor in config[CONF_MONITORED_CONDITIONS]: dev.append(SpeedtestSensor(data, sensor)) - add_devices(dev) + add_entities(dev) def update(call=None): """Update service for manual updates.""" diff --git a/homeassistant/components/sensor/spotcrime.py b/homeassistant/components/sensor/spotcrime.py index daa520f2ede077..46f5fdc1c85c97 100644 --- a/homeassistant/components/sensor/spotcrime.py +++ b/homeassistant/components/sensor/spotcrime.py @@ -44,7 +44,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Crime Reports platform.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) @@ -55,7 +55,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): include = config.get(CONF_INCLUDE) exclude = config.get(CONF_EXCLUDE) - add_devices([SpotCrimeSensor( + add_entities([SpotCrimeSensor( name, latitude, longitude, radius, include, exclude, api_key, days)], True) diff --git a/homeassistant/components/sensor/sql.py b/homeassistant/components/sensor/sql.py index 83f5478867fe30..a2e9549a117e7c 100644 --- a/homeassistant/components/sensor/sql.py +++ b/homeassistant/components/sensor/sql.py @@ -48,7 +48,7 @@ def validate_sql_select(value): }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SQL sensor platform.""" db_url = config.get(CONF_DB_URL, None) if not db_url: @@ -87,7 +87,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ) queries.append(sensor) - add_devices(queries, True) + add_entities(queries, True) class SQLSensor(Entity): diff --git a/homeassistant/components/sensor/startca.py b/homeassistant/components/sensor/startca.py index 374e14c5ac2ea2..d9a52e4aa235be 100644 --- a/homeassistant/components/sensor/startca.py +++ b/homeassistant/components/sensor/startca.py @@ -58,7 +58,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the sensor platform.""" websession = async_get_clientsession(hass) apikey = config.get(CONF_API_KEY) @@ -74,7 +75,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): sensors = [] for variable in config[CONF_MONITORED_VARIABLES]: sensors.append(StartcaSensor(ts_data, variable, name)) - async_add_devices(sensors, True) + async_add_entities(sensors, True) class StartcaSensor(Entity): diff --git a/homeassistant/components/sensor/statistics.py b/homeassistant/components/sensor/statistics.py index 353330909105c5..b1cfd447f0ee7f 100644 --- a/homeassistant/components/sensor/statistics.py +++ b/homeassistant/components/sensor/statistics.py @@ -54,14 +54,15 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Statistics sensor.""" entity_id = config.get(CONF_ENTITY_ID) name = config.get(CONF_NAME) sampling_size = config.get(CONF_SAMPLING_SIZE) max_age = config.get(CONF_MAX_AGE, None) - async_add_devices( + async_add_entities( [StatisticsSensor(hass, entity_id, name, sampling_size, max_age)], True) return True diff --git a/homeassistant/components/sensor/steam_online.py b/homeassistant/components/sensor/steam_online.py index 7521b74cd28e43..861a5958dd30d8 100644 --- a/homeassistant/components/sensor/steam_online.py +++ b/homeassistant/components/sensor/steam_online.py @@ -36,14 +36,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Steam platform.""" import steam as steamod steamod.api.key.set(config.get(CONF_API_KEY)) # Initialize steammods app list before creating sensors # to benefit from internal caching of the list. steam_app_list = steamod.apps.app_list() - add_devices( + add_entities( [SteamSensor(account, steamod, steam_app_list) diff --git a/homeassistant/components/sensor/supervisord.py b/homeassistant/components/sensor/supervisord.py index 5a302462bbf188..894881dad86a34 100644 --- a/homeassistant/components/sensor/supervisord.py +++ b/homeassistant/components/sensor/supervisord.py @@ -26,7 +26,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Supervisord platform.""" url = config.get(CONF_URL) try: @@ -36,7 +36,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Could not connect to Supervisord") return False - add_devices( + add_entities( [SupervisorProcessSensor(info, supervisor_server) for info in processes], True) diff --git a/homeassistant/components/sensor/swiss_hydrological_data.py b/homeassistant/components/sensor/swiss_hydrological_data.py index b4536b48c9ecf3..fb55c22b2e8d9f 100644 --- a/homeassistant/components/sensor/swiss_hydrological_data.py +++ b/homeassistant/components/sensor/swiss_hydrological_data.py @@ -49,7 +49,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Swiss hydrological sensor.""" import xmltodict @@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return False data = HydrologicalData(station) - add_devices([SwissHydrologicalDataSensor(name, data)], True) + add_entities([SwissHydrologicalDataSensor(name, data)], True) class SwissHydrologicalDataSensor(Entity): diff --git a/homeassistant/components/sensor/swiss_public_transport.py b/homeassistant/components/sensor/swiss_public_transport.py index 72c6aa2e1a3dfd..6f44350c5cf61f 100644 --- a/homeassistant/components/sensor/swiss_public_transport.py +++ b/homeassistant/components/sensor/swiss_public_transport.py @@ -48,7 +48,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Swiss public transport sensor.""" from opendata_transport import OpendataTransport, exceptions @@ -67,7 +67,7 @@ async def async_setup_platform( "if your station names are valid") return - async_add_devices( + async_add_entities( [SwissPublicTransportSensor(opendata, start, destination, name)]) diff --git a/homeassistant/components/sensor/syncthru.py b/homeassistant/components/sensor/syncthru.py index a24482bda012f8..45a529012e3cab 100644 --- a/homeassistant/components/sensor/syncthru.py +++ b/homeassistant/components/sensor/syncthru.py @@ -60,7 +60,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SyncThru component.""" from pysyncthru import SyncThru, test_syncthru @@ -101,7 +101,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if 'output_tray_{}'.format(key) in monitored: devices.append(SyncThruOutputTraySensor(printer, name, key)) - add_devices(devices, True) + add_entities(devices, True) class SyncThruSensor(Entity): diff --git a/homeassistant/components/sensor/synologydsm.py b/homeassistant/components/sensor/synologydsm.py index d431805ab191d2..39a9e75c47b8e6 100644 --- a/homeassistant/components/sensor/synologydsm.py +++ b/homeassistant/components/sensor/synologydsm.py @@ -86,7 +86,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Synology NAS Sensor.""" def run_setup(event): """Wait until Home Assistant is fully initialized before creating. @@ -123,7 +123,7 @@ def run_setup(event): for variable in monitored_conditions if variable in _STORAGE_DSK_MON_COND] - add_devices(sensors, True) + add_entities(sensors, True) # Wait until start event is sent to load this component. hass.bus.listen_once(EVENT_HOMEASSISTANT_START, run_setup) diff --git a/homeassistant/components/sensor/systemmonitor.py b/homeassistant/components/sensor/systemmonitor.py index 1883ee89d4e2f6..aa448ddf56ee0e 100644 --- a/homeassistant/components/sensor/systemmonitor.py +++ b/homeassistant/components/sensor/systemmonitor.py @@ -15,7 +15,7 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['psutil==5.4.6'] +REQUIREMENTS = ['psutil==5.4.7'] _LOGGER = logging.getLogger(__name__) @@ -67,7 +67,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the system monitor sensors.""" dev = [] for resource in config[CONF_RESOURCES]: @@ -76,7 +76,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev.append(SystemMonitorSensor( resource[CONF_TYPE], resource[CONF_ARG])) - add_devices(dev, True) + add_entities(dev, True) class SystemMonitorSensor(Entity): diff --git a/homeassistant/components/sensor/sytadin.py b/homeassistant/components/sensor/sytadin.py index ff8e7d7ddfeec2..18fa73f234157f 100644 --- a/homeassistant/components/sensor/sytadin.py +++ b/homeassistant/components/sensor/sytadin.py @@ -18,7 +18,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['beautifulsoup4==4.6.1'] +REQUIREMENTS = ['beautifulsoup4==4.6.3'] _LOGGER = logging.getLogger(__name__) @@ -49,7 +49,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up of the Sytadin Traffic sensor platform.""" name = config.get(CONF_NAME) @@ -61,7 +61,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev.append(SytadinSensor( sytadin, name, option, SENSOR_TYPES[option][0], SENSOR_TYPES[option][1])) - add_devices(dev, True) + add_entities(dev, True) class SytadinSensor(Entity): diff --git a/homeassistant/components/sensor/tado.py b/homeassistant/components/sensor/tado.py index aa6314b8c5bb98..46ad6206fff538 100644 --- a/homeassistant/components/sensor/tado.py +++ b/homeassistant/components/sensor/tado.py @@ -22,7 +22,7 @@ HOT_WATER_SENSOR_TYPES = ['power', 'link', 'tado mode', 'overlay'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the sensor platform.""" tado = hass.data[DATA_TADO] @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): me_data['homes'][0]['id'], "tado bridge status")) if sensor_items: - add_devices(sensor_items, True) + add_entities(sensor_items, True) def create_zone_sensor(tado, zone, name, zone_id, variable): diff --git a/homeassistant/components/sensor/tahoma.py b/homeassistant/components/sensor/tahoma.py index 6c6c296652a5c7..eafc6fdf616578 100644 --- a/homeassistant/components/sensor/tahoma.py +++ b/homeassistant/components/sensor/tahoma.py @@ -19,13 +19,13 @@ SCAN_INTERVAL = timedelta(seconds=10) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tahoma controller devices.""" controller = hass.data[TAHOMA_DOMAIN]['controller'] devices = [] for device in hass.data[TAHOMA_DOMAIN]['devices']['sensor']: devices.append(TahomaSensor(device, controller)) - add_devices(devices, True) + add_entities(devices, True) class TahomaSensor(TahomaDevice, Entity): diff --git a/homeassistant/components/sensor/tank_utility.py b/homeassistant/components/sensor/tank_utility.py index 01ace415159798..55928a80f136ee 100644 --- a/homeassistant/components/sensor/tank_utility.py +++ b/homeassistant/components/sensor/tank_utility.py @@ -47,7 +47,7 @@ ] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tank Utility sensor.""" from tank_utility import auth email = config.get(CONF_EMAIL) @@ -66,7 +66,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for device in devices: sensor = TankUtilitySensor(email, password, token, device) all_sensors.append(sensor) - add_devices(all_sensors, True) + add_entities(all_sensors, True) class TankUtilitySensor(Entity): diff --git a/homeassistant/components/sensor/tcp.py b/homeassistant/components/sensor/tcp.py index 30ceba776e9e50..19197c06295108 100644 --- a/homeassistant/components/sensor/tcp.py +++ b/homeassistant/components/sensor/tcp.py @@ -41,9 +41,9 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the TCP Sensor.""" - add_devices([TcpSensor(hass, config)]) + add_entities([TcpSensor(hass, config)]) class TcpSensor(Entity): diff --git a/homeassistant/components/sensor/ted5000.py b/homeassistant/components/sensor/ted5000.py index 7298181796a7ad..a6ea7cbd53454c 100644 --- a/homeassistant/components/sensor/ted5000.py +++ b/homeassistant/components/sensor/ted5000.py @@ -32,7 +32,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Ted5000 sensor.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev.append(Ted5000Sensor(gateway, name, mtu, 'W')) dev.append(Ted5000Sensor(gateway, name, mtu, 'V')) - add_devices(dev) + add_entities(dev) return True diff --git a/homeassistant/components/sensor/teksavvy.py b/homeassistant/components/sensor/teksavvy.py index 68a1cfc4fe17fa..87b89074cfb95d 100644 --- a/homeassistant/components/sensor/teksavvy.py +++ b/homeassistant/components/sensor/teksavvy.py @@ -59,7 +59,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the sensor platform.""" websession = async_get_clientsession(hass) apikey = config.get(CONF_API_KEY) @@ -75,7 +76,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): sensors = [] for variable in config[CONF_MONITORED_VARIABLES]: sensors.append(TekSavvySensor(ts_data, variable, name)) - async_add_devices(sensors, True) + async_add_entities(sensors, True) class TekSavvySensor(Entity): diff --git a/homeassistant/components/sensor/tellduslive.py b/homeassistant/components/sensor/tellduslive.py index 123c11021b4030..4676e08a247413 100644 --- a/homeassistant/components/sensor/tellduslive.py +++ b/homeassistant/components/sensor/tellduslive.py @@ -44,11 +44,11 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tellstick sensors.""" if discovery_info is None: return - add_devices(TelldusLiveSensor(hass, sensor) for sensor in discovery_info) + add_entities(TelldusLiveSensor(hass, sensor) for sensor in discovery_info) class TelldusLiveSensor(TelldusLiveEntity): diff --git a/homeassistant/components/sensor/tellstick.py b/homeassistant/components/sensor/tellstick.py index 2fc67e57162ced..aac97580f2c17e 100644 --- a/homeassistant/components/sensor/tellstick.py +++ b/homeassistant/components/sensor/tellstick.py @@ -37,7 +37,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tellstick sensors.""" from tellcore import telldus import tellcore.constants as tellcore_constants @@ -89,7 +89,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(TellstickSensor( sensor_name, tellcore_sensor, datatype, sensor_info)) - add_devices(sensors) + add_entities(sensors) class TellstickSensor(Entity): diff --git a/homeassistant/components/sensor/temper.py b/homeassistant/components/sensor/temper.py index f0a3e15834cf3e..72184df7c8f28f 100644 --- a/homeassistant/components/sensor/temper.py +++ b/homeassistant/components/sensor/temper.py @@ -33,7 +33,7 @@ def get_temper_devices(): return TemperHandler().get_devices() -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Temper sensors.""" temp_unit = hass.config.units.temperature_unit name = config.get(CONF_NAME) @@ -47,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if idx != 0: name = name + '_' + str(idx) TEMPER_SENSORS.append(TemperSensor(dev, temp_unit, name, scaling)) - add_devices(TEMPER_SENSORS) + add_entities(TEMPER_SENSORS) def reset_devices(): diff --git a/homeassistant/components/sensor/template.py b/homeassistant/components/sensor/template.py index 23c7c13f0edec1..f64e8b122ca895 100644 --- a/homeassistant/components/sensor/template.py +++ b/homeassistant/components/sensor/template.py @@ -42,7 +42,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the template sensors.""" sensors = [] @@ -96,7 +97,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): _LOGGER.error("No sensors added") return False - async_add_devices(sensors) + async_add_entities(sensors) return True diff --git a/homeassistant/components/sensor/tesla.py b/homeassistant/components/sensor/tesla.py index 3233ebb178085c..51b7ea2325d63f 100644 --- a/homeassistant/components/sensor/tesla.py +++ b/homeassistant/components/sensor/tesla.py @@ -21,7 +21,7 @@ SCAN_INTERVAL = timedelta(minutes=5) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tesla sensor platform.""" controller = hass.data[TESLA_DOMAIN]['devices']['controller'] devices = [] @@ -32,7 +32,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices.append(TeslaSensor(device, controller, 'outside')) else: devices.append(TeslaSensor(device, controller)) - add_devices(devices, True) + add_entities(devices, True) class TeslaSensor(TeslaDevice, Entity): diff --git a/homeassistant/components/sensor/thethingsnetwork.py b/homeassistant/components/sensor/thethingsnetwork.py index 0f27b65640441c..02661f2211d889 100644 --- a/homeassistant/components/sensor/thethingsnetwork.py +++ b/homeassistant/components/sensor/thethingsnetwork.py @@ -39,7 +39,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up The Things Network Data storage sensors.""" ttn = hass.data.get(DATA_TTN) device_id = config.get(CONF_DEVICE_ID) @@ -58,7 +59,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for value, unit_of_measurement in values.items(): devices.append(TtnDataSensor( ttn_data_storage, device_id, value, unit_of_measurement)) - async_add_devices(devices, True) + async_add_entities(devices, True) class TtnDataSensor(Entity): diff --git a/homeassistant/components/sensor/thinkingcleaner.py b/homeassistant/components/sensor/thinkingcleaner.py index 0b936d8c8c782e..17e2f717f5af9d 100644 --- a/homeassistant/components/sensor/thinkingcleaner.py +++ b/homeassistant/components/sensor/thinkingcleaner.py @@ -51,7 +51,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ThinkingCleaner platform.""" from pythinkingcleaner import Discovery @@ -70,7 +70,7 @@ def update_devices(): dev.append(ThinkingCleanerSensor(device, type_name, update_devices)) - add_devices(dev) + add_entities(dev) class ThinkingCleanerSensor(Entity): diff --git a/homeassistant/components/sensor/tibber.py b/homeassistant/components/sensor/tibber.py index c75c40dd929ca3..3670a5a59bdceb 100644 --- a/homeassistant/components/sensor/tibber.py +++ b/homeassistant/components/sensor/tibber.py @@ -34,7 +34,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Tibber sensor.""" import tibber @@ -50,7 +50,7 @@ async def async_setup_platform(hass, config, async_add_devices, except (asyncio.TimeoutError, aiohttp.ClientError): raise PlatformNotReady() - async_add_devices(dev, True) + async_add_entities(dev, True) class TibberSensor(Entity): diff --git a/homeassistant/components/sensor/time_date.py b/homeassistant/components/sensor/time_date.py index 0668b5bdbce246..e4c719acd0d56b 100644 --- a/homeassistant/components/sensor/time_date.py +++ b/homeassistant/components/sensor/time_date.py @@ -38,7 +38,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Time and Date sensor.""" if hass.config.time_zone is None: _LOGGER.error("Timezone is not set in Home Assistant configuration") @@ -51,7 +52,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): hass, device.point_in_time_listener, device.get_next_interval()) devices.append(device) - async_add_devices(devices, True) + async_add_entities(devices, True) class TimeDateSensor(Entity): diff --git a/homeassistant/components/sensor/toon.py b/homeassistant/components/sensor/toon.py index a8875f6904c7b8..fb057603a1ac36 100644 --- a/homeassistant/components/sensor/toon.py +++ b/homeassistant/components/sensor/toon.py @@ -16,7 +16,7 @@ STATE_ATTR_LAST_CONNECTED_CHANGE = 'last_connected_change' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Toon sensors.""" _toon_main = hass.data[toon_main.TOON_HANDLE] @@ -58,7 +58,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): smokedetector.device_uuid, 'alarm-bell', '%') ) - add_devices(sensor_items) + add_entities(sensor_items) class ToonSensor(Entity): diff --git a/homeassistant/components/sensor/torque.py b/homeassistant/components/sensor/torque.py index 4ed1b5907cf424..4941633677c833 100644 --- a/homeassistant/components/sensor/torque.py +++ b/homeassistant/components/sensor/torque.py @@ -46,14 +46,14 @@ def convert_pid(value): return int(value, 16) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Torque platform.""" vehicle = config.get(CONF_NAME) email = config.get(CONF_EMAIL) sensors = {} hass.http.register_view(TorqueReceiveDataView( - email, vehicle, sensors, add_devices)) + email, vehicle, sensors, add_entities)) return True @@ -63,12 +63,12 @@ class TorqueReceiveDataView(HomeAssistantView): url = API_PATH name = 'api:torque' - def __init__(self, email, vehicle, sensors, add_devices): + def __init__(self, email, vehicle, sensors, add_entities): """Initialize a Torque view.""" self.email = email self.vehicle = vehicle self.sensors = sensors - self.add_devices = add_devices + self.add_entities = add_entities @callback def get(self, request): @@ -102,7 +102,7 @@ def get(self, request): self.sensors[pid] = TorqueSensor( ENTITY_NAME_FORMAT.format(self.vehicle, names[pid]), units.get(pid, None)) - hass.async_add_job(self.add_devices, [self.sensors[pid]]) + hass.async_add_job(self.add_entities, [self.sensors[pid]]) return "OK!" diff --git a/homeassistant/components/sensor/tradfri.py b/homeassistant/components/sensor/tradfri.py index df931770cf21f5..0849169b7474ca 100644 --- a/homeassistant/components/sensor/tradfri.py +++ b/homeassistant/components/sensor/tradfri.py @@ -19,7 +19,7 @@ SCAN_INTERVAL = timedelta(minutes=5) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the IKEA Tradfri device platform.""" if discovery_info is None: @@ -33,7 +33,7 @@ async def async_setup_platform(hass, config, async_add_devices, devices_commands = await api(devices_command) all_devices = await api(devices_commands) devices = [dev for dev in all_devices if not dev.has_light_control] - async_add_devices(TradfriDevice(device, api) for device in devices) + async_add_entities(TradfriDevice(device, api) for device in devices) class TradfriDevice(Entity): diff --git a/homeassistant/components/sensor/trafikverket_weatherstation.py b/homeassistant/components/sensor/trafikverket_weatherstation.py index 77a2b0e7338e9e..a8ce6917dd3926 100644 --- a/homeassistant/components/sensor/trafikverket_weatherstation.py +++ b/homeassistant/components/sensor/trafikverket_weatherstation.py @@ -36,14 +36,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Trafikverket sensor platform.""" sensor_name = config.get(CONF_NAME) sensor_api = config.get(CONF_API_KEY) sensor_station = config.get(CONF_STATION) sensor_type = config.get(CONF_TYPE) - add_devices([TrafikverketWeatherStation( + add_entities([TrafikverketWeatherStation( sensor_name, sensor_api, sensor_station, sensor_type)], True) diff --git a/homeassistant/components/sensor/transmission.py b/homeassistant/components/sensor/transmission.py index 3e74b4549137de..a669db0e5be887 100644 --- a/homeassistant/components/sensor/transmission.py +++ b/homeassistant/components/sensor/transmission.py @@ -47,7 +47,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Transmission sensors.""" import transmissionrpc from transmissionrpc.error import TransmissionError @@ -75,7 +75,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in config[CONF_MONITORED_VARIABLES]: dev.append(TransmissionSensor(variable, transmission_api, name)) - add_devices(dev, True) + add_entities(dev, True) class TransmissionSensor(Entity): diff --git a/homeassistant/components/sensor/travisci.py b/homeassistant/components/sensor/travisci.py index 1ca08e7c0aaad2..40ae130d15025d 100644 --- a/homeassistant/components/sensor/travisci.py +++ b/homeassistant/components/sensor/travisci.py @@ -54,7 +54,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Travis CI sensor.""" from travispy import TravisPy from travispy.errors import TravisError @@ -92,7 +92,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append( TravisCISensor(travis, repo, user, branch, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) return True diff --git a/homeassistant/components/sensor/twitch.py b/homeassistant/components/sensor/twitch.py index 250911b49b1096..3763aa30fb4ad0 100644 --- a/homeassistant/components/sensor/twitch.py +++ b/homeassistant/components/sensor/twitch.py @@ -31,11 +31,11 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Twitch platform.""" channels = config.get(CONF_CHANNELS, []) - add_devices([TwitchSensor(channel) for channel in channels], True) + add_entities([TwitchSensor(channel) for channel in channels], True) class TwitchSensor(Entity): diff --git a/homeassistant/components/sensor/uber.py b/homeassistant/components/sensor/uber.py index cd476a1a2265ee..a97ccaffed0ac5 100644 --- a/homeassistant/components/sensor/uber.py +++ b/homeassistant/components/sensor/uber.py @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Uber sensor.""" from uber_rides.session import Session @@ -65,7 +65,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev.append(UberSensor( 'price', timeandpriceest, product_id, product)) - add_devices(dev, True) + add_entities(dev, True) class UberSensor(Entity): diff --git a/homeassistant/components/sensor/uk_transport.py b/homeassistant/components/sensor/uk_transport.py index 72d34411d5cfac..a7aba9a566bc70 100644 --- a/homeassistant/components/sensor/uk_transport.py +++ b/homeassistant/components/sensor/uk_transport.py @@ -47,7 +47,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Get the uk_transport sensor.""" sensors = [] number_sensors = len(config.get(CONF_QUERIES)) @@ -76,7 +76,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): calling_at, interval)) - add_devices(sensors, True) + add_entities(sensors, True) class UkTransportSensor(Entity): diff --git a/homeassistant/components/sensor/upnp.py b/homeassistant/components/sensor/upnp.py index 07b63553fcba70..d021312d15c5c5 100644 --- a/homeassistant/components/sensor/upnp.py +++ b/homeassistant/components/sensor/upnp.py @@ -27,7 +27,7 @@ } -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the IGD sensors.""" if discovery_info is None: @@ -36,7 +36,7 @@ async def async_setup_platform(hass, config, async_add_devices, device = hass.data[DATA_UPNP] service = device.find_first_service(CIC_SERVICE) unit = discovery_info['unit'] - async_add_devices([ + async_add_entities([ IGDSensor(service, t, unit if SENSOR_TYPES[t][1] else '#') for t in SENSOR_TYPES], True) diff --git a/homeassistant/components/sensor/ups.py b/homeassistant/components/sensor/ups.py index a864df384ad011..aa6ce930619ed3 100644 --- a/homeassistant/components/sensor/ups.py +++ b/homeassistant/components/sensor/ups.py @@ -38,7 +38,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the UPS platform.""" import upsmychoice try: @@ -50,8 +50,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.exception("Could not connect to UPS My Choice") return False - add_devices([UPSSensor(session, config.get(CONF_NAME), - config.get(CONF_UPDATE_INTERVAL))], True) + add_entities([UPSSensor(session, config.get(CONF_NAME), + config.get(CONF_UPDATE_INTERVAL))], True) class UPSSensor(Entity): diff --git a/homeassistant/components/sensor/uptime.py b/homeassistant/components/sensor/uptime.py index 7e893899815176..197233461fb625 100644 --- a/homeassistant/components/sensor/uptime.py +++ b/homeassistant/components/sensor/uptime.py @@ -28,12 +28,12 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the uptime sensor platform.""" name = config.get(CONF_NAME) units = config.get(CONF_UNIT_OF_MEASUREMENT) - async_add_devices([UptimeSensor(name, units)], True) + async_add_entities([UptimeSensor(name, units)], True) class UptimeSensor(Entity): diff --git a/homeassistant/components/sensor/uscis.py b/homeassistant/components/sensor/uscis.py index ed3c9ca8587557..e3a917b0a5a789 100644 --- a/homeassistant/components/sensor/uscis.py +++ b/homeassistant/components/sensor/uscis.py @@ -28,12 +28,12 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setting the platform in HASS and Case Information.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the platform in HASS and Case Information.""" uscis = UscisSensor(config['case_id'], config[CONF_FRIENDLY_NAME]) uscis.update() if uscis.valid_case_id: - add_devices([uscis]) + add_entities([uscis]) else: _LOGGER.error("Setup USCIS Sensor Fail" " check if your Case ID is Valid") @@ -72,7 +72,7 @@ def device_state_attributes(self): @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): - """Using Request to access USCIS website and fetch data.""" + """Fetch data from the USCIS website and update state attributes.""" import uscisstatus try: status = uscisstatus.get_case_status(self._case_id) diff --git a/homeassistant/components/sensor/usps.py b/homeassistant/components/sensor/usps.py index 6ca18442883be2..17fa11fe8d337b 100644 --- a/homeassistant/components/sensor/usps.py +++ b/homeassistant/components/sensor/usps.py @@ -20,13 +20,13 @@ STATUS_DELIVERED = 'delivered' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the USPS platform.""" if discovery_info is None: return usps = hass.data[DATA_USPS] - add_devices([USPSPackageSensor(usps), USPSMailSensor(usps)], True) + add_entities([USPSPackageSensor(usps), USPSMailSensor(usps)], True) class USPSPackageSensor(Entity): diff --git a/homeassistant/components/sensor/vasttrafik.py b/homeassistant/components/sensor/vasttrafik.py index 8cd084e1b718ff..7ef4170dd5a918 100644 --- a/homeassistant/components/sensor/vasttrafik.py +++ b/homeassistant/components/sensor/vasttrafik.py @@ -53,7 +53,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the departure sensor.""" import vasttrafik planner = vasttrafik.JournyPlanner( @@ -65,7 +65,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): vasttrafik, planner, departure.get(CONF_NAME), departure.get(CONF_FROM), departure.get(CONF_HEADING), departure.get(CONF_LINES), departure.get(CONF_DELAY))) - add_devices(sensors, True) + add_entities(sensors, True) class VasttrafikDepartureSensor(Entity): diff --git a/homeassistant/components/sensor/vera.py b/homeassistant/components/sensor/vera.py index eaef3dcf7f7379..c9b5a36afa36b1 100644 --- a/homeassistant/components/sensor/vera.py +++ b/homeassistant/components/sensor/vera.py @@ -22,9 +22,9 @@ SCAN_INTERVAL = timedelta(seconds=5) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vera controller devices.""" - add_devices( + add_entities( [VeraSensor(device, hass.data[VERA_CONTROLLER]) for device in hass.data[VERA_DEVICES]['sensor']], True) diff --git a/homeassistant/components/sensor/verisure.py b/homeassistant/components/sensor/verisure.py index 187a9bd7935c2e..b6ea75ae8cc878 100644 --- a/homeassistant/components/sensor/verisure.py +++ b/homeassistant/components/sensor/verisure.py @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Verisure platform.""" sensors = [] hub.update_overview() @@ -38,7 +38,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for device_label in hub.get( "$.eventCounts[?(@.deviceType=='MOUSE1')].deviceLabel")]) - add_devices(sensors) + add_entities(sensors) class VerisureThermometer(Entity): diff --git a/homeassistant/components/sensor/version.py b/homeassistant/components/sensor/version.py index db61d05978393f..eba4b1b83508de 100644 --- a/homeassistant/components/sensor/version.py +++ b/homeassistant/components/sensor/version.py @@ -23,11 +23,11 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the Version sensor platform.""" name = config.get(CONF_NAME) - async_add_devices([VersionSensor(name)]) + async_add_entities([VersionSensor(name)]) class VersionSensor(Entity): diff --git a/homeassistant/components/sensor/viaggiatreno.py b/homeassistant/components/sensor/viaggiatreno.py index 43ba80d2630548..1dd8523eb4b12f 100644 --- a/homeassistant/components/sensor/viaggiatreno.py +++ b/homeassistant/components/sensor/viaggiatreno.py @@ -57,14 +57,15 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the ViaggiaTreno platform.""" train_id = config.get(CONF_TRAIN_ID) station_id = config.get(CONF_STATION_ID) name = config.get(CONF_NAME) if not name: name = DEFAULT_NAME.format(train_id) - async_add_devices([ViaggiaTrenoSensor(train_id, station_id, name)]) + async_add_entities([ViaggiaTrenoSensor(train_id, station_id, name)]) @asyncio.coroutine diff --git a/homeassistant/components/sensor/volvooncall.py b/homeassistant/components/sensor/volvooncall.py index 78e8a7e76c63ad..a3f0c55b954d94 100644 --- a/homeassistant/components/sensor/volvooncall.py +++ b/homeassistant/components/sensor/volvooncall.py @@ -14,11 +14,11 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Volvo sensors.""" if discovery_info is None: return - add_devices([VolvoSensor(hass, *discovery_info)]) + add_entities([VolvoSensor(hass, *discovery_info)]) class VolvoSensor(VolvoEntity): diff --git a/homeassistant/components/sensor/vultr.py b/homeassistant/components/sensor/vultr.py index 291639c81d6b4f..a727e5bd2ecd5d 100644 --- a/homeassistant/components/sensor/vultr.py +++ b/homeassistant/components/sensor/vultr.py @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vultr subscription (server) sensor.""" vultr = hass.data[DATA_VULTR] @@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for condition in monitored_conditions: sensors.append(VultrSensor(vultr, subscription, condition, name)) - add_devices(sensors, True) + add_entities(sensors, True) class VultrSensor(Entity): diff --git a/homeassistant/components/sensor/waqi.py b/homeassistant/components/sensor/waqi.py index bf2e263a0bbc04..9f90f465fb2ab2 100644 --- a/homeassistant/components/sensor/waqi.py +++ b/homeassistant/components/sensor/waqi.py @@ -60,7 +60,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the requested World Air Quality Index locations.""" import waqiasync @@ -86,7 +87,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): asyncio.TimeoutError): _LOGGER.exception('Failed to connect to WAQI servers.') raise PlatformNotReady - async_add_devices(dev, True) + async_add_entities(dev, True) class WaqiSensor(Entity): diff --git a/homeassistant/components/sensor/waterfurnace.py b/homeassistant/components/sensor/waterfurnace.py index 76c5d2f648ed60..806b40551dff0d 100644 --- a/homeassistant/components/sensor/waterfurnace.py +++ b/homeassistant/components/sensor/waterfurnace.py @@ -46,7 +46,7 @@ def __init__(self, friendly_name, field, icon="mdi:gauge", ] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Waterfurnace sensor.""" if discovery_info is None: return @@ -56,7 +56,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for sconfig in SENSORS: sensors.append(WaterFurnaceSensor(client, sconfig)) - add_devices(sensors) + add_entities(sensors) class WaterFurnaceSensor(Entity): diff --git a/homeassistant/components/sensor/waze_travel_time.py b/homeassistant/components/sensor/waze_travel_time.py index 023da72299b901..8d046b0de0d0ef 100644 --- a/homeassistant/components/sensor/waze_travel_time.py +++ b/homeassistant/components/sensor/waze_travel_time.py @@ -55,7 +55,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Waze travel time sensor platform.""" destination = config.get(CONF_DESTINATION) name = config.get(CONF_NAME) @@ -68,7 +68,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensor = WazeTravelTime(name, origin, destination, region, incl_filter, excl_filter, realtime) - add_devices([sensor]) + add_entities([sensor]) # Wait until start event is sent to load this component. hass.bus.listen_once(EVENT_HOMEASSISTANT_START, sensor.update) diff --git a/homeassistant/components/sensor/whois.py b/homeassistant/components/sensor/whois.py index 21b1b99ca1377e..b589caddc791cb 100644 --- a/homeassistant/components/sensor/whois.py +++ b/homeassistant/components/sensor/whois.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the WHOIS sensor.""" from pythonwhois import get_whois from pythonwhois.shared import WhoisException @@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): try: if 'expiration_date' in get_whois(domain, normalized=True): - add_devices([WhoisSensor(name, domain)], True) + add_entities([WhoisSensor(name, domain)], True) else: _LOGGER.error( "WHOIS lookup for %s didn't contain expiration_date", diff --git a/homeassistant/components/sensor/wink.py b/homeassistant/components/sensor/wink.py index 75751bbbf8ac55..8e11b054b24366 100644 --- a/homeassistant/components/sensor/wink.py +++ b/homeassistant/components/sensor/wink.py @@ -18,7 +18,7 @@ SENSOR_TYPES = ['temperature', 'humidity', 'balance', 'proximity'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink platform.""" import pywink @@ -26,24 +26,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _id = sensor.object_id() + sensor.name() if _id not in hass.data[DOMAIN]['unique_ids']: if sensor.capability() in SENSOR_TYPES: - add_devices([WinkSensorDevice(sensor, hass)]) + add_entities([WinkSensorDevice(sensor, hass)]) for eggtray in pywink.get_eggtrays(): _id = eggtray.object_id() + eggtray.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkSensorDevice(eggtray, hass)]) + add_entities([WinkSensorDevice(eggtray, hass)]) for tank in pywink.get_propane_tanks(): _id = tank.object_id() + tank.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkSensorDevice(tank, hass)]) + add_entities([WinkSensorDevice(tank, hass)]) for piggy_bank in pywink.get_piggy_banks(): _id = piggy_bank.object_id() + piggy_bank.name() if _id not in hass.data[DOMAIN]['unique_ids']: try: if piggy_bank.capability() in SENSOR_TYPES: - add_devices([WinkSensorDevice(piggy_bank, hass)]) + add_entities([WinkSensorDevice(piggy_bank, hass)]) except AttributeError: _LOGGER.info("Device is not a sensor") diff --git a/homeassistant/components/sensor/wirelesstag.py b/homeassistant/components/sensor/wirelesstag.py index ad2115e9bd30c5..a68fb5d0caff39 100644 --- a/homeassistant/components/sensor/wirelesstag.py +++ b/homeassistant/components/sensor/wirelesstag.py @@ -1,5 +1,5 @@ """ -Sensor support for Wirelss Sensor Tags platform. +Sensor support for Wireless Sensor Tags platform. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.wirelesstag/ @@ -57,8 +57,8 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the sensor platform.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the sensor platform.""" platform = hass.data.get(WIRELESSTAG_DOMAIN) sensors = [] tags = platform.tags @@ -68,7 +68,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors.append(WirelessTagSensor( platform, tag, sensor_type, hass.config)) - add_devices(sensors, True) + add_entities(sensors, True) class WirelessTagSensor(WirelessTagBaseSensor): @@ -98,7 +98,7 @@ def allowed_sensors(cls, tag): else all_sensors) def __init__(self, api, tag, sensor_type, config): - """Constructor with platform(api), tag and hass sensor type.""" + """Initialize a WirelessTag sensor.""" super().__init__(api, tag) self._sensor_type = sensor_type diff --git a/homeassistant/components/sensor/worldclock.py b/homeassistant/components/sensor/worldclock.py index 1240480d4a3b4c..6bb5d1fee2ea6c 100644 --- a/homeassistant/components/sensor/worldclock.py +++ b/homeassistant/components/sensor/worldclock.py @@ -29,12 +29,12 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the World clock sensor.""" name = config.get(CONF_NAME) time_zone = dt_util.get_time_zone(config.get(CONF_TIME_ZONE)) - async_add_devices([WorldClockSensor(time_zone, name)], True) + async_add_entities([WorldClockSensor(time_zone, name)], True) class WorldClockSensor(Entity): diff --git a/homeassistant/components/sensor/worldtidesinfo.py b/homeassistant/components/sensor/worldtidesinfo.py index 597a971e208f3b..fea3e92a140a5f 100644 --- a/homeassistant/components/sensor/worldtidesinfo.py +++ b/homeassistant/components/sensor/worldtidesinfo.py @@ -1,24 +1,26 @@ """ -This component provides HA sensor support for the worldtides.info API. +Support for the worldtides.info API. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.worldtidesinfo/ """ +from datetime import timedelta import logging import time -from datetime import timedelta import requests import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import (CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, - CONF_NAME, STATE_UNKNOWN) +from homeassistant.const import ( + ATTR_ATTRIBUTION, CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME) +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity _LOGGER = logging.getLogger(__name__) +CONF_ATTRIBUTION = "Data provided by WorldTides" + DEFAULT_NAME = 'WorldTidesInfo' SCAN_INTERVAL = timedelta(seconds=3600) @@ -31,7 +33,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the WorldTidesInfo sensor.""" name = config.get(CONF_NAME) @@ -42,7 +44,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if None in (lat, lon): _LOGGER.error("Latitude or longitude not set in Home Assistant config") - add_devices([WorldTidesInfoSensor(name, lat, lon, key)], True) + tides = WorldTidesInfoSensor(name, lat, lon, key) + tides.update() + if tides.data.get('error') == 'No location found': + _LOGGER.error("Location not available") + return + + add_entities([tides]) class WorldTidesInfoSensor(Entity): @@ -64,13 +72,14 @@ def name(self): @property def device_state_attributes(self): """Return the state attributes of this device.""" - attr = {} - if "High" in str(self.data['extremes'][0]['type']): + attr = {ATTR_ATTRIBUTION: CONF_ATTRIBUTION} + + if 'High' in str(self.data['extremes'][0]['type']): attr['high_tide_time_utc'] = self.data['extremes'][0]['date'] attr['high_tide_height'] = self.data['extremes'][0]['height'] attr['low_tide_time_utc'] = self.data['extremes'][1]['date'] attr['low_tide_height'] = self.data['extremes'][1]['height'] - elif "Low" in str(self.data['extremes'][0]['type']): + elif 'Low' in str(self.data['extremes'][0]['type']): attr['high_tide_time_utc'] = self.data['extremes'][1]['date'] attr['high_tide_height'] = self.data['extremes'][1]['height'] attr['low_tide_time_utc'] = self.data['extremes'][0]['date'] @@ -81,30 +90,30 @@ def device_state_attributes(self): def state(self): """Return the state of the device.""" if self.data: - if "High" in str(self.data['extremes'][0]['type']): + if 'High' in str(self.data['extremes'][0]['type']): tidetime = time.strftime('%I:%M %p', time.localtime( self.data['extremes'][0]['dt'])) - return "High tide at %s" % (tidetime) - if "Low" in str(self.data['extremes'][0]['type']): + return "High tide at {}".format(tidetime) + if 'Low' in str(self.data['extremes'][0]['type']): tidetime = time.strftime('%I:%M %p', time.localtime( self.data['extremes'][0]['dt'])) - return "Low tide at %s" % (tidetime) - return STATE_UNKNOWN - return STATE_UNKNOWN + return "Low tide at {}".format(tidetime) + return None + return None def update(self): """Get the latest data from WorldTidesInfo API.""" start = int(time.time()) - resource = 'https://www.worldtides.info/api?extremes&length=86400' \ - '&key=%s&lat=%s&lon=%s&start=%s' % (self._key, self._lat, - self._lon, start) + resource = ('https://www.worldtides.info/api?extremes&length=86400' + '&key={}&lat={}&lon={}&start={}').format( + self._key, self._lat, self._lon, start) try: self.data = requests.get(resource, timeout=10).json() - _LOGGER.debug("Data = %s", self.data) - _LOGGER.info("Tide data queried with start time set to: %s", - (start)) + _LOGGER.debug("Data: %s", self.data) + _LOGGER.debug( + "Tide data queried with start time set to: %s", start) except ValueError as err: - _LOGGER.error("Check WorldTidesInfo %s", err.args) + _LOGGER.error( + "Error retrieving data from WorldTidesInfo: %s", err.args) self.data = None - raise diff --git a/homeassistant/components/sensor/worxlandroid.py b/homeassistant/components/sensor/worxlandroid.py index 8963bb135e029f..f6593f4b1c52c1 100644 --- a/homeassistant/components/sensor/worxlandroid.py +++ b/homeassistant/components/sensor/worxlandroid.py @@ -51,11 +51,11 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, +def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Worx Landroid sensors.""" for typ in ('battery', 'state'): - async_add_devices([WorxLandroidSensor(typ, config)]) + async_add_entities([WorxLandroidSensor(typ, config)]) class WorxLandroidSensor(Entity): diff --git a/homeassistant/components/sensor/wsdot.py b/homeassistant/components/sensor/wsdot.py index 0cd5ba44349825..84f2e8622c60a5 100644 --- a/homeassistant/components/sensor/wsdot.py +++ b/homeassistant/components/sensor/wsdot.py @@ -45,7 +45,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the WSDOT sensor.""" sensors = [] for travel_time in config.get(CONF_TRAVEL_TIMES): @@ -54,7 +54,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): WashingtonStateTravelTimeSensor( name, config.get(CONF_API_KEY), travel_time.get(CONF_ID))) - add_devices(sensors, True) + add_entities(sensors, True) class WashingtonStateTransportSensor(Entity): diff --git a/homeassistant/components/sensor/wunderground.py b/homeassistant/components/sensor/wunderground.py index 24ae2d0068ffb3..a14d4b947892b6 100644 --- a/homeassistant/components/sensor/wunderground.py +++ b/homeassistant/components/sensor/wunderground.py @@ -632,7 +632,7 @@ def _get_attributes(rest): async def async_setup_platform(hass: HomeAssistantType, config: ConfigType, - async_add_devices, discovery_info=None): + async_add_entities, discovery_info=None): """Set up the WUnderground sensor.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) @@ -656,7 +656,7 @@ async def async_setup_platform(hass: HomeAssistantType, config: ConfigType, if not rest.data: raise PlatformNotReady - async_add_devices(sensors, True) + async_add_entities(sensors, True) class WUndergroundSensor(Entity): diff --git a/homeassistant/components/sensor/xbox_live.py b/homeassistant/components/sensor/xbox_live.py index 250c74ee4933c4..3432927dda0ca7 100644 --- a/homeassistant/components/sensor/xbox_live.py +++ b/homeassistant/components/sensor/xbox_live.py @@ -27,7 +27,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Xbox platform.""" from xboxapi import xbox_api api = xbox_api.XboxApi(config.get(CONF_API_KEY)) @@ -48,7 +48,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices.append(new_device) if devices: - add_devices(devices, True) + add_entities(devices, True) class XboxSensor(Entity): diff --git a/homeassistant/components/sensor/xiaomi_aqara.py b/homeassistant/components/sensor/xiaomi_aqara.py index 32139b21976480..8a3a11db05148c 100644 --- a/homeassistant/components/sensor/xiaomi_aqara.py +++ b/homeassistant/components/sensor/xiaomi_aqara.py @@ -18,7 +18,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Perform the setup for Xiaomi devices.""" devices = [] for (_, gateway) in hass.data[PY_XIAOMI_GATEWAY].gateways.items(): @@ -41,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): elif device['model'] in ['gateway', 'gateway.v3', 'acpartner.v3']: devices.append(XiaomiSensor(device, 'Illumination', 'illumination', gateway)) - add_devices(devices) + add_entities(devices) class XiaomiSensor(XiaomiDevice): diff --git a/homeassistant/components/sensor/xiaomi_miio.py b/homeassistant/components/sensor/xiaomi_miio.py index 63d93d31cf37a4..15af57bf46bec2 100644 --- a/homeassistant/components/sensor/xiaomi_miio.py +++ b/homeassistant/components/sensor/xiaomi_miio.py @@ -25,7 +25,7 @@ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) -REQUIREMENTS = ['python-miio==0.4.0', 'construct==2.9.41'] +REQUIREMENTS = ['python-miio==0.4.1', 'construct==2.9.41'] ATTR_POWER = 'power' ATTR_CHARGING = 'charging' @@ -40,7 +40,7 @@ SUCCESS = ['ok'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the sensor from config.""" from miio import AirQualityMonitor, DeviceException @@ -68,7 +68,7 @@ async def async_setup_platform(hass, config, async_add_devices, raise PlatformNotReady hass.data[DATA_KEY][host] = device - async_add_devices([device], update_before_add=True) + async_add_entities([device], update_before_add=True) class XiaomiAirQualityMonitor(Entity): diff --git a/homeassistant/components/sensor/yahoo_finance.py b/homeassistant/components/sensor/yahoo_finance.py index 82cb7f845dc29a..4358dba2b25cd9 100644 --- a/homeassistant/components/sensor/yahoo_finance.py +++ b/homeassistant/components/sensor/yahoo_finance.py @@ -38,7 +38,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Yahoo Finance sensor.""" from yahoo_finance import Share @@ -52,7 +52,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data = YahooFinanceData(symbol) dev.append(YahooFinanceSensor(data, symbol)) - add_devices(dev, True) + add_entities(dev, True) class YahooFinanceSensor(Entity): diff --git a/homeassistant/components/sensor/yr.py b/homeassistant/components/sensor/yr.py index fcddf41af970f7..16ae98f9141b1d 100644 --- a/homeassistant/components/sensor/yr.py +++ b/homeassistant/components/sensor/yr.py @@ -67,7 +67,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Yr.no sensor.""" elevation = config.get(CONF_ELEVATION, hass.config.elevation or 0) forecast = config.get(CONF_FORECAST) @@ -88,7 +89,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): dev = [] for sensor_type in config[CONF_MONITORED_CONDITIONS]: dev.append(YrSensor(name, sensor_type)) - async_add_devices(dev) + async_add_entities(dev) weather = YrData(hass, coordinates, forecast, dev) async_track_utc_time_change(hass, weather.updating_devices, minute=31) diff --git a/homeassistant/components/sensor/yweather.py b/homeassistant/components/sensor/yweather.py index b2279e107da11b..e84a77b7bb69dd 100644 --- a/homeassistant/components/sensor/yweather.py +++ b/homeassistant/components/sensor/yweather.py @@ -51,7 +51,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Yahoo! weather sensor.""" from yahooweather import get_woeid, UNIT_C, UNIT_F @@ -88,7 +88,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for variable in config[CONF_MONITORED_CONDITIONS]: dev.append(YahooWeatherSensor(yahoo_api, name, forecast, variable)) - add_devices(dev, True) + add_entities(dev, True) class YahooWeatherSensor(Entity): diff --git a/homeassistant/components/sensor/zabbix.py b/homeassistant/components/sensor/zabbix.py index 21a3030b79b44f..7a468a9b90630c 100644 --- a/homeassistant/components/sensor/zabbix.py +++ b/homeassistant/components/sensor/zabbix.py @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Zabbix sensor platform.""" sensors = [] @@ -83,7 +83,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensor = ZabbixTriggerCountSensor(zapi) sensors.append(sensor) - add_devices(sensors) + add_entities(sensors) class ZabbixTriggerCountSensor(Entity): diff --git a/homeassistant/components/sensor/zamg.py b/homeassistant/components/sensor/zamg.py index e8e5fdec4d8ef4..c101e4da920ecd 100644 --- a/homeassistant/components/sensor/zamg.py +++ b/homeassistant/components/sensor/zamg.py @@ -71,7 +71,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZAMG sensor platform.""" name = config.get(CONF_NAME) latitude = config.get(CONF_LATITUDE, hass.config.latitude) @@ -91,8 +91,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Received error from ZAMG: %s", err) return False - add_devices([ZamgSensor(probe, variable, name) - for variable in config[CONF_MONITORED_CONDITIONS]], True) + add_entities([ZamgSensor(probe, variable, name) + for variable in config[CONF_MONITORED_CONDITIONS]], True) class ZamgSensor(Entity): diff --git a/homeassistant/components/sensor/zestimate.py b/homeassistant/components/sensor/zestimate.py index d8c759f17273bc..a04df22cf072cd 100644 --- a/homeassistant/components/sensor/zestimate.py +++ b/homeassistant/components/sensor/zestimate.py @@ -49,7 +49,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Zestimate sensor.""" name = config.get(CONF_NAME) properties = config[CONF_ZPID] @@ -59,7 +59,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for zpid in properties: params['zpid'] = zpid sensors.append(ZestimateDataSensor(name, params)) - add_devices(sensors, True) + add_entities(sensors, True) class ZestimateDataSensor(Entity): diff --git a/homeassistant/components/sensor/zha.py b/homeassistant/components/sensor/zha.py index 53e0e8d0329a52..6202f8cb7efd29 100644 --- a/homeassistant/components/sensor/zha.py +++ b/homeassistant/components/sensor/zha.py @@ -18,14 +18,15 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up Zigbee Home Automation sensors.""" discovery_info = zha.get_discovery_info(hass, discovery_info) if discovery_info is None: return sensor = yield from make_sensor(discovery_info) - async_add_devices([sensor], update_before_add=True) + async_add_entities([sensor], update_before_add=True) @asyncio.coroutine diff --git a/homeassistant/components/sensor/zigbee.py b/homeassistant/components/sensor/zigbee.py index 37cc6fabe2e71d..a0a1b8bb7fd329 100644 --- a/homeassistant/components/sensor/zigbee.py +++ b/homeassistant/components/sensor/zigbee.py @@ -30,7 +30,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZigBee platform. Uses the 'type' config value to work out which type of ZigBee sensor we're @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.exception("Unknown ZigBee sensor type: %s", typ) return - add_devices([sensor_class(hass, config_class(config))], True) + add_entities([sensor_class(hass, config_class(config))], True) class ZigBeeTemperatureSensor(Entity): diff --git a/homeassistant/components/sensor/zoneminder.py b/homeassistant/components/sensor/zoneminder.py index 60b6a018fc2bbe..80f8529d847d5d 100644 --- a/homeassistant/components/sensor/zoneminder.py +++ b/homeassistant/components/sensor/zoneminder.py @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZoneMinder sensor platform.""" include_archived = config.get(CONF_INCLUDE_ARCHIVED) @@ -58,7 +58,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): include_archived, sensor) ) - add_devices(sensors) + add_entities(sensors) class ZMSensorMonitors(Entity): diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml index 6b8bded59b83a8..62da489aab4c5f 100644 --- a/homeassistant/components/services.yaml +++ b/homeassistant/components/services.yaml @@ -506,9 +506,9 @@ homeassistant: reload_core_config: description: Reload the core configuration. restart: - description: Restart the Home Assistant service. It is normal to get a "Failed to call service homeassistant/restart" message. + description: Restart the Home Assistant service. stop: - description: Stop the Home Assistant service. It is normal to get a "Failed to call service homeassistant/stop" message. + description: Stop the Home Assistant service. toggle: description: Generic service to toggle devices on/off under any domain. Same usage as the light.turn_on, switch.turn_on, etc. services. fields: diff --git a/homeassistant/components/sonos/.translations/da.json b/homeassistant/components/sonos/.translations/da.json new file mode 100644 index 00000000000000..c303bca0aa83f2 --- /dev/null +++ b/homeassistant/components/sonos/.translations/da.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "no_devices_found": "Ingen Sonos-enheder kunne findes p\u00e5 netv\u00e6rket.", + "single_instance_allowed": "Det er kun n\u00f8dvendigt med en ops\u00e6tning af Sonos" + }, + "step": { + "confirm": { + "description": "Vil du ops\u00e6tte Sonos?", + "title": "Sonos" + } + }, + "title": "Sonos" + } +} \ No newline at end of file diff --git a/homeassistant/components/sonos/.translations/en.json b/homeassistant/components/sonos/.translations/en.json index c7aae4302f6bea..df9e9d2239ded5 100644 --- a/homeassistant/components/sonos/.translations/en.json +++ b/homeassistant/components/sonos/.translations/en.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Do you want to setup Sonos?", + "description": "Do you want to set up Sonos?", "title": "Sonos" } }, diff --git a/homeassistant/components/sonos/.translations/es.json b/homeassistant/components/sonos/.translations/es.json new file mode 100644 index 00000000000000..c91f9a78c292da --- /dev/null +++ b/homeassistant/components/sonos/.translations/es.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "confirm": { + "title": "Sonos" + } + }, + "title": "Sonos" + } +} \ No newline at end of file diff --git a/homeassistant/components/sonos/.translations/he.json b/homeassistant/components/sonos/.translations/he.json new file mode 100644 index 00000000000000..54aa43c6151bcd --- /dev/null +++ b/homeassistant/components/sonos/.translations/he.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9 Sonos \u05d1\u05e8\u05e9\u05ea.", + "single_instance_allowed": "\u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05e9\u05dc Sonos \u05e0\u05d7\u05d5\u05e6\u05d4." + }, + "step": { + "confirm": { + "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea Sonos?", + "title": "Sonos" + } + }, + "title": "Sonos" + } +} \ No newline at end of file diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index bbc05a3aa6116f..6c9280195ccdce 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -4,7 +4,7 @@ DOMAIN = 'sonos' -REQUIREMENTS = ['SoCo==0.14'] +REQUIREMENTS = ['SoCo==0.16'] async def async_setup(hass, config): diff --git a/homeassistant/components/sonos/strings.json b/homeassistant/components/sonos/strings.json index 4aa68712d599e7..0422919c1aa55c 100644 --- a/homeassistant/components/sonos/strings.json +++ b/homeassistant/components/sonos/strings.json @@ -4,7 +4,7 @@ "step": { "confirm": { "title": "Sonos", - "description": "Do you want to setup Sonos?" + "description": "Do you want to set up Sonos?" } }, "abort": { diff --git a/homeassistant/components/splunk.py b/homeassistant/components/splunk.py index a5b42eb9b5af9b..28863f6a43651c 100644 --- a/homeassistant/components/splunk.py +++ b/homeassistant/components/splunk.py @@ -15,7 +15,7 @@ CONF_SSL, CONF_HOST, CONF_NAME, CONF_PORT, CONF_TOKEN, EVENT_STATE_CHANGED) from homeassistant.helpers import state as state_helper import homeassistant.helpers.config_validation as cv -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/switch/__init__.py b/homeassistant/components/switch/__init__.py index cb69240ee73dca..c95c752435a42c 100644 --- a/homeassistant/components/switch/__init__.py +++ b/homeassistant/components/switch/__init__.py @@ -4,7 +4,6 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/switch/ """ -import asyncio from datetime import timedelta import logging @@ -99,42 +98,26 @@ async def async_setup(hass, config): _LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_SWITCHES) await component.async_setup(config) - async def async_handle_switch_service(service): - """Handle calls to the switch services.""" - target_switches = component.async_extract_from_service(service) - - update_tasks = [] - for switch in target_switches: - if service.service == SERVICE_TURN_ON: - await switch.async_turn_on() - elif service.service == SERVICE_TOGGLE: - await switch.async_toggle() - else: - await switch.async_turn_off() - - if not switch.should_poll: - continue - update_tasks.append( - switch.async_update_ha_state(True, service.context)) - - if update_tasks: - await asyncio.wait(update_tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_TURN_OFF, async_handle_switch_service, - schema=SWITCH_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_TURN_ON, async_handle_switch_service, - schema=SWITCH_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_TOGGLE, async_handle_switch_service, - schema=SWITCH_SERVICE_SCHEMA) + component.async_register_entity_service( + SERVICE_TURN_OFF, SWITCH_SERVICE_SCHEMA, + 'async_turn_off' + ) + + component.async_register_entity_service( + SERVICE_TURN_ON, SWITCH_SERVICE_SCHEMA, + 'async_turn_on' + ) + + component.async_register_entity_service( + SERVICE_TOGGLE, SWITCH_SERVICE_SCHEMA, + 'async_toggle' + ) return True async def async_setup_entry(hass, entry): - """Setup a config entry.""" + """Set up a config entry.""" return await hass.data[DOMAIN].async_setup_entry(entry) diff --git a/homeassistant/components/switch/abode.py b/homeassistant/components/switch/abode.py index 0ce1ddc59f8841..e3f993e5413e6c 100644 --- a/homeassistant/components/switch/abode.py +++ b/homeassistant/components/switch/abode.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Abode switch devices.""" import abodepy.helpers.constants as CONST import abodepy.helpers.timeline as TIMELINE @@ -43,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data.devices.extend(devices) - add_devices(devices) + add_entities(devices) class AbodeSwitch(AbodeDevice, SwitchDevice): diff --git a/homeassistant/components/switch/acer_projector.py b/homeassistant/components/switch/acer_projector.py index 527456d6d19fcc..7abb3d1edbc846 100644 --- a/homeassistant/components/switch/acer_projector.py +++ b/homeassistant/components/switch/acer_projector.py @@ -57,14 +57,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Connect with serial port and return Acer Projector.""" serial_port = config.get(CONF_FILENAME) name = config.get(CONF_NAME) timeout = config.get(CONF_TIMEOUT) write_timeout = config.get(CONF_WRITE_TIMEOUT) - add_devices([AcerSwitch(serial_port, name, timeout, write_timeout)], True) + add_entities([AcerSwitch(serial_port, name, timeout, write_timeout)], True) class AcerSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/ads.py b/homeassistant/components/switch/ads.py index b58c4d325e722a..8c13e9a8960567 100644 --- a/homeassistant/components/switch/ads.py +++ b/homeassistant/components/switch/ads.py @@ -27,14 +27,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up switch platform for ADS.""" ads_hub = hass.data.get(DATA_ADS) name = config.get(CONF_NAME) ads_var = config.get(CONF_ADS_VAR) - add_devices([AdsSwitch(ads_hub, name, ads_var)], True) + add_entities([AdsSwitch(ads_hub, name, ads_var)], True) class AdsSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/amcrest.py b/homeassistant/components/switch/amcrest.py index cfe33562f9f987..0805793fe955f7 100644 --- a/homeassistant/components/switch/amcrest.py +++ b/homeassistant/components/switch/amcrest.py @@ -18,7 +18,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the IP Amcrest camera switch platform.""" if discovery_info is None: return @@ -32,7 +33,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for setting in switches: all_switches.append(AmcrestSwitch(setting, camera, name)) - async_add_devices(all_switches, True) + async_add_entities(all_switches, True) class AmcrestSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/android_ip_webcam.py b/homeassistant/components/switch/android_ip_webcam.py index 8de2ce593afbc8..92e52c21caaeca 100644 --- a/homeassistant/components/switch/android_ip_webcam.py +++ b/homeassistant/components/switch/android_ip_webcam.py @@ -15,7 +15,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the IP Webcam switch platform.""" if discovery_info is None: return @@ -30,7 +31,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for setting in switches: all_switches.append(IPWebcamSettingsSwitch(name, host, ipcam, setting)) - async_add_devices(all_switches, True) + async_add_entities(all_switches, True) class IPWebcamSettingsSwitch(AndroidIPCamEntity, SwitchDevice): diff --git a/homeassistant/components/switch/anel_pwrctrl.py b/homeassistant/components/switch/anel_pwrctrl.py index 01d27b8abcd3b7..fadb3cd96ff909 100644 --- a/homeassistant/components/switch/anel_pwrctrl.py +++ b/homeassistant/components/switch/anel_pwrctrl.py @@ -33,7 +33,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up PwrCtrl devices/switches.""" host = config.get(CONF_HOST, None) username = config.get(CONF_USERNAME) @@ -60,7 +60,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for switch in device.switches.values() ) - add_devices(devices) + add_entities(devices) class PwrCtrlSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/arduino.py b/homeassistant/components/switch/arduino.py index 2bcb04c566e5c2..ee8f0e878a3076 100644 --- a/homeassistant/components/switch/arduino.py +++ b/homeassistant/components/switch/arduino.py @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Arduino platform.""" # Verify that Arduino board is present if arduino.BOARD is None: @@ -48,7 +48,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): switches = [] for pinnum, pin in pins.items(): switches.append(ArduinoSwitch(pinnum, pin)) - add_devices(switches) + add_entities(switches) class ArduinoSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/arest.py b/homeassistant/components/switch/arest.py index fd72d0728a00db..ab445db10d8473 100644 --- a/homeassistant/components/switch/arest.py +++ b/homeassistant/components/switch/arest.py @@ -37,7 +37,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the aREST switches.""" resource = config.get(CONF_RESOURCE) @@ -64,7 +64,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): resource, config.get(CONF_NAME, response.json()[CONF_NAME]), func.get(CONF_NAME), funcname)) - add_devices(dev) + add_entities(dev) class ArestSwitchBase(SwitchDevice): diff --git a/homeassistant/components/switch/bbb_gpio.py b/homeassistant/components/switch/bbb_gpio.py index 94952ac736b753..9e120beb442684 100644 --- a/homeassistant/components/switch/bbb_gpio.py +++ b/homeassistant/components/switch/bbb_gpio.py @@ -34,14 +34,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the BeagleBone Black GPIO devices.""" pins = config.get(CONF_PINS) switches = [] for pin, params in pins.items(): switches.append(BBBGPIOSwitch(pin, params)) - add_devices(switches) + add_entities(switches) class BBBGPIOSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/broadlink.py b/homeassistant/components/switch/broadlink.py index 6b754effaf1deb..3dd8eafcf1f267 100644 --- a/homeassistant/components/switch/broadlink.py +++ b/homeassistant/components/switch/broadlink.py @@ -69,7 +69,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Broadlink switches.""" import broadlink devices = config.get(CONF_SWITCHES) @@ -179,7 +179,7 @@ def _get_mp1_slot_name(switch_friendly_name, slot): except socket.timeout: _LOGGER.error("Failed to connect to device") - add_devices(switches) + add_entities(switches) class BroadlinkRMSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/command_line.py b/homeassistant/components/switch/command_line.py index 127c7578940f40..d25c57083167bb 100644 --- a/homeassistant/components/switch/command_line.py +++ b/homeassistant/components/switch/command_line.py @@ -32,7 +32,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return switches controlled by shell commands.""" devices = config.get(CONF_SWITCHES, {}) switches = [] @@ -59,7 +59,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("No switches added") return False - add_devices(switches) + add_entities(switches) class CommandSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/deconz.py b/homeassistant/components/switch/deconz.py index d5fb22e97c467f..35dbc3ef782e37 100644 --- a/homeassistant/components/switch/deconz.py +++ b/homeassistant/components/switch/deconz.py @@ -6,21 +6,22 @@ """ from homeassistant.components.deconz.const import ( DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, DATA_DECONZ_UNSUB, - POWER_PLUGS, SIRENS) + DECONZ_DOMAIN, POWER_PLUGS, SIRENS) from homeassistant.components.switch import SwitchDevice from homeassistant.core import callback +from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE from homeassistant.helpers.dispatcher import async_dispatcher_connect DEPENDENCIES = ['deconz'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Old way of setting up deCONZ switches.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up switches for deCONZ component. Switches are based same device class as lights in deCONZ. @@ -34,7 +35,7 @@ def async_add_switch(lights): entities.append(DeconzPowerPlug(light)) elif light.type in SIRENS: entities.append(DeconzSiren(light)) - async_add_devices(entities, True) + async_add_entities(entities, True) hass.data[DATA_DECONZ_UNSUB].append( async_dispatcher_connect(hass, 'deconz_new_light', async_add_switch)) @@ -79,6 +80,22 @@ def should_poll(self): """No polling needed.""" return False + @property + def device_info(self): + """Return a device description for device registry.""" + if (self._switch.uniqueid is None or + self._switch.uniqueid.count(':') != 7): + return None + serial = self._switch.uniqueid.split('-', 1)[0] + return { + 'connections': {(CONNECTION_ZIGBEE, serial)}, + 'identifiers': {(DECONZ_DOMAIN, serial)}, + 'manufacturer': self._switch.manufacturer, + 'model': self._switch.modelid, + 'name': self._switch.name, + 'sw_version': self._switch.swversion, + } + class DeconzPowerPlug(DeconzSwitch): """Representation of power plugs from deCONZ.""" diff --git a/homeassistant/components/switch/deluge.py b/homeassistant/components/switch/deluge.py index c71c3865f5dc19..0ece742aa03f2b 100644 --- a/homeassistant/components/switch/deluge.py +++ b/homeassistant/components/switch/deluge.py @@ -32,7 +32,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Deluge switch.""" from deluge_client import DelugeRPCClient @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Connection to Deluge Daemon failed") raise PlatformNotReady - add_devices([DelugeSwitch(deluge_api, name)]) + add_entities([DelugeSwitch(deluge_api, name)]) class DelugeSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/demo.py b/homeassistant/components/switch/demo.py index 7e22f962330d5b..0ac2011a6dc486 100644 --- a/homeassistant/components/switch/demo.py +++ b/homeassistant/components/switch/demo.py @@ -8,9 +8,9 @@ from homeassistant.const import DEVICE_DEFAULT_NAME -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up the demo switches.""" - add_devices_callback([ + add_entities_callback([ DemoSwitch('Decorative Lights', True, None, True), DemoSwitch('AC', False, 'mdi:air-conditioner', False) ]) diff --git a/homeassistant/components/switch/digital_ocean.py b/homeassistant/components/switch/digital_ocean.py index 12a6aabb170079..c17df81abba025 100644 --- a/homeassistant/components/switch/digital_ocean.py +++ b/homeassistant/components/switch/digital_ocean.py @@ -27,7 +27,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Digital Ocean droplet switch.""" digital = hass.data.get(DATA_DIGITAL_OCEAN) if not digital: @@ -43,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return False dev.append(DigitalOceanSwitch(digital, droplet_id)) - add_devices(dev, True) + add_entities(dev, True) class DigitalOceanSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/digitalloggers.py b/homeassistant/components/switch/digitalloggers.py index 29e6771d1d581f..7bb2be19899425 100644 --- a/homeassistant/components/switch/digitalloggers.py +++ b/homeassistant/components/switch/digitalloggers.py @@ -41,7 +41,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return DIN III Relay switch.""" import dlipower @@ -69,7 +69,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for outlet in power_switch[0:] ) - add_devices(outlets) + add_entities(outlets) class DINRelay(SwitchDevice): diff --git a/homeassistant/components/switch/dlink.py b/homeassistant/components/switch/dlink.py index 9ce324ef6bb8c0..f4eaefcae209c2 100644 --- a/homeassistant/components/switch/dlink.py +++ b/homeassistant/components/switch/dlink.py @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a D-Link Smart Plug.""" from pyW215.pyW215 import SmartPlug @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): username, use_legacy_protocol)) - add_devices([SmartPlugSwitch(hass, data, name)], True) + add_entities([SmartPlugSwitch(hass, data, name)], True) class SmartPlugSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/doorbird.py b/homeassistant/components/switch/doorbird.py index 92ba3640237edb..17a4757d4ac634 100644 --- a/homeassistant/components/switch/doorbird.py +++ b/homeassistant/components/switch/doorbird.py @@ -46,7 +46,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the DoorBird switch platform.""" switches = [] @@ -60,7 +60,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): SWITCHES[switch]["name"].format(doorstation.name)) switches.append(DoorBirdSwitch(device, switch, doorstation.name)) - add_devices(switches) + add_entities(switches) class DoorBirdSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/edimax.py b/homeassistant/components/switch/edimax.py index 9cd7c48488649d..90ad3fff57fbf1 100644 --- a/homeassistant/components/switch/edimax.py +++ b/homeassistant/components/switch/edimax.py @@ -29,7 +29,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return Edimax Smart Plugs.""" from pyedimax.smartplug import SmartPlug @@ -37,7 +37,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): auth = (config.get(CONF_USERNAME), config.get(CONF_PASSWORD)) name = config.get(CONF_NAME) - add_devices([SmartPlugSwitch(SmartPlug(host, auth), name)]) + add_entities([SmartPlugSwitch(SmartPlug(host, auth), name)]) class SmartPlugSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/enocean.py b/homeassistant/components/switch/enocean.py index 986744aeec11b6..ab979604f5043d 100644 --- a/homeassistant/components/switch/enocean.py +++ b/homeassistant/components/switch/enocean.py @@ -27,13 +27,13 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the EnOcean switch platform.""" dev_id = config.get(CONF_ID) devname = config.get(CONF_NAME) channel = config.get(CONF_CHANNEL) - add_devices([EnOceanSwitch(dev_id, devname, channel)]) + add_entities([EnOceanSwitch(dev_id, devname, channel)]) class EnOceanSwitch(enocean.EnOceanDevice, ToggleEntity): diff --git a/homeassistant/components/switch/eufy.py b/homeassistant/components/switch/eufy.py index 7320ea8d5571db..a226797c0f8b1e 100644 --- a/homeassistant/components/switch/eufy.py +++ b/homeassistant/components/switch/eufy.py @@ -13,11 +13,11 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Eufy switches.""" if discovery_info is None: return - add_devices([EufySwitch(discovery_info)], True) + add_entities([EufySwitch(discovery_info)], True) class EufySwitch(SwitchDevice): diff --git a/homeassistant/components/switch/flux.py b/homeassistant/components/switch/flux.py index 7df8f0e1aa620c..15fdee59eaf812 100644 --- a/homeassistant/components/switch/flux.py +++ b/homeassistant/components/switch/flux.py @@ -95,7 +95,7 @@ def set_lights_rgb(hass, lights, rgb, transition): transition=transition) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Flux switches.""" name = config.get(CONF_NAME) lights = config.get(CONF_LIGHTS) @@ -113,7 +113,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): start_colortemp, sunset_colortemp, stop_colortemp, brightness, disable_brightness_adjust, mode, interval, transition) - add_devices([flux]) + add_entities([flux]) def update(call=None): """Update lights.""" diff --git a/homeassistant/components/switch/fritzbox.py b/homeassistant/components/switch/fritzbox.py index 65a1aa6aabc1db..55fa8a0479684e 100644 --- a/homeassistant/components/switch/fritzbox.py +++ b/homeassistant/components/switch/fritzbox.py @@ -25,7 +25,7 @@ ATTR_TEMPERATURE_UNIT = 'temperature_unit' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Fritzbox smarthome switch platform.""" devices = [] fritz_list = hass.data[FRITZBOX_DOMAIN] @@ -36,7 +36,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if device.has_switch: devices.append(FritzboxSwitch(device, fritz)) - add_devices(devices) + add_entities(devices) class FritzboxSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/fritzdect.py b/homeassistant/components/switch/fritzdect.py index 9c0f852846a7c5..a04de7618afc7c 100644 --- a/homeassistant/components/switch/fritzdect.py +++ b/homeassistant/components/switch/fritzdect.py @@ -40,7 +40,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Add all switches connected to Fritz Box.""" from fritzhome.fritz import FritzBox @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if actor.has_switch: data = FritzDectSwitchData(fritz, actor.actor_id) data.is_online = True - add_devices([FritzDectSwitch(hass, data, actor.name)], True) + add_entities([FritzDectSwitch(hass, data, actor.name)], True) class FritzDectSwitch(SwitchDevice): @@ -105,7 +105,7 @@ def device_state_attributes(self): return attrs @property - def current_power_watt(self): + def current_power_w(self): """Return the current power usage in Watt.""" try: return float(self.data.current_consumption) diff --git a/homeassistant/components/switch/gc100.py b/homeassistant/components/switch/gc100.py index 34a29483d3cceb..2a8e7eaa847cc1 100644 --- a/homeassistant/components/switch/gc100.py +++ b/homeassistant/components/switch/gc100.py @@ -23,7 +23,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the GC100 devices.""" switches = [] ports = config.get(CONF_PORTS) @@ -31,7 +31,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for port_addr, port_name in port.items(): switches.append(GC100Switch( port_name, port_addr, hass.data[DATA_GC100])) - add_devices(switches, True) + add_entities(switches, True) class GC100Switch(ToggleEntity): diff --git a/homeassistant/components/switch/hdmi_cec.py b/homeassistant/components/switch/hdmi_cec.py index e81c09894abb8d..b2697b4a2c4efe 100644 --- a/homeassistant/components/switch/hdmi_cec.py +++ b/homeassistant/components/switch/hdmi_cec.py @@ -18,13 +18,13 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return HDMI devices as switches.""" if ATTR_NEW in discovery_info: _LOGGER.info("Setting up HDMI devices %s", discovery_info[ATTR_NEW]) - add_devices(CecSwitchDevice(hass, hass.data.get(device), - hass.data.get(device).logical_address) for - device in discovery_info[ATTR_NEW]) + add_entities(CecSwitchDevice(hass, hass.data.get(device), + hass.data.get(device).logical_address) for + device in discovery_info[ATTR_NEW]) class CecSwitchDevice(CecDevice, SwitchDevice): diff --git a/homeassistant/components/switch/hikvisioncam.py b/homeassistant/components/switch/hikvisioncam.py index c3e065abc0e482..3a3dec26e1d030 100644 --- a/homeassistant/components/switch/hikvisioncam.py +++ b/homeassistant/components/switch/hikvisioncam.py @@ -34,7 +34,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Hikvision camera.""" import hikvision.api from hikvision.error import HikvisionError, MissingParamError @@ -56,7 +56,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGING.error("Unable to connect: %s", conn_err) return False - add_devices([HikvisionMotionSwitch(name, hikvision_cam)]) + add_entities([HikvisionMotionSwitch(name, hikvision_cam)]) class HikvisionMotionSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/hive.py b/homeassistant/components/switch/hive.py index 49fc9696b5ef50..1927df28e97f3b 100644 --- a/homeassistant/components/switch/hive.py +++ b/homeassistant/components/switch/hive.py @@ -10,13 +10,13 @@ DEPENDENCIES = ['hive'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Hive switches.""" if discovery_info is None: return session = hass.data.get(DATA_HIVE) - add_devices([HiveDevicePlug(session, discovery_info)]) + add_entities([HiveDevicePlug(session, discovery_info)]) class HiveDevicePlug(SwitchDevice): diff --git a/homeassistant/components/switch/homekit_controller.py b/homeassistant/components/switch/homekit_controller.py index 3293c8fe1953bb..a3db6060fcf2b3 100644 --- a/homeassistant/components/switch/homekit_controller.py +++ b/homeassistant/components/switch/homekit_controller.py @@ -15,11 +15,11 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Homekit switch support.""" if discovery_info is not None: accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] - add_devices([HomeKitSwitch(accessory, discovery_info)], True) + add_entities([HomeKitSwitch(accessory, discovery_info)], True) class HomeKitSwitch(HomeKitEntity, SwitchDevice): diff --git a/homeassistant/components/switch/homematic.py b/homeassistant/components/switch/homematic.py index 487947598bbadf..b5921819ea4ffb 100644 --- a/homeassistant/components/switch/homematic.py +++ b/homeassistant/components/switch/homematic.py @@ -5,8 +5,9 @@ https://home-assistant.io/components/switch.homematic/ """ import logging + +from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice from homeassistant.components.switch import SwitchDevice -from homeassistant.components.homematic import HMDevice, ATTR_DISCOVER_DEVICES from homeassistant.const import STATE_UNKNOWN _LOGGER = logging.getLogger(__name__) @@ -14,7 +15,7 @@ DEPENDENCIES = ['homematic'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the HomeMatic switch platform.""" if discovery_info is None: return @@ -24,7 +25,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): new_device = HMSwitch(conf) devices.append(new_device) - add_devices(devices) + add_entities(devices) class HMSwitch(HMDevice, SwitchDevice): diff --git a/homeassistant/components/switch/homematicip_cloud.py b/homeassistant/components/switch/homematicip_cloud.py index 68884aaaa02963..e066c861dfb18d 100644 --- a/homeassistant/components/switch/homematicip_cloud.py +++ b/homeassistant/components/switch/homematicip_cloud.py @@ -1,16 +1,15 @@ """ -Support for HomematicIP switch. +Support for HomematicIP Cloud switch. -For more details about this component, please refer to the documentation at +For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.homematicip_cloud/ """ - import logging -from homeassistant.components.switch import SwitchDevice from homeassistant.components.homematicip_cloud import ( - HomematicipGenericDevice, DOMAIN as HMIPC_DOMAIN, - HMIPC_HAPID) + HMIPC_HAPID, HomematicipGenericDevice) +from homeassistant.components.homematicip_cloud import DOMAIN as HMIPC_DOMAIN +from homeassistant.components.switch import SwitchDevice DEPENDENCIES = ['homematicip_cloud'] @@ -21,17 +20,16 @@ ATTR_PROFILE_MODE = 'profile_mode' -async def async_setup_platform(hass, config, async_add_devices, - discovery_info=None): - """Set up the HomematicIP switch devices.""" +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the HomematicIP Cloud switch devices.""" pass -async def async_setup_entry(hass, config_entry, async_add_devices): +async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the HomematicIP switch from a config entry.""" from homematicip.device import ( - PlugableSwitch, PlugableSwitchMeasuring, - BrandSwitchMeasuring) + PlugableSwitch, PlugableSwitchMeasuring, BrandSwitchMeasuring) home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home devices = [] @@ -47,11 +45,11 @@ async def async_setup_entry(hass, config_entry, async_add_devices): devices.append(HomematicipSwitch(home, device)) if devices: - async_add_devices(devices) + async_add_entities(devices) class HomematicipSwitch(HomematicipGenericDevice, SwitchDevice): - """MomematicIP switch device.""" + """representation of a HomematicIP Cloud switch device.""" def __init__(self, home, device): """Initialize the switch device.""" @@ -72,7 +70,7 @@ async def async_turn_off(self, **kwargs): class HomematicipSwitchMeasuring(HomematicipSwitch): - """MomematicIP measuring switch device.""" + """Representation of a HomematicIP measuring switch device.""" @property def current_power_w(self): diff --git a/homeassistant/components/switch/hook.py b/homeassistant/components/switch/hook.py index 07425840b9af0b..5a86346aa76b9e 100644 --- a/homeassistant/components/switch/hook.py +++ b/homeassistant/components/switch/hook.py @@ -32,7 +32,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up Hook by getting the access token and list of actions.""" username = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) @@ -70,7 +71,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): _LOGGER.error("Failed getting devices: %s", error) return False - async_add_devices( + async_add_entities( HookSmartHome( hass, token, diff --git a/homeassistant/components/switch/hydrawise.py b/homeassistant/components/switch/hydrawise.py index d0abe5febf5549..6b73333431d674 100644 --- a/homeassistant/components/switch/hydrawise.py +++ b/homeassistant/components/switch/hydrawise.py @@ -28,7 +28,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for a Hydrawise device.""" hydrawise = hass.data[DATA_HYDRAWISE].data @@ -43,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): zone, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) class HydrawiseSwitch(HydrawiseEntity, SwitchDevice): diff --git a/homeassistant/components/switch/ihc.py b/homeassistant/components/switch/ihc.py index 3f461784693076..4ddafa228a7978 100644 --- a/homeassistant/components/switch/ihc.py +++ b/homeassistant/components/switch/ihc.py @@ -25,8 +25,8 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the ihc switch platform.""" +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the IHC switch platform.""" ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER] info = hass.data[IHC_DATA][IHC_INFO] devices = [] @@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensor = IHCSwitch(ihc_controller, name, ihc_id, info) devices.append(sensor) - add_devices(devices) + add_entities(devices) class IHCSwitch(IHCDevice, SwitchDevice): @@ -70,6 +70,6 @@ def turn_off(self, **kwargs): self.ihc_controller.set_runtime_value_bool(self.ihc_id, False) def on_ihc_change(self, ihc_id, value): - """Callback when the ihc resource changes.""" + """Handle IHC resource change.""" self._state = value self.schedule_update_ha_state() diff --git a/homeassistant/components/switch/insteon_plm.py b/homeassistant/components/switch/insteon.py similarity index 64% rename from homeassistant/components/switch/insteon_plm.py rename to homeassistant/components/switch/insteon.py index c357d1ccc0418e..744d278d394a06 100644 --- a/homeassistant/components/switch/insteon_plm.py +++ b/homeassistant/components/switch/insteon.py @@ -2,26 +2,27 @@ Support for INSTEON dimmers via PowerLinc Modem. For more details about this component, please refer to the documentation at -https://home-assistant.io/components/switch.insteon_plm/ +https://home-assistant.io/components/switch.insteon/ """ import asyncio import logging -from homeassistant.components.insteon_plm import InsteonPLMEntity +from homeassistant.components.insteon import InsteonEntity from homeassistant.components.switch import SwitchDevice -DEPENDENCIES = ['insteon_plm'] +DEPENDENCIES = ['insteon'] _LOGGER = logging.getLogger(__name__) @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): - """Set up the INSTEON PLM device class for the hass platform.""" - plm = hass.data['insteon_plm'].get('plm') +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): + """Set up the INSTEON device class for the hass platform.""" + insteon_modem = hass.data['insteon'].get('modem') address = discovery_info['address'] - device = plm.devices[address] + device = insteon_modem.devices[address] state_key = discovery_info['state_key'] state_name = device.states[state_key].name @@ -30,17 +31,16 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): device.address.hex, device.states[state_key].name) new_entity = None - if state_name in ['lightOnOff', 'outletTopOnOff', 'outletBottomOnOff', - 'x10OnOffSwitch']: - new_entity = InsteonPLMSwitchDevice(device, state_key) - elif state_name == 'openClosedRelay': - new_entity = InsteonPLMOpenClosedDevice(device, state_key) + if state_name == 'openClosedRelay': + new_entity = InsteonOpenClosedDevice(device, state_key) + else: + new_entity = InsteonSwitchDevice(device, state_key) if new_entity is not None: - async_add_devices([new_entity]) + async_add_entities([new_entity]) -class InsteonPLMSwitchDevice(InsteonPLMEntity, SwitchDevice): +class InsteonSwitchDevice(InsteonEntity, SwitchDevice): """A Class for an Insteon device.""" @property @@ -59,7 +59,7 @@ def async_turn_off(self, **kwargs): self._insteon_device_state.off() -class InsteonPLMOpenClosedDevice(InsteonPLMEntity, SwitchDevice): +class InsteonOpenClosedDevice(InsteonEntity, SwitchDevice): """A Class for an Insteon device.""" @property diff --git a/homeassistant/components/switch/insteon_local.py b/homeassistant/components/switch/insteon_local.py deleted file mode 100644 index c4c8a8546706c4..00000000000000 --- a/homeassistant/components/switch/insteon_local.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -Support for Insteon switch devices via local hub support. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/switch.insteon_local/ -""" -import logging -from datetime import timedelta - -from homeassistant.components.switch import SwitchDevice -from homeassistant import util - -_CONFIGURING = {} -_LOGGER = logging.getLogger(__name__) - -DEPENDENCIES = ['insteon_local'] -DOMAIN = 'switch' - -MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100) -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up the Insteon local switch platform.""" - insteonhub = hass.data['insteon_local'] - if discovery_info is None: - return - - linked = discovery_info['linked'] - device_list = [] - for device_id in linked: - if linked[device_id]['cat_type'] == 'switch': - device = insteonhub.switch(device_id) - device_list.append( - InsteonLocalSwitchDevice(device) - ) - - add_devices(device_list) - - -class InsteonLocalSwitchDevice(SwitchDevice): - """An abstract Class for an Insteon node.""" - - def __init__(self, node): - """Initialize the device.""" - self.node = node - self._state = False - - @property - def name(self): - """Return the name of the node.""" - return self.node.device_id - - @property - def unique_id(self): - """Return the ID of this Insteon node.""" - return self.node.device_id - - @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) - def update(self): - """Get the updated status of the switch.""" - resp = self.node.status(0) - - while 'error' in resp and resp['error'] is True: - resp = self.node.status(0) - - if 'cmd2' in resp: - self._state = int(resp['cmd2'], 16) > 0 - - @property - def is_on(self): - """Return the boolean response if the node is on.""" - return self._state - - def turn_on(self, **kwargs): - """Turn device on.""" - self.node.on() - self._state = True - - def turn_off(self, **kwargs): - """Turn device off.""" - self.node.off() - self._state = False diff --git a/homeassistant/components/switch/isy994.py b/homeassistant/components/switch/isy994.py index 2a7dee87747db7..6bb9c07de5b8c6 100644 --- a/homeassistant/components/switch/isy994.py +++ b/homeassistant/components/switch/isy994.py @@ -16,7 +16,7 @@ def setup_platform(hass, config: ConfigType, - add_devices: Callable[[list], None], discovery_info=None): + add_entities: Callable[[list], None], discovery_info=None): """Set up the ISY994 switch platform.""" devices = [] for node in hass.data[ISY994_NODES][DOMAIN]: @@ -26,7 +26,7 @@ def setup_platform(hass, config: ConfigType, for name, status, actions in hass.data[ISY994_PROGRAMS][DOMAIN]: devices.append(ISYSwitchProgram(name, status, actions)) - add_devices(devices) + add_entities(devices) class ISYSwitchDevice(ISYDevice, SwitchDevice): diff --git a/homeassistant/components/switch/kankun.py b/homeassistant/components/switch/kankun.py index c830e2299f6ce4..59966739b91588 100644 --- a/homeassistant/components/switch/kankun.py +++ b/homeassistant/components/switch/kankun.py @@ -34,7 +34,7 @@ }) -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up Kankun Wifi switches.""" switches = config.get('switches', {}) devices = [] @@ -50,7 +50,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): properties.get(CONF_USERNAME, None), properties.get(CONF_PASSWORD))) - add_devices_callback(devices) + add_entities_callback(devices) class KankunSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/knx.py b/homeassistant/components/switch/knx.py index c13631ca5e67c2..678a8d4775f468 100644 --- a/homeassistant/components/switch/knx.py +++ b/homeassistant/components/switch/knx.py @@ -26,27 +26,27 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up switch(es) for KNX platform.""" if discovery_info is not None: - async_add_devices_discovery(hass, discovery_info, async_add_devices) + async_add_entities_discovery(hass, discovery_info, async_add_entities) else: - async_add_devices_config(hass, config, async_add_devices) + async_add_entities_config(hass, config, async_add_entities) @callback -def async_add_devices_discovery(hass, discovery_info, async_add_devices): +def async_add_entities_discovery(hass, discovery_info, async_add_entities): """Set up switches for KNX platform configured via xknx.yaml.""" entities = [] for device_name in discovery_info[ATTR_DISCOVER_DEVICES]: device = hass.data[DATA_KNX].xknx.devices[device_name] entities.append(KNXSwitch(hass, device)) - async_add_devices(entities) + async_add_entities(entities) @callback -def async_add_devices_config(hass, config, async_add_devices): +def async_add_entities_config(hass, config, async_add_entities): """Set up switch for KNX platform configured within platform.""" import xknx switch = xknx.devices.Switch( @@ -55,7 +55,7 @@ def async_add_devices_config(hass, config, async_add_devices): group_address=config.get(CONF_ADDRESS), group_address_state=config.get(CONF_STATE_ADDRESS)) hass.data[DATA_KNX].xknx.devices.add(switch) - async_add_devices([KNXSwitch(hass, switch)]) + async_add_entities([KNXSwitch(hass, switch)]) class KNXSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/konnected.py b/homeassistant/components/switch/konnected.py index 53c6406b28a896..20774accbd579e 100644 --- a/homeassistant/components/switch/konnected.py +++ b/homeassistant/components/switch/konnected.py @@ -8,17 +8,18 @@ import logging from homeassistant.components.konnected import ( - DOMAIN as KONNECTED_DOMAIN, PIN_TO_ZONE, CONF_ACTIVATION, - STATE_LOW, STATE_HIGH) + DOMAIN as KONNECTED_DOMAIN, PIN_TO_ZONE, CONF_ACTIVATION, CONF_MOMENTARY, + CONF_PAUSE, CONF_REPEAT, STATE_LOW, STATE_HIGH) from homeassistant.helpers.entity import ToggleEntity -from homeassistant.const import (CONF_DEVICES, CONF_SWITCHES, ATTR_STATE) +from homeassistant.const import ( + CONF_DEVICES, CONF_SWITCHES, CONF_PIN, ATTR_STATE) _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['konnected'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set switches attached to a Konnected device.""" if discovery_info is None: @@ -26,27 +27,28 @@ async def async_setup_platform(hass, config, async_add_devices, data = hass.data[KONNECTED_DOMAIN] device_id = discovery_info['device_id'] - client = data[CONF_DEVICES][device_id]['client'] - switches = [KonnectedSwitch(device_id, pin_num, pin_data, client) - for pin_num, pin_data in - data[CONF_DEVICES][device_id][CONF_SWITCHES].items()] - async_add_devices(switches) + switches = [ + KonnectedSwitch(device_id, pin_data.get(CONF_PIN), pin_data) + for pin_data in data[CONF_DEVICES][device_id][CONF_SWITCHES]] + async_add_entities(switches) class KonnectedSwitch(ToggleEntity): """Representation of a Konnected switch.""" - def __init__(self, device_id, pin_num, data, client): + def __init__(self, device_id, pin_num, data): """Initialize the switch.""" self._data = data self._device_id = device_id self._pin_num = pin_num self._activation = self._data.get(CONF_ACTIVATION, STATE_HIGH) + self._momentary = self._data.get(CONF_MOMENTARY) + self._pause = self._data.get(CONF_PAUSE) + self._repeat = self._data.get(CONF_REPEAT) self._state = self._boolean_state(self._data.get(ATTR_STATE)) self._name = self._data.get( 'name', 'Konnected {} Actuator {}'.format( device_id, PIN_TO_ZONE[pin_num])) - self._client = client _LOGGER.debug('Created new switch: %s', self._name) @property @@ -59,17 +61,33 @@ def is_on(self): """Return the status of the sensor.""" return self._state + @property + def client(self): + """Return the Konnected HTTP client.""" + return \ + self.hass.data[KONNECTED_DOMAIN][CONF_DEVICES][self._device_id].\ + get('client') + def turn_on(self, **kwargs): """Send a command to turn on the switch.""" - resp = self._client.put_device( - self._pin_num, int(self._activation == STATE_HIGH)) + resp = self.client.put_device( + self._pin_num, + int(self._activation == STATE_HIGH), + self._momentary, + self._repeat, + self._pause + ) if resp.get(ATTR_STATE) is not None: - self._set_state(self._boolean_state(resp.get(ATTR_STATE))) + self._set_state(True) + + if self._momentary and resp.get(ATTR_STATE) != -1: + # Immediately set the state back off for momentary switches + self._set_state(self._boolean_state(False)) def turn_off(self, **kwargs): """Send a command to turn off the switch.""" - resp = self._client.put_device( + resp = self.client.put_device( self._pin_num, int(self._activation == STATE_LOW)) if resp.get(ATTR_STATE) is not None: diff --git a/homeassistant/components/switch/linode.py b/homeassistant/components/switch/linode.py index 43f4bdc31b4b72..47bba280e1cb2a 100644 --- a/homeassistant/components/switch/linode.py +++ b/homeassistant/components/switch/linode.py @@ -26,7 +26,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Linode Node switch.""" linode = hass.data.get(DATA_LINODE) nodes = config.get(CONF_NODES) @@ -39,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return dev.append(LinodeSwitch(linode, node_id)) - add_devices(dev, True) + add_entities(dev, True) class LinodeSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/litejet.py b/homeassistant/components/switch/litejet.py index 79ef4a5fd7f4f9..b9755569fd23db 100644 --- a/homeassistant/components/switch/litejet.py +++ b/homeassistant/components/switch/litejet.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the LiteJet switch platform.""" litejet_ = hass.data['litejet_system'] @@ -25,7 +25,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name = litejet_.get_switch_name(i) if not litejet.is_ignored(hass, name): devices.append(LiteJetSwitch(hass, litejet_, i, name)) - add_devices(devices, True) + add_entities(devices, True) class LiteJetSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/lutron_caseta.py b/homeassistant/components/switch/lutron_caseta.py index f5e7cf2836f892..8587c78a5d56bb 100644 --- a/homeassistant/components/switch/lutron_caseta.py +++ b/homeassistant/components/switch/lutron_caseta.py @@ -17,7 +17,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up Lutron switch.""" devs = [] bridge = hass.data[LUTRON_CASETA_SMARTBRIDGE] @@ -27,7 +28,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): dev = LutronCasetaLight(switch_device, bridge) devs.append(dev) - async_add_devices(devs, True) + async_add_entities(devs, True) return True diff --git a/homeassistant/components/switch/mfi.py b/homeassistant/components/switch/mfi.py index 2c547fa210f1b2..521230ccbd50ff 100644 --- a/homeassistant/components/switch/mfi.py +++ b/homeassistant/components/switch/mfi.py @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up mFi sensors.""" host = config.get(CONF_HOST) username = config.get(CONF_USERNAME) @@ -58,10 +58,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Unable to connect to mFi: %s", str(ex)) return False - add_devices(MfiSwitch(port) - for device in client.get_devices() - for port in device.ports.values() - if port.model in SWITCH_MODELS) + add_entities(MfiSwitch(port) + for device in client.get_devices() + for port in device.ports.values() + if port.model in SWITCH_MODELS) class MfiSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/mochad.py b/homeassistant/components/switch/mochad.py index f80784271c26dc..b703d91be34793 100644 --- a/homeassistant/components/switch/mochad.py +++ b/homeassistant/components/switch/mochad.py @@ -29,10 +29,10 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up X10 switches over a mochad controller.""" devs = config.get(CONF_DEVICES) - add_devices([MochadSwitch( + add_entities([MochadSwitch( hass, mochad.CONTROLLER.ctrl, dev) for dev in devs]) return True @@ -48,7 +48,7 @@ def __init__(self, hass, ctrl, dev): self._address = dev[CONF_ADDRESS] self._name = dev.get(CONF_NAME, 'x10_switch_dev_%s' % self._address) self._comm_type = dev.get(mochad.CONF_COMM_TYPE, 'pl') - self.device = device.Device(ctrl, self._address, + self.switch = device.Device(ctrl, self._address, comm_type=self._comm_type) # Init with false to avoid locking HA for long on CM19A (goes from rf # to pl via TM751, but not other way around) @@ -71,7 +71,7 @@ def turn_on(self, **kwargs): try: # Recycle socket on new command to recover mochad connection self._controller.reconnect() - self.device.send_cmd('on') + self.switch.send_cmd('on') # No read data on CM19A which is rf only if self._comm_type == 'pl': self._controller.read_data() @@ -88,7 +88,7 @@ def turn_off(self, **kwargs): try: # Recycle socket on new command to recover mochad connection self._controller.reconnect() - self.device.send_cmd('off') + self.switch.send_cmd('off') # No read data on CM19A which is rf only if self._comm_type == 'pl': self._controller.read_data() @@ -99,7 +99,7 @@ def turn_off(self, **kwargs): def _get_device_status(self): """Get the status of the switch from mochad.""" with mochad.REQ_LOCK: - status = self.device.get_status().rstrip() + status = self.switch.get_status().rstrip() return status == 'on' @property diff --git a/homeassistant/components/switch/modbus.py b/homeassistant/components/switch/modbus.py index 94e1d7ea6d63d9..a8c8358f0cf19e 100644 --- a/homeassistant/components/switch/modbus.py +++ b/homeassistant/components/switch/modbus.py @@ -59,7 +59,7 @@ })) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Read configuration and create Modbus devices.""" switches = [] if CONF_COILS in config: @@ -81,7 +81,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): register.get(CONF_REGISTER_TYPE), register.get(CONF_STATE_ON), register.get(CONF_STATE_OFF))) - add_devices(switches) + add_entities(switches) class ModbusCoilSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py index eb91f8d846ae12..f6075d5e49f86a 100644 --- a/homeassistant/components/switch/mqtt.py +++ b/homeassistant/components/switch/mqtt.py @@ -46,7 +46,7 @@ }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the MQTT switch.""" if discovery_info is not None: @@ -56,7 +56,7 @@ async def async_setup_platform(hass, config, async_add_devices, if value_template is not None: value_template.hass = hass - async_add_devices([MqttSwitch( + async_add_entities([MqttSwitch( config.get(CONF_NAME), config.get(CONF_ICON), config.get(CONF_STATE_TOPIC), diff --git a/homeassistant/components/switch/mysensors.py b/homeassistant/components/switch/mysensors.py index 340eed83b567e8..20e50518df8e52 100644 --- a/homeassistant/components/switch/mysensors.py +++ b/homeassistant/components/switch/mysensors.py @@ -21,7 +21,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the mysensors platform for switches.""" device_class_map = { 'S_DOOR': MySensorsSwitch, @@ -40,7 +40,7 @@ async def async_setup_platform( } mysensors.setup_mysensors_platform( hass, DOMAIN, discovery_info, device_class_map, - async_add_devices=async_add_devices) + async_add_entities=async_add_entities) async def async_send_ir_code_service(service): """Set IR code as device state attribute.""" diff --git a/homeassistant/components/switch/mystrom.py b/homeassistant/components/switch/mystrom.py index 85fc546d00e591..a0058cb925c44e 100644 --- a/homeassistant/components/switch/mystrom.py +++ b/homeassistant/components/switch/mystrom.py @@ -24,7 +24,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return myStrom switch.""" from pymystrom.switch import MyStromPlug, exceptions @@ -37,7 +37,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("No route to device: %s", host) return - add_devices([MyStromSwitch(name, host)]) + add_entities([MyStromSwitch(name, host)]) class MyStromSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/neato.py b/homeassistant/components/switch/neato.py index 34dad9bb5818ad..d9850c1589a054 100644 --- a/homeassistant/components/switch/neato.py +++ b/homeassistant/components/switch/neato.py @@ -24,14 +24,14 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Neato switches.""" dev = [] for robot in hass.data[NEATO_ROBOTS]: for type_name in SWITCH_TYPES: dev.append(NeatoConnectedSwitch(hass, robot, type_name)) _LOGGER.debug("Adding switches %s", dev) - add_devices(dev) + add_entities(dev) class NeatoConnectedSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/netio.py b/homeassistant/components/switch/netio.py index 365bbaa3679d14..2ccb4501d73560 100644 --- a/homeassistant/components/switch/netio.py +++ b/homeassistant/components/switch/netio.py @@ -49,7 +49,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Netio platform.""" from pynetio import Netio @@ -73,7 +73,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): DEVICES[host].netio, key, config[CONF_OUTLETS][key]) DEVICES[host].entities.append(switch) - add_devices(DEVICES[host].entities) + add_entities(DEVICES[host].entities) hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, dispose) return True diff --git a/homeassistant/components/switch/orvibo.py b/homeassistant/components/switch/orvibo.py index fdb4752f594432..9e6686ca3aef7c 100644 --- a/homeassistant/components/switch/orvibo.py +++ b/homeassistant/components/switch/orvibo.py @@ -31,7 +31,7 @@ }) -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up S20 switches.""" from orvibo.s20 import discover, S20, S20Exception @@ -54,7 +54,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): except S20Exception: _LOGGER.error("S20 at %s couldn't be initialized", host) - add_devices_callback(switches) + add_entities_callback(switches) class S20Switch(SwitchDevice): diff --git a/homeassistant/components/switch/pilight.py b/homeassistant/components/switch/pilight.py index 7ffce13ff6afd6..8ae8e64c2ff829 100644 --- a/homeassistant/components/switch/pilight.py +++ b/homeassistant/components/switch/pilight.py @@ -60,7 +60,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Pilight platform.""" switches = config.get(CONF_SWITCHES) devices = [] @@ -77,7 +77,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ) ) - add_devices(devices) + add_entities(devices) class _ReceiveHandle: diff --git a/homeassistant/components/switch/pulseaudio_loopback.py b/homeassistant/components/switch/pulseaudio_loopback.py index 06f2ee5f550cb1..f112608d760e29 100644 --- a/homeassistant/components/switch/pulseaudio_loopback.py +++ b/homeassistant/components/switch/pulseaudio_loopback.py @@ -54,7 +54,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Read in all of our configuration, and initialize the loopback switch.""" name = config.get(CONF_NAME) sink_name = config.get(CONF_SINK_NAME) @@ -72,7 +72,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): server = PAServer(host, port, buffer_size, tcp_timeout) _PULSEAUDIO_SERVERS[server_id] = server - add_devices([PALoopbackSwitch(hass, name, server, sink_name, source_name)]) + add_entities([ + PALoopbackSwitch(hass, name, server, sink_name, source_name)]) class PAServer(): diff --git a/homeassistant/components/switch/qwikswitch.py b/homeassistant/components/switch/qwikswitch.py index 193c27225349b9..3b8e6d74df2554 100644 --- a/homeassistant/components/switch/qwikswitch.py +++ b/homeassistant/components/switch/qwikswitch.py @@ -11,14 +11,14 @@ DEPENDENCIES = [QWIKSWITCH] -async def async_setup_platform(hass, _, add_devices, discovery_info=None): +async def async_setup_platform(hass, _, add_entities, discovery_info=None): """Add switches from the main Qwikswitch component.""" if discovery_info is None: return qsusb = hass.data[QWIKSWITCH] devs = [QSSwitch(qsid, qsusb) for qsid in discovery_info[QWIKSWITCH]] - add_devices(devs) + add_entities(devs) class QSSwitch(QSToggleEntity, SwitchDevice): diff --git a/homeassistant/components/switch/rachio.py b/homeassistant/components/switch/rachio.py index 5f0ca995c90f20..956befeeb9f9b1 100644 --- a/homeassistant/components/switch/rachio.py +++ b/homeassistant/components/switch/rachio.py @@ -47,7 +47,7 @@ ATTR_ZONE_NUMBER = 'Zone number' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Rachio switches.""" manual_run_time = timedelta(minutes=config.get(CONF_MANUAL_RUN_MINS)) _LOGGER.info("Rachio run time is %s", str(manual_run_time)) @@ -60,7 +60,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for zone in controller.list_zones(): devices.append(RachioZone(hass, controller, zone, manual_run_time)) - add_devices(devices) + add_entities(devices) _LOGGER.info("%d Rachio switch(es) added", len(devices)) diff --git a/homeassistant/components/switch/rainbird.py b/homeassistant/components/switch/rainbird.py index 9aa24b9360b6f3..86f25102f707e3 100644 --- a/homeassistant/components/switch/rainbird.py +++ b/homeassistant/components/switch/rainbird.py @@ -33,14 +33,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Rain Bird switches over a Rain Bird controller.""" controller = hass.data[DATA_RAINBIRD] devices = [] for dev_id, switch in config.get(CONF_SWITCHES).items(): devices.append(RainBirdSwitch(controller, switch, dev_id)) - add_devices(devices, True) + add_entities(devices, True) class RainBirdSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/raincloud.py b/homeassistant/components/switch/raincloud.py index a4bac8fee1cd46..969169edc64740 100644 --- a/homeassistant/components/switch/raincloud.py +++ b/homeassistant/components/switch/raincloud.py @@ -28,7 +28,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for a raincloud device.""" raincloud = hass.data[DATA_RAINCLOUD].data default_watering_timer = config.get(CONF_WATERING_TIME) @@ -42,7 +42,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): zone, sensor_type)) - add_devices(sensors, True) + add_entities(sensors, True) return True diff --git a/homeassistant/components/switch/rainmachine.py b/homeassistant/components/switch/rainmachine.py index b0cdf334cfa127..633a3e50a09911 100644 --- a/homeassistant/components/switch/rainmachine.py +++ b/homeassistant/components/switch/rainmachine.py @@ -100,7 +100,7 @@ async def async_setup_platform( - hass, config, async_add_devices, discovery_info=None): + hass, config, async_add_entities, discovery_info=None): """Set up the RainMachine Switch platform.""" if discovery_info is None: return @@ -129,7 +129,7 @@ async def async_setup_platform( _LOGGER.debug('Adding zone: %s', zone) entities.append(RainMachineZone(rainmachine, zone, zone_run_time)) - async_add_devices(entities, True) + async_add_entities(entities, True) class RainMachineSwitch(RainMachineEntity, SwitchDevice): diff --git a/homeassistant/components/switch/raspihats.py b/homeassistant/components/switch/raspihats.py index 7173ad35dafbfd..c697d7042a6c13 100644 --- a/homeassistant/components/switch/raspihats.py +++ b/homeassistant/components/switch/raspihats.py @@ -39,7 +39,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the raspihats switch devices.""" I2CHatSwitch.I2C_HATS_MANAGER = hass.data[I2C_HATS_MANAGER] switches = [] @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error( "Failed to register %s I2CHat@%s %s", board, hex(address), str(ex)) - add_devices(switches) + add_entities(switches) class I2CHatSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/rest.py b/homeassistant/components/switch/rest.py index 914408406a9e80..0e00bfe7844a53 100644 --- a/homeassistant/components/switch/rest.py +++ b/homeassistant/components/switch/rest.py @@ -48,7 +48,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the RESTful switch.""" body_off = config.get(CONF_BODY_OFF) body_on = config.get(CONF_BODY_ON) @@ -80,7 +81,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): if req.status >= 400: _LOGGER.error("Got non-ok response from resource: %s", req.status) else: - async_add_devices([switch]) + async_add_entities([switch]) except (TypeError, ValueError): _LOGGER.error("Missing resource or schema in configuration. " "Add http:// or https:// to your URL") diff --git a/homeassistant/components/switch/rflink.py b/homeassistant/components/switch/rflink.py index 366cb397d5b6a4..370436b3184fc0 100644 --- a/homeassistant/components/switch/rflink.py +++ b/homeassistant/components/switch/rflink.py @@ -86,9 +86,10 @@ def devices_from_config(domain_config, hass=None): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Rflink platform.""" - async_add_devices(devices_from_config(config, hass)) + async_add_entities(devices_from_config(config, hass)) class RflinkSwitch(SwitchableRflinkDevice, SwitchDevice): diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index 17b5c8e40d5510..c0f057da138afd 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -33,13 +33,13 @@ }) -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up the RFXtrx platform.""" import RFXtrx as rfxtrxmod # Add switch from config file switches = rfxtrx.get_devices_from_config(config, RfxtrxSwitch) - add_devices_callback(switches) + add_entities_callback(switches) def switch_update(event): """Handle sensor updates from the RFXtrx gateway.""" @@ -50,7 +50,7 @@ def switch_update(event): new_device = rfxtrx.get_new_device(event, config, RfxtrxSwitch) if new_device: - add_devices_callback([new_device]) + add_entities_callback([new_device]) rfxtrx.apply_received_command(event) diff --git a/homeassistant/components/switch/rpi_gpio.py b/homeassistant/components/switch/rpi_gpio.py index 300af4be61d1fc..3dec1135ec887c 100644 --- a/homeassistant/components/switch/rpi_gpio.py +++ b/homeassistant/components/switch/rpi_gpio.py @@ -34,7 +34,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Raspberry PI GPIO devices.""" invert_logic = config.get(CONF_INVERT_LOGIC) @@ -42,7 +42,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ports = config.get(CONF_PORTS) for port, name in ports.items(): switches.append(RPiGPIOSwitch(name, port, invert_logic)) - add_devices(switches) + add_entities(switches) class RPiGPIOSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/rpi_pfio.py b/homeassistant/components/switch/rpi_pfio.py index dad0c7c59ba39c..8b0871ca800879 100644 --- a/homeassistant/components/switch/rpi_pfio.py +++ b/homeassistant/components/switch/rpi_pfio.py @@ -36,7 +36,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the PiFace Digital Output devices.""" switches = [] ports = config.get(CONF_PORTS) @@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): invert_logic = port_entity[ATTR_INVERT_LOGIC] switches.append(RPiPFIOSwitch(port, name, invert_logic)) - add_devices(switches) + add_entities(switches) class RPiPFIOSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/rpi_rf.py b/homeassistant/components/switch/rpi_rf.py index 03f11de21f708c..ffadc0d38ded50 100644 --- a/homeassistant/components/switch/rpi_rf.py +++ b/homeassistant/components/switch/rpi_rf.py @@ -45,7 +45,7 @@ # pylint: disable=no-member -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return switches controlled by a generic RF device via GPIO.""" import rpi_rf from threading import RLock @@ -72,7 +72,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if devices: rfdevice.enable_tx() - add_devices(devices) + add_entities(devices) hass.bus.listen_once( EVENT_HOMEASSISTANT_STOP, lambda event: rfdevice.cleanup()) diff --git a/homeassistant/components/switch/scsgate.py b/homeassistant/components/switch/scsgate.py index b549f351afc84e..bb8c067ebd92cb 100644 --- a/homeassistant/components/switch/scsgate.py +++ b/homeassistant/components/switch/scsgate.py @@ -28,17 +28,17 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SCSGate switches.""" logger = logging.getLogger(__name__) _setup_traditional_switches( - logger=logger, config=config, add_devices_callback=add_devices) + logger=logger, config=config, add_entities_callback=add_entities) _setup_scenario_switches(logger=logger, config=config, hass=hass) -def _setup_traditional_switches(logger, config, add_devices_callback): +def _setup_traditional_switches(logger, config, add_entities_callback): """Add traditional SCSGate switches.""" traditional = config.get(CONF_TRADITIONAL) switches = [] @@ -56,7 +56,7 @@ def _setup_traditional_switches(logger, config, add_devices_callback): switch = SCSGateSwitch(name=name, scs_id=scs_id, logger=logger) switches.append(switch) - add_devices_callback(switches) + add_entities_callback(switches) scsgate.SCSGATE.add_devices_to_register(switches) diff --git a/homeassistant/components/switch/skybell.py b/homeassistant/components/switch/skybell.py index 726a5e7446e454..9d791aa3df3979 100644 --- a/homeassistant/components/switch/skybell.py +++ b/homeassistant/components/switch/skybell.py @@ -34,7 +34,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the platform for a Skybell device.""" skybell = hass.data.get(SKYBELL_DOMAIN) @@ -43,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for device in skybell.get_devices(): sensors.append(SkybellSwitch(device, switch_type)) - add_devices(sensors, True) + add_entities(sensors, True) class SkybellSwitch(SkybellDevice, SwitchDevice): diff --git a/homeassistant/components/switch/smappee.py b/homeassistant/components/switch/smappee.py index fd8f141500bab9..fc2716b8019ed0 100644 --- a/homeassistant/components/switch/smappee.py +++ b/homeassistant/components/switch/smappee.py @@ -16,7 +16,7 @@ ICON = 'mdi:power-plug' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Smappee Comfort Plugs.""" smappee = hass.data[DATA_SMAPPEE] @@ -37,7 +37,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): items.get('value'), None, items.get('key'))) - add_devices(dev) + add_entities(dev) class SmappeeSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/snmp.py b/homeassistant/components/switch/snmp.py index 9c84584e833b9e..f208c24286de0e 100644 --- a/homeassistant/components/switch/snmp.py +++ b/homeassistant/components/switch/snmp.py @@ -52,7 +52,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SNMP switch.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) @@ -66,7 +66,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): payload_on = config.get(CONF_PAYLOAD_ON) payload_off = config.get(CONF_PAYLOAD_OFF) - add_devices( + add_entities( [SnmpSwitch(name, host, port, community, baseoid, command_oid, version, payload_on, payload_off, command_payload_on, command_payload_off)], True) diff --git a/homeassistant/components/switch/spider.py b/homeassistant/components/switch/spider.py index 94b7db8f1e5b2b..f4bf74ad01029c 100644 --- a/homeassistant/components/switch/spider.py +++ b/homeassistant/components/switch/spider.py @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Spider thermostat.""" if discovery_info is None: return @@ -23,7 +23,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices = [SpiderPowerPlug(hass.data[SPIDER_DOMAIN]['controller'], device) for device in hass.data[SPIDER_DOMAIN]['power_plugs']] - add_devices(devices, True) + add_entities(devices, True) class SpiderPowerPlug(SwitchDevice): diff --git a/homeassistant/components/switch/tahoma.py b/homeassistant/components/switch/tahoma.py index aa3554a494c695..2904f06b432a1b 100644 --- a/homeassistant/components/switch/tahoma.py +++ b/homeassistant/components/switch/tahoma.py @@ -18,13 +18,13 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tahoma switches.""" controller = hass.data[TAHOMA_DOMAIN]['controller'] devices = [] for switch in hass.data[TAHOMA_DOMAIN]['devices']['switch']: devices.append(TahomaSwitch(switch, controller)) - add_devices(devices, True) + add_entities(devices, True) class TahomaSwitch(TahomaDevice, SwitchDevice): diff --git a/homeassistant/components/switch/tellduslive.py b/homeassistant/components/switch/tellduslive.py index eec63ebaa5cbcb..0263dfd8198c97 100644 --- a/homeassistant/components/switch/tellduslive.py +++ b/homeassistant/components/switch/tellduslive.py @@ -15,11 +15,11 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tellstick switches.""" if discovery_info is None: return - add_devices(TelldusLiveSwitch(hass, switch) for switch in discovery_info) + add_entities(TelldusLiveSwitch(hass, switch) for switch in discovery_info) class TelldusLiveSwitch(TelldusLiveEntity, ToggleEntity): diff --git a/homeassistant/components/switch/tellstick.py b/homeassistant/components/switch/tellstick.py index 5f7930a8a7c47a..51a04e9f5b327a 100644 --- a/homeassistant/components/switch/tellstick.py +++ b/homeassistant/components/switch/tellstick.py @@ -10,7 +10,7 @@ from homeassistant.helpers.entity import ToggleEntity -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tellstick switches.""" if (discovery_info is None or discovery_info[ATTR_DISCOVER_DEVICES] is None): @@ -20,10 +20,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): signal_repetitions = discovery_info.get(ATTR_DISCOVER_CONFIG, DEFAULT_SIGNAL_REPETITIONS) - add_devices([TellstickSwitch(hass.data[DATA_TELLSTICK][tellcore_id], - signal_repetitions) - for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]], - True) + add_entities([TellstickSwitch(hass.data[DATA_TELLSTICK][tellcore_id], + signal_repetitions) + for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]], + True) class TellstickSwitch(TellstickDevice, ToggleEntity): diff --git a/homeassistant/components/switch/telnet.py b/homeassistant/components/switch/telnet.py index 381f2ec9bec829..440279a70a857e 100644 --- a/homeassistant/components/switch/telnet.py +++ b/homeassistant/components/switch/telnet.py @@ -38,7 +38,7 @@ SCAN_INTERVAL = timedelta(seconds=10) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return switches controlled by telnet commands.""" devices = config.get(CONF_SWITCHES, {}) switches = [] @@ -67,7 +67,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("No switches added") return - add_devices(switches) + add_entities(switches) class TelnetSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/template.py b/homeassistant/components/switch/template.py index a6fa8241940b1c..7461aa2a7209ba 100644 --- a/homeassistant/components/switch/template.py +++ b/homeassistant/components/switch/template.py @@ -44,7 +44,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Template switch.""" switches = [] @@ -77,7 +78,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): _LOGGER.error("No switches added") return False - async_add_devices(switches) + async_add_entities(switches) return True diff --git a/homeassistant/components/switch/tesla.py b/homeassistant/components/switch/tesla.py index 0e1b7e819f7bb3..30972b1014b89d 100644 --- a/homeassistant/components/switch/tesla.py +++ b/homeassistant/components/switch/tesla.py @@ -15,7 +15,7 @@ DEPENDENCIES = ['tesla'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tesla switch platform.""" controller = hass.data[TESLA_DOMAIN]['devices']['controller'] devices = [] @@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices.append(ChargerSwitch(device, controller)) elif device.bin_type == 0x9: devices.append(RangeSwitch(device, controller)) - add_devices(devices, True) + add_entities(devices, True) class ChargerSwitch(TeslaDevice, SwitchDevice): diff --git a/homeassistant/components/switch/thinkingcleaner.py b/homeassistant/components/switch/thinkingcleaner.py index 0753435cfba12a..89586465b43631 100644 --- a/homeassistant/components/switch/thinkingcleaner.py +++ b/homeassistant/components/switch/thinkingcleaner.py @@ -29,7 +29,7 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ThinkingCleaner platform.""" from pythinkingcleaner import Discovery @@ -48,7 +48,7 @@ def update_devices(): dev.append(ThinkingCleanerSwitch(device, type_name, update_devices)) - add_devices(dev) + add_entities(dev) class ThinkingCleanerSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/toon.py b/homeassistant/components/switch/toon.py index 94086d819e2a44..087ca673e85ba1 100644 --- a/homeassistant/components/switch/toon.py +++ b/homeassistant/components/switch/toon.py @@ -12,14 +12,14 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the discovered Toon Smart Plugs.""" _toon_main = hass.data[toon_main.TOON_HANDLE] switch_items = [] for plug in _toon_main.toon.smartplugs: switch_items.append(EnecoSmartPlug(hass, plug)) - add_devices(switch_items) + add_entities(switch_items) class EnecoSmartPlug(SwitchDevice): diff --git a/homeassistant/components/switch/tplink.py b/homeassistant/components/switch/tplink.py index 0cacdfe1539d1e..a68ad2bf37df65 100644 --- a/homeassistant/components/switch/tplink.py +++ b/homeassistant/components/switch/tplink.py @@ -32,14 +32,14 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the TPLink switch platform.""" from pyHS100 import SmartPlug host = config.get(CONF_HOST) name = config.get(CONF_NAME) leds_on = config.get(CONF_LEDS) - add_devices([SmartPlugSwitch(SmartPlug(host), name, leds_on)], True) + add_entities([SmartPlugSwitch(SmartPlug(host), name, leds_on)], True) class SmartPlugSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/transmission.py b/homeassistant/components/switch/transmission.py index ffe285a23f3942..10ab0903dcf2cc 100644 --- a/homeassistant/components/switch/transmission.py +++ b/homeassistant/components/switch/transmission.py @@ -31,7 +31,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Transmission switch.""" import transmissionrpc from transmissionrpc.error import TransmissionError @@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ) return False - add_devices([TransmissionSwitch(transmission_api, name)]) + add_entities([TransmissionSwitch(transmission_api, name)]) class TransmissionSwitch(ToggleEntity): diff --git a/homeassistant/components/switch/tuya.py b/homeassistant/components/switch/tuya.py index 4f69e76f954b25..9fc1f92016e9d9 100644 --- a/homeassistant/components/switch/tuya.py +++ b/homeassistant/components/switch/tuya.py @@ -10,7 +10,7 @@ DEPENDENCIES = ['tuya'] -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tuya Switch device.""" if discovery_info is None: return @@ -22,7 +22,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if device is None: continue devices.append(TuyaSwitch(device)) - add_devices(devices) + add_entities(devices) class TuyaSwitch(TuyaDevice, SwitchDevice): diff --git a/homeassistant/components/switch/upcloud.py b/homeassistant/components/switch/upcloud.py index 5c3af45bede888..f2818e59a9b09a 100644 --- a/homeassistant/components/switch/upcloud.py +++ b/homeassistant/components/switch/upcloud.py @@ -23,7 +23,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the UpCloud server switch.""" upcloud = hass.data[DATA_UPCLOUD] @@ -31,7 +31,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices = [UpCloudSwitch(upcloud, uuid) for uuid in servers] - add_devices(devices, True) + add_entities(devices, True) class UpCloudSwitch(UpCloudServerEntity, SwitchDevice): diff --git a/homeassistant/components/switch/velbus.py b/homeassistant/components/switch/velbus.py index 46f6e893c9714d..300ff43d676267 100644 --- a/homeassistant/components/switch/velbus.py +++ b/homeassistant/components/switch/velbus.py @@ -15,7 +15,7 @@ DEPENDENCIES = ['velbus'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Velbus Switch platform.""" if discovery_info is None: @@ -25,7 +25,7 @@ async def async_setup_platform(hass, config, async_add_devices, module = hass.data[VELBUS_DOMAIN].get_module(switch[0]) channel = switch[1] switches.append(VelbusSwitch(module, channel)) - async_add_devices(switches) + async_add_entities(switches) class VelbusSwitch(VelbusEntity, SwitchDevice): diff --git a/homeassistant/components/switch/vera.py b/homeassistant/components/switch/vera.py index 82e2756c23054e..4742b755944438 100644 --- a/homeassistant/components/switch/vera.py +++ b/homeassistant/components/switch/vera.py @@ -16,9 +16,9 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vera switches.""" - add_devices( + add_entities( [VeraSwitch(device, hass.data[VERA_CONTROLLER]) for device in hass.data[VERA_DEVICES]['switch']], True) diff --git a/homeassistant/components/switch/verisure.py b/homeassistant/components/switch/verisure.py index 4b126e5d3320be..11ccd82696ef91 100644 --- a/homeassistant/components/switch/verisure.py +++ b/homeassistant/components/switch/verisure.py @@ -14,7 +14,7 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Verisure switch platform.""" if not int(hub.config.get(CONF_SMARTPLUGS, 1)): return False @@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): switches.extend([ VerisureSmartplug(device_label) for device_label in hub.get('$.smartPlugs[*].deviceLabel')]) - add_devices(switches) + add_entities(switches) class VerisureSmartplug(SwitchDevice): diff --git a/homeassistant/components/switch/vesync.py b/homeassistant/components/switch/vesync.py index d8579a508e20f5..382096ad5e4dd6 100644 --- a/homeassistant/components/switch/vesync.py +++ b/homeassistant/components/switch/vesync.py @@ -21,7 +21,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the VeSync switch platform.""" from pyvesync.vesync import VeSync @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): else: _LOGGER.info("No VeSync devices found") - add_devices(switches) + add_entities(switches) class VeSyncSwitchHA(SwitchDevice): diff --git a/homeassistant/components/switch/volvooncall.py b/homeassistant/components/switch/volvooncall.py index c1b18a11795a0a..96091a725a1234 100644 --- a/homeassistant/components/switch/volvooncall.py +++ b/homeassistant/components/switch/volvooncall.py @@ -14,11 +14,11 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tellstick switches.""" if discovery_info is None: return - add_devices([VolvoSwitch(hass, *discovery_info)]) + add_entities([VolvoSwitch(hass, *discovery_info)]) class VolvoSwitch(VolvoEntity, ToggleEntity): diff --git a/homeassistant/components/switch/vultr.py b/homeassistant/components/switch/vultr.py index fe3d67470d79b1..874d1979d7dc59 100644 --- a/homeassistant/components/switch/vultr.py +++ b/homeassistant/components/switch/vultr.py @@ -28,7 +28,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vultr subscription switch.""" vultr = hass.data[DATA_VULTR] @@ -39,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Subscription %s not found", subscription) return False - add_devices([VultrSwitch(vultr, subscription, name)], True) + add_entities([VultrSwitch(vultr, subscription, name)], True) class VultrSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/wake_on_lan.py b/homeassistant/components/switch/wake_on_lan.py index 80102621f7daf2..06f86865064801 100644 --- a/homeassistant/components/switch/wake_on_lan.py +++ b/homeassistant/components/switch/wake_on_lan.py @@ -35,7 +35,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a wake on lan switch.""" name = config.get(CONF_NAME) host = config.get(CONF_HOST) @@ -43,8 +43,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): broadcast_address = config.get(CONF_BROADCAST_ADDRESS) off_action = config.get(CONF_OFF_ACTION) - add_devices([WOLSwitch(hass, name, host, mac_address, - off_action, broadcast_address)], True) + add_entities([WOLSwitch(hass, name, host, mac_address, + off_action, broadcast_address)], True) class WOLSwitch(SwitchDevice): diff --git a/homeassistant/components/switch/wemo.py b/homeassistant/components/switch/wemo.py index 35ea435bf48390..94b86377b8da60 100644 --- a/homeassistant/components/switch/wemo.py +++ b/homeassistant/components/switch/wemo.py @@ -7,10 +7,12 @@ import asyncio import logging from datetime import datetime, timedelta +import requests import async_timeout from homeassistant.components.switch import SwitchDevice +from homeassistant.exceptions import PlatformNotReady from homeassistant.util import convert from homeassistant.const import ( STATE_OFF, STATE_ON, STATE_STANDBY, STATE_UNKNOWN) @@ -33,17 +35,23 @@ WEMO_STANDBY = 8 -def setup_platform(hass, config, add_devices_callback, discovery_info=None): +def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up discovered WeMo switches.""" from pywemo import discovery if discovery_info is not None: location = discovery_info['ssdp_description'] mac = discovery_info['mac_address'] - device = discovery.device_from_description(location, mac) + + try: + device = discovery.device_from_description(location, mac) + except (requests.exceptions.ConnectionError, + requests.exceptions.Timeout) as err: + _LOGGER.error('Unable to access %s (%s)', location, err) + raise PlatformNotReady if device: - add_devices_callback([WemoSwitch(device)]) + add_entities_callback([WemoSwitch(device)]) class WemoSwitch(SwitchDevice): @@ -69,7 +77,7 @@ def _subscription_callback(self, _device, _type, _params): self._async_locked_subscription_callback(not updated)) async def _async_locked_subscription_callback(self, force_update): - """Helper to handle an update from a subscription.""" + """Handle an update from a subscription.""" # If an update is in progress, we don't do anything if self._update_lock.locked(): return diff --git a/homeassistant/components/switch/wink.py b/homeassistant/components/switch/wink.py index 6a2446150651e7..0df59d6b51caab 100644 --- a/homeassistant/components/switch/wink.py +++ b/homeassistant/components/switch/wink.py @@ -15,26 +15,26 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink platform.""" import pywink for switch in pywink.get_switches(): _id = switch.object_id() + switch.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkToggleDevice(switch, hass)]) + add_entities([WinkToggleDevice(switch, hass)]) for switch in pywink.get_powerstrips(): _id = switch.object_id() + switch.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkToggleDevice(switch, hass)]) + add_entities([WinkToggleDevice(switch, hass)]) for sprinkler in pywink.get_sprinklers(): _id = sprinkler.object_id() + sprinkler.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkToggleDevice(sprinkler, hass)]) + add_entities([WinkToggleDevice(sprinkler, hass)]) for switch in pywink.get_binary_switch_groups(): _id = switch.object_id() + switch.name() if _id not in hass.data[DOMAIN]['unique_ids']: - add_devices([WinkToggleDevice(switch, hass)]) + add_entities([WinkToggleDevice(switch, hass)]) class WinkToggleDevice(WinkDevice, ToggleEntity): diff --git a/homeassistant/components/switch/wirelesstag.py b/homeassistant/components/switch/wirelesstag.py index cce8c349a31448..5796216d50f30a 100644 --- a/homeassistant/components/switch/wirelesstag.py +++ b/homeassistant/components/switch/wirelesstag.py @@ -45,7 +45,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up switches for a Wireless Sensor Tags.""" platform = hass.data.get(WIRELESSTAG_DOMAIN) @@ -56,7 +56,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if switch_type in WirelessTagSwitch.allowed_switches(tag): switches.append(WirelessTagSwitch(platform, tag, switch_type)) - add_devices(switches, True) + add_entities(switches, True) class WirelessTagSwitch(WirelessTagBaseSensor, SwitchDevice): diff --git a/homeassistant/components/switch/xiaomi_aqara.py b/homeassistant/components/switch/xiaomi_aqara.py index 4c44d6b2592f2e..a29a3c74a2e4af 100644 --- a/homeassistant/components/switch/xiaomi_aqara.py +++ b/homeassistant/components/switch/xiaomi_aqara.py @@ -19,7 +19,7 @@ IN_USE = 'inuse' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Perform the setup for Xiaomi devices.""" devices = [] for (_, gateway) in hass.data[PY_XIAOMI_GATEWAY].gateways.items(): @@ -59,7 +59,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): elif model in ['86plug', 'ctrl_86plug', 'ctrl_86plug.aq1']: devices.append(XiaomiGenericSwitch(device, 'Wall Plug', 'status', True, gateway)) - add_devices(devices) + add_entities(devices) class XiaomiGenericSwitch(XiaomiDevice, SwitchDevice): diff --git a/homeassistant/components/switch/xiaomi_miio.py b/homeassistant/components/switch/xiaomi_miio.py index 37b16f44ea8eec..821de5bf6472fb 100644 --- a/homeassistant/components/switch/xiaomi_miio.py +++ b/homeassistant/components/switch/xiaomi_miio.py @@ -39,7 +39,7 @@ 'chuangmi.plug.v3']), }) -REQUIREMENTS = ['python-miio==0.4.0', 'construct==2.9.41'] +REQUIREMENTS = ['python-miio==0.4.1', 'construct==2.9.41'] ATTR_POWER = 'power' ATTR_TEMPERATURE = 'temperature' @@ -97,7 +97,7 @@ } -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the switch from config.""" from miio import Device, DeviceException @@ -158,7 +158,7 @@ async def async_setup_platform(hass, config, async_add_devices, 'and provide the following data: %s', model) return False - async_add_devices(devices, update_before_add=True) + async_add_entities(devices, update_before_add=True) async def async_service_handler(service): """Map services to methods on XiaomiPlugGenericSwitch.""" diff --git a/homeassistant/components/switch/zha.py b/homeassistant/components/switch/zha.py index 6109dc192f3c95..9f780b631b655f 100644 --- a/homeassistant/components/switch/zha.py +++ b/homeassistant/components/switch/zha.py @@ -14,7 +14,7 @@ DEPENDENCIES = ['zha'] -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Zigbee Home Automation switches.""" discovery_info = zha.get_discovery_info(hass, discovery_info) @@ -27,7 +27,7 @@ async def async_setup_platform(hass, config, async_add_devices, await cluster.bind() await cluster.configure_reporting(0, 0, 600, 1,) - async_add_devices([Switch(**discovery_info)], update_before_add=True) + async_add_entities([Switch(**discovery_info)], update_before_add=True) class Switch(zha.Entity, SwitchDevice): diff --git a/homeassistant/components/switch/zigbee.py b/homeassistant/components/switch/zigbee.py index a0db5685a90cb7..8d54892399a06b 100644 --- a/homeassistant/components/switch/zigbee.py +++ b/homeassistant/components/switch/zigbee.py @@ -24,9 +24,9 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZigBee switch platform.""" - add_devices([ZigBeeSwitch(hass, ZigBeeDigitalOutConfig(config))]) + add_entities([ZigBeeSwitch(hass, ZigBeeDigitalOutConfig(config))]) class ZigBeeSwitch(ZigBeeDigitalOut, SwitchDevice): diff --git a/homeassistant/components/switch/zoneminder.py b/homeassistant/components/switch/zoneminder.py index fa32843eb4b94b..496e7549aaa28b 100644 --- a/homeassistant/components/switch/zoneminder.py +++ b/homeassistant/components/switch/zoneminder.py @@ -23,7 +23,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZoneMinder switch platform.""" on_state = config.get(CONF_COMMAND_ON) off_state = config.get(CONF_COMMAND_OFF) @@ -41,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ) ) - add_devices(switches) + add_entities(switches) class ZMSwitchMonitors(SwitchDevice): diff --git a/homeassistant/components/timer/__init__.py b/homeassistant/components/timer/__init__.py index 5a363e84d7bd13..8406b3ff5ecf1f 100644 --- a/homeassistant/components/timer/__init__.py +++ b/homeassistant/components/timer/__init__.py @@ -4,7 +4,6 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/timer/ """ -import asyncio import logging from datetime import timedelta @@ -141,39 +140,18 @@ async def async_setup(hass, config): if not entities: return False - async def async_handler_service(service): - """Handle a call to the timer services.""" - target_timers = component.async_extract_from_service(service) - - attr = None - if service.service == SERVICE_PAUSE: - attr = 'async_pause' - elif service.service == SERVICE_CANCEL: - attr = 'async_cancel' - elif service.service == SERVICE_FINISH: - attr = 'async_finish' - - tasks = [getattr(timer, attr)() for timer in target_timers if attr] - if service.service == SERVICE_START: - for timer in target_timers: - tasks.append( - timer.async_start(service.data.get(ATTR_DURATION)) - ) - if tasks: - await asyncio.wait(tasks, loop=hass.loop) - - hass.services.async_register( - DOMAIN, SERVICE_START, async_handler_service, - schema=SERVICE_SCHEMA_DURATION) - hass.services.async_register( - DOMAIN, SERVICE_PAUSE, async_handler_service, - schema=SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_CANCEL, async_handler_service, - schema=SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, SERVICE_FINISH, async_handler_service, - schema=SERVICE_SCHEMA) + component.async_register_entity_service( + SERVICE_START, SERVICE_SCHEMA_DURATION, + 'async_start') + component.async_register_entity_service( + SERVICE_PAUSE, SERVICE_SCHEMA, + 'async_pause') + component.async_register_entity_service( + SERVICE_CANCEL, SERVICE_SCHEMA, + 'async_cancel') + component.async_register_entity_service( + SERVICE_FINISH, SERVICE_SCHEMA, + 'async_finish') await component.async_add_entities(entities) return True diff --git a/homeassistant/components/upnp.py b/homeassistant/components/upnp.py index b4fe9d3fce9c68..5d7855f3959825 100644 --- a/homeassistant/components/upnp.py +++ b/homeassistant/components/upnp.py @@ -15,7 +15,7 @@ from homeassistant.helpers import discovery from homeassistant.util import get_local_ip -REQUIREMENTS = ['pyupnp-async==0.1.0.2'] +REQUIREMENTS = ['pyupnp-async==0.1.1.0'] DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) @@ -37,6 +37,7 @@ IGD_DEVICE = 'urn:schemas-upnp-org:device:InternetGatewayDevice:1' PPP_SERVICE = 'urn:schemas-upnp-org:service:WANPPPConnection:1' IP_SERVICE = 'urn:schemas-upnp-org:service:WANIPConnection:1' +IP_SERVICE2 = 'urn:schemas-upnp-org:service:WANIPConnection:2' CIC_SERVICE = 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1' UNITS = { @@ -48,7 +49,7 @@ CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Optional(CONF_ENABLE_PORT_MAPPING, default=True): cv.boolean, + vol.Optional(CONF_ENABLE_PORT_MAPPING, default=False): cv.boolean, vol.Optional(CONF_UNITS, default="MBytes"): vol.In(UNITS), vol.Optional(CONF_LOCAL_IP): vol.All(ip_address, cv.string), vol.Optional(CONF_PORTS): @@ -86,8 +87,10 @@ async def async_setup(hass, config): service = device.find_first_service(PPP_SERVICE) if _service['serviceType'] == IP_SERVICE: service = device.find_first_service(IP_SERVICE) + if _service['serviceType'] == IP_SERVICE2: + service = device.find_first_service(IP_SERVICE2) if _service['serviceType'] == CIC_SERVICE: - unit = config.get(CONF_UNITS) + unit = config[CONF_UNITS] hass.async_create_task(discovery.async_load_platform( hass, 'sensor', DOMAIN, {'unit': unit}, config)) except UpnpSoapError as error: @@ -98,7 +101,7 @@ async def async_setup(hass, config): _LOGGER.warning("Could not find any UPnP IGD") return False - port_mapping = config.get(CONF_ENABLE_PORT_MAPPING) + port_mapping = config[CONF_ENABLE_PORT_MAPPING] if not port_mapping: return True @@ -116,7 +119,8 @@ async def async_setup(hass, config): await service.add_port_mapping(internal, external, host, 'TCP', desc='Home Assistant') registered.append(external) - _LOGGER.debug("external %s -> %s @ %s", external, internal, host) + _LOGGER.debug("Mapping external TCP port %s -> %s @ %s", + external, internal, host) except UpnpSoapError as error: _LOGGER.error(error) hass.components.persistent_notification.create( diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 97d009626b8248..1808737d2815c1 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -62,23 +62,6 @@ vol.Optional(ATTR_PARAMS): vol.Any(dict, cv.ensure_list), }) -SERVICE_TO_METHOD = { - SERVICE_TURN_ON: {'method': 'async_turn_on'}, - SERVICE_TURN_OFF: {'method': 'async_turn_off'}, - SERVICE_TOGGLE: {'method': 'async_toggle'}, - SERVICE_START_PAUSE: {'method': 'async_start_pause'}, - SERVICE_START: {'method': 'async_start'}, - SERVICE_PAUSE: {'method': 'async_pause'}, - SERVICE_RETURN_TO_BASE: {'method': 'async_return_to_base'}, - SERVICE_CLEAN_SPOT: {'method': 'async_clean_spot'}, - SERVICE_LOCATE: {'method': 'async_locate'}, - SERVICE_STOP: {'method': 'async_stop'}, - SERVICE_SET_FAN_SPEED: {'method': 'async_set_fan_speed', - 'schema': VACUUM_SET_FAN_SPEED_SERVICE_SCHEMA}, - SERVICE_SEND_COMMAND: {'method': 'async_send_command', - 'schema': VACUUM_SEND_COMMAND_SERVICE_SCHEMA}, -} - STATE_CLEANING = 'cleaning' STATE_DOCKED = 'docked' STATE_IDLE = STATE_IDLE @@ -207,30 +190,54 @@ def async_setup(hass, config): yield from component.async_setup(config) - @asyncio.coroutine - def async_handle_vacuum_service(service): - """Map services to methods on VacuumDevice.""" - method = SERVICE_TO_METHOD.get(service.service) - target_vacuums = component.async_extract_from_service(service) - params = service.data.copy() - params.pop(ATTR_ENTITY_ID, None) - - update_tasks = [] - for vacuum in target_vacuums: - yield from getattr(vacuum, method['method'])(**params) - if not vacuum.should_poll: - continue - update_tasks.append(vacuum.async_update_ha_state(True)) - - if update_tasks: - yield from asyncio.wait(update_tasks, loop=hass.loop) - - for service in SERVICE_TO_METHOD: - schema = SERVICE_TO_METHOD[service].get( - 'schema', VACUUM_SERVICE_SCHEMA) - hass.services.async_register( - DOMAIN, service, async_handle_vacuum_service, - schema=schema) + component.async_register_entity_service( + SERVICE_TURN_ON, VACUUM_SERVICE_SCHEMA, + 'async_turn_on' + ) + component.async_register_entity_service( + SERVICE_TURN_OFF, VACUUM_SERVICE_SCHEMA, + 'async_turn_off' + ) + component.async_register_entity_service( + SERVICE_TOGGLE, VACUUM_SERVICE_SCHEMA, + 'async_toggle' + ) + component.async_register_entity_service( + SERVICE_START_PAUSE, VACUUM_SERVICE_SCHEMA, + 'async_start_pause' + ) + component.async_register_entity_service( + SERVICE_START, VACUUM_SERVICE_SCHEMA, + 'async_start' + ) + component.async_register_entity_service( + SERVICE_PAUSE, VACUUM_SERVICE_SCHEMA, + 'async_pause' + ) + component.async_register_entity_service( + SERVICE_RETURN_TO_BASE, VACUUM_SERVICE_SCHEMA, + 'async_return_to_base' + ) + component.async_register_entity_service( + SERVICE_CLEAN_SPOT, VACUUM_SERVICE_SCHEMA, + 'async_clean_spot' + ) + component.async_register_entity_service( + SERVICE_LOCATE, VACUUM_SERVICE_SCHEMA, + 'async_locate' + ) + component.async_register_entity_service( + SERVICE_STOP, VACUUM_SERVICE_SCHEMA, + 'async_stop' + ) + component.async_register_entity_service( + SERVICE_SET_FAN_SPEED, VACUUM_SET_FAN_SPEED_SERVICE_SCHEMA, + 'async_set_fan_speed' + ) + component.async_register_entity_service( + SERVICE_SEND_COMMAND, VACUUM_SEND_COMMAND_SERVICE_SCHEMA, + 'async_send_command' + ) return True diff --git a/homeassistant/components/vacuum/demo.py b/homeassistant/components/vacuum/demo.py index 5d4c6856a4dde2..12507683f516e3 100644 --- a/homeassistant/components/vacuum/demo.py +++ b/homeassistant/components/vacuum/demo.py @@ -43,9 +43,9 @@ DEMO_VACUUM_STATE = '5_Fifth_floor' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Demo vacuums.""" - add_devices([ + add_entities([ DemoVacuum(DEMO_VACUUM_COMPLETE, SUPPORT_ALL_SERVICES), DemoVacuum(DEMO_VACUUM_MOST, SUPPORT_MOST_SERVICES), DemoVacuum(DEMO_VACUUM_BASIC, SUPPORT_BASIC_SERVICES), diff --git a/homeassistant/components/vacuum/dyson.py b/homeassistant/components/vacuum/dyson.py index d423a8dacf51d5..943b97f636028e 100644 --- a/homeassistant/components/vacuum/dyson.py +++ b/homeassistant/components/vacuum/dyson.py @@ -29,7 +29,7 @@ SUPPORT_BATTERY | SUPPORT_STOP -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dyson 360 Eye robot vacuum platform.""" from libpurecoollink.dyson_360_eye import Dyson360Eye @@ -43,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dyson_entity = Dyson360EyeDevice(device) hass.data[DYSON_360_EYE_DEVICES].append(dyson_entity) - add_devices(hass.data[DYSON_360_EYE_DEVICES]) + add_entities(hass.data[DYSON_360_EYE_DEVICES]) return True diff --git a/homeassistant/components/vacuum/ecovacs.py b/homeassistant/components/vacuum/ecovacs.py new file mode 100644 index 00000000000000..ac01d8e7a203ce --- /dev/null +++ b/homeassistant/components/vacuum/ecovacs.py @@ -0,0 +1,198 @@ +""" +Support for Ecovacs Ecovacs Vaccums. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/vacuum.neato/ +""" +import logging + +from homeassistant.components.vacuum import ( + VacuumDevice, SUPPORT_BATTERY, SUPPORT_RETURN_HOME, SUPPORT_CLEAN_SPOT, + SUPPORT_STATUS, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, + SUPPORT_LOCATE, SUPPORT_FAN_SPEED, SUPPORT_SEND_COMMAND, ) +from homeassistant.components.ecovacs import ( + ECOVACS_DEVICES) +from homeassistant.helpers.icon import icon_for_battery_level + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['ecovacs'] + +SUPPORT_ECOVACS = ( + SUPPORT_BATTERY | SUPPORT_RETURN_HOME | SUPPORT_CLEAN_SPOT | + SUPPORT_STOP | SUPPORT_TURN_OFF | SUPPORT_TURN_ON | SUPPORT_LOCATE | + SUPPORT_STATUS | SUPPORT_SEND_COMMAND | SUPPORT_FAN_SPEED) + +ATTR_ERROR = 'error' +ATTR_COMPONENT_PREFIX = 'component_' + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the Ecovacs vacuums.""" + vacuums = [] + for device in hass.data[ECOVACS_DEVICES]: + vacuums.append(EcovacsVacuum(device)) + _LOGGER.debug("Adding Ecovacs Vacuums to Hass: %s", vacuums) + add_entities(vacuums, True) + + +class EcovacsVacuum(VacuumDevice): + """Ecovacs Vacuums such as Deebot.""" + + def __init__(self, device): + """Initialize the Ecovacs Vacuum.""" + self.device = device + self.device.connect_and_wait_until_ready() + try: + self._name = '{}'.format(self.device.vacuum['nick']) + except KeyError: + # In case there is no nickname defined, use the device id + self._name = '{}'.format(self.device.vacuum['did']) + + self._fan_speed = None + self._error = None + _LOGGER.debug("Vacuum initialized: %s", self.name) + + async def async_added_to_hass(self) -> None: + """Set up the event listeners now that hass is ready.""" + self.device.statusEvents.subscribe(lambda _: + self.schedule_update_ha_state()) + self.device.batteryEvents.subscribe(lambda _: + self.schedule_update_ha_state()) + self.device.lifespanEvents.subscribe(lambda _: + self.schedule_update_ha_state()) + self.device.errorEvents.subscribe(self.on_error) + + def on_error(self, error): + """Handle an error event from the robot. + + This will not change the entity's state. If the error caused the state + to change, that will come through as a separate on_status event + """ + if error == 'no_error': + self._error = None + else: + self._error = error + + self.hass.bus.fire('ecovacs_error', { + 'entity_id': self.entity_id, + 'error': error + }) + self.schedule_update_ha_state() + + @property + def should_poll(self) -> bool: + """Return True if entity has to be polled for state.""" + return False + + @property + def unique_id(self) -> str: + """Return an unique ID.""" + return self.device.vacuum.get('did', None) + + @property + def is_on(self): + """Return true if vacuum is currently cleaning.""" + return self.device.is_cleaning + + @property + def is_charging(self): + """Return true if vacuum is currently charging.""" + return self.device.is_charging + + @property + def name(self): + """Return the name of the device.""" + return self._name + + @property + def supported_features(self): + """Flag vacuum cleaner robot features that are supported.""" + return SUPPORT_ECOVACS + + @property + def status(self): + """Return the status of the vacuum cleaner.""" + return self.device.vacuum_status + + def return_to_base(self, **kwargs): + """Set the vacuum cleaner to return to the dock.""" + from sucks import Charge + self.device.run(Charge()) + + @property + def battery_icon(self): + """Return the battery icon for the vacuum cleaner.""" + return icon_for_battery_level( + battery_level=self.battery_level, charging=self.is_charging) + + @property + def battery_level(self): + """Return the battery level of the vacuum cleaner.""" + if self.device.battery_status is not None: + return self.device.battery_status * 100 + + return super().battery_level + + @property + def fan_speed(self): + """Return the fan speed of the vacuum cleaner.""" + return self.device.fan_speed + + @property + def fan_speed_list(self): + """Get the list of available fan speed steps of the vacuum cleaner.""" + from sucks import FAN_SPEED_NORMAL, FAN_SPEED_HIGH + return [FAN_SPEED_NORMAL, FAN_SPEED_HIGH] + + def turn_on(self, **kwargs): + """Turn the vacuum on and start cleaning.""" + from sucks import Clean + self.device.run(Clean()) + + def turn_off(self, **kwargs): + """Turn the vacuum off stopping the cleaning and returning home.""" + self.return_to_base() + + def stop(self, **kwargs): + """Stop the vacuum cleaner.""" + from sucks import Stop + self.device.run(Stop()) + + def clean_spot(self, **kwargs): + """Perform a spot clean-up.""" + from sucks import Spot + self.device.run(Spot()) + + def locate(self, **kwargs): + """Locate the vacuum cleaner.""" + from sucks import PlaySound + self.device.run(PlaySound()) + + def set_fan_speed(self, fan_speed, **kwargs): + """Set fan speed.""" + if self.is_on: + from sucks import Clean + self.device.run(Clean( + mode=self.device.clean_status, speed=fan_speed)) + + def send_command(self, command, params=None, **kwargs): + """Send a command to a vacuum cleaner.""" + from sucks import VacBotCommand + self.device.run(VacBotCommand(command, params)) + + @property + def device_state_attributes(self): + """Return the device-specific state attributes of this vacuum.""" + data = {} + data[ATTR_ERROR] = self._error + + for key, val in self.device.components.items(): + attr_name = ATTR_COMPONENT_PREFIX + key + data[attr_name] = int(val * 100 / 0.2777778) + # The above calculation includes a fix for a bug in sucks 0.9.1 + # When sucks 0.9.2+ is released, it should be changed to the + # following: + # data[attr_name] = int(val * 100) + + return data diff --git a/homeassistant/components/vacuum/mqtt.py b/homeassistant/components/vacuum/mqtt.py index fd80f4cdbfbaf7..47617277773f28 100644 --- a/homeassistant/components/vacuum/mqtt.py +++ b/homeassistant/components/vacuum/mqtt.py @@ -140,7 +140,8 @@ def strings_to_services(strings): @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the vacuum.""" name = config.get(CONF_NAME) supported_feature_strings = config.get(CONF_SUPPORTED_FEATURES) @@ -192,7 +193,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): payload_available = config.get(mqtt.CONF_PAYLOAD_AVAILABLE) payload_not_available = config.get(mqtt.CONF_PAYLOAD_NOT_AVAILABLE) - async_add_devices([ + async_add_entities([ MqttVacuum( name, supported_features, qos, retain, command_topic, payload_turn_on, payload_turn_off, payload_return_to_base, diff --git a/homeassistant/components/vacuum/neato.py b/homeassistant/components/vacuum/neato.py index 224e763a097d9e..dd27b2a33d242b 100644 --- a/homeassistant/components/vacuum/neato.py +++ b/homeassistant/components/vacuum/neato.py @@ -8,10 +8,10 @@ from datetime import timedelta import requests -from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.components.vacuum import ( - VacuumDevice, SUPPORT_BATTERY, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, - SUPPORT_STATUS, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, + StateVacuumDevice, SUPPORT_BATTERY, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, + SUPPORT_STATE, SUPPORT_STOP, SUPPORT_START, STATE_IDLE, + STATE_PAUSED, STATE_CLEANING, STATE_DOCKED, STATE_RETURNING, STATE_ERROR, SUPPORT_MAP, ATTR_STATUS, ATTR_BATTERY_LEVEL, ATTR_BATTERY_ICON, SUPPORT_LOCATE) from homeassistant.components.neato import ( @@ -24,8 +24,8 @@ SCAN_INTERVAL = timedelta(minutes=5) SUPPORT_NEATO = SUPPORT_BATTERY | SUPPORT_PAUSE | SUPPORT_RETURN_HOME | \ - SUPPORT_STOP | SUPPORT_TURN_OFF | SUPPORT_TURN_ON | \ - SUPPORT_STATUS | SUPPORT_MAP | SUPPORT_LOCATE + SUPPORT_STOP | SUPPORT_START | \ + SUPPORT_STATE | SUPPORT_MAP | SUPPORT_LOCATE ATTR_CLEAN_START = 'clean_start' ATTR_CLEAN_STOP = 'clean_stop' @@ -36,16 +36,16 @@ ATTR_CLEAN_SUSP_TIME = 'clean_suspension_time' -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Neato vacuum.""" dev = [] for robot in hass.data[NEATO_ROBOTS]: dev.append(NeatoConnectedVacuum(hass, robot)) _LOGGER.debug("Adding vacuums %s", dev) - add_devices(dev, True) + add_entities(dev, True) -class NeatoConnectedVacuum(VacuumDevice): +class NeatoConnectedVacuum(StateVacuumDevice): """Representation of a Neato Connected Vacuum.""" def __init__(self, hass, robot): @@ -79,36 +79,30 @@ def update(self): _LOGGER.debug('self._state=%s', self._state) if self._state['state'] == 1: if self._state['details']['isCharging']: + self._clean_state = STATE_DOCKED self._status_state = 'Charging' elif (self._state['details']['isDocked'] and not self._state['details']['isCharging']): + self._clean_state = STATE_DOCKED self._status_state = 'Docked' else: + self._clean_state = STATE_IDLE self._status_state = 'Stopped' elif self._state['state'] == 2: if ALERTS.get(self._state['error']) is None: + self._clean_state = STATE_CLEANING self._status_state = ( MODE.get(self._state['cleaning']['mode']) + ' ' + ACTION.get(self._state['action'])) else: self._status_state = ALERTS.get(self._state['error']) elif self._state['state'] == 3: + self._clean_state = STATE_PAUSED self._status_state = 'Paused' elif self._state['state'] == 4: + self._clean_state = STATE_ERROR self._status_state = ERRORS.get(self._state['error']) - if (self._state['action'] == 1 or - self._state['action'] == 2 or - self._state['action'] == 3 and - self._state['state'] == 2): - self._clean_state = STATE_ON - elif (self._state['action'] == 11 or - self._state['action'] == 12 and - self._state['state'] == 2): - self._clean_state = STATE_ON - else: - self._clean_state = STATE_OFF - if not self._mapdata.get(self.robot.serial, {}).get('maps', []): return self.clean_time_start = ( @@ -147,17 +141,17 @@ def battery_level(self): return self._state['details']['charge'] @property - def status(self): + def state(self): """Return the status of the vacuum cleaner.""" - return self._status_state + return self._clean_state @property - def state_attributes(self): + def device_state_attributes(self): """Return the state attributes of the vacuum cleaner.""" data = {} - if self.status is not None: - data[ATTR_STATUS] = self.status + if self._status_state is not None: + data[ATTR_STATUS] = self._status_state if self.battery_level is not None: data[ATTR_BATTERY_LEVEL] = self.battery_level @@ -181,38 +175,26 @@ def state_attributes(self): return data - def turn_on(self, **kwargs): - """Turn the vacuum on and start cleaning.""" - self.robot.start_cleaning() - - @property - def is_on(self): - """Return true if switch is on.""" - return self._clean_state == STATE_ON + def start(self): + """Start cleaning or resume cleaning.""" + if self._state['state'] == 1: + self.robot.start_cleaning() + elif self._state['state'] == 3: + self.robot.resume_cleaning() - def turn_off(self, **kwargs): - """Turn the switch off.""" + def pause(self): + """Pause the vacuum.""" self.robot.pause_cleaning() - self.robot.send_to_base() def return_to_base(self, **kwargs): """Set the vacuum cleaner to return to the dock.""" + self._clean_state = STATE_RETURNING self.robot.send_to_base() def stop(self, **kwargs): """Stop the vacuum cleaner.""" self.robot.stop_cleaning() - def start_pause(self, **kwargs): - """Start, pause or resume the cleaning task.""" - if self._state['state'] == 1: - self.robot.start_cleaning() - elif self._state['state'] == 2 and\ - ALERTS.get(self._state['error']) is None: - self.robot.pause_cleaning() - if self._state['state'] == 3: - self.robot.resume_cleaning() - def locate(self, **kwargs): """Locate the robot by making it emit a sound.""" self.robot.locate() diff --git a/homeassistant/components/vacuum/roomba.py b/homeassistant/components/vacuum/roomba.py index 750c2c0ae0abad..487fd573f37a63 100644 --- a/homeassistant/components/vacuum/roomba.py +++ b/homeassistant/components/vacuum/roomba.py @@ -69,7 +69,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the iRobot Roomba vacuum cleaner platform.""" from roomba import Roomba if PLATFORM not in hass.data: @@ -102,7 +103,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): roomba_vac = RoombaVacuum(name, roomba) hass.data[PLATFORM][host] = roomba_vac - async_add_devices([roomba_vac], update_before_add=True) + async_add_entities([roomba_vac], update_before_add=True) class RoombaVacuum(VacuumDevice): diff --git a/homeassistant/components/vacuum/xiaomi_miio.py b/homeassistant/components/vacuum/xiaomi_miio.py index f6789d78b9ae97..a6d8fccdee033e 100644 --- a/homeassistant/components/vacuum/xiaomi_miio.py +++ b/homeassistant/components/vacuum/xiaomi_miio.py @@ -13,13 +13,15 @@ from homeassistant.components.vacuum import ( ATTR_CLEANED_AREA, DOMAIN, PLATFORM_SCHEMA, SUPPORT_BATTERY, SUPPORT_CLEAN_SPOT, SUPPORT_FAN_SPEED, SUPPORT_LOCATE, SUPPORT_PAUSE, - SUPPORT_RETURN_HOME, SUPPORT_SEND_COMMAND, SUPPORT_STATUS, SUPPORT_STOP, - SUPPORT_TURN_OFF, SUPPORT_TURN_ON, VACUUM_SERVICE_SCHEMA, VacuumDevice) + SUPPORT_RETURN_HOME, SUPPORT_SEND_COMMAND, SUPPORT_STOP, + SUPPORT_STATE, SUPPORT_START, VACUUM_SERVICE_SCHEMA, StateVacuumDevice, + STATE_CLEANING, STATE_DOCKED, STATE_PAUSED, STATE_IDLE, STATE_RETURNING, + STATE_ERROR) from homeassistant.const import ( ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_TOKEN, STATE_OFF, STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-miio==0.4.0', 'construct==2.9.41'] +REQUIREMENTS = ['python-miio==0.4.1', 'construct==2.9.41'] _LOGGER = logging.getLogger(__name__) @@ -50,6 +52,7 @@ ATTR_MAIN_BRUSH_LEFT = 'main_brush_left' ATTR_SIDE_BRUSH_LEFT = 'side_brush_left' ATTR_FILTER_LEFT = 'filter_left' +ATTR_SENSOR_DIRTY_LEFT = 'sensor_dirty_left' ATTR_CLEANING_COUNT = 'cleaning_count' ATTR_CLEANED_TOTAL_AREA = 'total_cleaned_area' ATTR_CLEANING_TOTAL_TIME = 'total_cleaning_time' @@ -77,14 +80,30 @@ 'schema': SERVICE_SCHEMA_REMOTE_CONTROL}, } -SUPPORT_XIAOMI = SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PAUSE | \ +SUPPORT_XIAOMI = SUPPORT_STATE | SUPPORT_PAUSE | \ SUPPORT_STOP | SUPPORT_RETURN_HOME | SUPPORT_FAN_SPEED | \ SUPPORT_SEND_COMMAND | SUPPORT_LOCATE | \ - SUPPORT_STATUS | SUPPORT_BATTERY | SUPPORT_CLEAN_SPOT + SUPPORT_BATTERY | SUPPORT_CLEAN_SPOT | SUPPORT_START + + +STATE_CODE_TO_STATE = { + 3: STATE_IDLE, + 5: STATE_CLEANING, + 6: STATE_RETURNING, + 8: STATE_DOCKED, + 9: STATE_ERROR, + 10: STATE_PAUSED, + 11: STATE_CLEANING, + 12: STATE_ERROR, + 15: STATE_RETURNING, + 16: STATE_CLEANING, + 17: STATE_CLEANING, +} @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the Xiaomi vacuum cleaner robot platform.""" from miio import Vacuum if DATA_KEY not in hass.data: @@ -101,7 +120,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): mirobo = MiroboVacuum(name, vacuum) hass.data[DATA_KEY][host] = mirobo - async_add_devices([mirobo], update_before_add=True) + async_add_entities([mirobo], update_before_add=True) @asyncio.coroutine def async_service_handler(service): @@ -135,7 +154,7 @@ def async_service_handler(service): schema=schema) -class MiroboVacuum(VacuumDevice): +class MiroboVacuum(StateVacuumDevice): """Representation of a Xiaomi Vacuum cleaner robot.""" def __init__(self, name, vacuum): @@ -144,7 +163,6 @@ def __init__(self, name, vacuum): self._vacuum = vacuum self.vacuum_state = None - self._is_on = False self._available = False self.consumable_state = None @@ -157,10 +175,16 @@ def name(self): return self._name @property - def status(self): + def state(self): """Return the status of the vacuum cleaner.""" if self.vacuum_state is not None: - return self.vacuum_state.state + try: + return STATE_CODE_TO_STATE[int(self.vacuum_state.state_code)] + except KeyError: + _LOGGER.error("STATE not supported: %s, state_code: %s", + self.vacuum_state.state, + self.vacuum_state.state_code) + return None @property def battery_level(self): @@ -212,16 +236,16 @@ def device_state_attributes(self): / 3600), ATTR_FILTER_LEFT: int( self.consumable_state.filter_left.total_seconds() - / 3600)}) + / 3600), + ATTR_SENSOR_DIRTY_LEFT: int( + self.consumable_state.sensor_dirty_left.total_seconds() + / 3600) + }) + if self.vacuum_state.got_error: attrs[ATTR_ERROR] = self.vacuum_state.error return attrs - @property - def is_on(self) -> bool: - """Return True if entity is on.""" - return self._is_on - @property def available(self) -> bool: """Return True if entity is available.""" @@ -243,26 +267,22 @@ def _try_command(self, mask_error, func, *args, **kwargs): _LOGGER.error(mask_error, exc) return False - @asyncio.coroutine - def async_turn_on(self, **kwargs): - """Turn the vacuum on.""" - is_on = yield from self._try_command( + async def async_start(self): + """Start or resume the cleaning task.""" + await self._try_command( "Unable to start the vacuum: %s", self._vacuum.start) - self._is_on = is_on - @asyncio.coroutine - def async_turn_off(self, **kwargs): - """Turn the vacuum off and return to home.""" - yield from self.async_stop() - yield from self.async_return_to_base() + async def async_pause(self): + """Pause the cleaning task.""" + if self.state == STATE_CLEANING: + await self._try_command( + "Unable to set start/pause: %s", self._vacuum.pause) @asyncio.coroutine def async_stop(self, **kwargs): """Stop the vacuum cleaner.""" - stopped = yield from self._try_command( + yield from self._try_command( "Unable to stop: %s", self._vacuum.stop) - if stopped: - self._is_on = False @asyncio.coroutine def async_set_fan_speed(self, fan_speed, **kwargs): @@ -281,22 +301,11 @@ def async_set_fan_speed(self, fan_speed, **kwargs): "Unable to set fan speed: %s", self._vacuum.set_fan_speed, fan_speed) - @asyncio.coroutine - def async_start_pause(self, **kwargs): - """Start, pause or resume the cleaning task.""" - if self.vacuum_state and self.is_on: - yield from self._try_command( - "Unable to set start/pause: %s", self._vacuum.pause) - else: - yield from self.async_turn_on() - @asyncio.coroutine def async_return_to_base(self, **kwargs): """Set the vacuum cleaner to return to the dock.""" - return_home = yield from self._try_command( + yield from self._try_command( "Unable to return home: %s", self._vacuum.home) - if return_home: - self._is_on = False @asyncio.coroutine def async_clean_spot(self, **kwargs): @@ -365,7 +374,6 @@ def update(self): self.clean_history = self._vacuum.clean_history() self.dnd_state = self._vacuum.dnd_status() - self._is_on = state.is_on self._available = True except OSError as exc: _LOGGER.error("Got OSError while fetching the state: %s", exc) diff --git a/homeassistant/components/waterfurnace.py b/homeassistant/components/waterfurnace.py index a587285e0babb6..e9024131af84de 100644 --- a/homeassistant/components/waterfurnace.py +++ b/homeassistant/components/waterfurnace.py @@ -18,7 +18,8 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers import discovery -REQUIREMENTS = ["waterfurnace==0.4.0"] + +REQUIREMENTS = ["waterfurnace==0.7.0"] _LOGGER = logging.getLogger(__name__) @@ -26,6 +27,11 @@ UPDATE_TOPIC = DOMAIN + "_update" CONF_UNIT = "unit" SCAN_INTERVAL = timedelta(seconds=10) +ERROR_INTERVAL = timedelta(seconds=300) +MAX_FAILS = 10 +NOTIFICATION_ID = 'waterfurnace_website_notification' +NOTIFICATION_TITLE = 'WaterFurnace website status' + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -37,7 +43,7 @@ def setup(hass, base_config): - """Setup waterfurnace platform.""" + """Set up waterfurnace platform.""" import waterfurnace.waterfurnace as wf config = base_config.get(DOMAIN) @@ -80,6 +86,36 @@ def __init__(self, hass, client): self.unit = client.unit self.data = None self._shutdown = False + self._fails = 0 + + def _reconnect(self): + """Reconnect on a failure.""" + import waterfurnace.waterfurnace as wf + self._fails += 1 + if self._fails > MAX_FAILS: + _LOGGER.error( + "Failed to refresh login credentials. Thread stopped.") + self.hass.components.persistent_notification.create( + "Error:
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.seconds) + + try: + self.client.login() + self.data = self.client.read() + except wf.WFException: + _LOGGER.exception("Failed to reconnect attempt %s", self._fails) + else: + _LOGGER.debug("Reconnected to furnace") + self._fails = 0 def run(self): """Thread run loop.""" @@ -117,20 +153,7 @@ def shutdown(event): # that pretty much can all be solved by logging in and # back out again. _LOGGER.exception("Failed to read data, attempting to recover") - try: - self.client.login() - except Exception: # pylint: disable=broad-except - # nested exception handling, something really bad - # happened during the login, which means we're not - # in a recoverable state. Stop the thread so we - # don't do just keep poking at the service. - _LOGGER.error( - "Failed to refresh login credentials. Thread stopped.") - return - else: - _LOGGER.error( - "Lost our connection to websocket, trying again") - time.sleep(SCAN_INTERVAL.seconds) + self._reconnect() else: self.hass.helpers.dispatcher.dispatcher_send(UPDATE_TOPIC) diff --git a/homeassistant/components/weather/bom.py b/homeassistant/components/weather/bom.py index ad74bb4fb7777b..4c517824bca01a 100644 --- a/homeassistant/components/weather/bom.py +++ b/homeassistant/components/weather/bom.py @@ -24,7 +24,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the BOM weather platform.""" station = config.get(CONF_STATION) or closest_station( config.get(CONF_LATITUDE), @@ -39,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): except ValueError as err: _LOGGER.error("Received error from BOM_Current: %s", err) return False - add_devices([BOMWeather(bom_data, config.get(CONF_NAME))], True) + add_entities([BOMWeather(bom_data, config.get(CONF_NAME))], True) class BOMWeather(WeatherEntity): diff --git a/homeassistant/components/weather/buienradar.py b/homeassistant/components/weather/buienradar.py index 9b9707e87f6f7c..6b92eb97c9eb70 100644 --- a/homeassistant/components/weather/buienradar.py +++ b/homeassistant/components/weather/buienradar.py @@ -56,7 +56,8 @@ @asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the buienradar platform.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) @@ -82,7 +83,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): for condi in condlst: hass.data[DATA_CONDITION][condi] = cond - async_add_devices([BrWeather(data, config)]) + async_add_entities([BrWeather(data, config)]) # schedule the first update in 1 minute from now: yield from data.schedule_update(1) diff --git a/homeassistant/components/weather/darksky.py b/homeassistant/components/weather/darksky.py index 6dac22bc941a2a..34a6fd3d6f6de7 100644 --- a/homeassistant/components/weather/darksky.py +++ b/homeassistant/components/weather/darksky.py @@ -57,7 +57,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=3) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dark Sky weather.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) @@ -70,7 +70,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dark_sky = DarkSkyData( config.get(CONF_API_KEY), latitude, longitude, units) - add_devices([DarkSkyWeather(name, dark_sky)], True) + add_entities([DarkSkyWeather(name, dark_sky)], True) class DarkSkyWeather(WeatherEntity): diff --git a/homeassistant/components/weather/demo.py b/homeassistant/components/weather/demo.py index fffdf03d07df4c..6bcb89185042bd 100644 --- a/homeassistant/components/weather/demo.py +++ b/homeassistant/components/weather/demo.py @@ -29,9 +29,9 @@ } -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Demo weather.""" - add_devices([ + add_entities([ DemoWeather('South', 'Sunshine', 21.6414, 92, 1099, 0.5, TEMP_CELSIUS, [['rainy', 1, 22, 15], ['rainy', 5, 19, 8], ['cloudy', 0, 15, 9], ['sunny', 0, 12, 6], diff --git a/homeassistant/components/weather/ecobee.py b/homeassistant/components/weather/ecobee.py index 59737c578a5915..5a191aa7af1403 100644 --- a/homeassistant/components/weather/ecobee.py +++ b/homeassistant/components/weather/ecobee.py @@ -23,7 +23,7 @@ MISSING_DATA = -5002 -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Ecobee weather component.""" if discovery_info is None: return @@ -34,7 +34,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if 'weather' in thermostat: dev.append(EcobeeWeather(thermostat['name'], index)) - add_devices(dev, True) + add_entities(dev, True) class EcobeeWeather(WeatherEntity): diff --git a/homeassistant/components/weather/ipma.py b/homeassistant/components/weather/ipma.py index ef4f1b349d72a5..02d94e4755852a 100644 --- a/homeassistant/components/weather/ipma.py +++ b/homeassistant/components/weather/ipma.py @@ -54,7 +54,7 @@ }) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the ipma platform.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) @@ -74,7 +74,7 @@ async def async_setup_platform(hass, config, async_add_devices, _LOGGER.debug("Initializing ipma weather: coordinates %s, %s", latitude, longitude) - async_add_devices([IPMAWeather(station, config)], True) + async_add_entities([IPMAWeather(station, config)], True) class IPMAWeather(WeatherEntity): diff --git a/homeassistant/components/weather/metoffice.py b/homeassistant/components/weather/metoffice.py index d43d1d3c996abf..7382319e7a4fe6 100644 --- a/homeassistant/components/weather/metoffice.py +++ b/homeassistant/components/weather/metoffice.py @@ -31,7 +31,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Met Office weather platform.""" import datapoint as dp @@ -63,7 +63,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Received error from Met Office Datapoint: %s", err) return - add_devices([MetOfficeWeather(site, data, name)], True) + add_entities([MetOfficeWeather(site, data, name)], True) class MetOfficeWeather(WeatherEntity): diff --git a/homeassistant/components/weather/openweathermap.py b/homeassistant/components/weather/openweathermap.py index 46a0b3ecc14f3b..b300fcbcbecdbc 100644 --- a/homeassistant/components/weather/openweathermap.py +++ b/homeassistant/components/weather/openweathermap.py @@ -61,7 +61,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the OpenWeatherMap weather platform.""" import pyowm @@ -78,7 +78,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data = WeatherData(owm, latitude, longitude, mode) - add_devices([OpenWeatherMapWeather( + add_entities([OpenWeatherMapWeather( name, data, hass.config.units.temperature_unit, mode)], True) diff --git a/homeassistant/components/weather/yweather.py b/homeassistant/components/weather/yweather.py index 3f12195d6bfffc..505c287a99eee4 100644 --- a/homeassistant/components/weather/yweather.py +++ b/homeassistant/components/weather/yweather.py @@ -55,7 +55,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Yahoo! weather platform.""" from yahooweather import get_woeid, UNIT_C, UNIT_F @@ -85,7 +85,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for condi in condlst: hass.data[DATA_CONDITION][condi] = cond - add_devices([YahooWeatherWeather(yahoo_api, name, unit)], True) + add_entities([YahooWeatherWeather(yahoo_api, name, unit)], True) class YahooWeatherWeather(WeatherEntity): diff --git a/homeassistant/components/weather/zamg.py b/homeassistant/components/weather/zamg.py index 389b966ee7d74e..f76b733ef0bb16 100644 --- a/homeassistant/components/weather/zamg.py +++ b/homeassistant/components/weather/zamg.py @@ -31,7 +31,7 @@ }) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZAMG weather platform.""" name = config.get(CONF_NAME) latitude = config.get(CONF_LATITUDE, hass.config.latitude) @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.error("Received error from ZAMG: %s", err) return False - add_devices([ZamgWeather(probe, name)], True) + add_entities([ZamgWeather(probe, name)], True) class ZamgWeather(WeatherEntity): diff --git a/homeassistant/components/websocket_api.py b/homeassistant/components/websocket_api.py index 532f3672df478a..0c9ab366534f28 100644 --- a/homeassistant/components/websocket_api.py +++ b/homeassistant/components/websocket_api.py @@ -18,9 +18,9 @@ from homeassistant.const import ( MATCH_ALL, EVENT_TIME_CHANGED, EVENT_HOMEASSISTANT_STOP, __version__) -from homeassistant.core import Context, callback +from homeassistant.core import Context, callback, HomeAssistant from homeassistant.loader import bind_hass -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers import config_validation as cv from homeassistant.helpers.service import async_get_all_descriptions from homeassistant.components.http import HomeAssistantView @@ -325,7 +325,6 @@ async def handle(self): await wsock.prepare(request) self.debug("Connected") - # Get a reference to current task so we can cancel our connection self._handle_task = asyncio.Task.current_task(loop=self.hass.loop) @callback @@ -344,7 +343,10 @@ def handle_hass_stop(event): if request[KEY_AUTHENTICATED]: authenticated = True - else: + # always request auth when auth is active + # even request passed pre-authentication (trusted networks) + # or when using legacy api_password + if self.hass.auth.active or not authenticated: self.debug("Request auth") await self.wsock.send_json(auth_required_message()) msg = await wsock.receive_json() @@ -574,3 +576,59 @@ def handle_ping(hass, connection, msg): Async friendly. """ connection.to_write.put_nowait(pong_message(msg['id'])) + + +def ws_require_user( + only_owner=False, only_system_user=False, allow_system_user=True, + only_active_user=True, only_inactive_user=False): + """Decorate function validating login user exist in current WS connection. + + Will write out error message if not authenticated. + """ + def validator(func): + """Decorate func.""" + @wraps(func) + def check_current_user(hass: HomeAssistant, + connection: ActiveConnection, + msg): + """Check current user.""" + def output_error(message_id, message): + """Output error message.""" + connection.send_message_outside(error_message( + msg['id'], message_id, message)) + + if connection.user is None: + output_error('no_user', 'Not authenticated as a user') + return + + if only_owner and not connection.user.is_owner: + output_error('only_owner', 'Only allowed as owner') + return + + if (only_system_user and + not connection.user.system_generated): + output_error('only_system_user', + 'Only allowed as system user') + return + + if (not allow_system_user + and connection.user.system_generated): + output_error('not_system_user', 'Not allowed as system user') + return + + if (only_active_user and + not connection.user.is_active): + output_error('only_active_user', + 'Only allowed as active user') + return + + if only_inactive_user and connection.user.is_active: + output_error('only_inactive_user', + 'Not allowed as active user') + return + + return func(hass, connection, msg) + + return check_current_user + + return validator diff --git a/homeassistant/components/wemo.py b/homeassistant/components/wemo.py index 27027cc9eb4ec5..2ce2ff475a2b71 100644 --- a/homeassistant/components/wemo.py +++ b/homeassistant/components/wemo.py @@ -6,6 +6,7 @@ """ import logging +import requests import voluptuous as vol from homeassistant.components.discovery import SERVICE_WEMO @@ -36,11 +37,32 @@ _LOGGER = logging.getLogger(__name__) + +def coerce_host_port(value): + """Validate that provided value is either just host or host:port. + + Returns (host, None) or (host, port) respectively. + """ + host, _, port = value.partition(':') + + if not host: + raise vol.Invalid('host cannot be empty') + + if port: + port = cv.port(port) + else: + port = None + + return host, port + + CONF_STATIC = 'static' CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Optional(CONF_STATIC, default=[]): vol.Schema([cv.string]) + vol.Optional(CONF_STATIC, default=[]): vol.Schema([ + vol.All(cv.string, coerce_host_port) + ]) }), }, extra=vol.ALLOW_EXTRA) @@ -79,23 +101,47 @@ def discovery_dispatch(service, discovery_info): discovery.listen(hass, SERVICE_WEMO, discovery_dispatch) - _LOGGER.info("Scanning for WeMo devices.") - devices = [(device.host, device) for device in pywemo.discover_devices()] + def setup_url_for_device(device): + """Determine setup.xml url for given device.""" + return 'http://{}:{}/setup.xml'.format(device.host, device.port) - # Add static devices from the config file. - devices.extend((address, None) - for address in config.get(DOMAIN, {}).get(CONF_STATIC, [])) + def setup_url_for_address(host, port): + """Determine setup.xml url for given host and port pair.""" + if not port: + port = pywemo.ouimeaux_device.probe_wemo(host) - for address, device in devices: - port = pywemo.ouimeaux_device.probe_wemo(address) if not port: - _LOGGER.warning('Unable to probe wemo at %s', address) - continue - _LOGGER.info('Adding wemo at %s:%i', address, port) + return None - url = 'http://%s:%i/setup.xml' % (address, port) - if device is None: + return 'http://{}:{}/setup.xml'.format(host, port) + + devices = [] + + for host, port in config.get(DOMAIN, {}).get(CONF_STATIC, []): + url = setup_url_for_address(host, port) + + if not url: + _LOGGER.error( + 'Unable to get description url for %s', + '{}:{}'.format(host, port) if port else host) + return False + + try: device = pywemo.discovery.device_from_description(url, None) + except (requests.exceptions.ConnectionError, + requests.exceptions.Timeout) as err: + _LOGGER.error('Unable to access %s (%s)', url, err) + return False + + devices.append((url, device)) + + _LOGGER.info("Scanning for WeMo devices.") + devices.extend( + (setup_url_for_device(device), device) + for device in pywemo.discover_devices()) + + for url, device in devices: + _LOGGER.info('Adding wemo at %s:%i', device.host, device.port) discovery_info = { 'model_name': device.model_name, diff --git a/homeassistant/components/wirelesstag.py b/homeassistant/components/wirelesstag.py index 0f8f47f5100c6d..19fb2d40b5d8e8 100644 --- a/homeassistant/components/wirelesstag.py +++ b/homeassistant/components/wirelesstag.py @@ -80,14 +80,14 @@ def disarm(self, switch): # pylint: disable=no-self-use def make_push_notitication(self, name, url, content): - """Factory for notification config.""" + """Create notification config.""" from wirelesstagpy import NotificationConfig return NotificationConfig(name, { 'url': url, 'verb': 'POST', 'content': content, 'disabled': False, 'nat': True}) def install_push_notifications(self, binary_sensors): - """Setup local push notification from tag manager.""" + """Set up local push notification from tag manager.""" _LOGGER.info("Registering local push notifications.") configs = [] @@ -129,7 +129,7 @@ def binary_event_callback_url(self): self.hass.config.api.base_url) def handle_update_tags_event(self, event): - """Main entry to handle push event from wireless tag manager.""" + """Handle push event from wireless tag manager.""" _LOGGER.info("push notification for update arrived: %s", event) dispatcher_send( self.hass, @@ -215,7 +215,10 @@ def principal_value(self): return 0 def updated_state_value(self): - """Default implementation formats princial value.""" + """Return formatted value. + + The default implementation formats principal value. + """ return self.decorate_value(self.principal_value) # pylint: disable=no-self-use diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 030e342847d719..f17e7f0234480b 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -341,7 +341,7 @@ def __init__(self, endpoint, in_clusters, out_clusters, manufacturer, application_listener.register_entity(ieee, self) async def async_added_to_hass(self): - """Callback once the entity is added to hass. + """Handle entity addition to hass. It is now safe to update the entity state """ diff --git a/homeassistant/components/zone/.translations/da.json b/homeassistant/components/zone/.translations/da.json new file mode 100644 index 00000000000000..908ef9dc43aa77 --- /dev/null +++ b/homeassistant/components/zone/.translations/da.json @@ -0,0 +1,20 @@ +{ + "config": { + "error": { + "name_exists": "Navnet findes allerede" + }, + "step": { + "init": { + "data": { + "icon": "Ikon", + "latitude": "Breddegrad", + "longitude": "L\u00e6ngdegrad", + "name": "Navn", + "passive": "Passiv" + }, + "title": "Definer zoneparametre" + } + }, + "title": "Zone" + } +} \ No newline at end of file diff --git a/homeassistant/components/zone/.translations/he.json b/homeassistant/components/zone/.translations/he.json new file mode 100644 index 00000000000000..b6a2a30b62589b --- /dev/null +++ b/homeassistant/components/zone/.translations/he.json @@ -0,0 +1,21 @@ +{ + "config": { + "error": { + "name_exists": "\u05d4\u05e9\u05dd \u05db\u05d1\u05e8 \u05e7\u05d9\u05d9\u05dd" + }, + "step": { + "init": { + "data": { + "icon": "\u05e1\u05de\u05dc", + "latitude": "\u05e7\u05d5 \u05e8\u05d5\u05d7\u05d1", + "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da", + "name": "\u05e9\u05dd", + "passive": "\u05e4\u05e1\u05d9\u05d1\u05d9", + "radius": "\u05e8\u05d3\u05d9\u05d5\u05e1" + }, + "title": "\u05d4\u05d2\u05d3\u05e8 \u05e4\u05e8\u05de\u05d8\u05e8\u05d9\u05dd \u05e9\u05dc \u05d0\u05d6\u05d5\u05e8" + } + }, + "title": "\u05d0\u05d6\u05d5\u05e8" + } +} \ No newline at end of file diff --git a/homeassistant/components/zone/__init__.py b/homeassistant/components/zone/__init__.py index ee19e00266c7fc..3754bf5edbce6f 100644 --- a/homeassistant/components/zone/__init__.py +++ b/homeassistant/components/zone/__init__.py @@ -44,7 +44,7 @@ async def async_setup(hass, config): - """Setup configured zones as well as home assistant zone if necessary.""" + """Set up configured zones as well as home assistant zone if necessary.""" hass.data[DOMAIN] = {} entities = set() zone_entries = configured_zones(hass) diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index 8cf69e727027f6..4cb2f6b0f7b241 100644 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -203,7 +203,7 @@ def get_config_value(node, value_index, tries=5): return None -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Z-Wave platform (generic part).""" if discovery_info is None or DATA_NETWORK not in hass.data: @@ -214,7 +214,7 @@ async def async_setup_platform(hass, config, async_add_devices, if device is None: return False - async_add_devices([device]) + async_add_entities([device]) return True diff --git a/homeassistant/components/zwave/node_entity.py b/homeassistant/components/zwave/node_entity.py index 2c6d26802bd141..94de03686d310a 100644 --- a/homeassistant/components/zwave/node_entity.py +++ b/homeassistant/components/zwave/node_entity.py @@ -107,7 +107,7 @@ def __init__(self, node, network): @property def unique_id(self): - """Unique ID of Z-wave node.""" + """Return unique ID of Z-wave node.""" return self._unique_id def network_node_changed(self, node=None, value=None, args=None): diff --git a/homeassistant/config.py b/homeassistant/config.py index 6120a20fd63473..d742e62660b908 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -8,20 +8,22 @@ import shutil # pylint: disable=unused-import from typing import ( # noqa: F401 - Any, Tuple, Optional, Dict, List, Union, Callable) + Any, Tuple, Optional, Dict, List, Union, Callable, Sequence, Set) from types import ModuleType import voluptuous as vol from voluptuous.humanize import humanize_error from homeassistant import auth -from homeassistant.auth import providers as auth_providers +from homeassistant.auth import providers as auth_providers,\ + mfa_modules as auth_mfa_modules from homeassistant.const import ( ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_ASSUMED_STATE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_PACKAGES, CONF_UNIT_SYSTEM, CONF_TIME_ZONE, CONF_ELEVATION, CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, TEMP_CELSIUS, __version__, CONF_CUSTOMIZE, CONF_CUSTOMIZE_DOMAIN, CONF_CUSTOMIZE_GLOB, - CONF_WHITELIST_EXTERNAL_DIRS, CONF_AUTH_PROVIDERS, CONF_TYPE) + CONF_WHITELIST_EXTERNAL_DIRS, CONF_AUTH_PROVIDERS, CONF_AUTH_MFA_MODULES, + CONF_TYPE, CONF_ID) from homeassistant.core import callback, DOMAIN as CONF_CORE, HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.loader import get_component, get_platform @@ -73,11 +75,9 @@ # Enables configuration UI config: -http: - # Secrets are defined in the file secrets.yaml - # api_password: !secret http_password - # Uncomment this if you are using SSL/TLS, running in Docker container, etc. - # base_url: example.duckdns.org:8123 +# Uncomment this if you are using SSL/TLS, running in Docker container, etc. +# http: +# base_url: example.duckdns.org:8123 # Checks for available updates # Note: This component will send some information about your system to @@ -124,10 +124,52 @@ DEFAULT_SECRETS = """ # Use this file to store secrets like usernames and passwords. # Learn more at https://home-assistant.io/docs/configuration/secrets/ -http_password: welcome +some_password: welcome """ +def _no_duplicate_auth_provider(configs: Sequence[Dict[str, Any]]) \ + -> Sequence[Dict[str, Any]]: + """No duplicate auth provider config allowed in a list. + + Each type of auth provider can only have one config without optional id. + Unique id is required if same type of auth provider used multiple times. + """ + config_keys = set() # type: Set[Tuple[str, Optional[str]]] + for config in configs: + key = (config[CONF_TYPE], config.get(CONF_ID)) + if key in config_keys: + raise vol.Invalid( + 'Duplicate auth provider {} found. Please add unique IDs if ' + 'you want to have the same auth provider twice'.format( + config[CONF_TYPE] + )) + config_keys.add(key) + return configs + + +def _no_duplicate_auth_mfa_module(configs: Sequence[Dict[str, Any]]) \ + -> Sequence[Dict[str, Any]]: + """No duplicate auth mfa module item allowed in a list. + + Each type of mfa module can only have one config without optional id. + A global unique id is required if same type of mfa module used multiple + times. + Note: this is different than auth provider + """ + config_keys = set() # type: Set[str] + for config in configs: + key = config.get(CONF_ID, config[CONF_TYPE]) + if key in config_keys: + raise vol.Invalid( + 'Duplicate mfa module {} found. Please add unique IDs if ' + 'you want to have the same mfa module twice'.format( + config[CONF_TYPE] + )) + config_keys.add(key) + return configs + + PACKAGES_CONFIG_SCHEMA = vol.Schema({ cv.slug: vol.Schema( # Package names are slugs {cv.slug: vol.Any(dict, list, None)}) # Only slugs for component names @@ -166,7 +208,16 @@ CONF_TYPE: vol.NotIn(['insecure_example'], 'The insecure_example auth provider' ' is for testing only.') - })]) + })], + _no_duplicate_auth_provider), + vol.Optional(CONF_AUTH_MFA_MODULES): + vol.All(cv.ensure_list, + [auth_mfa_modules.MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({ + CONF_TYPE: vol.NotIn(['insecure_example'], + 'The insecure_example mfa module' + ' is for testing only.') + })], + _no_duplicate_auth_mfa_module), }) @@ -402,7 +453,9 @@ def _format_config_error(ex: vol.Invalid, domain: str, config: Dict) -> str: async def async_process_ha_core_config( - hass: HomeAssistant, config: Dict) -> None: + hass: HomeAssistant, config: Dict, + has_api_password: bool = False, + has_trusted_networks: bool = False) -> None: """Process the [homeassistant] section from the configuration. This method is a coroutine. @@ -411,8 +464,25 @@ async def async_process_ha_core_config( # Only load auth during startup. if not hasattr(hass, 'auth'): + auth_conf = config.get(CONF_AUTH_PROVIDERS) + + if auth_conf is None: + auth_conf = [ + {'type': 'homeassistant'} + ] + if has_api_password: + auth_conf.append({'type': 'legacy_api_password'}) + if has_trusted_networks: + auth_conf.append({'type': 'trusted_networks'}) + + mfa_conf = config.get(CONF_AUTH_MFA_MODULES, [ + {'type': 'totp', 'id': 'totp', 'name': 'Authenticator app'} + ]) + setattr(hass, 'auth', await auth.auth_manager_from_config( - hass, config.get(CONF_AUTH_PROVIDERS, []))) + hass, + auth_conf, + mfa_conf)) hac = hass.config diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index b2e8389e4494e2..8db09cdb8da026 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -136,6 +136,7 @@ async def async_step_discovery(info): # Components that have config flows. In future we will auto-generate this list. FLOWS = [ 'cast', + 'hangouts', 'deconz', 'homematicip_cloud', 'hue', @@ -302,7 +303,7 @@ def async_domains(self) -> List[str]: return result @callback - def async_entries(self, domain: str = None) -> List[ConfigEntry]: + def async_entries(self, domain: Optional[str] = None) -> List[ConfigEntry]: """Return all entries or entries for a specific domain.""" if domain is None: return list(self._entries) @@ -320,7 +321,7 @@ async def async_remove(self, entry_id): raise UnknownEntry entry = self._entries.pop(found) - await self._async_schedule_save() + self._async_schedule_save() unloaded = await entry.async_unload(self.hass) @@ -372,26 +373,27 @@ async def async_forward_entry_unload(self, entry, component): return await entry.async_unload( self.hass, component=getattr(self.hass.components, component)) - async def _async_finish_flow(self, context, result): + async def _async_finish_flow(self, flow, result): """Finish a config flow and add an entry.""" - # If no discovery config entries in progress, remove notification. + # Remove notification if no other discovery config entries in progress if not any(ent['context']['source'] in DISCOVERY_SOURCES for ent - in self.hass.config_entries.flow.async_progress()): + in self.hass.config_entries.flow.async_progress() + if ent['flow_id'] != flow.flow_id): self.hass.components.persistent_notification.async_dismiss( DISCOVERY_NOTIFICATION_ID) if result['type'] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY: - return None + return result entry = ConfigEntry( version=result['version'], domain=result['handler'], title=result['title'], data=result['data'], - source=context['source'], + source=flow.context['source'], ) self._entries.append(entry) - await self._async_schedule_save() + self._async_schedule_save() # Setup entry if entry.domain in self.hass.config.components: @@ -402,16 +404,13 @@ async def _async_finish_flow(self, context, result): await async_setup_component( self.hass, entry.domain, self._hass_config) - # Return Entry if they not from a discovery request - if context['source'] not in DISCOVERY_SOURCES: - return entry - - return entry + result['result'] = entry + return result async def _async_create_flow(self, handler_key, *, context, data): """Create a flow for specified handler. - Handler key is the domain of the component that we want to setup. + Handler key is the domain of the component that we want to set up. """ component = getattr(self.hass.components, handler_key) handler = HANDLERS.get(handler_key) @@ -439,12 +438,16 @@ async def _async_create_flow(self, handler_key, *, context, data): flow.init_step = source return flow - async def _async_schedule_save(self): + def _async_schedule_save(self): """Save the entity registry to a file.""" - data = { + self._store.async_delay_save(self._data_to_save, SAVE_DELAY) + + @callback + def _data_to_save(self): + """Return data to save.""" + return { 'entries': [entry.as_dict() for entry in self._entries] } - await self._store.async_save(data, delay=SAVE_DELAY) async def _old_conf_migrator(old_config): diff --git a/homeassistant/const.py b/homeassistant/const.py index 14054e44663eab..6587e13b727225 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,8 +1,8 @@ # coding: utf-8 """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 -MINOR_VERSION = 76 -PATCH_VERSION = '2' +MINOR_VERSION = 77 +PATCH_VERSION = '0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) @@ -30,6 +30,7 @@ CONF_API_VERSION = 'api_version' CONF_AT = 'at' CONF_AUTHENTICATION = 'authentication' +CONF_AUTH_MFA_MODULES = 'auth_mfa_modules' CONF_AUTH_PROVIDERS = 'auth_providers' CONF_BASE = 'base' CONF_BEFORE = 'before' diff --git a/homeassistant/data_entry_flow.py b/homeassistant/data_entry_flow.py index f820911e39680c..a54c07fc1b82bc 100644 --- a/homeassistant/data_entry_flow.py +++ b/homeassistant/data_entry_flow.py @@ -49,7 +49,8 @@ def async_progress(self) -> List[Dict]: 'context': flow.context, } for flow in self._progress.values()] - async def async_init(self, handler: Hashable, *, context: Dict = None, + async def async_init(self, handler: Hashable, *, + context: Optional[Dict] = None, data: Any = None) -> Any: """Start a configuration flow.""" flow = await self._async_create_flow( @@ -63,7 +64,7 @@ async def async_init(self, handler: Hashable, *, context: Dict = None, return await self._async_handle_step(flow, flow.init_step, data) async def async_configure( - self, flow_id: str, user_input: str = None) -> Any: + self, flow_id: str, user_input: Optional[Dict] = None) -> Any: """Continue a configuration flow.""" flow = self._progress.get(flow_id) @@ -85,7 +86,7 @@ def async_abort(self, flow_id: str) -> None: raise UnknownFlow async def _async_handle_step(self, flow: Any, step_id: str, - user_input: Optional[str]) -> Dict: + user_input: Optional[Dict]) -> Dict: """Handle a step of a flow.""" method = "async_step_{}".format(step_id) @@ -105,14 +106,17 @@ async def _async_handle_step(self, flow: Any, step_id: str, flow.cur_step = (result['step_id'], result['data_schema']) return result + # We pass a copy of the result because we're mutating our version + result = await self._async_finish_flow(flow, dict(result)) + + # _async_finish_flow may change result type, check it again + if result['type'] == RESULT_TYPE_FORM: + flow.cur_step = (result['step_id'], result['data_schema']) + return result + # Abort and Success results both finish the flow self._progress.pop(flow.flow_id) - # We pass a copy of the result because we're mutating our version - entry = await self._async_finish_flow(flow.context, dict(result)) - - if result['type'] == RESULT_TYPE_CREATE_ENTRY: - result['result'] = entry return result @@ -134,8 +138,9 @@ class FlowHandler: @callback def async_show_form(self, *, step_id: str, data_schema: vol.Schema = None, - errors: Dict = None, - description_placeholders: Dict = None) -> Dict: + errors: Optional[Dict] = None, + description_placeholders: Optional[Dict] = None) \ + -> Dict: """Return the definition of a form to gather user input.""" return { 'type': RESULT_TYPE_FORM, diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 056d45ad656512..90098a677a1476 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -60,21 +60,6 @@ def validate(obj: Dict) -> Dict: return validate -def has_at_least_one_key_value(*items: list) -> Callable: - """Validate that at least one (key, value) pair exists.""" - def validate(obj: Dict) -> Dict: - """Test (key,value) exist in dict.""" - if not isinstance(obj, dict): - raise vol.Invalid('expected dictionary') - - for item in obj.items(): - if item in items: - return obj - raise vol.Invalid('must contain one of {}.'.format(str(items))) - - return validate - - def boolean(value: Any) -> bool: """Validate and coerce a boolean value.""" if isinstance(value, str): @@ -107,7 +92,7 @@ def validator(value: Any) -> str: if not regex.match(value): raise vol.Invalid('value {} does not match regular expression {}' - .format(regex.pattern, value)) + .format(value, regex.pattern)) return value return validator @@ -613,6 +598,7 @@ def validator(value): vol.Optional(CONF_ALIAS): string, vol.Required("wait_template"): template, vol.Optional(CONF_TIMEOUT): vol.All(time_period, positive_timedelta), + vol.Optional("continue_on_timeout"): boolean, }) SCRIPT_SCHEMA = vol.All( diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py new file mode 100644 index 00000000000000..31da40134a5582 --- /dev/null +++ b/homeassistant/helpers/device_registry.py @@ -0,0 +1,142 @@ +"""Provide a way to connect entities belonging to one device.""" +import logging +import uuid + +import attr + +from homeassistant.core import callback +from homeassistant.loader import bind_hass + +_LOGGER = logging.getLogger(__name__) + +DATA_REGISTRY = 'device_registry' + +STORAGE_KEY = 'core.device_registry' +STORAGE_VERSION = 1 +SAVE_DELAY = 10 + +CONNECTION_NETWORK_MAC = 'mac' +CONNECTION_ZIGBEE = 'zigbee' + + +@attr.s(slots=True, frozen=True) +class DeviceEntry: + """Device Registry Entry.""" + + config_entries = attr.ib(type=set, converter=set) + connections = attr.ib(type=set, converter=set) + identifiers = attr.ib(type=set, converter=set) + manufacturer = attr.ib(type=str) + model = attr.ib(type=str) + name = attr.ib(type=str, default=None) + sw_version = attr.ib(type=str, default=None) + id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex)) + + +class DeviceRegistry: + """Class to hold a registry of devices.""" + + def __init__(self, hass): + """Initialize the device registry.""" + self.hass = hass + self.devices = None + self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + + @callback + def async_get_device(self, identifiers: str, connections: tuple): + """Check if device is registered.""" + for device in self.devices: + if any(iden in device.identifiers for iden in identifiers) or \ + any(conn in device.connections for conn in connections): + return device + return None + + @callback + def async_get_or_create(self, *, config_entry, connections, identifiers, + manufacturer, model, name=None, sw_version=None): + """Get device. Create if it doesn't exist.""" + if not identifiers and not connections: + return None + + device = self.async_get_device(identifiers, connections) + + if device is not None: + if config_entry not in device.config_entries: + device.config_entries.add(config_entry) + self.async_schedule_save() + return device + + device = DeviceEntry( + config_entries=[config_entry], + connections=connections, + identifiers=identifiers, + manufacturer=manufacturer, + model=model, + name=name, + sw_version=sw_version + ) + self.devices.append(device) + + self.async_schedule_save() + + return device + + async def async_load(self): + """Load the device registry.""" + devices = await self._store.async_load() + + if devices is None: + self.devices = [] + return + + self.devices = [DeviceEntry( + config_entries=device['config_entries'], + connections={tuple(conn) for conn in device['connections']}, + identifiers={tuple(iden) for iden in device['identifiers']}, + manufacturer=device['manufacturer'], + model=device['model'], + name=device['name'], + sw_version=device['sw_version'], + id=device['id'], + ) for device in devices['devices']] + + @callback + def async_schedule_save(self): + """Schedule saving the device registry.""" + self._store.async_delay_save(self._data_to_save, SAVE_DELAY) + + @callback + def _data_to_save(self): + """Return data of device registry to store in a file.""" + data = {} + + data['devices'] = [ + { + 'config_entries': list(entry.config_entries), + 'connections': list(entry.connections), + 'identifiers': list(entry.identifiers), + 'manufacturer': entry.manufacturer, + 'model': entry.model, + 'name': entry.name, + 'sw_version': entry.sw_version, + 'id': entry.id, + } for entry in self.devices + ] + + return data + + +@bind_hass +async def async_get_registry(hass) -> DeviceRegistry: + """Return device registry instance.""" + task = hass.data.get(DATA_REGISTRY) + + if task is None: + async def _load_reg(): + registry = DeviceRegistry(hass) + await registry.async_load() + return registry + + task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg()) + + return await task diff --git a/homeassistant/helpers/discovery.py b/homeassistant/helpers/discovery.py index 7d0730a969c68c..698cee0fcc2f02 100644 --- a/homeassistant/helpers/discovery.py +++ b/homeassistant/helpers/discovery.py @@ -159,7 +159,7 @@ async def async_load_platform(hass, component, platform, discovered=None, setup_success = await setup.async_setup_component( hass, component, hass_config) - # No need to fire event if we could not setup component + # No need to fire event if we could not set up component if not setup_success: return diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index c356c266db6950..695da5bce9c7d1 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -1,5 +1,6 @@ """An abstract class for entities.""" import asyncio +from datetime import timedelta import logging import functools as ft from timeit import default_timer as timer @@ -16,6 +17,7 @@ from homeassistant.exceptions import NoEntitySpecifiedError from homeassistant.util import ensure_unique_string, slugify from homeassistant.util.async_ import run_callback_threadsafe +from homeassistant.util import dt as dt_util _LOGGER = logging.getLogger(__name__) SLOW_UPDATE_WARNING = 10 @@ -85,6 +87,10 @@ class Entity: # Hold list for functions to call on remove. _on_remove = None + # Context + _context = None + _context_set = None + @property def should_poll(self) -> bool: """Return True if entity has to be polled for state. @@ -124,6 +130,14 @@ def device_state_attributes(self): """ return None + @property + def device_info(self): + """Return device specific attributes. + + Implemented by platform classes. + """ + return None + @property def device_class(self) -> str: """Return the class of this device, from component DEVICE_CLASSES.""" @@ -173,13 +187,24 @@ def supported_features(self) -> int: """Flag supported features.""" return None + @property + def context_recent_time(self): + """Time that a context is considered recent.""" + return timedelta(seconds=5) + # DO NOT OVERWRITE # These properties and methods are either managed by Home Assistant or they # are used to perform a very specific function. Overwriting these may # produce undesirable effects in the entity's operation. + @callback + def async_set_context(self, context): + """Set the context the entity currently operates under.""" + self._context = context + self._context_set = dt_util.utcnow() + @asyncio.coroutine - def async_update_ha_state(self, force_refresh=False, context=None): + def async_update_ha_state(self, force_refresh=False): """Update Home Assistant with current state of entity. If force_refresh == True will update entity before setting state. @@ -278,8 +303,14 @@ def async_update_ha_state(self, force_refresh=False, context=None): # Could not convert state to float pass + if (self._context is not None and + dt_util.utcnow() - self._context_set > + self.context_recent_time): + self._context = None + self._context_set = None + self.hass.states.async_set( - self.entity_id, state, attr, self.force_update, context) + self.entity_id, state, attr, self.force_update, self._context) def schedule_update_ha_state(self, force_refresh=False): """Schedule an update ha state change task. @@ -347,7 +378,7 @@ async def async_remove(self): @callback def async_registry_updated(self, old, new): - """Called when the entity registry has been updated.""" + """Handle entity registry update.""" self.registry_name = new.name if new.entity_id == self.entity_id: diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index 72b6ceecbfd44f..09f8838b160266 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -52,7 +52,7 @@ def entities(self): in self._platforms.values()) def get_entity(self, entity_id): - """Helper method to get an entity.""" + """Get an entity.""" for platform in self._platforms.values(): entity = platform.entities.get(entity_id) if entity is not None: @@ -94,7 +94,7 @@ async def component_platform_discovered(platform, info): self.hass, self.domain, component_platform_discovered) async def async_setup_entry(self, config_entry): - """Setup a config entry.""" + """Set up a config entry.""" platform_type = config_entry.domain platform = await async_prepare_setup_platform( self.hass, self.config, self.domain, platform_type) @@ -142,6 +142,18 @@ def async_extract_from_service(self, service, expand_group=True): return [entity for entity in self.entities if entity.available and entity.entity_id in entity_ids] + @callback + def async_register_entity_service(self, name, schema, func): + """Register an entity service.""" + async def handle_service(call): + """Handle the service.""" + await self.hass.helpers.service.entity_service_call( + self._platforms.values(), func, call + ) + + self.hass.services.async_register( + self.domain, name, handle_service, schema) + async def _async_setup_platform(self, platform_type, platform_config, discovery_info=None): """Set up a platform for this component.""" @@ -231,7 +243,7 @@ async def async_prepare_reload(self): def _async_init_entity_platform(self, platform_type, platform, scan_interval=None, entity_namespace=None): - """Helper to initialize an entity platform.""" + """Initialize an entity platform.""" if scan_interval is None: scan_interval = self.scan_interval diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index dc1e376f471a86..083a2946122fc8 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -8,7 +8,6 @@ run_callback_threadsafe, run_coroutine_threadsafe) from .event import async_track_time_interval, async_call_later -from .entity_registry import async_get_registry SLOW_SETUP_WARNING = 10 SLOW_SETUP_MAX_WAIT = 60 @@ -71,13 +70,13 @@ def __init__(self, *, hass, logger, domain, platform_name, platform, self.parallel_updates = None async def async_setup(self, platform_config, discovery_info=None): - """Setup the platform from a config file.""" + """Set up the platform from a config file.""" platform = self.platform hass = self.hass @callback def async_create_setup_task(): - """Get task to setup platform.""" + """Get task to set up platform.""" if getattr(platform, 'async_setup_platform', None): return platform.async_setup_platform( hass, platform_config, @@ -93,21 +92,21 @@ def async_create_setup_task(): await self._async_setup_platform(async_create_setup_task) async def async_setup_entry(self, config_entry): - """Setup the platform from a config entry.""" + """Set up the platform from a config entry.""" # Store it so that we can save config entry ID in entity registry self.config_entry = config_entry platform = self.platform @callback def async_create_setup_task(): - """Get task to setup platform.""" + """Get task to set up platform.""" return platform.async_setup_entry( self.hass, config_entry, self._async_schedule_add_entities) return await self._async_setup_platform(async_create_setup_task) async def _async_setup_platform(self, async_create_setup_task, tries=0): - """Helper to setup a platform via config file or config entry. + """Set up a platform via config file or config entry. async_create_setup_task creates a coroutine that sets up platform. """ @@ -169,7 +168,7 @@ async def setup_again(now): warn_task.cancel() def _schedule_add_entities(self, new_entities, update_before_add=False): - """Synchronously schedule adding entities for a single platform.""" + """Schedule adding entities for a single platform, synchronously.""" run_callback_threadsafe( self.hass.loop, self._async_schedule_add_entities, list(new_entities), @@ -209,11 +208,14 @@ async def async_add_entities(self, new_entities, update_before_add=False): hass = self.hass component_entities = set(hass.states.async_entity_ids(self.domain)) - registry = await async_get_registry(hass) - + device_registry = await \ + hass.helpers.device_registry.async_get_registry() + entity_registry = await \ + hass.helpers.entity_registry.async_get_registry() tasks = [ self._async_add_entity(entity, update_before_add, - component_entities, registry) + component_entities, entity_registry, + device_registry) for entity in new_entities] # No entities for processing @@ -233,8 +235,9 @@ async def async_add_entities(self, new_entities, update_before_add=False): ) async def _async_add_entity(self, entity, update_before_add, - component_entities, registry): - """Helper method to add an entity to the platform.""" + component_entities, entity_registry, + device_registry): + """Add an entity to the platform.""" if entity is None: raise ValueError('Entity cannot be None') @@ -269,10 +272,25 @@ async def _async_add_entity(self, entity, update_before_add, else: config_entry_id = None - entry = registry.async_get_or_create( + device_info = entity.device_info + if config_entry_id is not None and device_info is not None: + device = device_registry.async_get_or_create( + config_entry=config_entry_id, + connections=device_info.get('connections', []), + identifiers=device_info.get('identifiers', []), + manufacturer=device_info.get('manufacturer'), + model=device_info.get('model'), + name=device_info.get('name'), + sw_version=device_info.get('sw_version')) + device_id = device.id + else: + device_id = None + + entry = entity_registry.async_get_or_create( self.domain, self.platform_name, entity.unique_id, suggested_object_id=suggested_object_id, - config_entry_id=config_entry_id) + config_entry_id=config_entry_id, + device_id=device_id) if entry.disabled: self.logger.info( @@ -288,7 +306,7 @@ async def _async_add_entity(self, entity, update_before_add, # We won't generate an entity ID if the platform has already set one # We will however make sure that platform cannot pick a registered ID elif (entity.entity_id is not None and - registry.async_is_registered(entity.entity_id)): + entity_registry.async_is_registered(entity.entity_id)): # If entity already registered, convert entity id to suggestion suggested_object_id = split_entity_id(entity.entity_id)[1] entity.entity_id = None @@ -302,7 +320,7 @@ async def _async_add_entity(self, entity, update_before_add, suggested_object_id = '{} {}'.format(self.entity_namespace, suggested_object_id) - entity.entity_id = registry.async_generate_entity_id( + entity.entity_id = entity_registry.async_generate_entity_id( self.domain, suggested_object_id) # Make sure it is valid in case an entity set the value themselves diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index 2fa64ff8680f34..804ee4235d0887 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -6,15 +6,10 @@ The Entity Registry will persist itself 10 seconds after a new entity is registered. Registering a new entity while a timer is in progress resets the timer. - -After initializing, call EntityRegistry.async_ensure_loaded to load the data -from disk. """ - from collections import OrderedDict from itertools import chain import logging -import os import weakref import attr @@ -22,7 +17,7 @@ from homeassistant.core import callback, split_entity_id, valid_entity_id from homeassistant.loader import bind_hass from homeassistant.util import ensure_unique_string, slugify -from homeassistant.util.yaml import load_yaml, save_yaml +from homeassistant.util.yaml import load_yaml PATH_REGISTRY = 'entity_registry.yaml' DATA_REGISTRY = 'entity_registry' @@ -32,6 +27,9 @@ DISABLED_HASS = 'hass' DISABLED_USER = 'user' +STORAGE_VERSION = 1 +STORAGE_KEY = 'core.entity_registry' + @attr.s(slots=True, frozen=True) class RegistryEntry: @@ -41,6 +39,7 @@ class RegistryEntry: unique_id = attr.ib(type=str) platform = attr.ib(type=str) name = attr.ib(type=str, default=None) + device_id = attr.ib(type=str, default=None) config_entry_id = attr.ib(type=str, default=None) disabled_by = attr.ib( type=str, default=None, @@ -79,8 +78,7 @@ def __init__(self, hass): """Initialize the registry.""" self.hass = hass self.entities = None - self._load_task = None - self._sched_save = None + self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) @callback def async_is_registered(self, entity_id): @@ -110,7 +108,8 @@ def async_generate_entity_id(self, domain, suggested_object_id): @callback def async_get_or_create(self, domain, platform, unique_id, *, - suggested_object_id=None, config_entry_id=None): + suggested_object_id=None, config_entry_id=None, + device_id=None): """Get entity. Create if it doesn't exist.""" entity_id = self.async_get_entity_id(domain, platform, unique_id) if entity_id: @@ -119,7 +118,8 @@ def async_get_or_create(self, domain, platform, unique_id, *, return entry self._async_update_entity( - entity_id, config_entry_id=config_entry_id) + entity_id, config_entry_id=config_entry_id, + device_id=device_id) return self.entities[entity_id] entity_id = self.async_generate_entity_id( @@ -128,6 +128,7 @@ def async_get_or_create(self, domain, platform, unique_id, *, entity = RegistryEntry( entity_id=entity_id, config_entry_id=config_entry_id, + device_id=device_id, unique_id=unique_id, platform=platform, ) @@ -149,7 +150,8 @@ def async_update_entity(self, entity_id, *, name=_UNDEF, @callback def _async_update_entity(self, entity_id, *, name=_UNDEF, - config_entry_id=_UNDEF, new_entity_id=_UNDEF): + config_entry_id=_UNDEF, new_entity_id=_UNDEF, + device_id=_UNDEF): """Private facing update properties method.""" old = self.entities[entity_id] @@ -162,6 +164,9 @@ def _async_update_entity(self, entity_id, *, name=_UNDEF, config_entry_id != old.config_entry_id): changes['config_entry_id'] = config_entry_id + if (device_id is not _UNDEF and device_id != old.device_id): + changes['device_id'] = device_id + if new_entity_id is not _UNDEF and new_entity_id != old.entity_id: if self.async_is_registered(new_entity_id): raise ValueError('Entity is already registered') @@ -199,71 +204,74 @@ def _async_update_entity(self, entity_id, *, name=_UNDEF, return new - async def async_ensure_loaded(self): - """Load the registry from disk.""" - if self.entities is not None: - return - - if self._load_task is None: - self._load_task = self.hass.async_add_job(self._async_load) - - await self._load_task - - async def _async_load(self): + async def async_load(self): """Load the entity registry.""" - path = self.hass.config.path(PATH_REGISTRY) + data = await self.hass.helpers.storage.async_migrator( + self.hass.config.path(PATH_REGISTRY), self._store, + old_conf_load_func=load_yaml, + old_conf_migrate_func=_async_migrate + ) entities = OrderedDict() - if os.path.isfile(path): - data = await self.hass.async_add_job(load_yaml, path) - - for entity_id, info in data.items(): - entities[entity_id] = RegistryEntry( - entity_id=entity_id, - config_entry_id=info.get('config_entry_id'), - unique_id=info['unique_id'], - platform=info['platform'], - name=info.get('name'), - disabled_by=info.get('disabled_by') + if data is not None: + for entity in data['entities']: + entities[entity['entity_id']] = RegistryEntry( + entity_id=entity['entity_id'], + config_entry_id=entity.get('config_entry_id'), + device_id=entity.get('device_id'), + unique_id=entity['unique_id'], + platform=entity['platform'], + name=entity.get('name'), + disabled_by=entity.get('disabled_by') ) self.entities = entities - self._load_task = None @callback def async_schedule_save(self): """Schedule saving the entity registry.""" - if self._sched_save is not None: - self._sched_save.cancel() - - self._sched_save = self.hass.loop.call_later( - SAVE_DELAY, self.hass.async_add_job, self._async_save - ) + self._store.async_delay_save(self._data_to_save, SAVE_DELAY) - async def _async_save(self): - """Save the entity registry to a file.""" - self._sched_save = None - data = OrderedDict() + @callback + def _data_to_save(self): + """Return data of entity registry to store in a file.""" + data = {} - for entry in self.entities.values(): - data[entry.entity_id] = { + data['entities'] = [ + { + 'entity_id': entry.entity_id, 'config_entry_id': entry.config_entry_id, + 'device_id': entry.device_id, 'unique_id': entry.unique_id, 'platform': entry.platform, 'name': entry.name, - } + } for entry in self.entities.values() + ] - await self.hass.async_add_job( - save_yaml, self.hass.config.path(PATH_REGISTRY), data) + return data @bind_hass async def async_get_registry(hass) -> EntityRegistry: """Return entity registry instance.""" - registry = hass.data.get(DATA_REGISTRY) + task = hass.data.get(DATA_REGISTRY) + + if task is None: + async def _load_reg(): + registry = EntityRegistry(hass) + await registry.async_load() + return registry + + task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg()) + + return await task - if registry is None: - registry = hass.data[DATA_REGISTRY] = EntityRegistry(hass) - await registry.async_ensure_loaded() - return registry +async def _async_migrate(entities): + """Migrate the YAML config file to storage helper format.""" + return { + 'entities': [ + {'entity_id': entity_id, **info} + for entity_id, info in entities.items() + ] + } diff --git a/homeassistant/helpers/json.py b/homeassistant/helpers/json.py new file mode 100644 index 00000000000000..c28ee8c5c2cb68 --- /dev/null +++ b/homeassistant/helpers/json.py @@ -0,0 +1,27 @@ +"""Helpers to help with encoding Home Assistant objects in JSON.""" +from datetime import datetime +import json +import logging + +from typing import Any + +_LOGGER = logging.getLogger(__name__) + + +class JSONEncoder(json.JSONEncoder): + """JSONEncoder that supports Home Assistant objects.""" + + # pylint: disable=method-hidden + def default(self, o: Any) -> Any: + """Convert Home Assistant objects. + + Hand other objects to the original method. + """ + if isinstance(o, datetime): + return o.isoformat() + if isinstance(o, set): + return list(o) + if hasattr(o, 'as_dict'): + return o.as_dict() + + return json.JSONEncoder.default(self, o) diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index a139be4b2607bf..acaeb545815518 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -30,6 +30,7 @@ CONF_EVENT_DATA_TEMPLATE = 'event_data_template' CONF_DELAY = 'delay' CONF_WAIT_TEMPLATE = 'wait_template' +CONF_CONTINUE = 'continue_on_timeout' def call_from_config(hass: HomeAssistant, config: ConfigType, @@ -143,7 +144,8 @@ def async_script_wait(entity_id, from_s, to_s): self.hass.async_add_job(self._change_listener) if CONF_TIMEOUT in action: - self._async_set_timeout(action, variables) + self._async_set_timeout( + action, variables, action.get(CONF_CONTINUE, True)) return @@ -214,17 +216,23 @@ def _async_check_condition(self, action, variables): self._log("Test condition {}: {}".format(self.last_action, check)) return check - def _async_set_timeout(self, action, variables): - """Schedule a timeout to abort script.""" + def _async_set_timeout(self, action, variables, continue_on_timeout=True): + """Schedule a timeout to abort or continue script.""" timeout = action[CONF_TIMEOUT] unsub = None @callback def async_script_timeout(now): - """Call after timeout is retrieve stop script.""" + """Call after timeout is retrieve.""" self._async_listener.remove(unsub) - self._log("Timeout reached, abort script.") - self.async_stop() + + # Check if we want to continue to execute + # the script after the timeout + if continue_on_timeout: + self.hass.async_add_job(self.async_run(variables)) + else: + self._log("Timeout reached, abort script.") + self.async_stop() unsub = async_track_point_in_utc_time( self.hass, async_script_timeout, diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 8aa3b553f3a4a8..fcdc3cfe856655 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -1,4 +1,5 @@ """Service calling related helpers.""" +import asyncio import logging from os import path @@ -178,3 +179,54 @@ def load_services_files(yaml_files): descriptions[domain][service] = description return descriptions + + +@bind_hass +async def entity_service_call(hass, platforms, func, call): + """Handle an entity service call. + + Calls all platforms simultaneously. + """ + tasks = [] + all_entities = ATTR_ENTITY_ID not in call.data + if not all_entities: + entity_ids = set( + extract_entity_ids(hass, call, True)) + + if isinstance(func, str): + data = {key: val for key, val in call.data.items() + if key != ATTR_ENTITY_ID} + else: + data = call + + tasks = [ + _handle_service_platform_call(func, data, [ + entity for entity in platform.entities.values() + if all_entities or entity.entity_id in entity_ids + ], call.context) for platform in platforms + ] + + if tasks: + await asyncio.wait(tasks) + + +async def _handle_service_platform_call(func, data, entities, context): + """Handle a function call.""" + tasks = [] + + for entity in entities: + if not entity.available: + continue + + entity.async_set_context(context) + + if isinstance(func, str): + await getattr(entity, func)(**data) + else: + await func(entity, data) + + if entity.should_poll: + tasks.append(entity.async_update_ha_state(True)) + + if tasks: + await asyncio.wait(tasks) diff --git a/homeassistant/helpers/storage.py b/homeassistant/helpers/storage.py index a68b489868d1ce..95e6925b2a4770 100644 --- a/homeassistant/helpers/storage.py +++ b/homeassistant/helpers/storage.py @@ -2,7 +2,7 @@ import asyncio import logging import os -from typing import Dict, Optional +from typing import Dict, Optional, Callable from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import callback @@ -15,17 +15,19 @@ @bind_hass -async def async_migrator(hass, old_path, store, *, old_conf_migrate_func=None): - """Helper function to migrate old data to a store and then load data. +async def async_migrator(hass, old_path, store, *, + old_conf_load_func=json.load_json, + old_conf_migrate_func=None): + """Migrate old data to a store and then load data. async def old_conf_migrate_func(old_data) """ def load_old_config(): - """Helper to load old config.""" + """Load old config.""" if not os.path.isfile(old_path): return None - return json.load_json(old_path) + return old_conf_load_func(old_path) config = await hass.async_add_executor_job(load_old_config) @@ -52,7 +54,7 @@ def __init__(self, hass, version: int, key: str): self._data = None self._unsub_delay_listener = None self._unsub_stop_listener = None - self._write_lock = asyncio.Lock() + self._write_lock = asyncio.Lock(loop=hass.loop) self._load_task = None @property @@ -75,9 +77,14 @@ async def async_load(self): return await self._load_task async def _async_load(self): - """Helper to load the data.""" + """Load the data.""" + # Check if we have a pending write if self._data is not None: data = self._data + + # If we didn't generate data yet, do it now. + if 'data_func' in data: + data['data'] = data.pop('data_func')() else: data = await self.hass.async_add_executor_job( json.load_json, self.path) @@ -95,8 +102,8 @@ async def _async_load(self): self._load_task = None return stored - async def async_save(self, data: Dict, *, delay: Optional[int] = None): - """Save data with an optional delay.""" + async def async_save(self, data): + """Save data.""" self._data = { 'version': self.version, 'key': self.key, @@ -104,11 +111,20 @@ async def async_save(self, data: Dict, *, delay: Optional[int] = None): } self._async_cleanup_delay_listener() + self._async_cleanup_stop_listener() + await self._async_handle_write_data() + + @callback + def async_delay_save(self, data_func: Callable[[], Dict], + delay: Optional[int] = None): + """Save data with an optional delay.""" + self._data = { + 'version': self.version, + 'key': self.key, + 'data_func': data_func, + } - if delay is None: - self._async_cleanup_stop_listener() - await self._async_handle_write_data() - return + self._async_cleanup_delay_listener() self._unsub_delay_listener = async_call_later( self.hass, delay, self._async_callback_delayed_write) @@ -149,8 +165,12 @@ async def _async_callback_stop_write(self, _event): await self._async_handle_write_data() async def _async_handle_write_data(self, *_args): - """Handler to handle writing the config.""" + """Handle writing the config.""" data = self._data + + if 'data_func' in data: + data['data'] = data.pop('data_func')() + self._data = None async with self._write_lock: diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 26628d7fe6255c..3e9a763181a2cb 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -1,7 +1,8 @@ -aiohttp==3.3.2 +aiohttp==3.4.0 astral==1.6.1 async_timeout==3.0.0 attrs==18.1.0 +bcrypt==3.1.4 certifi>=2018.04.16 jinja2>=2.10 PyJWT==1.6.4 @@ -12,6 +13,8 @@ pyyaml>=3.13,<4 requests==2.19.1 voluptuous==0.11.5 +pycryptodome>=3.6.6 + # Breaks Python 3.6 and is not needed for our supported Python versions enum34==1000000000.0.0 diff --git a/homeassistant/remote.py b/homeassistant/remote.py deleted file mode 100644 index c254dd500f77ea..00000000000000 --- a/homeassistant/remote.py +++ /dev/null @@ -1,316 +0,0 @@ -""" -Support for an interface to work with a remote instance of Home Assistant. - -If a connection error occurs while communicating with the API a -HomeAssistantError will be raised. - -For more details about the Python API, please refer to the documentation at -https://home-assistant.io/developers/python_api/ -""" -from datetime import datetime -import enum -import json -import logging -import urllib.parse - -from typing import Optional, Dict, Any, List - -from aiohttp.hdrs import METH_GET, METH_POST, METH_DELETE, CONTENT_TYPE -import requests - -from homeassistant import core as ha -from homeassistant.const import ( - URL_API, SERVER_PORT, URL_API_CONFIG, URL_API_EVENTS, URL_API_STATES, - URL_API_SERVICES, CONTENT_TYPE_JSON, HTTP_HEADER_HA_AUTH, - URL_API_EVENTS_EVENT, URL_API_STATES_ENTITY, URL_API_SERVICES_SERVICE) -from homeassistant.exceptions import HomeAssistantError - -_LOGGER = logging.getLogger(__name__) - - -class APIStatus(enum.Enum): - """Representation of an API status.""" - - OK = "ok" - INVALID_PASSWORD = "invalid_password" - CANNOT_CONNECT = "cannot_connect" - UNKNOWN = "unknown" - - def __str__(self) -> str: - """Return the state.""" - return self.value # type: ignore - - -class API: - """Object to pass around Home Assistant API location and credentials.""" - - def __init__(self, host: str, api_password: Optional[str] = None, - port: Optional[int] = SERVER_PORT, - use_ssl: bool = False) -> None: - """Init the API.""" - _LOGGER.warning('This class is deprecated and will be removed in 0.77') - self.host = host - self.port = port - self.api_password = api_password - - if host.startswith(("http://", "https://")): - self.base_url = host - elif use_ssl: - self.base_url = "https://{}".format(host) - else: - self.base_url = "http://{}".format(host) - - if port is not None: - self.base_url += ':{}'.format(port) - - self.status = None # type: Optional[APIStatus] - self._headers = {CONTENT_TYPE: CONTENT_TYPE_JSON} - - if api_password is not None: - self._headers[HTTP_HEADER_HA_AUTH] = api_password - - def validate_api(self, force_validate: bool = False) -> bool: - """Test if we can communicate with the API.""" - if self.status is None or force_validate: - self.status = validate_api(self) - - return self.status == APIStatus.OK - - def __call__(self, method: str, path: str, data: Dict = None, - timeout: int = 5) -> requests.Response: - """Make a call to the Home Assistant API.""" - if data is None: - data_str = None - else: - data_str = json.dumps(data, cls=JSONEncoder) - - url = urllib.parse.urljoin(self.base_url, path) - - try: - if method == METH_GET: - return requests.get( - url, params=data_str, timeout=timeout, - headers=self._headers) - - return requests.request( - method, url, data=data_str, timeout=timeout, - headers=self._headers) - - except requests.exceptions.ConnectionError: - _LOGGER.exception("Error connecting to server") - raise HomeAssistantError("Error connecting to server") - - except requests.exceptions.Timeout: - error = "Timeout when talking to {}".format(self.host) - _LOGGER.exception(error) - raise HomeAssistantError(error) - - def __repr__(self) -> str: - """Return the representation of the API.""" - return "".format( - self.base_url, 'yes' if self.api_password is not None else 'no') - - -class JSONEncoder(json.JSONEncoder): - """JSONEncoder that supports Home Assistant objects.""" - - # pylint: disable=method-hidden - def default(self, o: Any) -> Any: - """Convert Home Assistant objects. - - Hand other objects to the original method. - """ - if isinstance(o, datetime): - return o.isoformat() - if isinstance(o, set): - return list(o) - if hasattr(o, 'as_dict'): - return o.as_dict() - - return json.JSONEncoder.default(self, o) - - -def validate_api(api: API) -> APIStatus: - """Make a call to validate API.""" - try: - req = api(METH_GET, URL_API) - - if req.status_code == 200: - return APIStatus.OK - - if req.status_code == 401: - return APIStatus.INVALID_PASSWORD - - return APIStatus.UNKNOWN - - except HomeAssistantError: - return APIStatus.CANNOT_CONNECT - - -def get_event_listeners(api: API) -> Dict: - """List of events that is being listened for.""" - try: - req = api(METH_GET, URL_API_EVENTS) - - return req.json() if req.status_code == 200 else {} # type: ignore - - except (HomeAssistantError, ValueError): - # ValueError if req.json() can't parse the json - _LOGGER.exception("Unexpected result retrieving event listeners") - - return {} - - -def fire_event(api: API, event_type: str, data: Dict = None) -> None: - """Fire an event at remote API.""" - try: - req = api(METH_POST, URL_API_EVENTS_EVENT.format(event_type), data) - - if req.status_code != 200: - _LOGGER.error("Error firing event: %d - %s", - req.status_code, req.text) - - except HomeAssistantError: - _LOGGER.exception("Error firing event") - - -def get_state(api: API, entity_id: str) -> Optional[ha.State]: - """Query given API for state of entity_id.""" - try: - req = api(METH_GET, URL_API_STATES_ENTITY.format(entity_id)) - - # req.status_code == 422 if entity does not exist - - return ha.State.from_dict(req.json()) \ - if req.status_code == 200 else None - - except (HomeAssistantError, ValueError): - # ValueError if req.json() can't parse the json - _LOGGER.exception("Error fetching state") - - return None - - -def get_states(api: API) -> List[ha.State]: - """Query given API for all states.""" - try: - req = api(METH_GET, - URL_API_STATES) - - return [ha.State.from_dict(item) for - item in req.json()] - - except (HomeAssistantError, ValueError, AttributeError): - # ValueError if req.json() can't parse the json - _LOGGER.exception("Error fetching states") - - return [] - - -def remove_state(api: API, entity_id: str) -> bool: - """Call API to remove state for entity_id. - - Return True if entity is gone (removed/never existed). - """ - try: - req = api(METH_DELETE, URL_API_STATES_ENTITY.format(entity_id)) - - if req.status_code in (200, 404): - return True - - _LOGGER.error("Error removing state: %d - %s", - req.status_code, req.text) - return False - except HomeAssistantError: - _LOGGER.exception("Error removing state") - - return False - - -def set_state(api: API, entity_id: str, new_state: str, - attributes: Dict = None, force_update: bool = False) -> bool: - """Tell API to update state for entity_id. - - Return True if success. - """ - attributes = attributes or {} - - data = {'state': new_state, - 'attributes': attributes, - 'force_update': force_update} - - try: - req = api(METH_POST, URL_API_STATES_ENTITY.format(entity_id), data) - - if req.status_code not in (200, 201): - _LOGGER.error("Error changing state: %d - %s", - req.status_code, req.text) - return False - - return True - - except HomeAssistantError: - _LOGGER.exception("Error setting state") - - return False - - -def is_state(api: API, entity_id: str, state: str) -> bool: - """Query API to see if entity_id is specified state.""" - cur_state = get_state(api, entity_id) - - return bool(cur_state and cur_state.state == state) - - -def get_services(api: API) -> Dict: - """Return a list of dicts. - - Each dict has a string "domain" and a list of strings "services". - """ - try: - req = api(METH_GET, URL_API_SERVICES) - - return req.json() if req.status_code == 200 else {} # type: ignore - - except (HomeAssistantError, ValueError): - # ValueError if req.json() can't parse the json - _LOGGER.exception("Got unexpected services result") - - return {} - - -def call_service(api: API, domain: str, service: str, - service_data: Dict = None, - timeout: int = 5) -> None: - """Call a service at the remote API.""" - try: - req = api(METH_POST, - URL_API_SERVICES_SERVICE.format(domain, service), - service_data, timeout=timeout) - - if req.status_code != 200: - _LOGGER.error("Error calling service: %d - %s", - req.status_code, req.text) - - except HomeAssistantError: - _LOGGER.exception("Error calling service") - - -def get_config(api: API) -> Dict: - """Return configuration.""" - try: - req = api(METH_GET, URL_API_CONFIG) - - if req.status_code != 200: - return {} - - result = req.json() - if 'components' in result: - result['components'] = set(result['components']) - return result # type: ignore - - except (HomeAssistantError, ValueError): - # ValueError if req.json() can't parse the JSON - _LOGGER.exception("Got unexpected configuration results") - - return {} diff --git a/homeassistant/scripts/auth.py b/homeassistant/scripts/auth.py index d141faa4c27637..be57957ef8c50f 100644 --- a/homeassistant/scripts/auth.py +++ b/homeassistant/scripts/auth.py @@ -5,15 +5,15 @@ import os from homeassistant.auth import auth_manager_from_config +from homeassistant.auth.providers import homeassistant as hass_auth from homeassistant.core import HomeAssistant from homeassistant.config import get_default_config_dir -from homeassistant.auth.providers import homeassistant as hass_auth def run(args): """Handle Home Assistant auth provider script.""" parser = argparse.ArgumentParser( - description=("Manage Home Assistant users")) + description="Manage Home Assistant users") parser.add_argument( '--script', choices=['auth']) parser.add_argument( @@ -56,7 +56,7 @@ async def run_command(hass, args): hass.config.config_dir = os.path.join(os.getcwd(), args.config) hass.auth = await auth_manager_from_config(hass, [{ 'type': 'homeassistant', - }]) + }], []) provider = hass.auth.auth_providers[0] await provider.async_initialize() await args.func(hass, provider, args) diff --git a/homeassistant/scripts/check_config.py b/homeassistant/scripts/check_config.py index d7be5b1a91c7d0..e0c933df5bbcf3 100644 --- a/homeassistant/scripts/check_config.py +++ b/homeassistant/scripts/check_config.py @@ -325,7 +325,7 @@ def _comp_error(ex, domain, config): # Merge packages merge_packages_config( hass, config, core_config.get(CONF_PACKAGES, {}), _pack_error) - del core_config[CONF_PACKAGES] + core_config.pop(CONF_PACKAGES, None) # Ensure we have no None values after merge for key, value in config.items(): diff --git a/homeassistant/setup.py b/homeassistant/setup.py index 31404b978eb406..41201264da22d6 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -64,7 +64,7 @@ async def _async_process_dependencies( if dep in loader.DEPENDENCY_BLACKLIST] if blacklisted: - _LOGGER.error("Unable to setup dependencies of %s: " + _LOGGER.error("Unable to set up dependencies of %s: " "found blacklisted dependencies: %s", name, ', '.join(blacklisted)) return False @@ -81,7 +81,7 @@ async def _async_process_dependencies( in enumerate(results) if not res] if failed: - _LOGGER.error("Unable to setup dependencies of %s. " + _LOGGER.error("Unable to set up dependencies of %s. " "Setup failed for dependencies: %s", name, ', '.join(failed)) @@ -238,7 +238,7 @@ async def async_process_deps_reqs( hass, config, name, module.DEPENDENCIES) # type: ignore if not dep_success: - raise HomeAssistantError("Could not setup all dependencies.") + raise HomeAssistantError("Could not set up all dependencies.") if not hass.config.skip_pip and hasattr(module, 'REQUIREMENTS'): req_success = await requirements.async_process_requirements( diff --git a/homeassistant/util/__init__.py b/homeassistant/util/__init__.py index 64c9f4f02c994c..1e74c500fc16da 100644 --- a/homeassistant/util/__init__.py +++ b/homeassistant/util/__init__.py @@ -154,7 +154,7 @@ def __lt__(self, other: ENUM_T) -> bool: class OrderedSet(MutableSet[T]): """Ordered set taken from http://code.activestate.com/recipes/576694/.""" - def __init__(self, iterable: Iterable[T] = None) -> None: + def __init__(self, iterable: Optional[Iterable[T]] = None) -> None: """Initialize the set.""" self.end = end = [] # type: List[Any] end += [None, end, end] # sentinel node for doubly linked list @@ -260,7 +260,7 @@ class Throttle: """ def __init__(self, min_time: timedelta, - limit_no_throttle: timedelta = None) -> None: + limit_no_throttle: Optional[timedelta] = None) -> None: """Initialize the throttle.""" self.min_time = min_time self.limit_no_throttle = limit_no_throttle diff --git a/homeassistant/util/dt.py b/homeassistant/util/dt.py index ce6775b9ea7e3d..729195fb3fd44a 100644 --- a/homeassistant/util/dt.py +++ b/homeassistant/util/dt.py @@ -53,7 +53,7 @@ def utcnow() -> dt.datetime: return dt.datetime.now(UTC) -def now(time_zone: dt.tzinfo = None) -> dt.datetime: +def now(time_zone: Optional[dt.tzinfo] = None) -> dt.datetime: """Get now in specified time zone.""" return dt.datetime.now(time_zone or DEFAULT_TIME_ZONE) @@ -97,8 +97,8 @@ def utc_from_timestamp(timestamp: float) -> dt.datetime: return UTC.localize(dt.datetime.utcfromtimestamp(timestamp)) -def start_of_local_day(dt_or_d: - Union[dt.date, dt.datetime] = None) -> dt.datetime: +def start_of_local_day( + dt_or_d: Union[dt.date, dt.datetime, None] = None) -> dt.datetime: """Return local datetime object of start of day from date or datetime.""" if dt_or_d is None: date = now().date() # type: dt.date diff --git a/homeassistant/util/package.py b/homeassistant/util/package.py index 9433046e6881be..3f12fc223b8177 100644 --- a/homeassistant/util/package.py +++ b/homeassistant/util/package.py @@ -32,7 +32,7 @@ def install_package(package: str, upgrade: bool = True, """ # Not using 'import pip; pip.main([])' because it breaks the logger with INSTALL_LOCK: - if check_package_exists(package): + if package_loadable(package): return True _LOGGER.info('Attempting install of %s', package) @@ -61,8 +61,8 @@ def install_package(package: str, upgrade: bool = True, return True -def check_package_exists(package: str) -> bool: - """Check if a package is installed globally or in lib_dir. +def package_loadable(package: str) -> bool: + """Check if a package is what will be loaded when we import it. Returns True when the requirement is met. Returns False when the package is not installed or doesn't meet req. @@ -73,8 +73,16 @@ def check_package_exists(package: str) -> bool: # This is a zip file req = pkg_resources.Requirement.parse(urlparse(package).fragment) - env = pkg_resources.Environment() - return any(dist in req for dist in env[req.project_name]) + req_proj_name = req.project_name.lower() + + for path in sys.path: + for dist in pkg_resources.find_distributions(path): + # If the project name is the same, it will be the one that is + # loaded when we import it. + if dist.project_name.lower() == req_proj_name: + return dist in req + + return False async def async_get_user_site(deps_dir: str) -> str: diff --git a/mypy.ini b/mypy.ini index 875aec5eda7993..1ffdaa0e509e1d 100644 --- a/mypy.ini +++ b/mypy.ini @@ -3,6 +3,7 @@ check_untyped_defs = true disallow_untyped_calls = true follow_imports = silent ignore_missing_imports = true +no_implicit_optional = true warn_incomplete_stub = true warn_redundant_casts = true warn_return_any = true diff --git a/requirements_all.txt b/requirements_all.txt index fe728bf2e0244b..98f8a336cfc543 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1,8 +1,9 @@ # Home Assistant core -aiohttp==3.3.2 +aiohttp==3.4.0 astral==1.6.1 async_timeout==3.0.0 attrs==18.1.0 +bcrypt==3.1.4 certifi>=2018.04.16 jinja2>=2.10 PyJWT==1.6.4 @@ -46,6 +47,9 @@ PyMVGLive==1.1.4 # homeassistant.components.arduino PyMata==2.14 +# homeassistant.auth.mfa_modules.totp +PyQRCode==1.2.1 + # homeassistant.components.sensor.rmvtransport PyRMVtransport==0.0.7 @@ -59,7 +63,7 @@ PyXiaomiGateway==0.9.5 RtmAPI==0.7.0 # homeassistant.components.sonos -SoCo==0.14 +SoCo==0.16 # homeassistant.components.sensor.travisci TravisPy==0.3.5 @@ -77,7 +81,7 @@ YesssSMS==0.1.1b3 abodepy==0.13.1 # homeassistant.components.media_player.frontier_silicon -afsapi==0.0.3 +afsapi==0.0.4 # homeassistant.components.device_tracker.automatic aioautomatic==0.6.5 @@ -111,7 +115,7 @@ aiolifx_effects==0.1.2 aiopvapi==1.5.4 # homeassistant.components.cover.aladdin_connect -aladdin_connect==0.1 +aladdin_connect==0.3 # homeassistant.components.alarmdecoder alarmdecoder==1.13.2 @@ -138,7 +142,7 @@ apns2==0.3.0 asterisk_mbox==0.4.0 # homeassistant.components.media_player.dlna_dmr -async-upnp-client==0.12.3 +async-upnp-client==0.12.4 # homeassistant.components.light.avion # avion==0.7 @@ -162,7 +166,7 @@ batinfo==0.4.2 # homeassistant.components.sensor.geizhals # homeassistant.components.sensor.scrape # homeassistant.components.sensor.sytadin -beautifulsoup4==4.6.1 +beautifulsoup4==4.6.3 # homeassistant.components.zha bellows==0.6.0 @@ -205,7 +209,7 @@ braviarc-homeassistant==0.3.7.dev0 broadlink==0.9.0 # homeassistant.components.cover.brunt -brunt==0.1.2 +brunt==0.1.3 # homeassistant.components.device_tracker.bluetooth_tracker bt_proximity==0.1.2 @@ -386,6 +390,9 @@ gearbest_parser==1.0.7 # homeassistant.components.sensor.gitter gitterpy==0.1.7 +# homeassistant.components.sensor.glances +glances_api==0.1.0 + # homeassistant.components.notify.gntp gntp==1.0.3 @@ -410,6 +417,9 @@ ha-ffmpeg==1.9 # homeassistant.components.media_player.philips_js ha-philipsjs==0.0.5 +# homeassistant.components.hangouts +hangups==0.4.5 + # homeassistant.components.sensor.geo_rss_events haversine==0.4.5 @@ -432,7 +442,7 @@ hole==0.3.0 holidays==0.9.6 # homeassistant.components.frontend -home-assistant-frontend==20180820.0 +home-assistant-frontend==20180829.0 # homeassistant.components.homekit_controller # homekit==0.10 @@ -465,11 +475,8 @@ ihcsdk==2.2.0 # homeassistant.components.sensor.influxdb influxdb==5.0.0 -# homeassistant.components.insteon_local -insteonlocal==0.53 - -# homeassistant.components.insteon_plm -insteonplm==0.11.7 +# homeassistant.components.insteon +insteonplm==0.12.3 # homeassistant.components.sensor.iperf3 iperf3==0.1.10 @@ -497,7 +504,7 @@ kiwiki-client==0.1.1 konnected==0.1.2 # homeassistant.components.eufy -lakeside==0.7 +lakeside==0.10 # homeassistant.components.device_tracker.owntracks # homeassistant.components.device_tracker.owntracks_http @@ -591,7 +598,7 @@ nad_receiver==0.0.9 nanoleaf==0.4.1 # homeassistant.components.device_tracker.keenetic_ndms2 -ndms2_client==0.0.3 +ndms2_client==0.0.4 # homeassistant.components.sensor.netdata netdata==0.1.2 @@ -613,7 +620,7 @@ nuheat==0.3.0 # homeassistant.components.binary_sensor.trend # homeassistant.components.image_processing.opencv -numpy==1.15.0 +numpy==1.15.1 # homeassistant.components.google oauth2client==4.0.0 @@ -692,7 +699,7 @@ proliphix==0.4.1 prometheus_client==0.2.0 # homeassistant.components.sensor.systemmonitor -psutil==5.4.6 +psutil==5.4.7 # homeassistant.components.wink pubnubsub-handler==1.0.2 @@ -738,6 +745,9 @@ pyTibber==0.4.1 # homeassistant.components.switch.dlink pyW215==0.6.0 +# homeassistant.components.sensor.noaa_tides +# py_noaa==0.3.0 + # homeassistant.components.cover.ryobi_gdo py_ryobi_gdo==0.0.10 @@ -804,7 +814,7 @@ pycsspeechtts==1.0.2 pydaikin==0.4 # homeassistant.components.deconz -pydeconz==43 +pydeconz==44 # homeassistant.components.zwave pydispatcher==2.0.5 @@ -834,7 +844,7 @@ pyemby==1.5 pyenvisalink==2.3 # homeassistant.components.climate.ephember -pyephember==0.1.1 +pyephember==0.2.0 # homeassistant.components.sensor.fido pyfido==2.1.1 @@ -873,7 +883,7 @@ pyhik==0.1.8 pyhiveapi==0.2.14 # homeassistant.components.homematic -pyhomematic==0.1.46 +pyhomematic==0.1.47 # homeassistant.components.sensor.hydroquebec pyhydroquebec==2.2.2 @@ -979,6 +989,7 @@ pyopenuv==1.0.1 # homeassistant.components.iota pyota==2.0.5 +# homeassistant.auth.mfa_modules.totp # homeassistant.components.sensor.otp pyotp==2.2.6 @@ -1085,7 +1096,7 @@ python-juicenet==0.0.5 # homeassistant.components.sensor.xiaomi_miio # homeassistant.components.switch.xiaomi_miio # homeassistant.components.vacuum.xiaomi_miio -python-miio==0.4.0 +python-miio==0.4.1 # homeassistant.components.media_player.mpd python-mpd2==1.0.0 @@ -1164,7 +1175,7 @@ pytradfri[async]==5.5.1 pyunifi==2.13 # homeassistant.components.upnp -pyupnp-async==0.1.0.2 +pyupnp-async==0.1.1.0 # homeassistant.components.binary_sensor.uptimerobot pyuptimerobot==0.0.5 @@ -1224,7 +1235,7 @@ rflink==0.0.37 ring_doorbell==0.2.1 # homeassistant.components.device_tracker.ritassist -ritassist==0.5 +ritassist==0.9.2 # homeassistant.components.notify.rocketchat rocketchat-API==0.6.1 @@ -1257,7 +1268,7 @@ schiene==0.22 scsgate==0.1.0 # homeassistant.components.notify.sendgrid -sendgrid==5.4.1 +sendgrid==5.6.0 # homeassistant.components.light.sensehat # homeassistant.components.sensor.sensehat @@ -1270,7 +1281,7 @@ sense_energy==0.4.1 sharp_aquos_rc==0.3.2 # homeassistant.components.sensor.shodan -shodan==1.9.0 +shodan==1.9.1 # homeassistant.components.notify.simplepush simplepush==1.1.4 @@ -1336,6 +1347,9 @@ statsd==3.2.1 # homeassistant.components.sensor.steam_online steamodd==4.21 +# homeassistant.components.ecovacs +sucks==0.9.1 + # homeassistant.components.camera.onvif suds-passworddigest-homeassistant==0.1.2a0.dev0 @@ -1443,7 +1457,7 @@ warrant==0.6.1 watchdog==0.8.3 # homeassistant.components.waterfurnace -waterfurnace==0.4.0 +waterfurnace==0.7.0 # homeassistant.components.media_player.gpmdp websocket-client==0.37.0 @@ -1486,7 +1500,7 @@ yeelight==0.4.0 yeelightsunflower==0.0.10 # homeassistant.components.media_extractor -youtube_dl==2018.08.04 +youtube_dl==2018.08.22 # homeassistant.components.light.zengge zengge==0.2 diff --git a/requirements_test.txt b/requirements_test.txt index 5c2bd3404ed3b4..e50ef699848b4d 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -3,15 +3,15 @@ # new version asynctest==0.12.2 coveralls==1.2.0 -flake8-docstrings==1.0.3 +flake8-docstrings==1.3.0 flake8==3.5 mock-open==1.3.1 mypy==0.620 -pydocstyle==1.1.1 +pydocstyle==2.1.1 pylint==2.1.1 pytest-aiohttp==0.3.0 pytest-cov==2.5.1 pytest-sugar==0.9.1 pytest-timeout==1.3.1 -pytest==3.7.1 +pytest==3.7.2 requests_mock==1.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7119259304e625..31c43319ea72f2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -4,17 +4,17 @@ # new version asynctest==0.12.2 coveralls==1.2.0 -flake8-docstrings==1.0.3 +flake8-docstrings==1.3.0 flake8==3.5 mock-open==1.3.1 mypy==0.620 -pydocstyle==1.1.1 +pydocstyle==2.1.1 pylint==2.1.1 pytest-aiohttp==0.3.0 pytest-cov==2.5.1 pytest-sugar==0.9.1 pytest-timeout==1.3.1 -pytest==3.7.1 +pytest==3.7.2 requests_mock==1.5.2 @@ -25,7 +25,7 @@ HAP-python==2.2.2 PyRMVtransport==0.0.7 # homeassistant.components.sonos -SoCo==0.14 +SoCo==0.16 # homeassistant.components.device_tracker.automatic aioautomatic==0.6.5 @@ -71,6 +71,9 @@ gTTS-token==1.1.1 # homeassistant.components.ffmpeg ha-ffmpeg==1.9 +# homeassistant.components.hangouts +hangups==0.4.5 + # homeassistant.components.sensor.geo_rss_events haversine==0.4.5 @@ -81,7 +84,7 @@ hbmqtt==0.9.2 holidays==0.9.6 # homeassistant.components.frontend -home-assistant-frontend==20180820.0 +home-assistant-frontend==20180829.0 # homeassistant.components.homematicip_cloud homematicip==0.9.8 @@ -102,7 +105,7 @@ mficlient==0.3.0 # homeassistant.components.binary_sensor.trend # homeassistant.components.image_processing.opencv -numpy==1.15.0 +numpy==1.15.1 # homeassistant.components.mqtt # homeassistant.components.shiftr @@ -136,7 +139,7 @@ py-canary==0.5.0 pyblackbird==0.5 # homeassistant.components.deconz -pydeconz==43 +pydeconz==44 # homeassistant.components.zwave pydispatcher==2.0.5 @@ -151,6 +154,10 @@ pymonoprice==0.3 # homeassistant.components.binary_sensor.nx584 pynx584==0.4 +# homeassistant.auth.mfa_modules.totp +# homeassistant.components.sensor.otp +pyotp==2.2.6 + # homeassistant.components.qwikswitch pyqwikswitch==0.8 @@ -171,7 +178,7 @@ pytradfri[async]==5.5.1 pyunifi==2.13 # homeassistant.components.upnp -pyupnp-async==0.1.0.2 +pyupnp-async==0.1.1.0 # homeassistant.components.notify.html5 pywebpush==1.6.0 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 7652d29086b227..4b694ec7ec0719 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -34,6 +34,7 @@ 'credstash', 'bme680', 'homekit', + 'py_noaa', ) TEST_REQUIREMENTS = ( @@ -50,6 +51,7 @@ 'feedparser', 'foobot_async', 'gTTS-token', + 'hangups', 'HAP-python', 'ha-ffmpeg', 'haversine', @@ -76,6 +78,7 @@ 'pylitejet', 'pymonoprice', 'pynx584', + 'pyotp', 'pyqwikswitch', 'PyRMVtransport', 'python-forecastio', @@ -104,7 +107,8 @@ IGNORE_PACKAGES = ( 'homeassistant.components.recorder.models', - 'homeassistant.components.homekit.*' + 'homeassistant.components.homekit.*', + 'homeassistant.components.hangouts.hangups_utils' ) IGNORE_PIN = ('colorlog>2.1,<3', 'keyring>=9.3,<10.0', 'urllib3') @@ -120,6 +124,8 @@ CONSTRAINT_PATH = os.path.join(os.path.dirname(__file__), '../homeassistant/package_constraints.txt') CONSTRAINT_BASE = """ +pycryptodome>=3.6.6 + # Breaks Python 3.6 and is not needed for our supported Python versions enum34==1000000000.0.0 @@ -155,7 +161,7 @@ def core_requirements(): def comment_requirement(req): - """Some requirements don't install on all systems.""" + """Comment out requirement. Some don't install on all systems.""" return any(ign in req for ign in COMMENT_REQUIREMENTS) @@ -165,8 +171,10 @@ def gather_modules(): errors = [] - for package in sorted(explore_module('homeassistant.components', True) + - explore_module('homeassistant.scripts', True)): + for package in sorted( + explore_module('homeassistant.components', True) + + explore_module('homeassistant.scripts', True) + + explore_module('homeassistant.auth', True)): try: module = importlib.import_module(package) except ImportError: @@ -292,7 +300,7 @@ def validate_constraints_file(data): def main(validate): - """Main section of the script.""" + """Run the script.""" if not os.path.isfile('requirements_all.txt'): print('Run this from HA root dir') return 1 diff --git a/script/inspect_schemas.py b/script/inspect_schemas.py index f2fdff22f7a098..46d5cf92eccb41 100755 --- a/script/inspect_schemas.py +++ b/script/inspect_schemas.py @@ -18,7 +18,7 @@ def explore_module(package): def main(): - """Main section of the script.""" + """Run the script.""" if not os.path.isfile('requirements_all.txt'): print('Run this from HA root dir') return diff --git a/script/lazytox.py b/script/lazytox.py index f0388a0fdcbb47..7f2340c726f8bc 100755 --- a/script/lazytox.py +++ b/script/lazytox.py @@ -153,7 +153,7 @@ async def lint(files): async def main(): - """The main loop.""" + """Run the main loop.""" # Ensure we are in the homeassistant root os.chdir(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) diff --git a/script/translations_download_split.py b/script/translations_download_split.py index 03718cf7cab723..180c7281a2fa5d 100755 --- a/script/translations_download_split.py +++ b/script/translations_download_split.py @@ -88,7 +88,7 @@ def save_language_translations(lang, translations): def main(): - """Main section of the script.""" + """Run the script.""" if not os.path.isfile("requirements_all.txt"): print("Run this from HA root dir") return diff --git a/script/translations_upload_merge.py b/script/translations_upload_merge.py index ce0a14c85e6fa4..c1a039363cd184 100755 --- a/script/translations_upload_merge.py +++ b/script/translations_upload_merge.py @@ -73,7 +73,7 @@ def get_translation_dict(translations, component, platform): def main(): - """Main section of the script.""" + """Run the script.""" if not os.path.isfile("requirements_all.txt"): print("Run this from HA root dir") return diff --git a/setup.py b/setup.py index 7484dc286e62ee..b1b0af70319c2f 100755 --- a/setup.py +++ b/setup.py @@ -32,10 +32,11 @@ PACKAGES = find_packages(exclude=['tests', 'tests.*']) REQUIRES = [ - 'aiohttp==3.3.2', + 'aiohttp==3.4.0', 'astral==1.6.1', 'async_timeout==3.0.0', 'attrs==18.1.0', + 'bcrypt==3.1.4', 'certifi>=2018.04.16', 'jinja2>=2.10', 'PyJWT==1.6.4', diff --git a/tests/auth/mfa_modules/__init__.py b/tests/auth/mfa_modules/__init__.py new file mode 100644 index 00000000000000..a49a158d1b0dac --- /dev/null +++ b/tests/auth/mfa_modules/__init__.py @@ -0,0 +1 @@ +"""Tests for the multi-factor auth modules.""" diff --git a/tests/auth/mfa_modules/test_insecure_example.py b/tests/auth/mfa_modules/test_insecure_example.py new file mode 100644 index 00000000000000..80109627140d58 --- /dev/null +++ b/tests/auth/mfa_modules/test_insecure_example.py @@ -0,0 +1,145 @@ +"""Test the example module auth module.""" +from homeassistant import auth, data_entry_flow +from homeassistant.auth.mfa_modules import auth_mfa_module_from_config +from homeassistant.auth.models import Credentials +from tests.common import MockUser + + +async def test_validate(hass): + """Test validating pin.""" + auth_module = await auth_mfa_module_from_config(hass, { + 'type': 'insecure_example', + 'data': [{'user_id': 'test-user', 'pin': '123456'}] + }) + + result = await auth_module.async_validation( + 'test-user', {'pin': '123456'}) + assert result is True + + result = await auth_module.async_validation( + 'test-user', {'pin': 'invalid'}) + assert result is False + + result = await auth_module.async_validation( + 'invalid-user', {'pin': '123456'}) + assert result is False + + +async def test_setup_user(hass): + """Test setup user.""" + auth_module = await auth_mfa_module_from_config(hass, { + 'type': 'insecure_example', + 'data': [] + }) + + await auth_module.async_setup_user( + 'test-user', {'pin': '123456'}) + assert len(auth_module._data) == 1 + + result = await auth_module.async_validation( + 'test-user', {'pin': '123456'}) + assert result is True + + +async def test_depose_user(hass): + """Test despose user.""" + auth_module = await auth_mfa_module_from_config(hass, { + 'type': 'insecure_example', + 'data': [{'user_id': 'test-user', 'pin': '123456'}] + }) + assert len(auth_module._data) == 1 + + await auth_module.async_depose_user('test-user') + assert len(auth_module._data) == 0 + + +async def test_is_user_setup(hass): + """Test is user setup.""" + auth_module = await auth_mfa_module_from_config(hass, { + 'type': 'insecure_example', + 'data': [{'user_id': 'test-user', 'pin': '123456'}] + }) + assert await auth_module.async_is_user_setup('test-user') is True + assert await auth_module.async_is_user_setup('invalid-user') is False + + +async def test_login(hass): + """Test login flow with auth module.""" + hass.auth = await auth.auth_manager_from_config(hass, [{ + 'type': 'insecure_example', + 'users': [{'username': 'test-user', 'password': 'test-pass'}], + }], [{ + 'type': 'insecure_example', + 'data': [{'user_id': 'mock-user', 'pin': '123456'}] + }]) + user = MockUser( + id='mock-user', + is_owner=False, + is_active=False, + name='Paulus', + ).add_to_auth_manager(hass.auth) + await hass.auth.async_link_user(user, Credentials( + id='mock-id', + auth_provider_type='insecure_example', + auth_provider_id=None, + data={'username': 'test-user'}, + is_new=False, + )) + + provider = hass.auth.auth_providers[0] + result = await hass.auth.login_flow.async_init( + (provider.type, provider.id)) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + + result = await hass.auth.login_flow.async_configure( + result['flow_id'], { + 'username': 'incorrect-user', + 'password': 'test-pass', + }) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['errors']['base'] == 'invalid_auth' + + result = await hass.auth.login_flow.async_configure( + result['flow_id'], { + 'username': 'test-user', + 'password': 'incorrect-pass', + }) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['errors']['base'] == 'invalid_auth' + + result = await hass.auth.login_flow.async_configure( + result['flow_id'], { + 'username': 'test-user', + 'password': 'test-pass', + }) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'mfa' + assert result['data_schema'].schema.get('pin') == str + + result = await hass.auth.login_flow.async_configure( + result['flow_id'], {'pin': 'invalid-code'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['errors']['base'] == 'invalid_code' + + result = await hass.auth.login_flow.async_configure( + result['flow_id'], {'pin': '123456'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['data'].id == 'mock-user' + + +async def test_setup_flow(hass): + """Test validating pin.""" + auth_module = await auth_mfa_module_from_config(hass, { + 'type': 'insecure_example', + 'data': [{'user_id': 'test-user', 'pin': '123456'}] + }) + + flow = await auth_module.async_setup_flow('new-user') + + result = await flow.async_step_init() + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + + result = await flow.async_step_init({'pin': 'abcdefg'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert auth_module._data[1]['user_id'] == 'new-user' + assert auth_module._data[1]['pin'] == 'abcdefg' diff --git a/tests/auth/mfa_modules/test_totp.py b/tests/auth/mfa_modules/test_totp.py new file mode 100644 index 00000000000000..6e3558ec5496e3 --- /dev/null +++ b/tests/auth/mfa_modules/test_totp.py @@ -0,0 +1,130 @@ +"""Test the Time-based One Time Password (MFA) auth module.""" +from unittest.mock import patch + +from homeassistant import data_entry_flow +from homeassistant.auth import models as auth_models, auth_manager_from_config +from homeassistant.auth.mfa_modules import auth_mfa_module_from_config +from tests.common import MockUser + +MOCK_CODE = '123456' + + +async def test_validating_mfa(hass): + """Test validating mfa code.""" + totp_auth_module = await auth_mfa_module_from_config(hass, { + 'type': 'totp' + }) + await totp_auth_module.async_setup_user('test-user', {}) + + with patch('pyotp.TOTP.verify', return_value=True): + assert await totp_auth_module.async_validation( + 'test-user', {'code': MOCK_CODE}) + + +async def test_validating_mfa_invalid_code(hass): + """Test validating an invalid mfa code.""" + totp_auth_module = await auth_mfa_module_from_config(hass, { + 'type': 'totp' + }) + await totp_auth_module.async_setup_user('test-user', {}) + + with patch('pyotp.TOTP.verify', return_value=False): + assert await totp_auth_module.async_validation( + 'test-user', {'code': MOCK_CODE}) is False + + +async def test_validating_mfa_invalid_user(hass): + """Test validating an mfa code with invalid user.""" + totp_auth_module = await auth_mfa_module_from_config(hass, { + 'type': 'totp' + }) + await totp_auth_module.async_setup_user('test-user', {}) + + assert await totp_auth_module.async_validation( + 'invalid-user', {'code': MOCK_CODE}) is False + + +async def test_setup_depose_user(hass): + """Test despose user.""" + totp_auth_module = await auth_mfa_module_from_config(hass, { + 'type': 'totp' + }) + result = await totp_auth_module.async_setup_user('test-user', {}) + assert len(totp_auth_module._users) == 1 + result2 = await totp_auth_module.async_setup_user('test-user', {}) + assert len(totp_auth_module._users) == 1 + assert result != result2 + + await totp_auth_module.async_depose_user('test-user') + assert len(totp_auth_module._users) == 0 + + result = await totp_auth_module.async_setup_user( + 'test-user2', {'secret': 'secret-code'}) + assert result == 'secret-code' + assert len(totp_auth_module._users) == 1 + + +async def test_login_flow_validates_mfa(hass): + """Test login flow with mfa enabled.""" + hass.auth = await auth_manager_from_config(hass, [{ + 'type': 'insecure_example', + 'users': [{'username': 'test-user', 'password': 'test-pass'}], + }], [{ + 'type': 'totp', + }]) + user = MockUser( + id='mock-user', + is_owner=False, + is_active=False, + name='Paulus', + ).add_to_auth_manager(hass.auth) + await hass.auth.async_link_user(user, auth_models.Credentials( + id='mock-id', + auth_provider_type='insecure_example', + auth_provider_id=None, + data={'username': 'test-user'}, + is_new=False, + )) + + await hass.auth.async_enable_user_mfa(user, 'totp', {}) + + provider = hass.auth.auth_providers[0] + + result = await hass.auth.login_flow.async_init( + (provider.type, provider.id)) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + + result = await hass.auth.login_flow.async_configure(result['flow_id'], { + 'username': 'incorrect-user', + 'password': 'test-pass', + }) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['errors']['base'] == 'invalid_auth' + + result = await hass.auth.login_flow.async_configure(result['flow_id'], { + 'username': 'test-user', + 'password': 'incorrect-pass', + }) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['errors']['base'] == 'invalid_auth' + + result = await hass.auth.login_flow.async_configure(result['flow_id'], { + 'username': 'test-user', + 'password': 'test-pass', + }) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'mfa' + assert result['data_schema'].schema.get('code') == str + + with patch('pyotp.TOTP.verify', return_value=False): + result = await hass.auth.login_flow.async_configure( + result['flow_id'], {'code': 'invalid-code'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'mfa' + assert result['errors']['base'] == 'invalid_code' + + with patch('pyotp.TOTP.verify', return_value=True): + result = await hass.auth.login_flow.async_configure( + result['flow_id'], {'code': MOCK_CODE}) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['data'].id == 'mock-user' diff --git a/tests/auth/providers/test_homeassistant.py b/tests/auth/providers/test_homeassistant.py index 9db6293d98a14f..84beb8cdd3f477 100644 --- a/tests/auth/providers/test_homeassistant.py +++ b/tests/auth/providers/test_homeassistant.py @@ -1,10 +1,12 @@ """Test the Home Assistant local auth provider.""" from unittest.mock import Mock +import base64 import pytest +import voluptuous as vol from homeassistant import data_entry_flow -from homeassistant.auth import auth_manager_from_config +from homeassistant.auth import auth_manager_from_config, auth_store from homeassistant.auth.providers import ( auth_provider_from_config, homeassistant as hass_auth) @@ -24,7 +26,7 @@ async def test_adding_user(data, hass): async def test_adding_user_duplicate_username(data, hass): - """Test adding a user.""" + """Test adding a user with duplicate username.""" data.add_auth('test-user', 'test-pass') with pytest.raises(hass_auth.InvalidUser): data.add_auth('test-user', 'other-pass') @@ -37,7 +39,7 @@ async def test_validating_password_invalid_user(data, hass): async def test_validating_password_invalid_password(data, hass): - """Test validating an invalid user.""" + """Test validating an invalid password.""" data.add_auth('test-user', 'test-pass') with pytest.raises(hass_auth.InvalidAuth): @@ -67,8 +69,9 @@ async def test_login_flow_validates(data, hass): data.add_auth('test-user', 'test-pass') await data.async_save() - provider = hass_auth.HassAuthProvider(hass, None, {}) - flow = hass_auth.LoginFlow(provider) + provider = hass_auth.HassAuthProvider(hass, auth_store.AuthStore(hass), + {'type': 'homeassistant'}) + flow = await provider.async_login_flow({}) result = await flow.async_step_init() assert result['type'] == data_entry_flow.RESULT_TYPE_FORM @@ -91,6 +94,7 @@ async def test_login_flow_validates(data, hass): 'password': 'test-pass', }) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['data']['username'] == 'test-user' async def test_saving_loading(data, hass): @@ -108,11 +112,11 @@ async def test_saving_loading(data, hass): async def test_not_allow_set_id(): """Test we are not allowed to set an ID in config.""" hass = Mock() - provider = await auth_provider_from_config(hass, None, { - 'type': 'homeassistant', - 'id': 'invalid', - }) - assert provider is None + with pytest.raises(vol.Invalid): + await auth_provider_from_config(hass, None, { + 'type': 'homeassistant', + 'id': 'invalid', + }) async def test_new_users_populate_values(hass, data): @@ -122,7 +126,7 @@ async def test_new_users_populate_values(hass, data): manager = await auth_manager_from_config(hass, [{ 'type': 'homeassistant' - }]) + }], []) provider = manager.auth_providers[0] credentials = await provider.async_get_or_create_credentials({ 'username': 'hello' @@ -130,3 +134,91 @@ async def test_new_users_populate_values(hass, data): user = await manager.async_get_or_create_user(credentials) assert user.name == 'hello' assert user.is_active + + +async def test_new_hashes_are_bcrypt(data, hass): + """Test that newly created hashes are using bcrypt.""" + data.add_auth('newuser', 'newpass') + found = None + for user in data.users: + if user['username'] == 'newuser': + found = user + assert found is not None + user_hash = base64.b64decode(found['password']) + assert (user_hash.startswith(b'$2a$') + or user_hash.startswith(b'$2b$') + or user_hash.startswith(b'$2x$') + or user_hash.startswith(b'$2y$')) + + +async def test_pbkdf2_to_bcrypt_hash_upgrade(hass_storage, hass): + """Test migrating user from pbkdf2 hash to bcrypt hash.""" + hass_storage[hass_auth.STORAGE_KEY] = { + 'version': hass_auth.STORAGE_VERSION, + 'key': hass_auth.STORAGE_KEY, + 'data': { + 'salt': '09c52f0b120eaa7dea5f73f9a9b985f3d493b30a08f3f2945ef613' + '0b08e6a3ea', + 'users': [ + { + 'password': 'L5PAbehB8LAQI2Ixu+d+PDNJKmljqLnBcYWYw35onC/8D' + 'BM1SpvT6A8ZFael5+deCt+s+43J08IcztnguouHSw==', + 'username': 'legacyuser' + } + ] + }, + } + data = hass_auth.Data(hass) + await data.async_load() + + # verify the correct (pbkdf2) password successfuly authenticates the user + await hass.async_add_executor_job( + data.validate_login, 'legacyuser', 'beer') + + # ...and that the hashes are now bcrypt hashes + user_hash = base64.b64decode( + hass_storage[hass_auth.STORAGE_KEY]['data']['users'][0]['password']) + assert (user_hash.startswith(b'$2a$') + or user_hash.startswith(b'$2b$') + or user_hash.startswith(b'$2x$') + or user_hash.startswith(b'$2y$')) + + +async def test_pbkdf2_to_bcrypt_hash_upgrade_with_incorrect_pass(hass_storage, + hass): + """Test migrating user from pbkdf2 hash to bcrypt hash.""" + hass_storage[hass_auth.STORAGE_KEY] = { + 'version': hass_auth.STORAGE_VERSION, + 'key': hass_auth.STORAGE_KEY, + 'data': { + 'salt': '09c52f0b120eaa7dea5f73f9a9b985f3d493b30a08f3f2945ef613' + '0b08e6a3ea', + 'users': [ + { + 'password': 'L5PAbehB8LAQI2Ixu+d+PDNJKmljqLnBcYWYw35onC/8D' + 'BM1SpvT6A8ZFael5+deCt+s+43J08IcztnguouHSw==', + 'username': 'legacyuser' + } + ] + }, + } + data = hass_auth.Data(hass) + await data.async_load() + + orig_user_hash = base64.b64decode( + hass_storage[hass_auth.STORAGE_KEY]['data']['users'][0]['password']) + + # Make sure invalid legacy passwords fail + with pytest.raises(hass_auth.InvalidAuth): + await hass.async_add_executor_job( + data.validate_login, 'legacyuser', 'wine') + + # Make sure we don't change the password/hash when password is incorrect + with pytest.raises(hass_auth.InvalidAuth): + await hass.async_add_executor_job( + data.validate_login, 'legacyuser', 'wine') + + same_user_hash = base64.b64decode( + hass_storage[hass_auth.STORAGE_KEY]['data']['users'][0]['password']) + + assert orig_user_hash == same_user_hash diff --git a/tests/auth/providers/test_insecure_example.py b/tests/auth/providers/test_insecure_example.py index b472e4c95df3b4..d50e8b0de967ff 100644 --- a/tests/auth/providers/test_insecure_example.py +++ b/tests/auth/providers/test_insecure_example.py @@ -40,7 +40,7 @@ def manager(hass, store, provider): """Mock manager.""" return AuthManager(hass, store, { (provider.type, provider.id): provider - }) + }, {}) async def test_create_new_credential(manager, provider): diff --git a/tests/auth/providers/test_legacy_api_password.py b/tests/auth/providers/test_legacy_api_password.py index 71642bd7a32c26..60916798d5b949 100644 --- a/tests/auth/providers/test_legacy_api_password.py +++ b/tests/auth/providers/test_legacy_api_password.py @@ -3,7 +3,7 @@ import pytest -from homeassistant import auth +from homeassistant import auth, data_entry_flow from homeassistant.auth import auth_store from homeassistant.auth.providers import legacy_api_password @@ -27,7 +27,7 @@ def manager(hass, store, provider): """Mock manager.""" return auth.AuthManager(hass, store, { (provider.type, provider.id): provider - }) + }, {}) async def test_create_new_credential(manager, provider): @@ -51,19 +51,8 @@ async def test_only_one_credentials(manager, provider): assert credentials2.is_new is False -async def test_verify_not_load(hass, provider): - """Test we raise if http module not load.""" - with pytest.raises(ValueError): - provider.async_validate_login('test-password') - hass.http = Mock(api_password=None) - with pytest.raises(ValueError): - provider.async_validate_login('test-password') - hass.http = Mock(api_password='test-password') - provider.async_validate_login('test-password') - - async def test_verify_login(hass, provider): - """Test we raise if http module not load.""" + """Test login using legacy api password auth provider.""" hass.http = Mock(api_password='test-password') provider.async_validate_login('test-password') hass.http = Mock(api_password='test-password') @@ -71,10 +60,43 @@ async def test_verify_login(hass, provider): provider.async_validate_login('invalid-password') -async def test_utf_8_username_password(provider): - """Test that we create a new credential.""" - credentials = await provider.async_get_or_create_credentials({ - 'username': '🎉', - 'password': '😎', - }) - assert credentials.is_new is True +async def test_login_flow_abort(hass, manager): + """Test wrong config.""" + for http in ( + None, + Mock(api_password=None), + Mock(api_password=''), + ): + hass.http = http + + result = await manager.login_flow.async_init( + handler=('legacy_api_password', None) + ) + assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert result['reason'] == 'no_api_password_set' + + +async def test_login_flow_works(hass, manager): + """Test wrong config.""" + hass.http = Mock(api_password='hello') + result = await manager.login_flow.async_init( + handler=('legacy_api_password', None) + ) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + + result = await manager.login_flow.async_configure( + flow_id=result['flow_id'], + user_input={ + 'password': 'not-hello' + } + ) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['errors']['base'] == 'invalid_auth' + + result = await manager.login_flow.async_configure( + flow_id=result['flow_id'], + user_input={ + 'password': 'hello' + } + ) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY diff --git a/tests/auth/providers/test_trusted_networks.py b/tests/auth/providers/test_trusted_networks.py new file mode 100644 index 00000000000000..0ca302f8273057 --- /dev/null +++ b/tests/auth/providers/test_trusted_networks.py @@ -0,0 +1,101 @@ +"""Test the Trusted Networks auth provider.""" +from unittest.mock import Mock + +import pytest +import voluptuous as vol + +from homeassistant import auth +from homeassistant.auth import auth_store +from homeassistant.auth.providers import trusted_networks as tn_auth + + +@pytest.fixture +def store(hass): + """Mock store.""" + return auth_store.AuthStore(hass) + + +@pytest.fixture +def provider(hass, store): + """Mock provider.""" + return tn_auth.TrustedNetworksAuthProvider(hass, store, { + 'type': 'trusted_networks' + }) + + +@pytest.fixture +def manager(hass, store, provider): + """Mock manager.""" + return auth.AuthManager(hass, store, { + (provider.type, provider.id): provider + }, {}) + + +async def test_trusted_networks_credentials(manager, provider): + """Test trusted_networks credentials related functions.""" + owner = await manager.async_create_user("test-owner") + tn_owner_cred = await provider.async_get_or_create_credentials({ + 'user': owner.id + }) + assert tn_owner_cred.is_new is False + assert any(cred.id == tn_owner_cred.id for cred in owner.credentials) + + user = await manager.async_create_user("test-user") + tn_user_cred = await provider.async_get_or_create_credentials({ + 'user': user.id + }) + assert tn_user_cred.id != tn_owner_cred.id + assert tn_user_cred.is_new is False + assert any(cred.id == tn_user_cred.id for cred in user.credentials) + + with pytest.raises(tn_auth.InvalidUserError): + await provider.async_get_or_create_credentials({ + 'user': 'invalid-user' + }) + + +async def test_validate_access(provider): + """Test validate access from trusted networks.""" + with pytest.raises(tn_auth.InvalidAuthError): + provider.async_validate_access('192.168.0.1') + + provider.hass.http = Mock(trusted_networks=['192.168.0.1']) + provider.async_validate_access('192.168.0.1') + + with pytest.raises(tn_auth.InvalidAuthError): + provider.async_validate_access('127.0.0.1') + + +async def test_login_flow(manager, provider): + """Test login flow.""" + owner = await manager.async_create_user("test-owner") + user = await manager.async_create_user("test-user") + + # trusted network didn't loaded + flow = await provider.async_login_flow({'ip_address': '127.0.0.1'}) + step = await flow.async_step_init() + assert step['type'] == 'abort' + assert step['reason'] == 'not_whitelisted' + + provider.hass.http = Mock(trusted_networks=['192.168.0.1']) + + # not from trusted network + flow = await provider.async_login_flow({'ip_address': '127.0.0.1'}) + step = await flow.async_step_init() + assert step['type'] == 'abort' + assert step['reason'] == 'not_whitelisted' + + # from trusted network, list users + flow = await provider.async_login_flow({'ip_address': '192.168.0.1'}) + step = await flow.async_step_init() + assert step['step_id'] == 'init' + + schema = step['data_schema'] + assert schema({'user': owner.id}) + with pytest.raises(vol.Invalid): + assert schema({'user': 'invalid-user'}) + + # login with valid user + step = await flow.async_step_init({'user': user.id}) + assert step['type'] == 'create_entry' + assert step['data']['user'] == user.id diff --git a/tests/auth/test_init.py b/tests/auth/test_init.py index da5daca7cf63c5..63b2b4408dd8af 100644 --- a/tests/auth/test_init.py +++ b/tests/auth/test_init.py @@ -3,10 +3,12 @@ from unittest.mock import Mock, patch import pytest +import voluptuous as vol from homeassistant import auth, data_entry_flow from homeassistant.auth import ( models as auth_models, auth_store, const as auth_const) +from homeassistant.auth.mfa_modules import SESSION_EXPIRATION from homeassistant.util import dt as dt_util from tests.common import ( MockUser, ensure_auth_manager_loaded, flush_store, CLIENT_ID) @@ -20,32 +22,92 @@ def mock_hass(loop): return hass -async def test_auth_manager_from_config_validates_config_and_id(mock_hass): +async def test_auth_manager_from_config_validates_config(mock_hass): """Test get auth providers.""" + with pytest.raises(vol.Invalid): + manager = await auth.auth_manager_from_config(mock_hass, [{ + 'name': 'Test Name', + 'type': 'insecure_example', + 'users': [], + }, { + 'name': 'Invalid config because no users', + 'type': 'insecure_example', + 'id': 'invalid_config', + }], []) + manager = await auth.auth_manager_from_config(mock_hass, [{ 'name': 'Test Name', 'type': 'insecure_example', 'users': [], }, { - 'name': 'Invalid config because no users', + 'name': 'Test Name 2', + 'type': 'insecure_example', + 'id': 'another', + 'users': [], + }], []) + + providers = [{ + 'name': provider.name, + 'id': provider.id, + 'type': provider.type, + } for provider in manager.auth_providers] + + assert providers == [{ + 'name': 'Test Name', 'type': 'insecure_example', - 'id': 'invalid_config', + 'id': None, }, { 'name': 'Test Name 2', 'type': 'insecure_example', 'id': 'another', + }] + + +async def test_auth_manager_from_config_auth_modules(mock_hass): + """Test get auth modules.""" + with pytest.raises(vol.Invalid): + manager = await auth.auth_manager_from_config(mock_hass, [{ + 'name': 'Test Name', + 'type': 'insecure_example', + 'users': [], + }, { + 'name': 'Test Name 2', + 'type': 'insecure_example', + 'id': 'another', + 'users': [], + }], [{ + 'name': 'Module 1', + 'type': 'insecure_example', + 'data': [], + }, { + 'name': 'Invalid config because no data', + 'type': 'insecure_example', + 'id': 'another', + }]) + + manager = await auth.auth_manager_from_config(mock_hass, [{ + 'name': 'Test Name', + 'type': 'insecure_example', 'users': [], }, { - 'name': 'Wrong because duplicate ID', + 'name': 'Test Name 2', 'type': 'insecure_example', 'id': 'another', 'users': [], + }], [{ + 'name': 'Module 1', + 'type': 'insecure_example', + 'data': [], + }, { + 'name': 'Module 2', + 'type': 'insecure_example', + 'id': 'another', + 'data': [], }]) - providers = [{ 'name': provider.name, - 'id': provider.id, 'type': provider.type, + 'id': provider.id, } for provider in manager.auth_providers] assert providers == [{ 'name': 'Test Name', @@ -57,8 +119,23 @@ async def test_auth_manager_from_config_validates_config_and_id(mock_hass): 'id': 'another', }] + modules = [{ + 'name': module.name, + 'type': module.type, + 'id': module.id, + } for module in manager.auth_mfa_modules] + assert modules == [{ + 'name': 'Module 1', + 'type': 'insecure_example', + 'id': 'insecure_example', + }, { + 'name': 'Module 2', + 'type': 'insecure_example', + 'id': 'another', + }] + -async def test_create_new_user(hass, hass_storage): +async def test_create_new_user(hass): """Test creating new user.""" manager = await auth.auth_manager_from_config(hass, [{ 'type': 'insecure_example', @@ -67,7 +144,7 @@ async def test_create_new_user(hass, hass_storage): 'password': 'test-pass', 'name': 'Test Name' }] - }]) + }], []) step = await manager.login_flow.async_init(('insecure_example', None)) assert step['type'] == data_entry_flow.RESULT_TYPE_FORM @@ -77,8 +154,7 @@ async def test_create_new_user(hass, hass_storage): 'password': 'test-pass', }) assert step['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - credentials = step['result'] - user = await manager.async_get_or_create_user(credentials) + user = step['result'] assert user is not None assert user.is_owner is False assert user.name == 'Test Name' @@ -93,7 +169,8 @@ async def test_login_as_existing_user(mock_hass): 'password': 'test-pass', 'name': 'Test Name' }] - }]) + }], []) + mock_hass.auth = manager ensure_auth_manager_loaded(manager) # Add a fake user that we're not going to log in with @@ -134,9 +211,8 @@ async def test_login_as_existing_user(mock_hass): 'password': 'test-pass', }) assert step['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - credentials = step['result'] - user = await manager.async_get_or_create_user(credentials) + user = step['result'] assert user is not None assert user.id == 'mock-user' assert user.is_owner is False @@ -159,23 +235,25 @@ async def test_linking_user_to_two_auth_providers(hass, hass_storage): 'username': 'another-user', 'password': 'another-password', }] - }]) + }], []) step = await manager.login_flow.async_init(('insecure_example', None)) step = await manager.login_flow.async_configure(step['flow_id'], { 'username': 'test-user', 'password': 'test-pass', }) - user = await manager.async_get_or_create_user(step['result']) + user = step['result'] assert user is not None - step = await manager.login_flow.async_init(('insecure_example', - 'another-provider')) + step = await manager.login_flow.async_init( + ('insecure_example', 'another-provider'), + context={'credential_only': True}) step = await manager.login_flow.async_configure(step['flow_id'], { 'username': 'another-user', 'password': 'another-password', }) - await manager.async_link_user(user, step['result']) + new_credential = step['result'] + await manager.async_link_user(user, new_credential) assert len(user.credentials) == 2 @@ -190,14 +268,14 @@ async def test_saving_loading(hass, hass_storage): 'username': 'test-user', 'password': 'test-pass', }] - }]) + }], []) step = await manager.login_flow.async_init(('insecure_example', None)) step = await manager.login_flow.async_configure(step['flow_id'], { 'username': 'test-user', 'password': 'test-pass', }) - user = await manager.async_get_or_create_user(step['result']) + user = step['result'] await manager.async_activate_user(user) await manager.async_create_refresh_token(user, CLIENT_ID) @@ -211,7 +289,7 @@ async def test_saving_loading(hass, hass_storage): async def test_cannot_retrieve_expired_access_token(hass): """Test that we cannot retrieve expired access tokens.""" - manager = await auth.auth_manager_from_config(hass, []) + manager = await auth.auth_manager_from_config(hass, [], []) user = MockUser().add_to_auth_manager(manager) refresh_token = await manager.async_create_refresh_token(user, CLIENT_ID) assert refresh_token.user.id is user.id @@ -236,7 +314,7 @@ async def test_cannot_retrieve_expired_access_token(hass): async def test_generating_system_user(hass): """Test that we can add a system user.""" - manager = await auth.auth_manager_from_config(hass, []) + manager = await auth.auth_manager_from_config(hass, [], []) user = await manager.async_create_system_user('Hass.io') token = await manager.async_create_refresh_token(user) assert user.system_generated @@ -246,7 +324,7 @@ async def test_generating_system_user(hass): async def test_refresh_token_requires_client_for_user(hass): """Test that we can add a system user.""" - manager = await auth.auth_manager_from_config(hass, []) + manager = await auth.auth_manager_from_config(hass, [], []) user = MockUser().add_to_auth_manager(manager) assert user.system_generated is False @@ -260,7 +338,7 @@ async def test_refresh_token_requires_client_for_user(hass): async def test_refresh_token_not_requires_client_for_system_user(hass): """Test that we can add a system user.""" - manager = await auth.auth_manager_from_config(hass, []) + manager = await auth.auth_manager_from_config(hass, [], []) user = await manager.async_create_system_user('Hass.io') assert user.system_generated is True @@ -274,10 +352,295 @@ async def test_refresh_token_not_requires_client_for_system_user(hass): async def test_cannot_deactive_owner(mock_hass): """Test that we cannot deactive the owner.""" - manager = await auth.auth_manager_from_config(mock_hass, []) + manager = await auth.auth_manager_from_config(mock_hass, [], []) owner = MockUser( is_owner=True, ).add_to_auth_manager(manager) with pytest.raises(ValueError): await manager.async_deactivate_user(owner) + + +async def test_remove_refresh_token(mock_hass): + """Test that we can remove a refresh token.""" + manager = await auth.auth_manager_from_config(mock_hass, [], []) + user = MockUser().add_to_auth_manager(manager) + refresh_token = await manager.async_create_refresh_token(user, CLIENT_ID) + access_token = manager.async_create_access_token(refresh_token) + + await manager.async_remove_refresh_token(refresh_token) + + assert ( + await manager.async_get_refresh_token(refresh_token.id) is None + ) + assert ( + await manager.async_validate_access_token(access_token) is None + ) + + +async def test_login_with_auth_module(mock_hass): + """Test login as existing user with auth module.""" + manager = await auth.auth_manager_from_config(mock_hass, [{ + 'type': 'insecure_example', + 'users': [{ + 'username': 'test-user', + 'password': 'test-pass', + 'name': 'Test Name' + }], + }], [{ + 'type': 'insecure_example', + 'data': [{ + 'user_id': 'mock-user', + 'pin': 'test-pin' + }] + }]) + mock_hass.auth = manager + ensure_auth_manager_loaded(manager) + + # Add fake user with credentials for example auth provider. + user = MockUser( + id='mock-user', + is_owner=False, + is_active=False, + name='Paulus', + ).add_to_auth_manager(manager) + user.credentials.append(auth_models.Credentials( + id='mock-id', + auth_provider_type='insecure_example', + auth_provider_id=None, + data={'username': 'test-user'}, + is_new=False, + )) + + step = await manager.login_flow.async_init(('insecure_example', None)) + assert step['type'] == data_entry_flow.RESULT_TYPE_FORM + + step = await manager.login_flow.async_configure(step['flow_id'], { + 'username': 'test-user', + 'password': 'test-pass', + }) + + # After auth_provider validated, request auth module input form + assert step['type'] == data_entry_flow.RESULT_TYPE_FORM + assert step['step_id'] == 'mfa' + + step = await manager.login_flow.async_configure(step['flow_id'], { + 'pin': 'invalid-pin', + }) + + # Invalid code error + assert step['type'] == data_entry_flow.RESULT_TYPE_FORM + assert step['step_id'] == 'mfa' + assert step['errors'] == {'base': 'invalid_code'} + + step = await manager.login_flow.async_configure(step['flow_id'], { + 'pin': 'test-pin', + }) + + # Finally passed, get user + assert step['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + user = step['result'] + assert user is not None + assert user.id == 'mock-user' + assert user.is_owner is False + assert user.is_active is False + assert user.name == 'Paulus' + + +async def test_login_with_multi_auth_module(mock_hass): + """Test login as existing user with multiple auth modules.""" + manager = await auth.auth_manager_from_config(mock_hass, [{ + 'type': 'insecure_example', + 'users': [{ + 'username': 'test-user', + 'password': 'test-pass', + 'name': 'Test Name' + }], + }], [{ + 'type': 'insecure_example', + 'data': [{ + 'user_id': 'mock-user', + 'pin': 'test-pin' + }] + }, { + 'type': 'insecure_example', + 'id': 'module2', + 'data': [{ + 'user_id': 'mock-user', + 'pin': 'test-pin2' + }] + }]) + mock_hass.auth = manager + ensure_auth_manager_loaded(manager) + + # Add fake user with credentials for example auth provider. + user = MockUser( + id='mock-user', + is_owner=False, + is_active=False, + name='Paulus', + ).add_to_auth_manager(manager) + user.credentials.append(auth_models.Credentials( + id='mock-id', + auth_provider_type='insecure_example', + auth_provider_id=None, + data={'username': 'test-user'}, + is_new=False, + )) + + step = await manager.login_flow.async_init(('insecure_example', None)) + assert step['type'] == data_entry_flow.RESULT_TYPE_FORM + + step = await manager.login_flow.async_configure(step['flow_id'], { + 'username': 'test-user', + 'password': 'test-pass', + }) + + # After auth_provider validated, request select auth module + assert step['type'] == data_entry_flow.RESULT_TYPE_FORM + assert step['step_id'] == 'select_mfa_module' + + step = await manager.login_flow.async_configure(step['flow_id'], { + 'multi_factor_auth_module': 'module2', + }) + + assert step['type'] == data_entry_flow.RESULT_TYPE_FORM + assert step['step_id'] == 'mfa' + + step = await manager.login_flow.async_configure(step['flow_id'], { + 'pin': 'test-pin2', + }) + + # Finally passed, get user + assert step['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + user = step['result'] + assert user is not None + assert user.id == 'mock-user' + assert user.is_owner is False + assert user.is_active is False + assert user.name == 'Paulus' + + +async def test_auth_module_expired_session(mock_hass): + """Test login as existing user.""" + manager = await auth.auth_manager_from_config(mock_hass, [{ + 'type': 'insecure_example', + 'users': [{ + 'username': 'test-user', + 'password': 'test-pass', + 'name': 'Test Name' + }], + }], [{ + 'type': 'insecure_example', + 'data': [{ + 'user_id': 'mock-user', + 'pin': 'test-pin' + }] + }]) + mock_hass.auth = manager + ensure_auth_manager_loaded(manager) + + # Add fake user with credentials for example auth provider. + user = MockUser( + id='mock-user', + is_owner=False, + is_active=False, + name='Paulus', + ).add_to_auth_manager(manager) + user.credentials.append(auth_models.Credentials( + id='mock-id', + auth_provider_type='insecure_example', + auth_provider_id=None, + data={'username': 'test-user'}, + is_new=False, + )) + + step = await manager.login_flow.async_init(('insecure_example', None)) + assert step['type'] == data_entry_flow.RESULT_TYPE_FORM + + step = await manager.login_flow.async_configure(step['flow_id'], { + 'username': 'test-user', + 'password': 'test-pass', + }) + + assert step['type'] == data_entry_flow.RESULT_TYPE_FORM + assert step['step_id'] == 'mfa' + + with patch('homeassistant.util.dt.utcnow', + return_value=dt_util.utcnow() + SESSION_EXPIRATION): + step = await manager.login_flow.async_configure(step['flow_id'], { + 'pin': 'test-pin', + }) + # login flow abort due session timeout + assert step['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert step['reason'] == 'login_expired' + + +async def test_enable_mfa_for_user(hass, hass_storage): + """Test enable mfa module for user.""" + manager = await auth.auth_manager_from_config(hass, [{ + 'type': 'insecure_example', + 'users': [{ + 'username': 'test-user', + 'password': 'test-pass', + }] + }], [{ + 'type': 'insecure_example', + 'data': [], + }]) + + step = await manager.login_flow.async_init(('insecure_example', None)) + step = await manager.login_flow.async_configure(step['flow_id'], { + 'username': 'test-user', + 'password': 'test-pass', + }) + user = step['result'] + assert user is not None + + # new user don't have mfa enabled + modules = await manager.async_get_enabled_mfa(user) + assert len(modules) == 0 + + module = manager.get_auth_mfa_module('insecure_example') + # mfa module don't have data + assert bool(module._data) is False + + # test enable mfa for user + await manager.async_enable_user_mfa(user, 'insecure_example', + {'pin': 'test-pin'}) + assert len(module._data) == 1 + assert module._data[0] == {'user_id': user.id, 'pin': 'test-pin'} + + # test get enabled mfa + modules = await manager.async_get_enabled_mfa(user) + assert len(modules) == 1 + assert 'insecure_example' in modules + + # re-enable mfa for user will override + await manager.async_enable_user_mfa(user, 'insecure_example', + {'pin': 'test-pin-new'}) + assert len(module._data) == 1 + assert module._data[0] == {'user_id': user.id, 'pin': 'test-pin-new'} + modules = await manager.async_get_enabled_mfa(user) + assert len(modules) == 1 + assert 'insecure_example' in modules + + # system user cannot enable mfa + system_user = await manager.async_create_system_user('system-user') + with pytest.raises(ValueError): + await manager.async_enable_user_mfa(system_user, 'insecure_example', + {'pin': 'test-pin'}) + assert len(module._data) == 1 + modules = await manager.async_get_enabled_mfa(system_user) + assert len(modules) == 0 + + # disable mfa for user + await manager.async_disable_user_mfa(user, 'insecure_example') + assert bool(module._data) is False + + # test get enabled mfa + modules = await manager.async_get_enabled_mfa(user) + assert len(modules) == 0 + + # disable mfa for user don't enabled just silent fail + await manager.async_disable_user_mfa(user, 'insecure_example') diff --git a/tests/common.py b/tests/common.py index 81e4774ccd4779..738c51fb3f0982 100644 --- a/tests/common.py +++ b/tests/common.py @@ -118,19 +118,35 @@ def async_test_home_assistant(loop): hass = ha.HomeAssistant(loop) hass.config.async_load = Mock() store = auth_store.AuthStore(hass) - hass.auth = auth.AuthManager(hass, store, {}) + hass.auth = auth.AuthManager(hass, store, {}, {}) ensure_auth_manager_loaded(hass.auth) INSTANCES.append(hass) orig_async_add_job = hass.async_add_job + orig_async_add_executor_job = hass.async_add_executor_job + orig_async_create_task = hass.async_create_task def async_add_job(target, *args): - """Add a magic mock.""" + """Add job.""" if isinstance(target, Mock): return mock_coro(target(*args)) return orig_async_add_job(target, *args) + def async_add_executor_job(target, *args): + """Add executor job.""" + if isinstance(target, Mock): + return mock_coro(target(*args)) + return orig_async_add_executor_job(target, *args) + + def async_create_task(coroutine): + """Create task.""" + if isinstance(coroutine, Mock): + return mock_coro() + return orig_async_create_task(coroutine) + hass.async_add_job = async_add_job + hass.async_add_executor_job = async_add_executor_job + hass.async_create_task = async_create_task hass.config.location_name = 'test home' hass.config.config_dir = get_test_config_dir() @@ -307,7 +323,12 @@ def mock_registry(hass, mock_entries=None): """Mock the Entity Registry.""" registry = entity_registry.EntityRegistry(hass) registry.entities = mock_entries or {} - hass.data[entity_registry.DATA_REGISTRY] = registry + + async def _get_reg(): + return registry + + hass.data[entity_registry.DATA_REGISTRY] = \ + hass.loop.create_task(_get_reg()) return registry @@ -321,7 +342,7 @@ def __init__(self, id=None, is_owner=False, is_active=True, 'is_owner': is_owner, 'is_active': is_active, 'name': name, - 'system_generated': system_generated + 'system_generated': system_generated, } if id is not None: kwargs['id'] = id @@ -339,7 +360,7 @@ def add_to_auth_manager(self, auth_mgr): async def register_auth_provider(hass, config): - """Helper to register an auth provider.""" + """Register an auth provider.""" provider = await auth_providers.auth_provider_from_config( hass, hass.auth._store, config) assert provider is not None, 'Invalid config specified' @@ -728,7 +749,7 @@ def available(self): return self._handle('available') def _handle(self, attr): - """Helper for the attributes.""" + """Return attribute value.""" if attr in self._values: return self._values[attr] return getattr(super(), attr) diff --git a/tests/components/alarm_control_panel/test_manual.py b/tests/components/alarm_control_panel/test_manual.py index e4b29d43e48110..29f630093d93b8 100644 --- a/tests/components/alarm_control_panel/test_manual.py +++ b/tests/components/alarm_control_panel/test_manual.py @@ -22,7 +22,7 @@ class TestAlarmControlPanelManual(unittest.TestCase): """Test the manual alarm module.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name @@ -32,9 +32,9 @@ def tearDown(self): # pylint: disable=invalid-name def test_setup_demo_platform(self): """Test setup.""" mock = MagicMock() - add_devices = mock.MagicMock() - demo.setup_platform(self.hass, {}, add_devices) - self.assertEqual(add_devices.call_count, 1) + add_entities = mock.MagicMock() + demo.setup_platform(self.hass, {}, add_entities) + self.assertEqual(add_entities.call_count, 1) def test_arm_home_no_pending(self): """Test arm home method.""" diff --git a/tests/components/alarm_control_panel/test_manual_mqtt.py b/tests/components/alarm_control_panel/test_manual_mqtt.py index 719352c5419097..5b601f089dd716 100644 --- a/tests/components/alarm_control_panel/test_manual_mqtt.py +++ b/tests/components/alarm_control_panel/test_manual_mqtt.py @@ -21,7 +21,7 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): """Test the manual_mqtt alarm module.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_publish = mock_mqtt_component(self.hass) diff --git a/tests/components/alarm_control_panel/test_mqtt.py b/tests/components/alarm_control_panel/test_mqtt.py index dee9b3959cacb9..ce152a3d7c9e98 100644 --- a/tests/components/alarm_control_panel/test_mqtt.py +++ b/tests/components/alarm_control_panel/test_mqtt.py @@ -21,7 +21,7 @@ class TestAlarmControlPanelMQTT(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_publish = mock_mqtt_component(self.hass) diff --git a/tests/components/alarm_control_panel/test_spc.py b/tests/components/alarm_control_panel/test_spc.py index 63b797814046a9..0a4ba0916eaeef 100644 --- a/tests/components/alarm_control_panel/test_spc.py +++ b/tests/components/alarm_control_panel/test_spc.py @@ -54,7 +54,7 @@ def add_entities(entities): yield from spc.async_setup_platform(hass=hass, config={}, - async_add_devices=add_entities, + async_add_entities=add_entities, discovery_info=areas) assert len(added_entities) == 2 diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py index cf8535653a96f4..88c69194407824 100644 --- a/tests/components/alexa/test_smart_home.py +++ b/tests/components/alexa/test_smart_home.py @@ -5,9 +5,10 @@ import pytest +from homeassistant.core import Context, callback from homeassistant.const import ( - TEMP_FAHRENHEIT, STATE_LOCKED, STATE_UNLOCKED, - STATE_UNKNOWN) + TEMP_CELSIUS, TEMP_FAHRENHEIT, STATE_LOCKED, + STATE_UNLOCKED, STATE_UNKNOWN) from homeassistant.setup import async_setup_component from homeassistant.components import alexa from homeassistant.components.alexa import smart_home @@ -18,6 +19,17 @@ DEFAULT_CONFIG = smart_home.Config(should_expose=lambda entity_id: True) +@pytest.fixture +def events(hass): + """Fixture that catches alexa events.""" + events = [] + hass.bus.async_listen( + smart_home.EVENT_ALEXA_SMART_HOME, + callback(lambda e: events.append(e)) + ) + yield events + + def get_new_request(namespace, name, endpoint=None): """Generate a new API message.""" raw_msg = { @@ -145,7 +157,7 @@ def assert_endpoint_capabilities(endpoint, *interfaces): @asyncio.coroutine -def test_switch(hass): +def test_switch(hass, events): """Test switch discovery.""" device = ('switch.test', 'on', {'friendly_name': "Test switch"}) appliance = yield from discovery_test(device, hass) @@ -695,6 +707,7 @@ def test_unknown_sensor(hass): async def test_thermostat(hass): """Test thermostat discovery.""" + hass.config.units.temperature_unit = TEMP_FAHRENHEIT device = ( 'climate.test_thermostat', 'cool', @@ -709,7 +722,6 @@ async def test_thermostat(hass): 'operation_list': ['heat', 'cool', 'auto', 'off'], 'min_temp': 50, 'max_temp': 90, - 'unit_of_measurement': TEMP_FAHRENHEIT, } ) appliance = await discovery_test(device, hass) @@ -826,6 +838,7 @@ async def test_thermostat(hass): payload={'thermostatMode': {'value': 'INVALID'}} ) assert msg['event']['payload']['type'] == 'UNSUPPORTED_THERMOSTAT_MODE' + hass.config.units.temperature_unit = TEMP_CELSIUS @asyncio.coroutine @@ -963,23 +976,26 @@ def assert_request_calls_service( response_type='Response', payload=None): """Assert an API request calls a hass service.""" + context = Context() request = get_new_request(namespace, name, endpoint) if payload: request['directive']['payload'] = payload domain, service_name = service.split('.') - call = async_mock_service(hass, domain, service_name) + calls = async_mock_service(hass, domain, service_name) msg = yield from smart_home.async_handle_message( - hass, DEFAULT_CONFIG, request) + hass, DEFAULT_CONFIG, request, context) yield from hass.async_block_till_done() - assert len(call) == 1 + assert len(calls) == 1 + call = calls[0] assert 'event' in msg - assert call[0].data['entity_id'] == endpoint.replace('#', '.') + assert call.data['entity_id'] == endpoint.replace('#', '.') assert msg['event']['header']['name'] == response_type + assert call.context == context - return call[0], msg + return call, msg @asyncio.coroutine @@ -1372,3 +1388,53 @@ def test_api_select_input(hass, domain, payload, source_list, idx): hass, payload={'input': payload}) assert call.data['source'] == source_list[idx] + + +async def test_logging_request(hass, events): + """Test that we log requests.""" + context = Context() + request = get_new_request('Alexa.Discovery', 'Discover') + await smart_home.async_handle_message( + hass, DEFAULT_CONFIG, request, context) + + # To trigger event listener + await hass.async_block_till_done() + + assert len(events) == 1 + event = events[0] + + assert event.data['request'] == { + 'namespace': 'Alexa.Discovery', + 'name': 'Discover', + } + assert event.data['response'] == { + 'namespace': 'Alexa.Discovery', + 'name': 'Discover.Response' + } + assert event.context == context + + +async def test_logging_request_with_entity(hass, events): + """Test that we log requests.""" + context = Context() + request = get_new_request('Alexa.PowerController', 'TurnOn', 'switch#xy') + await smart_home.async_handle_message( + hass, DEFAULT_CONFIG, request, context) + + # To trigger event listener + await hass.async_block_till_done() + + assert len(events) == 1 + event = events[0] + + assert event.data['request'] == { + 'namespace': 'Alexa.PowerController', + 'name': 'TurnOn', + 'entity_id': 'switch.xy' + } + # Entity doesn't exist + assert event.data['response'] == { + 'namespace': 'Alexa', + 'name': 'ErrorResponse' + } + assert event.context == context diff --git a/tests/components/auth/__init__.py b/tests/components/auth/__init__.py index ce94d1ecbfadaf..799d31f3db814e 100644 --- a/tests/components/auth/__init__.py +++ b/tests/components/auth/__init__.py @@ -15,11 +15,14 @@ }] }] +EMPTY_CONFIG = [] + async def async_setup_auth(hass, aiohttp_client, provider_configs=BASE_CONFIG, - setup_api=False): - """Helper to setup authentication and create a HTTP client.""" - hass.auth = await auth.auth_manager_from_config(hass, provider_configs) + module_configs=EMPTY_CONFIG, setup_api=False): + """Set up authentication and create an HTTP client.""" + hass.auth = await auth.auth_manager_from_config( + hass, provider_configs, module_configs) ensure_auth_manager_loaded(hass.auth) await async_setup_component(hass, 'auth', { 'http': { diff --git a/tests/components/auth/test_indieauth.py b/tests/components/auth/test_indieauth.py index 75e61af2e715f1..d30ead10cb238c 100644 --- a/tests/components/auth/test_indieauth.py +++ b/tests/components/auth/test_indieauth.py @@ -1,4 +1,5 @@ """Tests for the client validator.""" +import asyncio from unittest.mock import patch import pytest @@ -6,6 +7,18 @@ from homeassistant.components.auth import indieauth from tests.common import mock_coro +from tests.test_util.aiohttp import AiohttpClientMocker + + +@pytest.fixture +def mock_session(): + """Mock aiohttp.ClientSession.""" + mocker = AiohttpClientMocker() + + with patch('aiohttp.ClientSession', + side_effect=lambda *args, **kwargs: + mocker.create_session(asyncio.get_event_loop())): + yield mocker def test_client_id_scheme(): @@ -120,9 +133,9 @@ async def test_verify_redirect_uri(): ) -async def test_find_link_tag(hass, aioclient_mock): +async def test_find_link_tag(hass, mock_session): """Test finding link tag.""" - aioclient_mock.get("http://127.0.0.1:8000", text=""" + mock_session.get("http://127.0.0.1:8000", text=""" @@ -142,11 +155,15 @@ async def test_find_link_tag(hass, aioclient_mock): ] -async def test_find_link_tag_max_size(hass, aioclient_mock): +async def test_find_link_tag_max_size(hass, mock_session): """Test finding link tag.""" - text = ("0" * 1024 * 10) + '' - aioclient_mock.get("http://127.0.0.1:8000", text=text) + text = ''.join([ + '', + ("0" * 1024 * 10), + '', + ]) + mock_session.get("http://127.0.0.1:8000", text=text) redirect_uris = await indieauth.fetch_redirect_uris( hass, "http://127.0.0.1:8000") - assert redirect_uris == [] + assert redirect_uris == ["http://127.0.0.1:8000/wine"] diff --git a/tests/components/auth/test_init.py b/tests/components/auth/test_init.py index f1a1bb5bd3cc97..7b9dda6acb39e4 100644 --- a/tests/components/auth/test_init.py +++ b/tests/components/auth/test_init.py @@ -3,13 +3,14 @@ from unittest.mock import patch from homeassistant.auth.models import Credentials +from homeassistant.components.auth import RESULT_TYPE_USER from homeassistant.setup import async_setup_component from homeassistant.util.dt import utcnow from homeassistant.components import auth from . import async_setup_auth -from tests.common import CLIENT_ID, CLIENT_REDIRECT_URI +from tests.common import CLIENT_ID, CLIENT_REDIRECT_URI, MockUser async def test_login_new_user_and_trying_refresh_token(hass, aiohttp_client): @@ -74,26 +75,26 @@ async def test_login_new_user_and_trying_refresh_token(hass, aiohttp_client): assert resp.status == 200 -def test_credential_store_expiration(): - """Test that the credential store will not return expired tokens.""" - store, retrieve = auth._create_cred_store() +def test_auth_code_store_expiration(): + """Test that the auth code store will not return expired tokens.""" + store, retrieve = auth._create_auth_code_store() client_id = 'bla' - credentials = 'creds' + user = MockUser(id='mock_user') now = utcnow() with patch('homeassistant.util.dt.utcnow', return_value=now): - code = store(client_id, credentials) + code = store(client_id, user) with patch('homeassistant.util.dt.utcnow', return_value=now + timedelta(minutes=10)): - assert retrieve(client_id, code) is None + assert retrieve(client_id, RESULT_TYPE_USER, code) is None with patch('homeassistant.util.dt.utcnow', return_value=now): - code = store(client_id, credentials) + code = store(client_id, user) with patch('homeassistant.util.dt.utcnow', return_value=now + timedelta(minutes=9, seconds=59)): - assert retrieve(client_id, code) == credentials + assert retrieve(client_id, RESULT_TYPE_USER, code) == user async def test_ws_current_user(hass, hass_ws_client, hass_access_token): @@ -223,3 +224,46 @@ async def test_refresh_token_different_client_id(hass, aiohttp_client): await hass.auth.async_validate_access_token(tokens['access_token']) is not None ) + + +async def test_revoking_refresh_token(hass, aiohttp_client): + """Test that we can revoke refresh tokens.""" + client = await async_setup_auth(hass, aiohttp_client) + user = await hass.auth.async_create_user('Test User') + refresh_token = await hass.auth.async_create_refresh_token(user, CLIENT_ID) + + # Test that we can create an access token + resp = await client.post('/auth/token', data={ + 'client_id': CLIENT_ID, + 'grant_type': 'refresh_token', + 'refresh_token': refresh_token.token, + }) + + assert resp.status == 200 + tokens = await resp.json() + assert ( + await hass.auth.async_validate_access_token(tokens['access_token']) + is not None + ) + + # Revoke refresh token + resp = await client.post('/auth/token', data={ + 'token': refresh_token.token, + 'action': 'revoke', + }) + assert resp.status == 200 + + # Old access token should be no longer valid + assert ( + await hass.auth.async_validate_access_token(tokens['access_token']) + is None + ) + + # Test that we no longer can create an access token + resp = await client.post('/auth/token', data={ + 'client_id': CLIENT_ID, + 'grant_type': 'refresh_token', + 'refresh_token': refresh_token.token, + }) + + assert resp.status == 400 diff --git a/tests/components/auth/test_init_link_user.py b/tests/components/auth/test_init_link_user.py index e209e0ee85696b..6c9fdf3fbc290d 100644 --- a/tests/components/auth/test_init_link_user.py +++ b/tests/components/auth/test_init_link_user.py @@ -5,7 +5,7 @@ async def async_get_code(hass, aiohttp_client): - """Helper for link user tests that returns authorization code.""" + """Return authorization code for link user tests.""" config = [{ 'name': 'Example', 'type': 'insecure_example', @@ -34,6 +34,7 @@ async def async_get_code(hass, aiohttp_client): 'client_id': CLIENT_ID, 'handler': ['insecure_example', '2nd auth'], 'redirect_uri': CLIENT_REDIRECT_URI, + 'type': 'link_user', }) assert resp.status == 200 step = await resp.json() diff --git a/tests/components/auth/test_mfa_setup_flow.py b/tests/components/auth/test_mfa_setup_flow.py new file mode 100644 index 00000000000000..93b5cdf7bb9196 --- /dev/null +++ b/tests/components/auth/test_mfa_setup_flow.py @@ -0,0 +1,99 @@ +"""Tests for the mfa setup flow.""" +from homeassistant import data_entry_flow +from homeassistant.auth import auth_manager_from_config +from homeassistant.components.auth import mfa_setup_flow +from homeassistant.setup import async_setup_component + +from tests.common import MockUser, CLIENT_ID, ensure_auth_manager_loaded + + +async def test_ws_setup_depose_mfa(hass, hass_ws_client): + """Test set up mfa module for current user.""" + hass.auth = await auth_manager_from_config( + hass, provider_configs=[{ + 'type': 'insecure_example', + 'users': [{ + 'username': 'test-user', + 'password': 'test-pass', + 'name': 'Test Name', + }] + }], module_configs=[{ + 'type': 'insecure_example', + 'id': 'example_module', + 'data': [{'user_id': 'mock-user', 'pin': '123456'}] + }]) + ensure_auth_manager_loaded(hass.auth) + await async_setup_component(hass, 'auth', {'http': {}}) + + user = MockUser(id='mock-user').add_to_hass(hass) + cred = await hass.auth.auth_providers[0].async_get_or_create_credentials( + {'username': 'test-user'}) + await hass.auth.async_link_user(user, cred) + refresh_token = await hass.auth.async_create_refresh_token(user, CLIENT_ID) + access_token = hass.auth.async_create_access_token(refresh_token) + + client = await hass_ws_client(hass, access_token) + + await client.send_json({ + 'id': 10, + 'type': mfa_setup_flow.WS_TYPE_SETUP_MFA, + }) + + result = await client.receive_json() + assert result['id'] == 10 + assert result['success'] is False + assert result['error']['code'] == 'no_module' + + await client.send_json({ + 'id': 11, + 'type': mfa_setup_flow.WS_TYPE_SETUP_MFA, + 'mfa_module_id': 'example_module', + }) + + result = await client.receive_json() + assert result['id'] == 11 + assert result['success'] + + flow = result['result'] + assert flow['type'] == data_entry_flow.RESULT_TYPE_FORM + assert flow['handler'] == 'example_module' + assert flow['step_id'] == 'init' + assert flow['data_schema'][0] == {'type': 'string', 'name': 'pin'} + + await client.send_json({ + 'id': 12, + 'type': mfa_setup_flow.WS_TYPE_SETUP_MFA, + 'flow_id': flow['flow_id'], + 'user_input': {'pin': '654321'}, + }) + + result = await client.receive_json() + assert result['id'] == 12 + assert result['success'] + + flow = result['result'] + assert flow['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert flow['handler'] == 'example_module' + assert flow['data']['result'] is None + + await client.send_json({ + 'id': 13, + 'type': mfa_setup_flow.WS_TYPE_DEPOSE_MFA, + 'mfa_module_id': 'invalid_id', + }) + + result = await client.receive_json() + assert result['id'] == 13 + assert result['success'] is False + assert result['error']['code'] == 'disable_failed' + + await client.send_json({ + 'id': 14, + 'type': mfa_setup_flow.WS_TYPE_DEPOSE_MFA, + 'mfa_module_id': 'example_module', + }) + + result = await client.receive_json() + assert result['id'] == 14 + assert result['success'] + assert result['result'] == 'done' diff --git a/tests/components/automation/test_event.py b/tests/components/automation/test_event.py index aea6e517e3853e..c52ea7b9d292a0 100644 --- a/tests/components/automation/test_event.py +++ b/tests/components/automation/test_event.py @@ -13,14 +13,14 @@ class TestAutomationEvent(unittest.TestCase): """Test the event automation.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'group') self.calls = [] @callback def record_call(service): - """Helper for recording the call.""" + """Record the call.""" self.calls.append(service) self.hass.services.register('test', 'automation', record_call) diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index b1990fb80aac50..c3bd6c224af295 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -22,7 +22,7 @@ class TestAutomation(unittest.TestCase): """Test the event automation.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.calls = mock_service(self.hass, 'test', 'automation') diff --git a/tests/components/automation/test_litejet.py b/tests/components/automation/test_litejet.py index e644541549092b..ca6f7796cfcb5d 100644 --- a/tests/components/automation/test_litejet.py +++ b/tests/components/automation/test_litejet.py @@ -23,7 +23,7 @@ class TestLiteJetTrigger(unittest.TestCase): @mock.patch('pylitejet.LiteJet') def setup_method(self, method, mock_pylitejet): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.start() diff --git a/tests/components/automation/test_mqtt.py b/tests/components/automation/test_mqtt.py index d1eb0d63ee8f30..8ec5351af94d54 100644 --- a/tests/components/automation/test_mqtt.py +++ b/tests/components/automation/test_mqtt.py @@ -14,7 +14,7 @@ class TestAutomationMQTT(unittest.TestCase): """Test the event automation.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'group') mock_mqtt_component(self.hass) @@ -22,7 +22,7 @@ def setUp(self): @callback def record_call(service): - """Helper to record calls.""" + """Record calls.""" self.calls.append(service) self.hass.services.register('test', 'automation', record_call) diff --git a/tests/components/automation/test_numeric_state.py b/tests/components/automation/test_numeric_state.py index de453675a577f8..49565c37222688 100644 --- a/tests/components/automation/test_numeric_state.py +++ b/tests/components/automation/test_numeric_state.py @@ -18,14 +18,14 @@ class TestAutomationNumericState(unittest.TestCase): """Test the event automation.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'group') self.calls = [] @callback def record_call(service): - """Helper to record calls.""" + """Record calls.""" self.calls.append(service) self.hass.services.register('test', 'automation', record_call) diff --git a/tests/components/automation/test_state.py b/tests/components/automation/test_state.py index 22c84b88935117..6b1a8914aadae1 100644 --- a/tests/components/automation/test_state.py +++ b/tests/components/automation/test_state.py @@ -19,7 +19,7 @@ class TestAutomationState(unittest.TestCase): """Test the event automation.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'group') self.hass.states.set('test.entity', 'hello') diff --git a/tests/components/automation/test_sun.py b/tests/components/automation/test_sun.py index 355d088719f180..4556b7cbe45723 100644 --- a/tests/components/automation/test_sun.py +++ b/tests/components/automation/test_sun.py @@ -19,7 +19,7 @@ class TestAutomationSun(unittest.TestCase): """Test the sun automation.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'group') setup_component(self.hass, sun.DOMAIN, { diff --git a/tests/components/automation/test_template.py b/tests/components/automation/test_template.py index 937fa16988a248..ef064a2c1d8a1c 100644 --- a/tests/components/automation/test_template.py +++ b/tests/components/automation/test_template.py @@ -14,7 +14,7 @@ class TestAutomationTemplate(unittest.TestCase): """Test the event automation.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'group') self.hass.states.set('test.entity', 'hello') @@ -22,7 +22,7 @@ def setUp(self): @callback def record_call(service): - """Helper to record calls.""" + """Record calls.""" self.calls.append(service) self.hass.services.register('test', 'automation', record_call) diff --git a/tests/components/automation/test_time.py b/tests/components/automation/test_time.py index 5d5d5ea29ec016..5f928cf92a0260 100644 --- a/tests/components/automation/test_time.py +++ b/tests/components/automation/test_time.py @@ -18,14 +18,14 @@ class TestAutomationTime(unittest.TestCase): """Test the event automation.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'group') self.calls = [] @callback def record_call(service): - """Helper to record calls.""" + """Record calls.""" self.calls.append(service) self.hass.services.register('test', 'automation', record_call) diff --git a/tests/components/automation/test_zone.py b/tests/components/automation/test_zone.py index 3dc4b75b8ae052..baa3bdc1d28f6d 100644 --- a/tests/components/automation/test_zone.py +++ b/tests/components/automation/test_zone.py @@ -13,7 +13,7 @@ class TestAutomationZone(unittest.TestCase): """Test the event automation.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'group') assert setup_component(self.hass, zone.DOMAIN, { @@ -29,7 +29,7 @@ def setUp(self): @callback def record_call(service): - """Helper to record calls.""" + """Record calls.""" self.calls.append(service) self.hass.services.register('test', 'automation', record_call) diff --git a/tests/components/binary_sensor/test_command_line.py b/tests/components/binary_sensor/test_command_line.py index 07389c7c8a9cfb..d469fc65e8ea32 100644 --- a/tests/components/binary_sensor/test_command_line.py +++ b/tests/components/binary_sensor/test_command_line.py @@ -12,7 +12,7 @@ class TestCommandSensorBinarySensor(unittest.TestCase): """Test the Command line Binary sensor.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/binary_sensor/test_ffmpeg.py b/tests/components/binary_sensor/test_ffmpeg.py index da9350008d865c..4e6629c0afdfe0 100644 --- a/tests/components/binary_sensor/test_ffmpeg.py +++ b/tests/components/binary_sensor/test_ffmpeg.py @@ -11,7 +11,7 @@ class TestFFmpegNoiseSetup: """Test class for ffmpeg.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.config = { @@ -29,7 +29,7 @@ def teardown_method(self): self.hass.stop() def test_setup_component(self): - """Setup ffmpeg component.""" + """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) @@ -38,7 +38,7 @@ def test_setup_component(self): @patch('haffmpeg.SensorNoise.open_sensor', return_value=mock_coro()) def test_setup_component_start(self, mock_start): - """Setup ffmpeg component.""" + """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) @@ -53,7 +53,7 @@ def test_setup_component_start(self, mock_start): @patch('haffmpeg.SensorNoise') def test_setup_component_start_callback(self, mock_ffmpeg): - """Setup ffmpeg component.""" + """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) @@ -76,7 +76,7 @@ class TestFFmpegMotionSetup: """Test class for ffmpeg.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.config = { @@ -94,7 +94,7 @@ def teardown_method(self): self.hass.stop() def test_setup_component(self): - """Setup ffmpeg component.""" + """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) @@ -103,7 +103,7 @@ def test_setup_component(self): @patch('haffmpeg.SensorMotion.open_sensor', return_value=mock_coro()) def test_setup_component_start(self, mock_start): - """Setup ffmpeg component.""" + """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) @@ -118,7 +118,7 @@ def test_setup_component_start(self, mock_start): @patch('haffmpeg.SensorMotion') def test_setup_component_start_callback(self, mock_ffmpeg): - """Setup ffmpeg component.""" + """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) diff --git a/tests/components/binary_sensor/test_mqtt.py b/tests/components/binary_sensor/test_mqtt.py index 71eba2df95039f..57050c2cbf53b3 100644 --- a/tests/components/binary_sensor/test_mqtt.py +++ b/tests/components/binary_sensor/test_mqtt.py @@ -16,7 +16,7 @@ class TestSensorMQTT(unittest.TestCase): """Test the MQTT sensor.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_mqtt_component(self.hass) diff --git a/tests/components/binary_sensor/test_nx584.py b/tests/components/binary_sensor/test_nx584.py index 4d1d85d30fb9d8..252996201bbfc7 100644 --- a/tests/components/binary_sensor/test_nx584.py +++ b/tests/components/binary_sensor/test_nx584.py @@ -21,7 +21,7 @@ class TestNX584SensorSetup(unittest.TestCase): """Test the NX584 sensor platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self._mock_client = mock.patch.object(nx584_client, 'Client') self._mock_client.start() @@ -45,17 +45,17 @@ def tearDown(self): @mock.patch('homeassistant.components.binary_sensor.nx584.NX584ZoneSensor') def test_setup_defaults(self, mock_nx, mock_watcher): """Test the setup with no configuration.""" - add_devices = mock.MagicMock() + add_entities = mock.MagicMock() config = { 'host': nx584.DEFAULT_HOST, 'port': nx584.DEFAULT_PORT, 'exclude_zones': [], 'zone_types': {}, } - self.assertTrue(nx584.setup_platform(self.hass, config, add_devices)) + self.assertTrue(nx584.setup_platform(self.hass, config, add_entities)) mock_nx.assert_has_calls( [mock.call(zone, 'opening') for zone in self.fake_zones]) - self.assertTrue(add_devices.called) + self.assertTrue(add_entities.called) self.assertEqual(nx584_client.Client.call_count, 1) self.assertEqual( nx584_client.Client.call_args, mock.call('http://localhost:5007') @@ -71,13 +71,13 @@ def test_setup_full_config(self, mock_nx, mock_watcher): 'exclude_zones': [2], 'zone_types': {3: 'motion'}, } - add_devices = mock.MagicMock() - self.assertTrue(nx584.setup_platform(self.hass, config, add_devices)) + add_entities = mock.MagicMock() + self.assertTrue(nx584.setup_platform(self.hass, config, add_entities)) mock_nx.assert_has_calls([ mock.call(self.fake_zones[0], 'opening'), mock.call(self.fake_zones[2], 'motion'), ]) - self.assertTrue(add_devices.called) + self.assertTrue(add_entities.called) self.assertEqual(nx584_client.Client.call_count, 1) self.assertEqual( nx584_client.Client.call_args, mock.call('http://foo:123') @@ -120,9 +120,9 @@ def test_setup_version_too_old(self): def test_setup_no_zones(self): """Test the setup with no zones.""" nx584_client.Client.return_value.list_zones.return_value = [] - add_devices = mock.MagicMock() - self.assertTrue(nx584.setup_platform(self.hass, {}, add_devices)) - self.assertFalse(add_devices.called) + add_entities = mock.MagicMock() + self.assertTrue(nx584.setup_platform(self.hass, {}, add_entities)) + self.assertFalse(add_entities.called) class TestNX584ZoneSensor(unittest.TestCase): diff --git a/tests/components/binary_sensor/test_rest.py b/tests/components/binary_sensor/test_rest.py index d0670bf5154a48..d1c266244520c4 100644 --- a/tests/components/binary_sensor/test_rest.py +++ b/tests/components/binary_sensor/test_rest.py @@ -19,7 +19,7 @@ class TestRestBinarySensorSetup(unittest.TestCase): """Tests for setting up the REST binary sensor platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): @@ -121,7 +121,7 @@ class TestRestBinarySensor(unittest.TestCase): """Tests for REST binary sensor platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.rest = Mock('RestData') self.rest.update = Mock('RestData.update', diff --git a/tests/components/binary_sensor/test_ring.py b/tests/components/binary_sensor/test_ring.py index e557050ae48782..b7564dff464335 100644 --- a/tests/components/binary_sensor/test_ring.py +++ b/tests/components/binary_sensor/test_ring.py @@ -16,7 +16,7 @@ class TestRingBinarySensorSetup(unittest.TestCase): DEVICES = [] - def add_devices(self, devices, action): + def add_entities(self, devices, action): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -58,7 +58,7 @@ def test_binary_sensor(self, mock): base_ring.setup(self.hass, VALID_CONFIG) ring.setup_platform(self.hass, self.config, - self.add_devices, + self.add_entities, None) for device in self.DEVICES: diff --git a/tests/components/binary_sensor/test_sleepiq.py b/tests/components/binary_sensor/test_sleepiq.py index 40e0aa35e03b59..9cd5dfa7f2e698 100644 --- a/tests/components/binary_sensor/test_sleepiq.py +++ b/tests/components/binary_sensor/test_sleepiq.py @@ -16,7 +16,7 @@ class TestSleepIQBinarySensorSetup(unittest.TestCase): DEVICES = [] - def add_devices(self, devices): + def add_entities(self, devices): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -45,7 +45,7 @@ def test_setup(self, mock): sleepiq.setup_platform(self.hass, self.config, - self.add_devices, + self.add_entities, MagicMock()) self.assertEqual(2, len(self.DEVICES)) diff --git a/tests/components/binary_sensor/test_spc.py b/tests/components/binary_sensor/test_spc.py index d22998745276d6..0a91b59e14dcf8 100644 --- a/tests/components/binary_sensor/test_spc.py +++ b/tests/components/binary_sensor/test_spc.py @@ -54,7 +54,7 @@ def add_entities(entities): yield from spc.async_setup_platform(hass=hass, config={}, - async_add_devices=add_entities, + async_add_entities=add_entities, discovery_info=zones) assert len(added_entities) == 3 diff --git a/tests/components/binary_sensor/test_tcp.py b/tests/components/binary_sensor/test_tcp.py index 8602de84d2512a..cb21d7d2c7146a 100644 --- a/tests/components/binary_sensor/test_tcp.py +++ b/tests/components/binary_sensor/test_tcp.py @@ -13,7 +13,7 @@ class TestTCPBinarySensor(unittest.TestCase): """Test the TCP Binary Sensor.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): @@ -38,13 +38,13 @@ def test_setup_platform_invalid_config(self): @patch('homeassistant.components.sensor.tcp.TcpSensor.update') def test_setup_platform_devices(self, mock_update): - """Check the supplied config and call add_devices with sensor.""" - add_devices = Mock() - ret = bin_tcp.setup_platform(None, test_tcp.TEST_CONFIG, add_devices) + """Check the supplied config and call add_entities with sensor.""" + add_entities = Mock() + ret = bin_tcp.setup_platform(None, test_tcp.TEST_CONFIG, add_entities) assert ret is None - assert add_devices.called + assert add_entities.called assert isinstance( - add_devices.call_args[0][0][0], bin_tcp.TcpBinarySensor) + add_entities.call_args[0][0][0], bin_tcp.TcpBinarySensor) @patch('homeassistant.components.sensor.tcp.TcpSensor.update') def test_is_on_true(self, mock_update): diff --git a/tests/components/binary_sensor/test_template.py b/tests/components/binary_sensor/test_template.py index 62623a04f3c276..eba3a368f5600a 100644 --- a/tests/components/binary_sensor/test_template.py +++ b/tests/components/binary_sensor/test_template.py @@ -23,7 +23,7 @@ class TestBinarySensorTemplate(unittest.TestCase): # pylint: disable=invalid-name def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): diff --git a/tests/components/binary_sensor/test_vultr.py b/tests/components/binary_sensor/test_vultr.py index a13944aef9fd75..f356149dddee86 100644 --- a/tests/components/binary_sensor/test_vultr.py +++ b/tests/components/binary_sensor/test_vultr.py @@ -26,7 +26,7 @@ class TestVultrBinarySensorSetup(unittest.TestCase): DEVICES = [] - def add_devices(self, devices, action): + def add_entities(self, devices, action): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -71,7 +71,7 @@ def test_binary_sensor(self, mock): for config in self.configs: vultr.setup_platform(self.hass, config, - self.add_devices, + self.add_entities, None) self.assertEqual(len(self.DEVICES), 3) @@ -147,7 +147,7 @@ def test_invalid_sensors(self, mock): no_subs_setup = vultr.setup_platform(self.hass, bad_conf, - self.add_devices, + self.add_entities, None) self.assertFalse(no_subs_setup) @@ -159,7 +159,7 @@ def test_invalid_sensors(self, mock): wrong_subs_setup = vultr.setup_platform(self.hass, bad_conf, - self.add_devices, + self.add_entities, None) self.assertFalse(wrong_subs_setup) diff --git a/tests/components/binary_sensor/test_workday.py b/tests/components/binary_sensor/test_workday.py index 893745ce3ded80..5aa5a5dad5cf0a 100644 --- a/tests/components/binary_sensor/test_workday.py +++ b/tests/components/binary_sensor/test_workday.py @@ -16,7 +16,7 @@ class TestWorkdaySetup: """Test class for workday sensor.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # Set valid default config for test @@ -97,7 +97,7 @@ def teardown_method(self): self.hass.stop() def test_setup_component_province(self): - """Setup workday component.""" + """Set up workday component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config_province) @@ -145,7 +145,7 @@ def test_public_holiday_province(self, mock_date): assert entity.state == 'off' def test_setup_component_noprovince(self): - """Setup workday component.""" + """Set up workday component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config_noprovince) @@ -191,7 +191,7 @@ def test_public_holiday_nostate(self, mock_date): assert entity.state == 'on' def test_setup_component_invalidprovince(self): - """Setup workday component.""" + """Set up workday component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config_invalidprovince) diff --git a/tests/components/calendar/test_google.py b/tests/components/calendar/test_google.py index d176cd758b43a9..e07f7a6306af81 100644 --- a/tests/components/calendar/test_google.py +++ b/tests/components/calendar/test_google.py @@ -25,7 +25,7 @@ class TestComponentsGoogleCalendar(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.http = Mock() diff --git a/tests/components/camera/test_demo.py b/tests/components/camera/test_demo.py index b901b723c0be0b..63c70ddc6ca583 100644 --- a/tests/components/camera/test_demo.py +++ b/tests/components/camera/test_demo.py @@ -1,5 +1,5 @@ """The tests for local file camera component.""" -from unittest.mock import mock_open, patch, PropertyMock +from unittest.mock import mock_open, patch import pytest @@ -67,19 +67,6 @@ async def test_turn_off_invalid_camera(hass, demo_camera): assert demo_camera.state == STATE_STREAMING -async def test_turn_off_unsupport_camera(hass, demo_camera): - """Turn off unsupported camera should quietly fail.""" - assert demo_camera.state == STATE_STREAMING - with patch('homeassistant.components.camera.demo.DemoCamera' - '.supported_features', new_callable=PropertyMock) as m: - m.return_value = 0 - - await camera.async_turn_off(hass, demo_camera.entity_id) - await hass.async_block_till_done() - - assert demo_camera.state == STATE_STREAMING - - async def test_motion_detection(hass): """Test motion detection services.""" # Setup platform diff --git a/tests/components/camera/test_generic.py b/tests/components/camera/test_generic.py index 01edca1e996a27..b981fced32020a 100644 --- a/tests/components/camera/test_generic.py +++ b/tests/components/camera/test_generic.py @@ -1,5 +1,6 @@ """The tests for generic camera component.""" import asyncio + from unittest import mock from homeassistant.setup import async_setup_component @@ -32,6 +33,50 @@ def test_fetching_url(aioclient_mock, hass, aiohttp_client): assert aioclient_mock.call_count == 2 +@asyncio.coroutine +def test_fetching_without_verify_ssl(aioclient_mock, hass, aiohttp_client): + """Test that it fetches the given url when ssl verify is off.""" + aioclient_mock.get('https://example.com', text='hello world') + + yield from async_setup_component(hass, 'camera', { + 'camera': { + 'name': 'config_test', + 'platform': 'generic', + 'still_image_url': 'https://example.com', + 'username': 'user', + 'password': 'pass', + 'verify_ssl': 'false', + }}) + + client = yield from aiohttp_client(hass.http.app) + + resp = yield from client.get('/api/camera_proxy/camera.config_test') + + assert resp.status == 200 + + +@asyncio.coroutine +def test_fetching_url_with_verify_ssl(aioclient_mock, hass, aiohttp_client): + """Test that it fetches the given url when ssl verify is explicitly on.""" + aioclient_mock.get('https://example.com', text='hello world') + + yield from async_setup_component(hass, 'camera', { + 'camera': { + 'name': 'config_test', + 'platform': 'generic', + 'still_image_url': 'https://example.com', + 'username': 'user', + 'password': 'pass', + 'verify_ssl': 'true', + }}) + + client = yield from aiohttp_client(hass.http.app) + + resp = yield from client.get('/api/camera_proxy/camera.config_test') + + assert resp.status == 200 + + @asyncio.coroutine def test_limit_refetch(aioclient_mock, hass, aiohttp_client): """Test that it fetches the given url.""" diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index cf902ca177901a..053fa6d29dc8a6 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -34,7 +34,7 @@ class TestSetupCamera: """Test class for setup camera.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self): @@ -42,7 +42,7 @@ def teardown_method(self): self.hass.stop() def test_setup_component(self): - """Setup demo platform on camera component.""" + """Set up demo platform on camera component.""" config = { camera.DOMAIN: { 'platform': 'demo' @@ -57,7 +57,7 @@ class TestGetImage: """Test class for camera.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() setup_component( diff --git a/tests/components/camera/test_push.py b/tests/components/camera/test_push.py index 78053e540f5cd8..f9a3c62aa4aa6c 100644 --- a/tests/components/camera/test_push.py +++ b/tests/components/camera/test_push.py @@ -30,7 +30,7 @@ async def test_bad_posting(aioclient_mock, hass, aiohttp_client): assert resp.status == 400 -async def test_posting_url(aioclient_mock, hass, aiohttp_client): +async def test_posting_url(hass, aiohttp_client): """Test that posting to api endpoint works.""" await async_setup_component(hass, 'camera', { 'camera': { @@ -38,7 +38,7 @@ async def test_posting_url(aioclient_mock, hass, aiohttp_client): 'name': 'config_test', }}) - client = await async_setup_auth(hass, aiohttp_client) + client = await aiohttp_client(hass.http.app) files = {'image': io.BytesIO(b'fake')} # initial state diff --git a/tests/components/camera/test_uvc.py b/tests/components/camera/test_uvc.py index 18292d32a024fc..328ba5096eaca9 100644 --- a/tests/components/camera/test_uvc.py +++ b/tests/components/camera/test_uvc.py @@ -17,7 +17,7 @@ class TestUVCSetup(unittest.TestCase): """Test the UVC camera platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): @@ -134,7 +134,7 @@ def test_setup_incomplete_config(self, mock_uvc): @mock.patch.object(uvc, 'UnifiVideoCamera') @mock.patch('uvcclient.nvr.UVCRemote') def setup_nvr_errors_during_indexing(self, error, mock_remote, mock_uvc): - """Setup test for NVR errors during indexing.""" + """Set up test for NVR errors during indexing.""" config = { 'platform': 'uvc', 'nvr': 'foo', @@ -163,7 +163,7 @@ def test_setup_nvr_error_during_indexing_connectionerror(self): @mock.patch('uvcclient.nvr.UVCRemote.__init__') def setup_nvr_errors_during_initialization(self, error, mock_remote, mock_uvc): - """Setup test for NVR errors during initialization.""" + """Set up test for NVR errors during initialization.""" config = { 'platform': 'uvc', 'nvr': 'foo', @@ -195,7 +195,7 @@ class TestUVC(unittest.TestCase): """Test class for UVC.""" def setup_method(self, method): - """Setup the mock camera.""" + """Set up the mock camera.""" self.nvr = mock.MagicMock() self.uuid = 'uuid' self.name = 'name' diff --git a/tests/components/climate/test_demo.py b/tests/components/climate/test_demo.py index b2633a75583492..0cd6d288536be1 100644 --- a/tests/components/climate/test_demo.py +++ b/tests/components/climate/test_demo.py @@ -19,7 +19,7 @@ class TestDemoClimate(unittest.TestCase): """Test the demo climate hvac.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.units = METRIC_SYSTEM self.assertTrue(setup_component(self.hass, climate.DOMAIN, { diff --git a/tests/components/climate/test_generic_thermostat.py b/tests/components/climate/test_generic_thermostat.py index 7bc0b0a18e7ac8..ac587db13fa3e8 100644 --- a/tests/components/climate/test_generic_thermostat.py +++ b/tests/components/climate/test_generic_thermostat.py @@ -9,7 +9,6 @@ from homeassistant.core import callback, CoreState, State from homeassistant.setup import setup_component, async_setup_component from homeassistant.const import ( - ATTR_UNIT_OF_MEASUREMENT, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON, @@ -42,7 +41,7 @@ class TestSetupClimateGenericThermostat(unittest.TestCase): """Test the Generic thermostat with custom config.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name @@ -79,7 +78,7 @@ class TestGenericThermostatHeaterSwitching(unittest.TestCase): """ def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.units = METRIC_SYSTEM self.assertTrue(run_coroutine_threadsafe( @@ -141,18 +140,16 @@ def test_heater_switch(self): self.assertEqual(STATE_ON, self.hass.states.get(heater_switch).state) - def _setup_sensor(self, temp, unit=TEMP_CELSIUS): - """Setup the test sensor.""" - self.hass.states.set(ENT_SENSOR, temp, { - ATTR_UNIT_OF_MEASUREMENT: unit - }) + def _setup_sensor(self, temp): + """Set up the test sensor.""" + self.hass.states.set(ENT_SENSOR, temp) class TestClimateGenericThermostat(unittest.TestCase): """Test the Generic thermostat.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.units = METRIC_SYSTEM assert setup_component(self.hass, climate.DOMAIN, {'climate': { @@ -222,30 +219,15 @@ def test_set_away_mode_and_restore_prev_temp(self): state = self.hass.states.get(ENTITY) self.assertEqual(23, state.attributes.get('temperature')) - def test_sensor_bad_unit(self): - """Test sensor that have bad unit.""" - state = self.hass.states.get(ENTITY) - temp = state.attributes.get('current_temperature') - unit = state.attributes.get('unit_of_measurement') - - self._setup_sensor(22.0, unit='bad_unit') - self.hass.block_till_done() - - state = self.hass.states.get(ENTITY) - self.assertEqual(unit, state.attributes.get('unit_of_measurement')) - self.assertEqual(temp, state.attributes.get('current_temperature')) - def test_sensor_bad_value(self): """Test sensor that have None as state.""" state = self.hass.states.get(ENTITY) temp = state.attributes.get('current_temperature') - unit = state.attributes.get('unit_of_measurement') self._setup_sensor(None) self.hass.block_till_done() state = self.hass.states.get(ENTITY) - self.assertEqual(unit, state.attributes.get('unit_of_measurement')) self.assertEqual(temp, state.attributes.get('current_temperature')) def test_set_target_temp_heater_on(self): @@ -367,14 +349,12 @@ def test_operating_mode_heat(self): self.assertEqual(SERVICE_TURN_ON, call.service) self.assertEqual(ENT_SWITCH, call.data['entity_id']) - def _setup_sensor(self, temp, unit=TEMP_CELSIUS): - """Setup the test sensor.""" - self.hass.states.set(ENT_SENSOR, temp, { - ATTR_UNIT_OF_MEASUREMENT: unit - }) + def _setup_sensor(self, temp): + """Set up the test sensor.""" + self.hass.states.set(ENT_SENSOR, temp) def _setup_switch(self, is_on): - """Setup the test switch.""" + """Set up the test switch.""" self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.calls = [] @@ -391,7 +371,7 @@ class TestClimateGenericThermostatACMode(unittest.TestCase): """Test the Generic thermostat.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.temperature_unit = TEMP_CELSIUS assert setup_component(self.hass, climate.DOMAIN, {'climate': { @@ -532,14 +512,12 @@ def test_no_state_change_when_operation_mode_off(self): self.hass.block_till_done() self.assertEqual(0, len(self.calls)) - def _setup_sensor(self, temp, unit=TEMP_CELSIUS): - """Setup the test sensor.""" - self.hass.states.set(ENT_SENSOR, temp, { - ATTR_UNIT_OF_MEASUREMENT: unit - }) + def _setup_sensor(self, temp): + """Set up the test sensor.""" + self.hass.states.set(ENT_SENSOR, temp) def _setup_switch(self, is_on): - """Setup the test switch.""" + """Set up the test switch.""" self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.calls = [] @@ -556,7 +534,7 @@ class TestClimateGenericThermostatACModeMinCycle(unittest.TestCase): """Test the Generic Thermostat.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.temperature_unit = TEMP_CELSIUS assert setup_component(self.hass, climate.DOMAIN, {'climate': { @@ -626,14 +604,12 @@ def test_temp_change_ac_trigger_off_long_enough(self): self.assertEqual(SERVICE_TURN_OFF, call.service) self.assertEqual(ENT_SWITCH, call.data['entity_id']) - def _setup_sensor(self, temp, unit=TEMP_CELSIUS): - """Setup the test sensor.""" - self.hass.states.set(ENT_SENSOR, temp, { - ATTR_UNIT_OF_MEASUREMENT: unit - }) + def _setup_sensor(self, temp): + """Set up the test sensor.""" + self.hass.states.set(ENT_SENSOR, temp) def _setup_switch(self, is_on): - """Setup the test switch.""" + """Set up the test switch.""" self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.calls = [] @@ -650,7 +626,7 @@ class TestClimateGenericThermostatMinCycle(unittest.TestCase): """Test the Generic thermostat.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.temperature_unit = TEMP_CELSIUS assert setup_component(self.hass, climate.DOMAIN, {'climate': { @@ -719,14 +695,12 @@ def test_temp_change_heater_trigger_off_long_enough(self): self.assertEqual(SERVICE_TURN_OFF, call.service) self.assertEqual(ENT_SWITCH, call.data['entity_id']) - def _setup_sensor(self, temp, unit=TEMP_CELSIUS): - """Setup the test sensor.""" - self.hass.states.set(ENT_SENSOR, temp, { - ATTR_UNIT_OF_MEASUREMENT: unit - }) + def _setup_sensor(self, temp): + """Set up the test sensor.""" + self.hass.states.set(ENT_SENSOR, temp) def _setup_switch(self, is_on): - """Setup the test switch.""" + """Set up the test switch.""" self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.calls = [] @@ -743,7 +717,7 @@ class TestClimateGenericThermostatACKeepAlive(unittest.TestCase): """Test the Generic Thermostat.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.temperature_unit = TEMP_CELSIUS assert setup_component(self.hass, climate.DOMAIN, {'climate': { @@ -812,14 +786,12 @@ def _send_time_changed(self, now): """Send a time changed event.""" self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: now}) - def _setup_sensor(self, temp, unit=TEMP_CELSIUS): - """Setup the test sensor.""" - self.hass.states.set(ENT_SENSOR, temp, { - ATTR_UNIT_OF_MEASUREMENT: unit - }) + def _setup_sensor(self, temp): + """Set up the test sensor.""" + self.hass.states.set(ENT_SENSOR, temp) def _setup_switch(self, is_on): - """Setup the test switch.""" + """Set up the test switch.""" self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.calls = [] @@ -836,7 +808,7 @@ class TestClimateGenericThermostatKeepAlive(unittest.TestCase): """Test the Generic Thermostat.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.temperature_unit = TEMP_CELSIUS assert setup_component(self.hass, climate.DOMAIN, {'climate': { @@ -904,14 +876,12 @@ def _send_time_changed(self, now): """Send a time changed event.""" self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: now}) - def _setup_sensor(self, temp, unit=TEMP_CELSIUS): - """Setup the test sensor.""" - self.hass.states.set(ENT_SENSOR, temp, { - ATTR_UNIT_OF_MEASUREMENT: unit - }) + def _setup_sensor(self, temp): + """Set up the test sensor.""" + self.hass.states.set(ENT_SENSOR, temp) def _setup_switch(self, is_on): - """Setup the test switch.""" + """Set up the test switch.""" self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.calls = [] @@ -999,7 +969,7 @@ class TestClimateGenericThermostatRestoreState(unittest.TestCase): """Test generic thermostat when restore state from HA startup.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.temperature_unit = TEMP_CELSIUS @@ -1047,14 +1017,12 @@ def _setup_climate(self): 'ac_mode': True }}) - def _setup_sensor(self, temp, unit=TEMP_CELSIUS): - """Setup the test sensor.""" - self.hass.states.set(ENT_SENSOR, temp, { - ATTR_UNIT_OF_MEASUREMENT: unit - }) + def _setup_sensor(self, temp): + """Set up the test sensor.""" + self.hass.states.set(ENT_SENSOR, temp) def _setup_switch(self, is_on): - """Setup the test switch.""" + """Set up the test switch.""" self.hass.states.set(ENT_SWITCH, STATE_ON if is_on else STATE_OFF) self.calls = [] diff --git a/tests/components/climate/test_honeywell.py b/tests/components/climate/test_honeywell.py index 69df11715e91a0..7072090591ba09 100644 --- a/tests/components/climate/test_honeywell.py +++ b/tests/components/climate/test_honeywell.py @@ -56,7 +56,7 @@ def test_setup_us(self, mock_ht, mock_sc): honeywell.PLATFORM_SCHEMA(bad_region_config) hass = mock.MagicMock() - add_devices = mock.MagicMock() + add_entities = mock.MagicMock() locations = [ mock.MagicMock(), @@ -69,7 +69,7 @@ def test_setup_us(self, mock_ht, mock_sc): locations[0].devices_by_id.values.return_value = devices_1 locations[1].devices_by_id.values.return_value = devices_2 - result = honeywell.setup_platform(hass, config, add_devices) + result = honeywell.setup_platform(hass, config, add_entities) self.assertTrue(result) self.assertEqual(mock_sc.call_count, 1) self.assertEqual(mock_sc.call_args, mock.call('user', 'pass')) @@ -86,7 +86,7 @@ def test_setup_us(self, mock_ht, mock_sc): def test_setup_us_failures(self, mock_sc): """Test the US setup.""" hass = mock.MagicMock() - add_devices = mock.MagicMock() + add_entities = mock.MagicMock() config = { CONF_USERNAME: 'user', CONF_PASSWORD: 'pass', @@ -94,14 +94,14 @@ def test_setup_us_failures(self, mock_sc): } mock_sc.side_effect = somecomfort.AuthError - result = honeywell.setup_platform(hass, config, add_devices) + result = honeywell.setup_platform(hass, config, add_entities) self.assertFalse(result) - self.assertFalse(add_devices.called) + self.assertFalse(add_entities.called) mock_sc.side_effect = somecomfort.SomeComfortError - result = honeywell.setup_platform(hass, config, add_devices) + result = honeywell.setup_platform(hass, config, add_entities) self.assertFalse(result) - self.assertFalse(add_devices.called) + self.assertFalse(add_entities.called) @mock.patch('somecomfort.SomeComfort') @mock.patch('homeassistant.components.climate.' @@ -136,9 +136,9 @@ def _test_us_filtered_devices(self, mock_ht, mock_sc, loc=None, dev=None): } mock_sc.return_value = mock.MagicMock(locations_by_id=locations) hass = mock.MagicMock() - add_devices = mock.MagicMock() + add_entities = mock.MagicMock() self.assertEqual(True, - honeywell.setup_platform(hass, config, add_devices)) + honeywell.setup_platform(hass, config, add_entities)) return mock_ht.call_args_list, mock_sc @@ -185,8 +185,8 @@ def test_eu_setup_full_config(self, mock_round, mock_evo): mock_evo.return_value.temperatures.return_value = [ {'id': 'foo'}, {'id': 'bar'}] hass = mock.MagicMock() - add_devices = mock.MagicMock() - self.assertTrue(honeywell.setup_platform(hass, config, add_devices)) + add_entities = mock.MagicMock() + self.assertTrue(honeywell.setup_platform(hass, config, add_entities)) self.assertEqual(mock_evo.call_count, 1) self.assertEqual(mock_evo.call_args, mock.call('user', 'pass')) self.assertEqual(mock_evo.return_value.temperatures.call_count, 1) @@ -198,7 +198,7 @@ def test_eu_setup_full_config(self, mock_round, mock_evo): mock.call(mock_evo.return_value, 'foo', True, 20.0), mock.call(mock_evo.return_value, 'bar', False, 20.0), ]) - self.assertEqual(2, add_devices.call_count) + self.assertEqual(2, add_entities.call_count) @mock.patch('evohomeclient.EvohomeClient') @mock.patch('homeassistant.components.climate.honeywell.' @@ -217,8 +217,8 @@ def test_eu_setup_partial_config(self, mock_round, mock_evo): honeywell.DEFAULT_AWAY_TEMPERATURE hass = mock.MagicMock() - add_devices = mock.MagicMock() - self.assertTrue(honeywell.setup_platform(hass, config, add_devices)) + add_entities = mock.MagicMock() + self.assertTrue(honeywell.setup_platform(hass, config, add_entities)) mock_round.assert_has_calls([ mock.call(mock_evo.return_value, 'foo', True, 16), mock.call(mock_evo.return_value, 'bar', False, 16), @@ -251,9 +251,9 @@ def test_eu_setup_error(self, mock_round, mock_evo): honeywell.CONF_REGION: 'eu', } mock_evo.return_value.temperatures.side_effect = socket.error - add_devices = mock.MagicMock() + add_entities = mock.MagicMock() hass = mock.MagicMock() - self.assertFalse(honeywell.setup_platform(hass, config, add_devices)) + self.assertFalse(honeywell.setup_platform(hass, config, add_entities)) class TestHoneywellRound(unittest.TestCase): diff --git a/tests/components/climate/test_melissa.py b/tests/components/climate/test_melissa.py index 5022c556b7d105..563f74383e5f8c 100644 --- a/tests/components/climate/test_melissa.py +++ b/tests/components/climate/test_melissa.py @@ -76,11 +76,11 @@ def test_setup_platform(self, mocked_thermostat): self.hass.data[DATA_MELISSA] = self.api config = {} - add_devices = Mock() + add_entities = Mock() discovery_info = {} - melissa.setup_platform(self.hass, config, add_devices, discovery_info) - add_devices.assert_called_once_with(thermostats) + melissa.setup_platform(self.hass, config, add_entities, discovery_info) + add_entities.assert_called_once_with(thermostats) def test_get_name(self): """Test name property.""" diff --git a/tests/components/climate/test_mqtt.py b/tests/components/climate/test_mqtt.py index 5db77331cd4038..f46a23e4f970c5 100644 --- a/tests/components/climate/test_mqtt.py +++ b/tests/components/climate/test_mqtt.py @@ -35,7 +35,7 @@ class TestMQTTClimate(unittest.TestCase): """Test the mqtt climate hvac.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_publish = mock_mqtt_component(self.hass) self.hass.config.units = METRIC_SYSTEM diff --git a/tests/components/climate/test_nuheat.py b/tests/components/climate/test_nuheat.py index 6ec63646bec297..5b47a5a75af3fb 100644 --- a/tests/components/climate/test_nuheat.py +++ b/tests/components/climate/test_nuheat.py @@ -67,11 +67,11 @@ def test_setup_platform(self, mocked_thermostat): self.hass.data[nuheat.NUHEAT_DOMAIN] = (self.api, ["12345"]) config = {} - add_devices = Mock() + add_entities = Mock() discovery_info = {} - nuheat.setup_platform(self.hass, config, add_devices, discovery_info) - add_devices.assert_called_once_with(thermostats, True) + nuheat.setup_platform(self.hass, config, add_entities, discovery_info) + add_entities.assert_called_once_with(thermostats, True) @patch("homeassistant.components.climate.nuheat.NuHeatThermostat") def test_resume_program_service(self, mocked_thermostat): diff --git a/tests/components/counter/test_init.py b/tests/components/counter/test_init.py index f4c6ee9c7da53d..af36c1c8f9516a 100644 --- a/tests/components/counter/test_init.py +++ b/tests/components/counter/test_init.py @@ -4,7 +4,7 @@ import unittest import logging -from homeassistant.core import CoreState, State +from homeassistant.core import CoreState, State, Context from homeassistant.setup import setup_component, async_setup_component from homeassistant.components.counter import ( DOMAIN, decrement, increment, reset, CONF_INITIAL, CONF_STEP, CONF_NAME, @@ -202,3 +202,24 @@ def test_no_initial_state_and_no_restore_state(hass): state = hass.states.get('counter.test1') assert state assert int(state.state) == 0 + + +async def test_counter_context(hass): + """Test that counter context works.""" + assert await async_setup_component(hass, 'counter', { + 'counter': { + 'test': {} + } + }) + + state = hass.states.get('counter.test') + assert state is not None + + await hass.services.async_call('counter', 'increment', { + 'entity_id': state.entity_id, + }, True, Context(user_id='abcd')) + + state2 = hass.states.get('counter.test') + assert state2 is not None + assert state.state != state2.state + assert state2.context.user_id == 'abcd' diff --git a/tests/components/cover/test_command_line.py b/tests/components/cover/test_command_line.py index b7049d350219bd..346c3f94683fa9 100644 --- a/tests/components/cover/test_command_line.py +++ b/tests/components/cover/test_command_line.py @@ -17,7 +17,7 @@ class TestCommandCover(unittest.TestCase): """Test the cover command line platform.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.rs = cmd_rs.CommandCover(self.hass, 'foo', 'command_open', 'command_close', diff --git a/tests/components/cover/test_demo.py b/tests/components/cover/test_demo.py index 9d26a6a4f4a354..65aa9a9b9ef629 100644 --- a/tests/components/cover/test_demo.py +++ b/tests/components/cover/test_demo.py @@ -14,7 +14,7 @@ class TestCoverDemo(unittest.TestCase): """Test the Demo cover.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.assertTrue(setup_component(self.hass, cover.DOMAIN, {'cover': { 'platform': 'demo', diff --git a/tests/components/cover/test_group.py b/tests/components/cover/test_group.py index 288e1c5e04726f..028845983a0b4f 100644 --- a/tests/components/cover/test_group.py +++ b/tests/components/cover/test_group.py @@ -35,7 +35,7 @@ class TestMultiCover(unittest.TestCase): """Test the group cover platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/cover/test_mqtt.py b/tests/components/cover/test_mqtt.py index aea6398e3ae874..ad68c2416ca176 100644 --- a/tests/components/cover/test_mqtt.py +++ b/tests/components/cover/test_mqtt.py @@ -15,7 +15,7 @@ class TestCoverMQTT(unittest.TestCase): """Test the MQTT cover.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_publish = mock_mqtt_component(self.hass) diff --git a/tests/components/cover/test_rfxtrx.py b/tests/components/cover/test_rfxtrx.py index be2c456296bcee..ab8b8f9a93ccfd 100644 --- a/tests/components/cover/test_rfxtrx.py +++ b/tests/components/cover/test_rfxtrx.py @@ -14,7 +14,7 @@ class TestCoverRfxtrx(unittest.TestCase): """Test the Rfxtrx cover platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component('rfxtrx') diff --git a/tests/components/deconz/test_init.py b/tests/components/deconz/test_init.py index c6fc130a4a41a6..049a3b961b6109 100644 --- a/tests/components/deconz/test_init.py +++ b/tests/components/deconz/test_init.py @@ -7,6 +7,16 @@ from tests.common import mock_coro +CONFIG = { + "config": { + "bridgeid": "0123456789ABCDEF", + "mac": "12:34:56:78:90:ab", + "modelid": "deCONZ", + "name": "Phoscon", + "swversion": "2.05.35" + } +} + async def test_config_with_host_passed_to_config_entry(hass): """Test that configured options for a host are loaded via config entry.""" @@ -93,8 +103,11 @@ async def test_setup_entry_successful(hass): entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} with patch.object(hass, 'async_create_task') as mock_add_job, \ patch.object(hass, 'config_entries') as mock_config_entries, \ - patch('pydeconz.DeconzSession.async_load_parameters', - return_value=mock_coro(True)): + patch('pydeconz.DeconzSession.async_get_state', + return_value=mock_coro(CONFIG)), \ + patch('pydeconz.DeconzSession.start', return_value=True), \ + patch('homeassistant.helpers.device_registry.async_get_registry', + return_value=mock_coro(Mock())): assert await deconz.async_setup_entry(hass, entry) is True assert hass.data[deconz.DOMAIN] assert hass.data[deconz.DATA_DECONZ_ID] == {} @@ -117,10 +130,15 @@ async def test_unload_entry(hass): """Test being able to unload an entry.""" entry = Mock() entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} - with patch('pydeconz.DeconzSession.async_load_parameters', - return_value=mock_coro(True)): + entry.async_unload.return_value = mock_coro(True) + deconzmock = Mock() + deconzmock.async_load_parameters.return_value = mock_coro(True) + deconzmock.sensors = {} + with patch('pydeconz.DeconzSession', return_value=deconzmock): assert await deconz.async_setup_entry(hass, entry) is True + assert deconz.DATA_DECONZ_EVENT in hass.data + hass.data[deconz.DATA_DECONZ_EVENT].append(Mock()) hass.data[deconz.DATA_DECONZ_ID] = {'id': 'deconzid'} assert await deconz.async_unload_entry(hass, entry) @@ -132,6 +150,9 @@ async def test_unload_entry(hass): async def test_add_new_device(hass): """Test adding a new device generates a signal for platforms.""" + entry = Mock() + entry.data = {'host': '1.2.3.4', 'port': 80, + 'api_key': '1234567890ABCDEF', 'allow_clip_sensor': False} new_event = { "t": "event", "e": "added", @@ -147,11 +168,10 @@ async def test_add_new_device(hass): "type": "ZHASwitch" } } - entry = Mock() - entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} with patch.object(deconz, 'async_dispatcher_send') as mock_dispatch_send, \ - patch('pydeconz.DeconzSession.async_load_parameters', - return_value=mock_coro(True)): + patch('pydeconz.DeconzSession.async_get_state', + return_value=mock_coro(CONFIG)), \ + patch('pydeconz.DeconzSession.start', return_value=True): assert await deconz.async_setup_entry(hass, entry) is True hass.data[deconz.DOMAIN].async_event_handler(new_event) await hass.async_block_till_done() @@ -162,15 +182,16 @@ async def test_add_new_device(hass): async def test_add_new_remote(hass): """Test new added device creates a new remote.""" entry = Mock() - entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} + entry.data = {'host': '1.2.3.4', 'port': 80, + 'api_key': '1234567890ABCDEF', 'allow_clip_sensor': False} remote = Mock() remote.name = 'name' remote.type = 'ZHASwitch' remote.register_async_callback = Mock() - with patch('pydeconz.DeconzSession.async_load_parameters', - return_value=mock_coro(True)): + with patch('pydeconz.DeconzSession.async_get_state', + return_value=mock_coro(CONFIG)), \ + patch('pydeconz.DeconzSession.start', return_value=True): assert await deconz.async_setup_entry(hass, entry) is True - async_dispatcher_send(hass, 'deconz_new_sensor', [remote]) await hass.async_block_till_done() assert len(hass.data[deconz.DATA_DECONZ_EVENT]) == 1 @@ -185,8 +206,9 @@ async def test_do_not_allow_clip_sensor(hass): remote.name = 'name' remote.type = 'CLIPSwitch' remote.register_async_callback = Mock() - with patch('pydeconz.DeconzSession.async_load_parameters', - return_value=mock_coro(True)): + with patch('pydeconz.DeconzSession.async_get_state', + return_value=mock_coro(CONFIG)), \ + patch('pydeconz.DeconzSession.start', return_value=True): assert await deconz.async_setup_entry(hass, entry) is True async_dispatcher_send(hass, 'deconz_new_sensor', [remote]) diff --git a/tests/components/device_tracker/test_asuswrt.py b/tests/components/device_tracker/test_asuswrt.py index 956b407eeaa25f..8c5af618288886 100644 --- a/tests/components/device_tracker/test_asuswrt.py +++ b/tests/components/device_tracker/test_asuswrt.py @@ -116,7 +116,7 @@ def setup_module(): - """Setup the test module.""" + """Set up the test module.""" global FAKEFILE FAKEFILE = get_test_config_dir('fake_file') with open(FAKEFILE, 'w') as out: @@ -138,7 +138,7 @@ class TestComponentsDeviceTrackerASUSWRT(unittest.TestCase): hass = None def setup_method(self, _): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'zone') @@ -500,7 +500,7 @@ class TestSshConnection(unittest.TestCase): """Testing SshConnection.""" def setUp(self): - """Setup test env.""" + """Set up test env.""" self.connection = SshConnection( 'fake', 'fake', 'fake', 'fake', 'fake') self.connection._connected = True @@ -544,7 +544,7 @@ class TestTelnetConnection(unittest.TestCase): """Testing TelnetConnection.""" def setUp(self): - """Setup test env.""" + """Set up test env.""" self.connection = TelnetConnection( 'fake', 'fake', 'fake', 'fake') self.connection._connected = True diff --git a/tests/components/device_tracker/test_ddwrt.py b/tests/components/device_tracker/test_ddwrt.py index 416b7be4a8a45e..3e60e1bae468f2 100644 --- a/tests/components/device_tracker/test_ddwrt.py +++ b/tests/components/device_tracker/test_ddwrt.py @@ -41,7 +41,7 @@ def run(self, result=None): super().run(result) def setup_method(self, _): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'zone') @@ -220,7 +220,7 @@ def test_update_no_data(self): with requests_mock.Mocker() as mock_request: mock_request.register_uri( 'GET', r'http://%s/Status_Wireless.live.asp' % TEST_HOST, - # First request has to work to setup connection + # First request has to work to set up connection [{'text': load_fixture('Ddwrt_Status_Wireless.txt')}, # Second request to get active devices fails {'text': None}]) diff --git a/tests/components/device_tracker/test_geofency.py b/tests/components/device_tracker/test_geofency.py index a955dd0cc118fd..d84940d9fbf282 100644 --- a/tests/components/device_tracker/test_geofency.py +++ b/tests/components/device_tracker/test_geofency.py @@ -122,7 +122,7 @@ def geofency_client(loop, hass, aiohttp_client): @pytest.fixture(autouse=True) def setup_zones(loop, hass): - """Setup Zone config in HA.""" + """Set up Zone config in HA.""" assert loop.run_until_complete(async_setup_component( hass, zone.DOMAIN, { 'zone': { diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index 0b17b4e0ac8f13..b1b68ff92df610 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -20,7 +20,7 @@ STATE_HOME, STATE_NOT_HOME, CONF_PLATFORM, ATTR_ICON) import homeassistant.components.device_tracker as device_tracker from homeassistant.exceptions import HomeAssistantError -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder from tests.common import ( get_test_home_assistant, fire_time_changed, @@ -39,7 +39,7 @@ class TestComponentsDeviceTracker(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.yaml_devices = self.hass.config.path(device_tracker.YAML_DEVICES) @@ -323,7 +323,7 @@ def test_new_device_event_fired(self): @callback def listener(event): - """Helper method that will verify our event got called.""" + """Record that our event got called.""" test_events.append(event) self.hass.bus.listen("device_tracker_new_device", listener) diff --git a/tests/components/device_tracker/test_locative.py b/tests/components/device_tracker/test_locative.py index 90adccf770319a..7cfef8f52197e0 100644 --- a/tests/components/device_tracker/test_locative.py +++ b/tests/components/device_tracker/test_locative.py @@ -11,7 +11,7 @@ def _url(data=None): - """Helper method to generate URLs.""" + """Generate URL.""" data = data or {} data = "&".join(["{}={}".format(name, value) for name, value in data.items()]) diff --git a/tests/components/device_tracker/test_mqtt.py b/tests/components/device_tracker/test_mqtt.py index de7865517a8366..8e4d0dc2769fd5 100644 --- a/tests/components/device_tracker/test_mqtt.py +++ b/tests/components/device_tracker/test_mqtt.py @@ -19,7 +19,7 @@ class TestComponentsDeviceTrackerMQTT(unittest.TestCase): """Test MQTT device tracker platform.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_mqtt_component(self.hass) diff --git a/tests/components/device_tracker/test_mqtt_json.py b/tests/components/device_tracker/test_mqtt_json.py index 8ab6346f19be6b..41c1d9c0885c00 100644 --- a/tests/components/device_tracker/test_mqtt_json.py +++ b/tests/components/device_tracker/test_mqtt_json.py @@ -29,7 +29,7 @@ class TestComponentsDeviceTrackerJSONMQTT(unittest.TestCase): """Test JSON MQTT device tracker platform.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_mqtt_component(self.hass) diff --git a/tests/components/device_tracker/test_owntracks.py b/tests/components/device_tracker/test_owntracks.py index 37a3e570b53694..8883ea22600c59 100644 --- a/tests/components/device_tracker/test_owntracks.py +++ b/tests/components/device_tracker/test_owntracks.py @@ -327,7 +327,7 @@ class TestDeviceTrackerOwnTracks(BaseMQTT): # pylint: disable=invalid-name def setup_method(self, _): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_mqtt_component(self.hass) mock_component(self.hass, 'group') @@ -1316,7 +1316,7 @@ class TestDeviceTrackerOwnTrackConfigs(BaseMQTT): # pylint: disable=invalid-name def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_mqtt_component(self.hass) mock_component(self.hass, 'group') diff --git a/tests/components/device_tracker/test_tplink.py b/tests/components/device_tracker/test_tplink.py index 88e38108133cca..b9f1f5f5e5a771 100644 --- a/tests/components/device_tracker/test_tplink.py +++ b/tests/components/device_tracker/test_tplink.py @@ -16,7 +16,7 @@ class TestTplink4DeviceScanner(unittest.TestCase): """Tests for the Tplink4DeviceScanner class.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/device_tracker/test_unifi.py b/tests/components/device_tracker/test_unifi.py index ccc58d728edef1..33adff9adf816f 100644 --- a/tests/components/device_tracker/test_unifi.py +++ b/tests/components/device_tracker/test_unifi.py @@ -10,7 +10,8 @@ from homeassistant.components.device_tracker import DOMAIN, unifi as unifi from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD, - CONF_PLATFORM, CONF_VERIFY_SSL) + CONF_PLATFORM, CONF_VERIFY_SSL, + CONF_MONITORED_CONDITIONS) DEFAULT_DETECTION_TIME = timedelta(seconds=300) @@ -54,7 +55,7 @@ def test_config_valid_verify_ssl(hass, mock_scanner, mock_ctrl): assert mock_scanner.call_count == 1 assert mock_scanner.call_args == mock.call(mock_ctrl.return_value, DEFAULT_DETECTION_TIME, - None) + None, None) def test_config_minimal(hass, mock_scanner, mock_ctrl): @@ -76,7 +77,7 @@ def test_config_minimal(hass, mock_scanner, mock_ctrl): assert mock_scanner.call_count == 1 assert mock_scanner.call_args == mock.call(mock_ctrl.return_value, DEFAULT_DETECTION_TIME, - None) + None, None) def test_config_full(hass, mock_scanner, mock_ctrl): @@ -88,6 +89,7 @@ def test_config_full(hass, mock_scanner, mock_ctrl): CONF_PASSWORD: 'password', CONF_HOST: 'myhost', CONF_VERIFY_SSL: False, + CONF_MONITORED_CONDITIONS: ['essid', 'signal'], 'port': 123, 'site_id': 'abcdef01', 'detection_time': 300, @@ -101,9 +103,11 @@ def test_config_full(hass, mock_scanner, mock_ctrl): version='v4', site_id='abcdef01', ssl_verify=False) assert mock_scanner.call_count == 1 - assert mock_scanner.call_args == mock.call(mock_ctrl.return_value, - DEFAULT_DETECTION_TIME, - None) + assert mock_scanner.call_args == mock.call( + mock_ctrl.return_value, + DEFAULT_DETECTION_TIME, + None, + config[DOMAIN][CONF_MONITORED_CONDITIONS]) def test_config_error(): @@ -157,7 +161,7 @@ def test_scanner_update(): 'last_seen': dt_util.as_timestamp(dt_util.utcnow())}, ] ctrl.get_clients.return_value = fake_clients - unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, None) + unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, None, None) assert ctrl.get_clients.call_count == 1 assert ctrl.get_clients.call_args == mock.call() @@ -167,7 +171,7 @@ def test_scanner_update_error(): ctrl = mock.MagicMock() ctrl.get_clients.side_effect = APIError( '/', 500, 'foo', {}, None) - unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, None) + unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, None, None) def test_scan_devices(): @@ -180,7 +184,7 @@ def test_scan_devices(): 'last_seen': dt_util.as_timestamp(dt_util.utcnow())}, ] ctrl.get_clients.return_value = fake_clients - scanner = unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, None) + scanner = unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, None, None) assert set(scanner.scan_devices()) == set(['123', '234']) @@ -200,7 +204,8 @@ def test_scan_devices_filtered(): ssid_filter = ['foonet', 'barnet'] ctrl.get_clients.return_value = fake_clients - scanner = unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, ssid_filter) + scanner = unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, ssid_filter, + None) assert set(scanner.scan_devices()) == set(['123', '234', '890']) @@ -221,8 +226,37 @@ def test_get_device_name(): 'last_seen': '1504786810'}, ] ctrl.get_clients.return_value = fake_clients - scanner = unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, None) + scanner = unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, None, None) assert scanner.get_device_name('123') == 'foobar' assert scanner.get_device_name('234') == 'Nice Name' assert scanner.get_device_name('456') is None assert scanner.get_device_name('unknown') is None + + +def test_monitored_conditions(): + """Test the filtering of attributes.""" + ctrl = mock.MagicMock() + fake_clients = [ + {'mac': '123', + 'hostname': 'foobar', + 'essid': 'barnet', + 'signal': -60, + 'last_seen': dt_util.as_timestamp(dt_util.utcnow())}, + {'mac': '234', + 'name': 'Nice Name', + 'essid': 'barnet', + 'signal': -42, + 'last_seen': dt_util.as_timestamp(dt_util.utcnow())}, + {'mac': '456', + 'hostname': 'wired', + 'essid': 'barnet', + 'last_seen': dt_util.as_timestamp(dt_util.utcnow())}, + ] + ctrl.get_clients.return_value = fake_clients + scanner = unifi.UnifiScanner(ctrl, DEFAULT_DETECTION_TIME, None, + ['essid', 'signal']) + assert scanner.get_extra_attributes('123') == {'essid': 'barnet', + 'signal': -60} + assert scanner.get_extra_attributes('234') == {'essid': 'barnet', + 'signal': -42} + assert scanner.get_extra_attributes('456') == {'essid': 'barnet'} diff --git a/tests/components/device_tracker/test_unifi_direct.py b/tests/components/device_tracker/test_unifi_direct.py index d1ede7211426c7..1f9cbd24f12e2b 100644 --- a/tests/components/device_tracker/test_unifi_direct.py +++ b/tests/components/device_tracker/test_unifi_direct.py @@ -31,7 +31,7 @@ class TestComponentsDeviceTrackerUnifiDirect(unittest.TestCase): 'unifi_direct.UnifiDeviceScanner' def setup_method(self, _): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'zone') diff --git a/tests/components/device_tracker/test_upc_connect.py b/tests/components/device_tracker/test_upc_connect.py index 6294ba3467a11f..6b38edc3ce94d5 100644 --- a/tests/components/device_tracker/test_upc_connect.py +++ b/tests/components/device_tracker/test_upc_connect.py @@ -37,7 +37,7 @@ class TestUPCConnect: """Tests for the Ddwrt device tracker platform.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'zone') mock_component(self.hass, 'group') @@ -52,7 +52,7 @@ def teardown_method(self): 'UPCDeviceScanner.async_scan_devices', return_value=async_scan_devices_mock) def test_setup_platform(self, scan_mock, aioclient_mock): - """Setup a platform.""" + """Set up a platform.""" aioclient_mock.get( "http://{}/common_page/login.html".format(self.host), cookies={'sessionToken': '654321'} @@ -74,7 +74,7 @@ def test_setup_platform(self, scan_mock, aioclient_mock): @patch('homeassistant.components.device_tracker._LOGGER.error') def test_setup_platform_timeout_webservice(self, mock_error, aioclient_mock): - """Setup a platform with api timeout.""" + """Set up a platform with api timeout.""" aioclient_mock.get( "http://{}/common_page/login.html".format(self.host), cookies={'sessionToken': '654321'}, @@ -97,7 +97,7 @@ def test_setup_platform_timeout_webservice(self, mock_error, @patch('homeassistant.components.device_tracker._LOGGER.error') def test_setup_platform_timeout_loginpage(self, mock_error, aioclient_mock): - """Setup a platform with timeout on loginpage.""" + """Set up a platform with timeout on loginpage.""" aioclient_mock.get( "http://{}/common_page/login.html".format(self.host), exc=asyncio.TimeoutError() @@ -120,7 +120,7 @@ def test_setup_platform_timeout_loginpage(self, mock_error, str(mock_error.call_args_list[-1]) def test_scan_devices(self, aioclient_mock): - """Setup a upc platform and scan device.""" + """Set up a upc platform and scan device.""" aioclient_mock.get( "http://{}/common_page/login.html".format(self.host), cookies={'sessionToken': '654321'} @@ -156,7 +156,7 @@ def test_scan_devices(self, aioclient_mock): '70:EE:50:27:A1:38'] def test_scan_devices_without_session(self, aioclient_mock): - """Setup a upc platform and scan device with no token.""" + """Set up a upc platform and scan device with no token.""" aioclient_mock.get( "http://{}/common_page/login.html".format(self.host), cookies={'sessionToken': '654321'} @@ -197,7 +197,7 @@ def test_scan_devices_without_session(self, aioclient_mock): '70:EE:50:27:A1:38'] def test_scan_devices_without_session_wrong_re(self, aioclient_mock): - """Setup a upc platform and scan device with no token and wrong.""" + """Set up a upc platform and scan device with no token and wrong.""" aioclient_mock.get( "http://{}/common_page/login.html".format(self.host), cookies={'sessionToken': '654321'} @@ -237,7 +237,7 @@ def test_scan_devices_without_session_wrong_re(self, aioclient_mock): assert mac_list == [] def test_scan_devices_parse_error(self, aioclient_mock): - """Setup a upc platform and scan device with parse error.""" + """Set up a upc platform and scan device with parse error.""" aioclient_mock.get( "http://{}/common_page/login.html".format(self.host), cookies={'sessionToken': '654321'} diff --git a/tests/components/emulated_hue/test_hue_api.py b/tests/components/emulated_hue/test_hue_api.py index c99d273a4580a7..3920a45ddf6eef 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -26,7 +26,7 @@ @pytest.fixture def hass_hue(loop, hass): - """Setup a Home Assistant instance for these tests.""" + """Set up a Home Assistant instance for these tests.""" # We need to do this to get access to homeassistant/turn_(on,off) loop.run_until_complete( core_components.async_setup(hass, {core.DOMAIN: {}})) diff --git a/tests/components/emulated_hue/test_upnp.py b/tests/components/emulated_hue/test_upnp.py index 8315de34e061d8..f5377b1812ca56 100644 --- a/tests/components/emulated_hue/test_upnp.py +++ b/tests/components/emulated_hue/test_upnp.py @@ -50,7 +50,7 @@ class TestEmulatedHue(unittest.TestCase): @classmethod def setUpClass(cls): - """Setup the class.""" + """Set up the class.""" cls.hass = hass = get_test_home_assistant() # We need to do this to get access to homeassistant/turn_(on,off) diff --git a/tests/components/fan/test_demo.py b/tests/components/fan/test_demo.py index 0d066af8cf6001..69680fb1cfdff2 100644 --- a/tests/components/fan/test_demo.py +++ b/tests/components/fan/test_demo.py @@ -15,7 +15,7 @@ class TestDemoFan(unittest.TestCase): """Test the fan demo platform.""" def get_entity(self): - """Helper method to get the fan entity.""" + """Get the fan entity.""" return self.hass.states.get(FAN_ENTITY_ID) def setUp(self): diff --git a/tests/components/fan/test_dyson.py b/tests/components/fan/test_dyson.py index 2953ea2754ba02..452f8e199eb466 100644 --- a/tests/components/fan/test_dyson.py +++ b/tests/components/fan/test_dyson.py @@ -68,7 +68,7 @@ class DysonTest(unittest.TestCase): """Dyson Sensor component test class.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name @@ -78,9 +78,9 @@ def tearDown(self): # pylint: disable=invalid-name def test_setup_component_with_no_devices(self): """Test setup component with no devices.""" self.hass.data[dyson.DYSON_DEVICES] = [] - add_devices = mock.MagicMock() - dyson.setup_platform(self.hass, None, add_devices) - add_devices.assert_called_with([]) + add_entities = mock.MagicMock() + dyson.setup_platform(self.hass, None, add_entities) + add_entities.assert_called_with([]) def test_setup_component(self): """Test setup component with devices.""" diff --git a/tests/components/fan/test_mqtt.py b/tests/components/fan/test_mqtt.py index 9060d7b9986f24..7f69e56218b0cd 100644 --- a/tests/components/fan/test_mqtt.py +++ b/tests/components/fan/test_mqtt.py @@ -13,7 +13,7 @@ class TestMqttFan(unittest.TestCase): """Test the MQTT fan platform.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_publish = mock_mqtt_component(self.hass) diff --git a/tests/components/fan/test_template.py b/tests/components/fan/test_template.py index 53eb9e8e2d486d..e229083069d5df 100644 --- a/tests/components/fan/test_template.py +++ b/tests/components/fan/test_template.py @@ -33,7 +33,7 @@ class TestTemplateFan: # pylint: disable=invalid-name def setup_method(self, method): - """Setup.""" + """Set up.""" self.hass = get_test_home_assistant() self.calls = [] diff --git a/tests/components/google_assistant/test_google_assistant.py b/tests/components/google_assistant/test_google_assistant.py index d45680d132e532..d9682940bdcf6b 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -232,10 +232,10 @@ def test_query_climate_request(hass_fixture, assistant_client): def test_query_climate_request_f(hass_fixture, assistant_client): """Test a query request.""" # Mock demo devices as fahrenheit to see if we convert to celsius + hass_fixture.config.units.temperature_unit = const.TEMP_FAHRENHEIT for entity_id in ('climate.hvac', 'climate.heatpump', 'climate.ecobee'): state = hass_fixture.states.get(entity_id) attr = dict(state.attributes) - attr[const.ATTR_UNIT_OF_MEASUREMENT] = const.TEMP_FAHRENHEIT hass_fixture.states.async_set(entity_id, state.state, attr) reqid = '5711642932632160984' @@ -282,6 +282,7 @@ def test_query_climate_request_f(hass_fixture, assistant_client): 'thermostatMode': 'cool', 'thermostatHumidityAmbient': 54, } + hass_fixture.config.units.temperature_unit = const.TEMP_CELSIUS @asyncio.coroutine diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index 1f7ee011e61bbf..c18ed4b7bf34aa 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -3,7 +3,7 @@ from homeassistant.const import ( STATE_ON, STATE_OFF, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, - ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS, TEMP_FAHRENHEIT) + TEMP_CELSIUS, TEMP_FAHRENHEIT) from homeassistant.core import State, DOMAIN as HA_DOMAIN from homeassistant.components import ( climate, @@ -27,7 +27,7 @@ async def test_brightness_light(hass): assert trait.BrightnessTrait.supported(light.DOMAIN, light.SUPPORT_BRIGHTNESS) - trt = trait.BrightnessTrait(State('light.bla', light.STATE_ON, { + trt = trait.BrightnessTrait(hass, State('light.bla', light.STATE_ON, { light.ATTR_BRIGHTNESS: 243 })) @@ -38,7 +38,7 @@ async def test_brightness_light(hass): } calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON) - await trt.execute(hass, trait.COMMAND_BRIGHTNESS_ABSOLUTE, { + await trt.execute(trait.COMMAND_BRIGHTNESS_ABSOLUTE, { 'brightness': 50 }) assert len(calls) == 1 @@ -53,7 +53,7 @@ async def test_brightness_cover(hass): assert trait.BrightnessTrait.supported(cover.DOMAIN, cover.SUPPORT_SET_POSITION) - trt = trait.BrightnessTrait(State('cover.bla', cover.STATE_OPEN, { + trt = trait.BrightnessTrait(hass, State('cover.bla', cover.STATE_OPEN, { cover.ATTR_CURRENT_POSITION: 75 })) @@ -65,7 +65,7 @@ async def test_brightness_cover(hass): calls = async_mock_service( hass, cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION) - await trt.execute(hass, trait.COMMAND_BRIGHTNESS_ABSOLUTE, { + await trt.execute(trait.COMMAND_BRIGHTNESS_ABSOLUTE, { 'brightness': 50 }) assert len(calls) == 1 @@ -80,7 +80,7 @@ async def test_brightness_media_player(hass): assert trait.BrightnessTrait.supported(media_player.DOMAIN, media_player.SUPPORT_VOLUME_SET) - trt = trait.BrightnessTrait(State( + trt = trait.BrightnessTrait(hass, State( 'media_player.bla', media_player.STATE_PLAYING, { media_player.ATTR_MEDIA_VOLUME_LEVEL: .3 })) @@ -93,7 +93,7 @@ async def test_brightness_media_player(hass): calls = async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_SET) - await trt.execute(hass, trait.COMMAND_BRIGHTNESS_ABSOLUTE, { + await trt.execute(trait.COMMAND_BRIGHTNESS_ABSOLUTE, { 'brightness': 60 }) assert len(calls) == 1 @@ -107,7 +107,7 @@ async def test_onoff_group(hass): """Test OnOff trait support for group domain.""" assert trait.OnOffTrait.supported(media_player.DOMAIN, 0) - trt_on = trait.OnOffTrait(State('group.bla', STATE_ON)) + trt_on = trait.OnOffTrait(hass, State('group.bla', STATE_ON)) assert trt_on.sync_attributes() == {} @@ -115,13 +115,13 @@ async def test_onoff_group(hass): 'on': True } - trt_off = trait.OnOffTrait(State('group.bla', STATE_OFF)) + trt_off = trait.OnOffTrait(hass, State('group.bla', STATE_OFF)) assert trt_off.query_attributes() == { 'on': False } on_calls = async_mock_service(hass, HA_DOMAIN, SERVICE_TURN_ON) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': True }) assert len(on_calls) == 1 @@ -130,7 +130,7 @@ async def test_onoff_group(hass): } off_calls = async_mock_service(hass, HA_DOMAIN, SERVICE_TURN_OFF) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': False }) assert len(off_calls) == 1 @@ -143,7 +143,7 @@ async def test_onoff_input_boolean(hass): """Test OnOff trait support for input_boolean domain.""" assert trait.OnOffTrait.supported(media_player.DOMAIN, 0) - trt_on = trait.OnOffTrait(State('input_boolean.bla', STATE_ON)) + trt_on = trait.OnOffTrait(hass, State('input_boolean.bla', STATE_ON)) assert trt_on.sync_attributes() == {} @@ -151,13 +151,13 @@ async def test_onoff_input_boolean(hass): 'on': True } - trt_off = trait.OnOffTrait(State('input_boolean.bla', STATE_OFF)) + trt_off = trait.OnOffTrait(hass, State('input_boolean.bla', STATE_OFF)) assert trt_off.query_attributes() == { 'on': False } on_calls = async_mock_service(hass, input_boolean.DOMAIN, SERVICE_TURN_ON) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': True }) assert len(on_calls) == 1 @@ -167,7 +167,7 @@ async def test_onoff_input_boolean(hass): off_calls = async_mock_service(hass, input_boolean.DOMAIN, SERVICE_TURN_OFF) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': False }) assert len(off_calls) == 1 @@ -180,7 +180,7 @@ async def test_onoff_switch(hass): """Test OnOff trait support for switch domain.""" assert trait.OnOffTrait.supported(media_player.DOMAIN, 0) - trt_on = trait.OnOffTrait(State('switch.bla', STATE_ON)) + trt_on = trait.OnOffTrait(hass, State('switch.bla', STATE_ON)) assert trt_on.sync_attributes() == {} @@ -188,13 +188,13 @@ async def test_onoff_switch(hass): 'on': True } - trt_off = trait.OnOffTrait(State('switch.bla', STATE_OFF)) + trt_off = trait.OnOffTrait(hass, State('switch.bla', STATE_OFF)) assert trt_off.query_attributes() == { 'on': False } on_calls = async_mock_service(hass, switch.DOMAIN, SERVICE_TURN_ON) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': True }) assert len(on_calls) == 1 @@ -203,7 +203,7 @@ async def test_onoff_switch(hass): } off_calls = async_mock_service(hass, switch.DOMAIN, SERVICE_TURN_OFF) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': False }) assert len(off_calls) == 1 @@ -216,7 +216,7 @@ async def test_onoff_fan(hass): """Test OnOff trait support for fan domain.""" assert trait.OnOffTrait.supported(media_player.DOMAIN, 0) - trt_on = trait.OnOffTrait(State('fan.bla', STATE_ON)) + trt_on = trait.OnOffTrait(hass, State('fan.bla', STATE_ON)) assert trt_on.sync_attributes() == {} @@ -224,13 +224,13 @@ async def test_onoff_fan(hass): 'on': True } - trt_off = trait.OnOffTrait(State('fan.bla', STATE_OFF)) + trt_off = trait.OnOffTrait(hass, State('fan.bla', STATE_OFF)) assert trt_off.query_attributes() == { 'on': False } on_calls = async_mock_service(hass, fan.DOMAIN, SERVICE_TURN_ON) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': True }) assert len(on_calls) == 1 @@ -239,7 +239,7 @@ async def test_onoff_fan(hass): } off_calls = async_mock_service(hass, fan.DOMAIN, SERVICE_TURN_OFF) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': False }) assert len(off_calls) == 1 @@ -252,7 +252,7 @@ async def test_onoff_light(hass): """Test OnOff trait support for light domain.""" assert trait.OnOffTrait.supported(media_player.DOMAIN, 0) - trt_on = trait.OnOffTrait(State('light.bla', STATE_ON)) + trt_on = trait.OnOffTrait(hass, State('light.bla', STATE_ON)) assert trt_on.sync_attributes() == {} @@ -260,13 +260,13 @@ async def test_onoff_light(hass): 'on': True } - trt_off = trait.OnOffTrait(State('light.bla', STATE_OFF)) + trt_off = trait.OnOffTrait(hass, State('light.bla', STATE_OFF)) assert trt_off.query_attributes() == { 'on': False } on_calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': True }) assert len(on_calls) == 1 @@ -275,7 +275,7 @@ async def test_onoff_light(hass): } off_calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_OFF) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': False }) assert len(off_calls) == 1 @@ -288,7 +288,7 @@ async def test_onoff_cover(hass): """Test OnOff trait support for cover domain.""" assert trait.OnOffTrait.supported(media_player.DOMAIN, 0) - trt_on = trait.OnOffTrait(State('cover.bla', cover.STATE_OPEN)) + trt_on = trait.OnOffTrait(hass, State('cover.bla', cover.STATE_OPEN)) assert trt_on.sync_attributes() == {} @@ -296,13 +296,13 @@ async def test_onoff_cover(hass): 'on': True } - trt_off = trait.OnOffTrait(State('cover.bla', cover.STATE_CLOSED)) + trt_off = trait.OnOffTrait(hass, State('cover.bla', cover.STATE_CLOSED)) assert trt_off.query_attributes() == { 'on': False } on_calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_OPEN_COVER) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': True }) assert len(on_calls) == 1 @@ -312,7 +312,7 @@ async def test_onoff_cover(hass): off_calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_CLOSE_COVER) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': False }) assert len(off_calls) == 1 @@ -325,7 +325,7 @@ async def test_onoff_media_player(hass): """Test OnOff trait support for media_player domain.""" assert trait.OnOffTrait.supported(media_player.DOMAIN, 0) - trt_on = trait.OnOffTrait(State('media_player.bla', STATE_ON)) + trt_on = trait.OnOffTrait(hass, State('media_player.bla', STATE_ON)) assert trt_on.sync_attributes() == {} @@ -333,13 +333,13 @@ async def test_onoff_media_player(hass): 'on': True } - trt_off = trait.OnOffTrait(State('media_player.bla', STATE_OFF)) + trt_off = trait.OnOffTrait(hass, State('media_player.bla', STATE_OFF)) assert trt_off.query_attributes() == { 'on': False } on_calls = async_mock_service(hass, media_player.DOMAIN, SERVICE_TURN_ON) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': True }) assert len(on_calls) == 1 @@ -348,7 +348,7 @@ async def test_onoff_media_player(hass): } off_calls = async_mock_service(hass, media_player.DOMAIN, SERVICE_TURN_OFF) - await trt_on.execute(hass, trait.COMMAND_ONOFF, { + await trt_on.execute(trait.COMMAND_ONOFF, { 'on': False }) assert len(off_calls) == 1 @@ -363,7 +363,7 @@ async def test_color_spectrum_light(hass): assert trait.ColorSpectrumTrait.supported(light.DOMAIN, light.SUPPORT_COLOR) - trt = trait.ColorSpectrumTrait(State('light.bla', STATE_ON, { + trt = trait.ColorSpectrumTrait(hass, State('light.bla', STATE_ON, { light.ATTR_HS_COLOR: (0, 94), })) @@ -389,7 +389,7 @@ async def test_color_spectrum_light(hass): }) calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON) - await trt.execute(hass, trait.COMMAND_COLOR_ABSOLUTE, { + await trt.execute(trait.COMMAND_COLOR_ABSOLUTE, { 'color': { 'spectrumRGB': 1052927 } @@ -407,7 +407,7 @@ async def test_color_temperature_light(hass): assert trait.ColorTemperatureTrait.supported(light.DOMAIN, light.SUPPORT_COLOR_TEMP) - trt = trait.ColorTemperatureTrait(State('light.bla', STATE_ON, { + trt = trait.ColorTemperatureTrait(hass, State('light.bla', STATE_ON, { light.ATTR_MIN_MIREDS: 200, light.ATTR_COLOR_TEMP: 300, light.ATTR_MAX_MIREDS: 500, @@ -438,14 +438,14 @@ async def test_color_temperature_light(hass): calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON) with pytest.raises(helpers.SmartHomeError) as err: - await trt.execute(hass, trait.COMMAND_COLOR_ABSOLUTE, { + await trt.execute(trait.COMMAND_COLOR_ABSOLUTE, { 'color': { 'temperature': 5555 } }) assert err.value.code == const.ERR_VALUE_OUT_OF_RANGE - await trt.execute(hass, trait.COMMAND_COLOR_ABSOLUTE, { + await trt.execute(trait.COMMAND_COLOR_ABSOLUTE, { 'color': { 'temperature': 2857 } @@ -461,13 +461,13 @@ async def test_scene_scene(hass): """Test Scene trait support for scene domain.""" assert trait.SceneTrait.supported(scene.DOMAIN, 0) - trt = trait.SceneTrait(State('scene.bla', scene.STATE)) + trt = trait.SceneTrait(hass, State('scene.bla', scene.STATE)) assert trt.sync_attributes() == {} assert trt.query_attributes() == {} assert trt.can_execute(trait.COMMAND_ACTIVATE_SCENE, {}) calls = async_mock_service(hass, scene.DOMAIN, SERVICE_TURN_ON) - await trt.execute(hass, trait.COMMAND_ACTIVATE_SCENE, {}) + await trt.execute(trait.COMMAND_ACTIVATE_SCENE, {}) assert len(calls) == 1 assert calls[0].data == { ATTR_ENTITY_ID: 'scene.bla', @@ -478,13 +478,13 @@ async def test_scene_script(hass): """Test Scene trait support for script domain.""" assert trait.SceneTrait.supported(script.DOMAIN, 0) - trt = trait.SceneTrait(State('script.bla', STATE_OFF)) + trt = trait.SceneTrait(hass, State('script.bla', STATE_OFF)) assert trt.sync_attributes() == {} assert trt.query_attributes() == {} assert trt.can_execute(trait.COMMAND_ACTIVATE_SCENE, {}) calls = async_mock_service(hass, script.DOMAIN, SERVICE_TURN_ON) - await trt.execute(hass, trait.COMMAND_ACTIVATE_SCENE, {}) + await trt.execute(trait.COMMAND_ACTIVATE_SCENE, {}) # We don't wait till script execution is done. await hass.async_block_till_done() @@ -501,7 +501,9 @@ async def test_temperature_setting_climate_range(hass): assert trait.TemperatureSettingTrait.supported( climate.DOMAIN, climate.SUPPORT_OPERATION_MODE) - trt = trait.TemperatureSettingTrait(State( + hass.config.units.temperature_unit = TEMP_FAHRENHEIT + + trt = trait.TemperatureSettingTrait(hass, State( 'climate.bla', climate.STATE_AUTO, { climate.ATTR_CURRENT_TEMPERATURE: 70, climate.ATTR_CURRENT_HUMIDITY: 25, @@ -515,8 +517,7 @@ async def test_temperature_setting_climate_range(hass): climate.ATTR_TARGET_TEMP_HIGH: 75, climate.ATTR_TARGET_TEMP_LOW: 65, climate.ATTR_MIN_TEMP: 50, - climate.ATTR_MAX_TEMP: 80, - ATTR_UNIT_OF_MEASUREMENT: TEMP_FAHRENHEIT, + climate.ATTR_MAX_TEMP: 80 })) assert trt.sync_attributes() == { 'availableThermostatModes': 'off,cool,heat,heatcool', @@ -535,7 +536,7 @@ async def test_temperature_setting_climate_range(hass): calls = async_mock_service( hass, climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE) - await trt.execute(hass, trait.COMMAND_THERMOSTAT_TEMPERATURE_SET_RANGE, { + await trt.execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SET_RANGE, { 'thermostatTemperatureSetpointHigh': 25, 'thermostatTemperatureSetpointLow': 20, }) @@ -548,7 +549,7 @@ async def test_temperature_setting_climate_range(hass): calls = async_mock_service( hass, climate.DOMAIN, climate.SERVICE_SET_OPERATION_MODE) - await trt.execute(hass, trait.COMMAND_THERMOSTAT_SET_MODE, { + await trt.execute(trait.COMMAND_THERMOSTAT_SET_MODE, { 'thermostatMode': 'heatcool', }) assert len(calls) == 1 @@ -558,11 +559,11 @@ async def test_temperature_setting_climate_range(hass): } with pytest.raises(helpers.SmartHomeError) as err: - await trt.execute( - hass, trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, { - 'thermostatTemperatureSetpoint': -100, - }) + await trt.execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, { + 'thermostatTemperatureSetpoint': -100, + }) assert err.value.code == const.ERR_VALUE_OUT_OF_RANGE + hass.config.units.temperature_unit = TEMP_CELSIUS async def test_temperature_setting_climate_setpoint(hass): @@ -571,7 +572,9 @@ async def test_temperature_setting_climate_setpoint(hass): assert trait.TemperatureSettingTrait.supported( climate.DOMAIN, climate.SUPPORT_OPERATION_MODE) - trt = trait.TemperatureSettingTrait(State( + hass.config.units.temperature_unit = TEMP_CELSIUS + + trt = trait.TemperatureSettingTrait(hass, State( 'climate.bla', climate.STATE_AUTO, { climate.ATTR_OPERATION_MODE: climate.STATE_COOL, climate.ATTR_OPERATION_LIST: [ @@ -581,8 +584,7 @@ async def test_temperature_setting_climate_setpoint(hass): climate.ATTR_MIN_TEMP: 10, climate.ATTR_MAX_TEMP: 30, climate.ATTR_TEMPERATURE: 18, - climate.ATTR_CURRENT_TEMPERATURE: 20, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, + climate.ATTR_CURRENT_TEMPERATURE: 20 })) assert trt.sync_attributes() == { 'availableThermostatModes': 'off,cool', @@ -601,12 +603,11 @@ async def test_temperature_setting_climate_setpoint(hass): hass, climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE) with pytest.raises(helpers.SmartHomeError): - await trt.execute( - hass, trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, { - 'thermostatTemperatureSetpoint': -100, - }) + await trt.execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, { + 'thermostatTemperatureSetpoint': -100, + }) - await trt.execute(hass, trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, { + await trt.execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, { 'thermostatTemperatureSetpoint': 19, }) assert len(calls) == 1 diff --git a/tests/components/group/test_init.py b/tests/components/group/test_init.py index a5e9bbc0b820c2..47101dd415a8e5 100644 --- a/tests/components/group/test_init.py +++ b/tests/components/group/test_init.py @@ -19,7 +19,7 @@ class TestComponentsGroup(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name @@ -28,7 +28,7 @@ def tearDown(self): self.hass.stop() def test_setup_group_with_mixed_groupable_states(self): - """Try to setup a group with mixed groupable states.""" + """Try to set up a group with mixed groupable states.""" self.hass.states.set('light.Bowl', STATE_ON) self.hass.states.set('device_tracker.Paulus', STATE_HOME) group.Group.create_group( @@ -41,7 +41,7 @@ def test_setup_group_with_mixed_groupable_states(self): group.ENTITY_ID_FORMAT.format('person_and_light')).state) def test_setup_group_with_a_non_existing_state(self): - """Try to setup a group with a non existing state.""" + """Try to set up a group with a non existing state.""" self.hass.states.set('light.Bowl', STATE_ON) grp = group.Group.create_group( @@ -62,7 +62,7 @@ def test_setup_group_with_non_groupable_states(self): self.assertEqual(STATE_UNKNOWN, grp.state) def test_setup_empty_group(self): - """Try to setup an empty group.""" + """Try to set up an empty group.""" grp = group.Group.create_group(self.hass, 'nothing', []) self.assertEqual(STATE_UNKNOWN, grp.state) diff --git a/tests/components/hangouts/__init__.py b/tests/components/hangouts/__init__.py new file mode 100644 index 00000000000000..81174356c2e13c --- /dev/null +++ b/tests/components/hangouts/__init__.py @@ -0,0 +1 @@ +"""Tests for the Hangouts Component.""" diff --git a/tests/components/hangouts/test_config_flow.py b/tests/components/hangouts/test_config_flow.py new file mode 100644 index 00000000000000..af9bb018919500 --- /dev/null +++ b/tests/components/hangouts/test_config_flow.py @@ -0,0 +1,92 @@ +"""Tests for the Google Hangouts config flow.""" + +from unittest.mock import patch + +from homeassistant import data_entry_flow +from homeassistant.components.hangouts import config_flow + + +async def test_flow_works(hass, aioclient_mock): + """Test config flow without 2fa.""" + flow = config_flow.HangoutsFlowHandler() + + flow.hass = hass + + with patch('hangups.get_auth'): + result = await flow.async_step_user( + {'email': 'test@test.com', 'password': '1232456'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['title'] == 'test@test.com' + + +async def test_flow_works_with_2fa(hass, aioclient_mock): + """Test config flow with 2fa.""" + from homeassistant.components.hangouts.hangups_utils import Google2FAError + + flow = config_flow.HangoutsFlowHandler() + + flow.hass = hass + + with patch('hangups.get_auth', side_effect=Google2FAError): + result = await flow.async_step_user( + {'email': 'test@test.com', 'password': '1232456'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == '2fa' + + with patch('hangups.get_auth'): + result = await flow.async_step_2fa({'2fa': 123456}) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['title'] == 'test@test.com' + + +async def test_flow_with_unknown_2fa(hass, aioclient_mock): + """Test config flow with invalid 2fa method.""" + from homeassistant.components.hangouts.hangups_utils import GoogleAuthError + + flow = config_flow.HangoutsFlowHandler() + + flow.hass = hass + + with patch('hangups.get_auth', + side_effect=GoogleAuthError('Unknown verification code input')): + result = await flow.async_step_user( + {'email': 'test@test.com', 'password': '1232456'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['errors']['base'] == 'invalid_2fa_method' + + +async def test_flow_invalid_login(hass, aioclient_mock): + """Test config flow with invalid 2fa method.""" + from homeassistant.components.hangouts.hangups_utils import GoogleAuthError + + flow = config_flow.HangoutsFlowHandler() + + flow.hass = hass + + with patch('hangups.get_auth', + side_effect=GoogleAuthError): + result = await flow.async_step_user( + {'email': 'test@test.com', 'password': '1232456'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['errors']['base'] == 'invalid_login' + + +async def test_flow_invalid_2fa(hass, aioclient_mock): + """Test config flow with 2fa.""" + from homeassistant.components.hangouts.hangups_utils import Google2FAError + + flow = config_flow.HangoutsFlowHandler() + + flow.hass = hass + + with patch('hangups.get_auth', side_effect=Google2FAError): + result = await flow.async_step_user( + {'email': 'test@test.com', 'password': '1232456'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == '2fa' + + with patch('hangups.get_auth', side_effect=Google2FAError): + result = await flow.async_step_2fa({'2fa': 123456}) + + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['errors']['base'] == 'invalid_2fa' diff --git a/tests/components/homekit/test_type_thermostats.py b/tests/components/homekit/test_type_thermostats.py index 45c340e58c4c27..687a9e9513c247 100644 --- a/tests/components/homekit/test_type_thermostats.py +++ b/tests/components/homekit/test_type_thermostats.py @@ -12,8 +12,8 @@ from homeassistant.components.homekit.const import ( PROP_MAX_VALUE, PROP_MIN_VALUE) from homeassistant.const import ( - ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, - CONF_TEMPERATURE_UNIT, STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT) + ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, CONF_TEMPERATURE_UNIT, STATE_OFF, + TEMP_FAHRENHEIT) from tests.common import async_mock_service from tests.components.homekit.common import patch_debounce @@ -58,8 +58,7 @@ async def test_default_thermostat(hass, hk_driver, cls): hass.states.async_set(entity_id, STATE_HEAT, {ATTR_OPERATION_MODE: STATE_HEAT, ATTR_TEMPERATURE: 22.0, - ATTR_CURRENT_TEMPERATURE: 18.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 18.0}) await hass.async_block_till_done() assert acc.char_target_temp.value == 22.0 assert acc.char_current_heat_cool.value == 1 @@ -70,8 +69,7 @@ async def test_default_thermostat(hass, hk_driver, cls): hass.states.async_set(entity_id, STATE_HEAT, {ATTR_OPERATION_MODE: STATE_HEAT, ATTR_TEMPERATURE: 22.0, - ATTR_CURRENT_TEMPERATURE: 23.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 23.0}) await hass.async_block_till_done() assert acc.char_target_temp.value == 22.0 assert acc.char_current_heat_cool.value == 0 @@ -82,8 +80,7 @@ async def test_default_thermostat(hass, hk_driver, cls): hass.states.async_set(entity_id, STATE_COOL, {ATTR_OPERATION_MODE: STATE_COOL, ATTR_TEMPERATURE: 20.0, - ATTR_CURRENT_TEMPERATURE: 25.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 25.0}) await hass.async_block_till_done() assert acc.char_target_temp.value == 20.0 assert acc.char_current_heat_cool.value == 2 @@ -94,8 +91,7 @@ async def test_default_thermostat(hass, hk_driver, cls): hass.states.async_set(entity_id, STATE_COOL, {ATTR_OPERATION_MODE: STATE_COOL, ATTR_TEMPERATURE: 20.0, - ATTR_CURRENT_TEMPERATURE: 19.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 19.0}) await hass.async_block_till_done() assert acc.char_target_temp.value == 20.0 assert acc.char_current_heat_cool.value == 0 @@ -106,8 +102,7 @@ async def test_default_thermostat(hass, hk_driver, cls): hass.states.async_set(entity_id, STATE_OFF, {ATTR_OPERATION_MODE: STATE_OFF, ATTR_TEMPERATURE: 22.0, - ATTR_CURRENT_TEMPERATURE: 18.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 18.0}) await hass.async_block_till_done() assert acc.char_target_temp.value == 22.0 assert acc.char_current_heat_cool.value == 0 @@ -119,8 +114,7 @@ async def test_default_thermostat(hass, hk_driver, cls): {ATTR_OPERATION_MODE: STATE_AUTO, ATTR_OPERATION_LIST: [STATE_HEAT, STATE_COOL], ATTR_TEMPERATURE: 22.0, - ATTR_CURRENT_TEMPERATURE: 18.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 18.0}) await hass.async_block_till_done() assert acc.char_target_temp.value == 22.0 assert acc.char_current_heat_cool.value == 1 @@ -132,8 +126,7 @@ async def test_default_thermostat(hass, hk_driver, cls): {ATTR_OPERATION_MODE: STATE_AUTO, ATTR_OPERATION_LIST: [STATE_HEAT, STATE_COOL], ATTR_TEMPERATURE: 22.0, - ATTR_CURRENT_TEMPERATURE: 25.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 25.0}) await hass.async_block_till_done() assert acc.char_target_temp.value == 22.0 assert acc.char_current_heat_cool.value == 2 @@ -145,8 +138,7 @@ async def test_default_thermostat(hass, hk_driver, cls): {ATTR_OPERATION_MODE: STATE_AUTO, ATTR_OPERATION_LIST: [STATE_HEAT, STATE_COOL], ATTR_TEMPERATURE: 22.0, - ATTR_CURRENT_TEMPERATURE: 22.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 22.0}) await hass.async_block_till_done() assert acc.char_target_temp.value == 22.0 assert acc.char_current_heat_cool.value == 0 @@ -201,8 +193,7 @@ async def test_auto_thermostat(hass, hk_driver, cls): {ATTR_OPERATION_MODE: STATE_AUTO, ATTR_TARGET_TEMP_HIGH: 22.0, ATTR_TARGET_TEMP_LOW: 20.0, - ATTR_CURRENT_TEMPERATURE: 18.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 18.0}) await hass.async_block_till_done() assert acc.char_heating_thresh_temp.value == 20.0 assert acc.char_cooling_thresh_temp.value == 22.0 @@ -215,8 +206,7 @@ async def test_auto_thermostat(hass, hk_driver, cls): {ATTR_OPERATION_MODE: STATE_AUTO, ATTR_TARGET_TEMP_HIGH: 23.0, ATTR_TARGET_TEMP_LOW: 19.0, - ATTR_CURRENT_TEMPERATURE: 24.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 24.0}) await hass.async_block_till_done() assert acc.char_heating_thresh_temp.value == 19.0 assert acc.char_cooling_thresh_temp.value == 23.0 @@ -229,8 +219,7 @@ async def test_auto_thermostat(hass, hk_driver, cls): {ATTR_OPERATION_MODE: STATE_AUTO, ATTR_TARGET_TEMP_HIGH: 23.0, ATTR_TARGET_TEMP_LOW: 19.0, - ATTR_CURRENT_TEMPERATURE: 21.0, - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) + ATTR_CURRENT_TEMPERATURE: 21.0}) await hass.async_block_till_done() assert acc.char_heating_thresh_temp.value == 19.0 assert acc.char_cooling_thresh_temp.value == 23.0 @@ -334,8 +323,7 @@ async def test_thermostat_fahrenheit(hass, hk_driver, cls): ATTR_TARGET_TEMP_HIGH: 75.2, ATTR_TARGET_TEMP_LOW: 68, ATTR_TEMPERATURE: 71.6, - ATTR_CURRENT_TEMPERATURE: 73.4, - ATTR_UNIT_OF_MEASUREMENT: TEMP_FAHRENHEIT}) + ATTR_CURRENT_TEMPERATURE: 73.4}) await hass.async_block_till_done() assert acc.char_heating_thresh_temp.value == 20.0 assert acc.char_cooling_thresh_temp.value == 24.0 diff --git a/tests/components/http/test_auth.py b/tests/components/http/test_auth.py index 8e7a62e2e9faa5..e96531c096179e 100644 --- a/tests/components/http/test_auth.py +++ b/tests/components/http/test_auth.py @@ -38,7 +38,7 @@ async def mock_handler(request): @pytest.fixture def app(hass): - """Fixture to setup a web.Application.""" + """Fixture to set up a web.Application.""" app = web.Application() app['hass'] = hass app.router.add_get('/', mock_handler) @@ -48,7 +48,7 @@ def app(hass): @pytest.fixture def app2(hass): - """Fixture to setup a web.Application without real_ip middleware.""" + """Fixture to set up a web.Application without real_ip middleware.""" app = web.Application() app['hass'] = hass app.router.add_get('/', mock_handler) diff --git a/tests/components/http/test_cors.py b/tests/components/http/test_cors.py index a510d2b3829e75..c95146d5ccae61 100644 --- a/tests/components/http/test_cors.py +++ b/tests/components/http/test_cors.py @@ -49,7 +49,7 @@ async def mock_handler(request): @pytest.fixture def client(loop, aiohttp_client): - """Fixture to setup a web.Application.""" + """Fixture to set up a web.Application.""" app = web.Application() app.router.add_get('/', mock_handler) setup_cors(app, [TRUSTED_ORIGIN]) diff --git a/tests/components/http/test_real_ip.py b/tests/components/http/test_real_ip.py index 6cf6fec6bce994..c28d810d41b864 100644 --- a/tests/components/http/test_real_ip.py +++ b/tests/components/http/test_real_ip.py @@ -8,7 +8,7 @@ async def mock_handler(request): - """Handler that returns the real IP as text.""" + """Return the real IP as text.""" return web.Response(text=str(request[KEY_REAL_IP])) diff --git a/tests/components/hue/test_init.py b/tests/components/hue/test_init.py index ea656ba8fc6723..d12270cd908dc1 100644 --- a/tests/components/hue/test_init.py +++ b/tests/components/hue/test_init.py @@ -8,7 +8,7 @@ async def test_setup_with_no_config(hass): - """Test that we do not discover anything or try to setup a bridge.""" + """Test that we do not discover anything or try to set up a bridge.""" with patch.object(hass, 'config_entries') as mock_config_entries, \ patch.object(hue, 'configured_hosts', return_value=[]): assert await async_setup_component(hass, hue.DOMAIN, {}) is True diff --git a/tests/components/image_processing/test_facebox.py b/tests/components/image_processing/test_facebox.py index b1d9fb8bf79d30..62e47a07c08429 100644 --- a/tests/components/image_processing/test_facebox.py +++ b/tests/components/image_processing/test_facebox.py @@ -141,13 +141,13 @@ def test_valid_file_path(): async def test_setup_platform(hass, mock_healthybox): - """Setup platform with one entity.""" + """Set up platform with one entity.""" await async_setup_component(hass, ip.DOMAIN, VALID_CONFIG) assert hass.states.get(VALID_ENTITY_ID) async def test_setup_platform_with_auth(hass, mock_healthybox): - """Setup platform with one entity and auth.""" + """Set up platform with one entity and auth.""" valid_config_auth = VALID_CONFIG.copy() valid_config_auth[ip.DOMAIN][CONF_USERNAME] = MOCK_USERNAME valid_config_auth[ip.DOMAIN][CONF_PASSWORD] = MOCK_PASSWORD @@ -297,7 +297,7 @@ async def test_teach_service( async def test_setup_platform_with_name(hass, mock_healthybox): - """Setup platform with one entity and a name.""" + """Set up platform with one entity and a name.""" named_entity_id = 'image_processing.{}'.format(MOCK_NAME) valid_config_named = VALID_CONFIG.copy() diff --git a/tests/components/image_processing/test_init.py b/tests/components/image_processing/test_init.py index ab2e3be11d68a4..4240e173b26460 100644 --- a/tests/components/image_processing/test_init.py +++ b/tests/components/image_processing/test_init.py @@ -16,7 +16,7 @@ class TestSetupImageProcessing: """Test class for setup image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self): @@ -24,7 +24,7 @@ def teardown_method(self): self.hass.stop() def test_setup_component(self): - """Setup demo platform on image_process component.""" + """Set up demo platform on image_process component.""" config = { ip.DOMAIN: { 'platform': 'demo' @@ -35,7 +35,7 @@ def test_setup_component(self): setup_component(self.hass, ip.DOMAIN, config) def test_setup_component_with_service(self): - """Setup demo platform on image_process component test service.""" + """Set up demo platform on image_process component test service.""" config = { ip.DOMAIN: { 'platform': 'demo' @@ -52,7 +52,7 @@ class TestImageProcessing: """Test class for image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() setup_component( @@ -113,7 +113,7 @@ class TestImageProcessingAlpr: """Test class for alpr image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() config = { @@ -149,7 +149,7 @@ def teardown_method(self): self.hass.stop() def test_alpr_event_single_call(self, aioclient_mock): - """Setup and scan a picture and test plates from event.""" + """Set up and scan a picture and test plates from event.""" aioclient_mock.get(self.url, content=b'image') ip.scan(self.hass, entity_id='image_processing.demo_alpr') @@ -168,7 +168,7 @@ def test_alpr_event_single_call(self, aioclient_mock): assert event_data[0]['entity_id'] == 'image_processing.demo_alpr' def test_alpr_event_double_call(self, aioclient_mock): - """Setup and scan a picture and test plates from event.""" + """Set up and scan a picture and test plates from event.""" aioclient_mock.get(self.url, content=b'image') ip.scan(self.hass, entity_id='image_processing.demo_alpr') @@ -192,7 +192,7 @@ def test_alpr_event_double_call(self, aioclient_mock): new_callable=PropertyMock(return_value=95)) def test_alpr_event_single_call_confidence(self, confidence_mock, aioclient_mock): - """Setup and scan a picture and test plates from event.""" + """Set up and scan a picture and test plates from event.""" aioclient_mock.get(self.url, content=b'image') ip.scan(self.hass, entity_id='image_processing.demo_alpr') @@ -215,7 +215,7 @@ class TestImageProcessingFace: """Test class for face image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() config = { @@ -251,7 +251,7 @@ def teardown_method(self): self.hass.stop() def test_face_event_call(self, aioclient_mock): - """Setup and scan a picture and test faces from event.""" + """Set up and scan a picture and test faces from event.""" aioclient_mock.get(self.url, content=b'image') ip.scan(self.hass, entity_id='image_processing.demo_face') @@ -276,7 +276,7 @@ def test_face_event_call(self, aioclient_mock): 'DemoImageProcessingFace.confidence', new_callable=PropertyMock(return_value=None)) def test_face_event_call_no_confidence(self, mock_config, aioclient_mock): - """Setup and scan a picture and test faces from event.""" + """Set up and scan a picture and test faces from event.""" aioclient_mock.get(self.url, content=b'image') ip.scan(self.hass, entity_id='image_processing.demo_face') diff --git a/tests/components/image_processing/test_microsoft_face_detect.py b/tests/components/image_processing/test_microsoft_face_detect.py index acc2519c9b722b..9047c5b847549b 100644 --- a/tests/components/image_processing/test_microsoft_face_detect.py +++ b/tests/components/image_processing/test_microsoft_face_detect.py @@ -15,7 +15,7 @@ class TestMicrosoftFaceDetectSetup: """Test class for image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self): @@ -25,7 +25,7 @@ def teardown_method(self): @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_setup_platform(self, store_mock): - """Setup platform with one entity.""" + """Set up platform with one entity.""" config = { ip.DOMAIN: { 'platform': 'microsoft_face_detect', @@ -51,7 +51,7 @@ def test_setup_platform(self, store_mock): @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_setup_platform_name(self, store_mock): - """Setup platform with one entity and set name.""" + """Set up platform with one entity and set name.""" config = { ip.DOMAIN: { 'platform': 'microsoft_face_detect', @@ -78,7 +78,7 @@ class TestMicrosoftFaceDetect: """Test class for image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.config = { @@ -108,7 +108,7 @@ def teardown_method(self): 'MicrosoftFaceDetectEntity.should_poll', new_callable=PropertyMock(return_value=False)) def test_ms_detect_process_image(self, poll_mock, aioclient_mock): - """Setup and scan a picture and test plates from event.""" + """Set up and scan a picture and test plates from event.""" aioclient_mock.get( self.endpoint_url.format("persongroups"), text=load_fixture('microsoft_face_persongroups.json') diff --git a/tests/components/image_processing/test_microsoft_face_identify.py b/tests/components/image_processing/test_microsoft_face_identify.py index 8797f661767eb2..6d3eae38728f28 100644 --- a/tests/components/image_processing/test_microsoft_face_identify.py +++ b/tests/components/image_processing/test_microsoft_face_identify.py @@ -15,7 +15,7 @@ class TestMicrosoftFaceIdentifySetup: """Test class for image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self): @@ -25,7 +25,7 @@ def teardown_method(self): @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_setup_platform(self, store_mock): - """Setup platform with one entity.""" + """Set up platform with one entity.""" config = { ip.DOMAIN: { 'platform': 'microsoft_face_identify', @@ -51,7 +51,7 @@ def test_setup_platform(self, store_mock): @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_setup_platform_name(self, store_mock): - """Setup platform with one entity and set name.""" + """Set up platform with one entity and set name.""" config = { ip.DOMAIN: { 'platform': 'microsoft_face_identify', @@ -79,7 +79,7 @@ class TestMicrosoftFaceIdentify: """Test class for image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.config = { @@ -109,7 +109,7 @@ def teardown_method(self): 'MicrosoftFaceIdentifyEntity.should_poll', new_callable=PropertyMock(return_value=False)) def test_ms_identify_process_image(self, poll_mock, aioclient_mock): - """Setup and scan a picture and test plates from event.""" + """Set up and scan a picture and test plates from event.""" aioclient_mock.get( self.endpoint_url.format("persongroups"), text=load_fixture('microsoft_face_persongroups.json') diff --git a/tests/components/image_processing/test_openalpr_cloud.py b/tests/components/image_processing/test_openalpr_cloud.py index 65e735a6f7e01d..2d6015e3fe7e2c 100644 --- a/tests/components/image_processing/test_openalpr_cloud.py +++ b/tests/components/image_processing/test_openalpr_cloud.py @@ -16,7 +16,7 @@ class TestOpenAlprCloudSetup: """Test class for image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self): @@ -24,7 +24,7 @@ def teardown_method(self): self.hass.stop() def test_setup_platform(self): - """Setup platform with one entity.""" + """Set up platform with one entity.""" config = { ip.DOMAIN: { 'platform': 'openalpr_cloud', @@ -45,7 +45,7 @@ def test_setup_platform(self): assert self.hass.states.get('image_processing.openalpr_demo_camera') def test_setup_platform_name(self): - """Setup platform with one entity and set name.""" + """Set up platform with one entity and set name.""" config = { ip.DOMAIN: { 'platform': 'openalpr_cloud', @@ -67,7 +67,7 @@ def test_setup_platform_name(self): assert self.hass.states.get('image_processing.test_local') def test_setup_platform_without_api_key(self): - """Setup platform with one entity without api_key.""" + """Set up platform with one entity without api_key.""" config = { ip.DOMAIN: { 'platform': 'openalpr_cloud', @@ -85,7 +85,7 @@ def test_setup_platform_without_api_key(self): setup_component(self.hass, ip.DOMAIN, config) def test_setup_platform_without_region(self): - """Setup platform with one entity without region.""" + """Set up platform with one entity without region.""" config = { ip.DOMAIN: { 'platform': 'openalpr_cloud', @@ -107,7 +107,7 @@ class TestOpenAlprCloud: """Test class for image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() config = { @@ -151,7 +151,7 @@ def teardown_method(self): self.hass.stop() def test_openalpr_process_image(self, aioclient_mock): - """Setup and scan a picture and test plates from event.""" + """Set up and scan a picture and test plates from event.""" aioclient_mock.post( OPENALPR_API_URL, params=self.params, text=load_fixture('alpr_cloud.json'), status=200 @@ -179,7 +179,7 @@ def test_openalpr_process_image(self, aioclient_mock): 'image_processing.test_local' def test_openalpr_process_image_api_error(self, aioclient_mock): - """Setup and scan a picture and test api error.""" + """Set up and scan a picture and test api error.""" aioclient_mock.post( OPENALPR_API_URL, params=self.params, text="{'error': 'error message'}", status=400 @@ -195,7 +195,7 @@ def test_openalpr_process_image_api_error(self, aioclient_mock): assert len(self.alpr_events) == 0 def test_openalpr_process_image_api_timeout(self, aioclient_mock): - """Setup and scan a picture and test api error.""" + """Set up and scan a picture and test api error.""" aioclient_mock.post( OPENALPR_API_URL, params=self.params, exc=asyncio.TimeoutError() diff --git a/tests/components/image_processing/test_openalpr_local.py b/tests/components/image_processing/test_openalpr_local.py index 38e94166c5a983..772d66670a0caf 100644 --- a/tests/components/image_processing/test_openalpr_local.py +++ b/tests/components/image_processing/test_openalpr_local.py @@ -30,7 +30,7 @@ class TestOpenAlprLocalSetup: """Test class for image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self): @@ -38,7 +38,7 @@ def teardown_method(self): self.hass.stop() def test_setup_platform(self): - """Setup platform with one entity.""" + """Set up platform with one entity.""" config = { ip.DOMAIN: { 'platform': 'openalpr_local', @@ -58,7 +58,7 @@ def test_setup_platform(self): assert self.hass.states.get('image_processing.openalpr_demo_camera') def test_setup_platform_name(self): - """Setup platform with one entity and set name.""" + """Set up platform with one entity and set name.""" config = { ip.DOMAIN: { 'platform': 'openalpr_local', @@ -79,7 +79,7 @@ def test_setup_platform_name(self): assert self.hass.states.get('image_processing.test_local') def test_setup_platform_without_region(self): - """Setup platform with one entity without region.""" + """Set up platform with one entity without region.""" config = { ip.DOMAIN: { 'platform': 'openalpr_local', @@ -100,7 +100,7 @@ class TestOpenAlprLocal: """Test class for image processing.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() config = { @@ -143,7 +143,7 @@ def teardown_method(self): @patch('asyncio.create_subprocess_exec', return_value=mock_async_subprocess()) def test_openalpr_process_image(self, popen_mock, aioclient_mock): - """Setup and scan a picture and test plates from event.""" + """Set up and scan a picture and test plates from event.""" aioclient_mock.get(self.url, content=b'image') ip.scan(self.hass, entity_id='image_processing.test_local') diff --git a/tests/components/light/test_demo.py b/tests/components/light/test_demo.py index 8ba6385166b46c..db575bba5ba9a4 100644 --- a/tests/components/light/test_demo.py +++ b/tests/components/light/test_demo.py @@ -15,7 +15,7 @@ class TestDemoLight(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.assertTrue(setup_component(self.hass, light.DOMAIN, {'light': { 'platform': 'demo', diff --git a/tests/components/light/test_group.py b/tests/components/light/test_group.py index 901535c5465139..4619e9fb9bd507 100644 --- a/tests/components/light/test_group.py +++ b/tests/components/light/test_group.py @@ -346,13 +346,13 @@ async def test_service_calls(hass): async def test_invalid_service_calls(hass): """Test invalid service call arguments get discarded.""" - add_devices = MagicMock() + add_entities = MagicMock() await group.async_setup_platform(hass, { 'entities': ['light.test1', 'light.test2'] - }, add_devices) + }, add_entities) - assert add_devices.call_count == 1 - grouped_light = add_devices.call_args[0][0][0] + assert add_entities.call_count == 1 + grouped_light = add_entities.call_args[0][0][0] grouped_light.hass = hass with asynctest.patch.object(hass.services, 'async_call') as mock_call: diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index 4d779eef461271..66dbadb5c380af 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -14,7 +14,7 @@ from homeassistant.helpers.intent import IntentHandleError from tests.common import ( - async_mock_service, mock_service, get_test_home_assistant) + async_mock_service, mock_service, get_test_home_assistant, mock_storage) class TestLight(unittest.TestCase): @@ -22,7 +22,7 @@ class TestLight(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name @@ -333,10 +333,11 @@ def _mock_open(path): "group.all_lights.default,.4,.6,99\n" with mock.patch('os.path.isfile', side_effect=_mock_isfile): with mock.patch('builtins.open', side_effect=_mock_open): - self.assertTrue(setup_component( - self.hass, light.DOMAIN, - {light.DOMAIN: {CONF_PLATFORM: 'test'}} - )) + with mock_storage(): + self.assertTrue(setup_component( + self.hass, light.DOMAIN, + {light.DOMAIN: {CONF_PLATFORM: 'test'}} + )) dev, _, _ = platform.DEVICES light.turn_on(self.hass, dev.entity_id) @@ -371,10 +372,11 @@ def _mock_open(path): "light.ceiling_2.default,.6,.6,100\n" with mock.patch('os.path.isfile', side_effect=_mock_isfile): with mock.patch('builtins.open', side_effect=_mock_open): - self.assertTrue(setup_component( - self.hass, light.DOMAIN, - {light.DOMAIN: {CONF_PLATFORM: 'test'}} - )) + with mock_storage(): + self.assertTrue(setup_component( + self.hass, light.DOMAIN, + {light.DOMAIN: {CONF_PLATFORM: 'test'}} + )) dev = next(filter(lambda x: x.entity_id == 'light.ceiling_2', platform.DEVICES)) diff --git a/tests/components/light/test_litejet.py b/tests/components/light/test_litejet.py index dd4b4b4a56ed74..3040c95e0ac007 100644 --- a/tests/components/light/test_litejet.py +++ b/tests/components/light/test_litejet.py @@ -21,7 +21,7 @@ class TestLiteJetLight(unittest.TestCase): @mock.patch('pylitejet.LiteJet') def setup_method(self, method, mock_pylitejet): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.start() diff --git a/tests/components/light/test_mochad.py b/tests/components/light/test_mochad.py index 5c82ab06085358..fa122777ca4402 100644 --- a/tests/components/light/test_mochad.py +++ b/tests/components/light/test_mochad.py @@ -28,7 +28,7 @@ class TestMochadSwitchSetup(unittest.TestCase): THING = 'light' def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): @@ -57,7 +57,7 @@ class TestMochadLight(unittest.TestCase): """Test for mochad light platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() controller_mock = mock.MagicMock() dev_dict = {'address': 'a1', 'name': 'fake_light', @@ -76,25 +76,25 @@ def test_name(self): def test_turn_on_with_no_brightness(self): """Test turn_on.""" self.light.turn_on() - self.light.device.send_cmd.assert_called_once_with('on') + self.light.light.send_cmd.assert_called_once_with('on') def test_turn_on_with_brightness(self): """Test turn_on.""" self.light.turn_on(brightness=45) - self.light.device.send_cmd.assert_has_calls( + self.light.light.send_cmd.assert_has_calls( [mock.call('on'), mock.call('dim 25')]) def test_turn_off(self): """Test turn_off.""" self.light.turn_off() - self.light.device.send_cmd.assert_called_once_with('off') + self.light.light.send_cmd.assert_called_once_with('off') class TestMochadLight256Levels(unittest.TestCase): """Test for mochad light platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() controller_mock = mock.MagicMock() dev_dict = {'address': 'a1', 'name': 'fake_light', @@ -109,24 +109,24 @@ def teardown_method(self, method): def test_turn_on_with_no_brightness(self): """Test turn_on.""" self.light.turn_on() - self.light.device.send_cmd.assert_called_once_with('xdim 255') + self.light.light.send_cmd.assert_called_once_with('xdim 255') def test_turn_on_with_brightness(self): """Test turn_on.""" self.light.turn_on(brightness=45) - self.light.device.send_cmd.assert_called_once_with('xdim 45') + self.light.light.send_cmd.assert_called_once_with('xdim 45') def test_turn_off(self): """Test turn_off.""" self.light.turn_off() - self.light.device.send_cmd.assert_called_once_with('off') + self.light.light.send_cmd.assert_called_once_with('off') class TestMochadLight64Levels(unittest.TestCase): """Test for mochad light platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() controller_mock = mock.MagicMock() dev_dict = {'address': 'a1', 'name': 'fake_light', @@ -141,14 +141,14 @@ def teardown_method(self, method): def test_turn_on_with_no_brightness(self): """Test turn_on.""" self.light.turn_on() - self.light.device.send_cmd.assert_called_once_with('xdim 63') + self.light.light.send_cmd.assert_called_once_with('xdim 63') def test_turn_on_with_brightness(self): """Test turn_on.""" self.light.turn_on(brightness=45) - self.light.device.send_cmd.assert_called_once_with('xdim 11') + self.light.light.send_cmd.assert_called_once_with('xdim 11') def test_turn_off(self): """Test turn_off.""" self.light.turn_off() - self.light.device.send_cmd.assert_called_once_with('off') + self.light.light.send_cmd.assert_called_once_with('off') diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index 404d60c0a2e0fc..1245411dcc4fe5 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -158,7 +158,7 @@ class TestLightMQTT(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_publish = mock_mqtt_component(self.hass) diff --git a/tests/components/light/test_mqtt_json.py b/tests/components/light/test_mqtt_json.py index f16685b3575827..90875285f17b63 100644 --- a/tests/components/light/test_mqtt_json.py +++ b/tests/components/light/test_mqtt_json.py @@ -107,7 +107,7 @@ class TestLightMQTTJSON(unittest.TestCase): """Test the MQTT JSON light.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_publish = mock_mqtt_component(self.hass) diff --git a/tests/components/light/test_mqtt_template.py b/tests/components/light/test_mqtt_template.py index 1cf09f2ccb5a95..8f92d659b9bd94 100644 --- a/tests/components/light/test_mqtt_template.py +++ b/tests/components/light/test_mqtt_template.py @@ -43,7 +43,7 @@ class TestLightMQTTTemplate(unittest.TestCase): """Test the MQTT Template light.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_publish = mock_mqtt_component(self.hass) diff --git a/tests/components/light/test_rflink.py b/tests/components/light/test_rflink.py index a6e6d3c1a8570b..c55c16077e0b01 100644 --- a/tests/components/light/test_rflink.py +++ b/tests/components/light/test_rflink.py @@ -356,11 +356,10 @@ def test_signal_repetitions_cancelling(hass, monkeypatch): yield from hass.async_block_till_done() - print(protocol.send_command_ack.call_args_list) - assert protocol.send_command_ack.call_args_list[0][0][1] == 'off' - assert protocol.send_command_ack.call_args_list[1][0][1] == 'on' - assert protocol.send_command_ack.call_args_list[2][0][1] == 'on' - assert protocol.send_command_ack.call_args_list[3][0][1] == 'on' + assert protocol.send_command_ack.call_args_list[0][0][1] == 'on' + assert protocol.send_command_ack.call_args_list[1][0][1] == 'off' + assert protocol.send_command_ack.call_args_list[2][0][1] == 'off' + assert protocol.send_command_ack.call_args_list[3][0][1] == 'off' @asyncio.coroutine diff --git a/tests/components/light/test_rfxtrx.py b/tests/components/light/test_rfxtrx.py index a1f63e4574825f..8a8e94ec179cdd 100644 --- a/tests/components/light/test_rfxtrx.py +++ b/tests/components/light/test_rfxtrx.py @@ -14,7 +14,7 @@ class TestLightRfxtrx(unittest.TestCase): """Test the Rfxtrx light platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'rfxtrx') diff --git a/tests/components/light/test_template.py b/tests/components/light/test_template.py index 962760672f1634..cc481fabb5cc6e 100644 --- a/tests/components/light/test_template.py +++ b/tests/components/light/test_template.py @@ -20,7 +20,7 @@ class TestTemplateLight: # pylint: disable=invalid-name def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.calls = [] diff --git a/tests/components/lock/test_demo.py b/tests/components/lock/test_demo.py index 1d774248f350cf..500cc7f9a6a671 100644 --- a/tests/components/lock/test_demo.py +++ b/tests/components/lock/test_demo.py @@ -14,7 +14,7 @@ class TestLockDemo(unittest.TestCase): """Test the demo lock.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.assertTrue(setup_component(self.hass, lock.DOMAIN, { 'lock': { diff --git a/tests/components/lock/test_mqtt.py b/tests/components/lock/test_mqtt.py index f87b8f8b74befa..4ef8532f39ea46 100644 --- a/tests/components/lock/test_mqtt.py +++ b/tests/components/lock/test_mqtt.py @@ -13,7 +13,7 @@ class TestLockMQTT(unittest.TestCase): """Test the MQTT lock.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_publish = mock_mqtt_component(self.hass) diff --git a/tests/components/media_player/test_async_helpers.py b/tests/components/media_player/test_async_helpers.py index 11e324e9132b2d..5908ea02a37ccc 100644 --- a/tests/components/media_player/test_async_helpers.py +++ b/tests/components/media_player/test_async_helpers.py @@ -123,7 +123,7 @@ class TestAsyncMediaPlayer(unittest.TestCase): """Test the media_player module.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.player = AsyncMediaPlayer(self.hass) @@ -176,7 +176,7 @@ class TestSyncMediaPlayer(unittest.TestCase): """Test the media_player module.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.player = SyncMediaPlayer(self.hass) diff --git a/tests/components/media_player/test_cast.py b/tests/components/media_player/test_cast.py index 47be39c68e5836..8fe285a59cd74a 100644 --- a/tests/components/media_player/test_cast.py +++ b/tests/components/media_player/test_cast.py @@ -48,26 +48,26 @@ def get_fake_chromecast_info(host='192.168.178.42', port=8009, async def async_setup_cast(hass, config=None, discovery_info=None): - """Helper to setup the cast platform.""" + """Set up the cast platform.""" if config is None: config = {} - add_devices = Mock() + add_entities = Mock() - await cast.async_setup_platform(hass, config, add_devices, + await cast.async_setup_platform(hass, config, add_entities, discovery_info=discovery_info) await hass.async_block_till_done() - return add_devices + return add_entities async def async_setup_cast_internal_discovery(hass, config=None, discovery_info=None): - """Setup the cast platform and the discovery.""" + """Set up the cast platform and the discovery.""" listener = MagicMock(services={}) with patch('pychromecast.start_discovery', return_value=(listener, None)) as start_discovery: - add_devices = await async_setup_cast(hass, config, discovery_info) + add_entities = await async_setup_cast(hass, config, discovery_info) await hass.async_block_till_done() await hass.async_block_till_done() @@ -80,12 +80,12 @@ def discover_chromecast(service_name: str, info: ChromecastInfo) -> None: listener.services[service_name] = attr.astuple(info) discovery_callback(service_name) - return discover_chromecast, add_devices + return discover_chromecast, add_entities async def async_setup_media_player_cast(hass: HomeAssistantType, info: ChromecastInfo): - """Setup the cast platform with async_setup_component.""" + """Set up the cast platform with async_setup_component.""" chromecast = get_fake_chromecast(info) cast.CastStatusListener = MagicMock() @@ -214,30 +214,30 @@ async def test_normal_chromecast_not_starting_discovery(hass): with patch('homeassistant.components.media_player.cast.' '_setup_internal_discovery') as setup_discovery: # normal (non-group) chromecast shouldn't start discovery. - add_devices = await async_setup_cast(hass, {'host': 'host1'}) + add_entities = await async_setup_cast(hass, {'host': 'host1'}) await hass.async_block_till_done() - assert add_devices.call_count == 1 + assert add_entities.call_count == 1 assert setup_discovery.call_count == 0 # Same entity twice - add_devices = await async_setup_cast(hass, {'host': 'host1'}) + add_entities = await async_setup_cast(hass, {'host': 'host1'}) await hass.async_block_till_done() - assert add_devices.call_count == 0 + assert add_entities.call_count == 0 assert setup_discovery.call_count == 0 hass.data[cast.ADDED_CAST_DEVICES_KEY] = set() - add_devices = await async_setup_cast( + add_entities = await async_setup_cast( hass, discovery_info={'host': 'host1', 'port': 8009}) await hass.async_block_till_done() - assert add_devices.call_count == 1 + assert add_entities.call_count == 1 assert setup_discovery.call_count == 0 # group should start discovery. hass.data[cast.ADDED_CAST_DEVICES_KEY] = set() - add_devices = await async_setup_cast( + add_entities = await async_setup_cast( hass, discovery_info={'host': 'host1', 'port': 42}) await hass.async_block_till_done() - assert add_devices.call_count == 0 + assert add_entities.call_count == 0 assert setup_discovery.call_count == 1 diff --git a/tests/components/media_player/test_demo.py b/tests/components/media_player/test_demo.py index 65ca2eb6a01956..121018e7541a15 100644 --- a/tests/components/media_player/test_demo.py +++ b/tests/components/media_player/test_demo.py @@ -25,7 +25,7 @@ class TestDemoMediaPlayer(unittest.TestCase): """Test the media_player module.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): @@ -227,7 +227,7 @@ class TestMediaPlayerWeb(unittest.TestCase): """Test the media player web views sensor.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() assert setup_component(self.hass, http.DOMAIN, { diff --git a/tests/components/media_player/test_samsungtv.py b/tests/components/media_player/test_samsungtv.py index 349067f7cd30c8..2fedfb6a65e76d 100644 --- a/tests/components/media_player/test_samsungtv.py +++ b/tests/components/media_player/test_samsungtv.py @@ -52,7 +52,7 @@ class TestSamsungTv(unittest.TestCase): @MockDependency('samsungctl') @MockDependency('wakeonlan') def setUp(self, samsung_mock, wol_mock): - """Setting up test environment.""" + """Set up test environment.""" self.hass = tests.common.get_test_home_assistant() self.hass.start() self.hass.block_till_done() @@ -72,9 +72,9 @@ def test_setup(self, samsung_mock, wol_mock): """Testing setup of platform.""" with mock.patch( 'homeassistant.components.media_player.samsungtv.socket'): - add_devices = mock.Mock() + add_entities = mock.Mock() setup_platform( - self.hass, WORKING_CONFIG, add_devices) + self.hass, WORKING_CONFIG, add_entities) @MockDependency('samsungctl') @MockDependency('wakeonlan') @@ -82,8 +82,8 @@ def test_setup_discovery(self, samsung_mock, wol_mock): """Testing setup of platform with discovery.""" with mock.patch( 'homeassistant.components.media_player.samsungtv.socket'): - add_devices = mock.Mock() - setup_platform(self.hass, {}, add_devices, + add_entities = mock.Mock() + setup_platform(self.hass, {}, add_entities, discovery_info=DISCOVERY_INFO) @MockDependency('samsungctl') @@ -94,11 +94,11 @@ def test_setup_none(self, samsung_mock, wol_mock, mocked_warn): """Testing setup of platform with no data.""" with mock.patch( 'homeassistant.components.media_player.samsungtv.socket'): - add_devices = mock.Mock() - setup_platform(self.hass, {}, add_devices, + add_entities = mock.Mock() + setup_platform(self.hass, {}, add_entities, discovery_info=None) mocked_warn.assert_called_once_with("Cannot determine device") - add_devices.assert_not_called() + add_entities.assert_not_called() @mock.patch( 'homeassistant.components.media_player.samsungtv.subprocess.Popen' diff --git a/tests/components/media_player/test_sonos.py b/tests/components/media_player/test_sonos.py index 7d0d675f66fcc1..5a845738fa394a 100644 --- a/tests/components/media_player/test_sonos.py +++ b/tests/components/media_player/test_sonos.py @@ -121,13 +121,13 @@ def group(self): return -def add_devices_factory(hass): +def add_entities_factory(hass): """Add devices factory.""" - def add_devices(devices, update_befor_add=False): + def add_entities(devices, update_befor_add=False): """Fake add device.""" hass.data[sonos.DATA_SONOS].devices = devices - return add_devices + return add_entities class TestSonosMediaPlayer(unittest.TestCase): @@ -135,7 +135,7 @@ class TestSonosMediaPlayer(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def monkey_available(self): @@ -157,7 +157,7 @@ def tearDown(self): @mock.patch('socket.create_connection', side_effect=socket.error()) def test_ensure_setup_discovery(self, *args): """Test a single device using the autodiscovery provided by HASS.""" - sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), { + sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), { 'host': '192.0.2.1' }) @@ -240,7 +240,7 @@ def test_ensure_setup_config_hosts_list(self, *args): @mock.patch('socket.create_connection', side_effect=socket.error()) def test_ensure_setup_sonos_discovery(self, *args): """Test a single device using the autodiscovery provided by Sonos.""" - sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass)) + sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass)) devices = list(self.hass.data[sonos.DATA_SONOS].devices) self.assertEqual(len(devices), 1) self.assertEqual(devices[0].name, 'Kitchen') @@ -250,7 +250,7 @@ def test_ensure_setup_sonos_discovery(self, *args): @mock.patch.object(SoCoMock, 'set_sleep_timer') def test_sonos_set_sleep_timer(self, set_sleep_timerMock, *args): """Ensuring soco methods called for sonos_set_sleep_timer service.""" - sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), { + sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), { 'host': '192.0.2.1' }) device = list(self.hass.data[sonos.DATA_SONOS].devices)[-1] @@ -264,7 +264,7 @@ def test_sonos_set_sleep_timer(self, set_sleep_timerMock, *args): @mock.patch.object(SoCoMock, 'set_sleep_timer') def test_sonos_clear_sleep_timer(self, set_sleep_timerMock, *args): """Ensuring soco methods called for sonos_clear_sleep_timer service.""" - sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), { + sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), { 'host': '192.0.2.1' }) device = list(self.hass.data[sonos.DATA_SONOS].devices)[-1] @@ -278,7 +278,7 @@ def test_sonos_clear_sleep_timer(self, set_sleep_timerMock, *args): @mock.patch('socket.create_connection', side_effect=socket.error()) def test_set_alarm(self, soco_mock, alarm_mock, *args): """Ensuring soco methods called for sonos_set_sleep_timer service.""" - sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), { + sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), { 'host': '192.0.2.1' }) device = list(self.hass.data[sonos.DATA_SONOS].devices)[-1] @@ -308,7 +308,7 @@ def test_set_alarm(self, soco_mock, alarm_mock, *args): @mock.patch.object(soco.snapshot.Snapshot, 'snapshot') def test_sonos_snapshot(self, snapshotMock, *args): """Ensuring soco methods called for sonos_snapshot service.""" - sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), { + sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), { 'host': '192.0.2.1' }) device = list(self.hass.data[sonos.DATA_SONOS].devices)[-1] @@ -326,7 +326,7 @@ def test_sonos_restore(self, restoreMock, *args): """Ensuring soco methods called for sonos_restor service.""" from soco.snapshot import Snapshot - sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), { + sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), { 'host': '192.0.2.1' }) device = list(self.hass.data[sonos.DATA_SONOS].devices)[-1] diff --git a/tests/components/media_player/test_soundtouch.py b/tests/components/media_player/test_soundtouch.py index 2da2622e08ac25..62356e6afcabe3 100644 --- a/tests/components/media_player/test_soundtouch.py +++ b/tests/components/media_player/test_soundtouch.py @@ -148,7 +148,7 @@ class TestSoundtouchMediaPlayer(unittest.TestCase): """Bose Soundtouch test class.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() logging.disable(logging.CRITICAL) diff --git a/tests/components/media_player/test_universal.py b/tests/components/media_player/test_universal.py index c9a1cdc79d8459..279809a283fc1e 100644 --- a/tests/components/media_player/test_universal.py +++ b/tests/components/media_player/test_universal.py @@ -94,7 +94,7 @@ def state(self): @property def volume_level(self): - """The volume level of player.""" + """Return the volume level of player.""" return self._volume_level @property @@ -158,7 +158,7 @@ class TestMediaPlayer(unittest.TestCase): """Test the media_player module.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_mp_1 = MockMediaPlayer(self.hass, 'mock1') @@ -279,7 +279,7 @@ def test_platform_setup(self): bad_config = {'platform': 'universal'} entities = [] - def add_devices(new_entities): + def add_entities(new_entities): """Add devices to list.""" for dev in new_entities: entities.append(dev) @@ -288,7 +288,7 @@ def add_devices(new_entities): try: run_coroutine_threadsafe( universal.async_setup_platform( - self.hass, validate_config(bad_config), add_devices), + self.hass, validate_config(bad_config), add_entities), self.hass.loop).result() except MultipleInvalid: setup_ok = False @@ -297,7 +297,7 @@ def add_devices(new_entities): run_coroutine_threadsafe( universal.async_setup_platform( - self.hass, validate_config(config), add_devices), + self.hass, validate_config(config), add_entities), self.hass.loop).result() self.assertEqual(1, len(entities)) self.assertEqual('test', entities[0].name) diff --git a/tests/components/media_player/test_yamaha.py b/tests/components/media_player/test_yamaha.py index 980284737a2b1e..a55429c0c7b2ab 100644 --- a/tests/components/media_player/test_yamaha.py +++ b/tests/components/media_player/test_yamaha.py @@ -33,7 +33,7 @@ class TestYamahaMediaPlayer(unittest.TestCase): """Test the Yamaha media player.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.main_zone = _create_zone_mock('Main zone', 'http://main') self.device = FakeYamahaDevice( diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 05c5de71b8cb07..ecbc7cb9b02b12 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -45,7 +45,7 @@ class TestMQTTComponent(unittest.TestCase): """Test the MQTT component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_mqtt_component(self.hass) self.calls = [] @@ -56,7 +56,7 @@ def tearDown(self): # pylint: disable=invalid-name @callback def record_calls(self, *args): - """Helper for recording calls.""" + """Record calls.""" self.calls.append(args) def aiohttp_client_stops_on_home_assistant_start(self): @@ -188,7 +188,7 @@ class TestMQTTCallbacks(unittest.TestCase): """Test the MQTT callbacks.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_mqtt_client(self.hass) self.calls = [] @@ -199,7 +199,7 @@ def tearDown(self): # pylint: disable=invalid-name @callback def record_calls(self, *args): - """Helper for recording calls.""" + """Record calls.""" self.calls.append(args) def aiohttp_client_starts_on_home_assistant_mqtt_setup(self): diff --git a/tests/components/mqtt/test_server.py b/tests/components/mqtt/test_server.py index d5d54f457d683e..c761c47542fd82 100644 --- a/tests/components/mqtt/test_server.py +++ b/tests/components/mqtt/test_server.py @@ -18,7 +18,7 @@ class TestMQTT: """Test the MQTT component.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): @@ -33,7 +33,7 @@ def teardown_method(self, method): def test_creating_config_with_http_pass_only(self, mock_mqtt): """Test if the MQTT server failed starts. - Since 0.77, MQTT server has to setup its own password. + Since 0.77, MQTT server has to set up its own password. If user has api_password but don't have mqtt.password, MQTT component will fail to start """ @@ -51,7 +51,7 @@ def test_creating_config_with_http_pass_only(self, mock_mqtt): def test_creating_config_with_pass_and_no_http_pass(self, mock_mqtt): """Test if the MQTT server gets started with password. - Since 0.77, MQTT server has to setup its own password. + Since 0.77, MQTT server has to set up its own password. """ mock_mqtt().async_connect.return_value = mock_coro(True) self.hass.bus.listen_once = MagicMock() @@ -74,7 +74,7 @@ def test_creating_config_with_pass_and_no_http_pass(self, mock_mqtt): def test_creating_config_with_pass_and_http_pass(self, mock_mqtt): """Test if the MQTT server gets started with password. - Since 0.77, MQTT server has to setup its own password. + Since 0.77, MQTT server has to set up its own password. """ mock_mqtt().async_connect.return_value = mock_coro(True) self.hass.bus.listen_once = MagicMock() diff --git a/tests/components/notify/test_apns.py b/tests/components/notify/test_apns.py index 0bd0333a6fb771..dc120dc4ff621b 100644 --- a/tests/components/notify/test_apns.py +++ b/tests/components/notify/test_apns.py @@ -28,7 +28,7 @@ class TestApns(unittest.TestCase): """Test the APNS component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/notify/test_command_line.py b/tests/components/notify/test_command_line.py index 2575e1418f4751..57933063ba1dfa 100644 --- a/tests/components/notify/test_command_line.py +++ b/tests/components/notify/test_command_line.py @@ -13,7 +13,7 @@ class TestCommandLine(unittest.TestCase): """Test the command line notifications.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/notify/test_demo.py b/tests/components/notify/test_demo.py index 71b472afe74c47..f9c107a447eeae 100644 --- a/tests/components/notify/test_demo.py +++ b/tests/components/notify/test_demo.py @@ -20,7 +20,7 @@ class TestNotifyDemo(unittest.TestCase): """Test the demo notify.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.events = [] self.calls = [] @@ -73,7 +73,7 @@ def test_discover_notify(self, mock_demo_get_service): @callback def record_calls(self, *args): - """Helper for recording calls.""" + """Record calls.""" self.calls.append(args) def test_sending_none_message(self): diff --git a/tests/components/notify/test_file.py b/tests/components/notify/test_file.py index d59bbe4d720ac4..fb4ab42e97b3e4 100644 --- a/tests/components/notify/test_file.py +++ b/tests/components/notify/test_file.py @@ -16,7 +16,7 @@ class TestNotifyFile(unittest.TestCase): """Test the file notify.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/notify/test_group.py b/tests/components/notify/test_group.py index 8e7ef4348f7dfd..bbd7c11ffebd65 100644 --- a/tests/components/notify/test_group.py +++ b/tests/components/notify/test_group.py @@ -14,7 +14,7 @@ class TestNotifyGroup(unittest.TestCase): """Test the notify.group platform.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.events = [] self.service1 = demo.DemoNotificationService(self.hass) diff --git a/tests/components/notify/test_smtp.py b/tests/components/notify/test_smtp.py index 29e34974c6c50f..fca0e3c79e3e9c 100644 --- a/tests/components/notify/test_smtp.py +++ b/tests/components/notify/test_smtp.py @@ -19,7 +19,7 @@ class TestNotifySmtp(unittest.TestCase): """Test the smtp notify.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mailer = MockSMTP('localhost', 25, 5, 'test@test.com', 1, 'testuser', 'testpass', diff --git a/tests/components/persistent_notification/test_init.py b/tests/components/persistent_notification/test_init.py index df780675a18aa2..a609247b839d9c 100644 --- a/tests/components/persistent_notification/test_init.py +++ b/tests/components/persistent_notification/test_init.py @@ -9,7 +9,7 @@ class TestPersistentNotification: """Test persistent notification component.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() setup_component(self.hass, pn.DOMAIN, {}) diff --git a/tests/components/recorder/models_original.py b/tests/components/recorder/models_original.py index 990414d77135d0..7096e84c82b511 100644 --- a/tests/components/recorder/models_original.py +++ b/tests/components/recorder/models_original.py @@ -14,7 +14,7 @@ import homeassistant.util.dt as dt_util from homeassistant.core import Event, EventOrigin, State, split_entity_id -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder # SQLAlchemy Schema # pylint: disable=invalid-name diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 191c0d6e73346e..7460a65b0ce9e3 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -19,7 +19,7 @@ class TestRecorder(unittest.TestCase): """Test the recorder module.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() init_recorder_component(self.hass) self.hass.start() @@ -92,7 +92,7 @@ def hass_recorder(): hass = get_test_home_assistant() def setup_recorder(config=None): - """Setup with params.""" + """Set up with params.""" init_recorder_component(hass, config) hass.start() hass.block_till_done() diff --git a/tests/components/recorder/test_purge.py b/tests/components/recorder/test_purge.py index 91aa69b4484486..f33236f0ceb691 100644 --- a/tests/components/recorder/test_purge.py +++ b/tests/components/recorder/test_purge.py @@ -16,7 +16,7 @@ class TestRecorderPurge(unittest.TestCase): """Base class for common recorder tests.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() init_recorder_component(self.hass) self.hass.start() diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index ad130b1ca91f4e..83d109fcfc51b3 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -14,7 +14,7 @@ def hass_recorder(): hass = get_test_home_assistant() def setup_recorder(config=None): - """Setup with params.""" + """Set up with params.""" init_recorder_component(hass, config) hass.start() hass.block_till_done() diff --git a/tests/components/remote/test_demo.py b/tests/components/remote/test_demo.py index ed9968c2d10540..a0290987ff2e85 100644 --- a/tests/components/remote/test_demo.py +++ b/tests/components/remote/test_demo.py @@ -15,7 +15,7 @@ class TestDemoRemote(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.assertTrue(setup_component(self.hass, remote.DOMAIN, {'remote': { 'platform': 'demo', diff --git a/tests/components/remote/test_init.py b/tests/components/remote/test_init.py index b4d2ff98688ded..d98ec941f8bf80 100644 --- a/tests/components/remote/test_init.py +++ b/tests/components/remote/test_init.py @@ -19,7 +19,7 @@ class TestRemote(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name diff --git a/tests/components/remote/test_kira.py b/tests/components/remote/test_kira.py index eaa78d44a60783..74c8e2854d0fa2 100644 --- a/tests/components/remote/test_kira.py +++ b/tests/components/remote/test_kira.py @@ -24,7 +24,7 @@ class TestKiraSensor(unittest.TestCase): # pylint: disable=invalid-name DEVICES = [] - def add_devices(self, devices): + def add_entities(self, devices): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -42,7 +42,7 @@ def tearDown(self): def test_service_call(self): """Test Kira's ability to send commands.""" - kira.setup_platform(self.hass, TEST_CONFIG, self.add_devices, + kira.setup_platform(self.hass, TEST_CONFIG, self.add_entities, DISCOVERY_INFO) assert len(self.DEVICES) == 1 remote = self.DEVICES[0] diff --git a/tests/components/scene/test_init.py b/tests/components/scene/test_init.py index a832e249832dcd..3298d7648d9ce0 100644 --- a/tests/components/scene/test_init.py +++ b/tests/components/scene/test_init.py @@ -14,7 +14,7 @@ class TestScene(unittest.TestCase): """Test the scene component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() test_light = loader.get_component(self.hass, 'light.test') test_light.init() diff --git a/tests/components/scene/test_litejet.py b/tests/components/scene/test_litejet.py index 37a9aa5b2b5572..864ffc41735308 100644 --- a/tests/components/scene/test_litejet.py +++ b/tests/components/scene/test_litejet.py @@ -21,7 +21,7 @@ class TestLiteJetScene(unittest.TestCase): @mock.patch('pylitejet.LiteJet') def setup_method(self, method, mock_pylitejet): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.start() diff --git a/tests/components/sensor/test_arlo.py b/tests/components/sensor/test_arlo.py index d31490ab2afa10..732e47099c4bb6 100644 --- a/tests/components/sensor/test_arlo.py +++ b/tests/components/sensor/test_arlo.py @@ -64,14 +64,14 @@ def captured_sensor(): class PlatformSetupFixture(): - """Fixture for testing platform setup call to add_devices().""" + """Fixture for testing platform setup call to add_entities().""" def __init__(self): """Instantiate the platform setup fixture.""" self.sensors = None self.update = False - def add_devices(self, sensors, update): + def add_entities(self, sensors, update): """Mock method for adding devices.""" self.sensors = sensors self.update = update @@ -101,7 +101,7 @@ def mock_dispatch(): def test_setup_with_no_data(platform_setup, hass): """Test setup_platform with no data.""" - arlo.setup_platform(hass, None, platform_setup.add_devices) + arlo.setup_platform(hass, None, platform_setup.add_entities) assert platform_setup.sensors is None assert not platform_setup.update @@ -132,7 +132,7 @@ def test_setup_with_valid_data(platform_setup, hass): })] }) - arlo.setup_platform(hass, config, platform_setup.add_devices) + arlo.setup_platform(hass, config, platform_setup.add_entities) assert len(platform_setup.sensors) == 8 assert platform_setup.update diff --git a/tests/components/sensor/test_canary.py b/tests/components/sensor/test_canary.py index 346929a4685b51..47a1f330d05171 100644 --- a/tests/components/sensor/test_canary.py +++ b/tests/components/sensor/test_canary.py @@ -24,7 +24,7 @@ class TestCanarySensorSetup(unittest.TestCase): DEVICES = [] - def add_devices(self, devices, action): + def add_entities(self, devices, action): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -51,7 +51,7 @@ def test_setup_sensors(self): mock_location("Work", True, devices=[online_device_at_work]), ] - canary.setup_platform(self.hass, self.config, self.add_devices, None) + canary.setup_platform(self.hass, self.config, self.add_entities, None) self.assertEqual(6, len(self.DEVICES)) diff --git a/tests/components/sensor/test_command_line.py b/tests/components/sensor/test_command_line.py index 808f8cff6a146d..82cdef014b9f9b 100644 --- a/tests/components/sensor/test_command_line.py +++ b/tests/components/sensor/test_command_line.py @@ -11,7 +11,7 @@ class TestCommandSensorSensor(unittest.TestCase): """Test the Command line sensor.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/sensor/test_dyson.py b/tests/components/sensor/test_dyson.py index dcbafcae6e32f1..5b5be4a587d8b1 100644 --- a/tests/components/sensor/test_dyson.py +++ b/tests/components/sensor/test_dyson.py @@ -52,7 +52,7 @@ class DysonTest(unittest.TestCase): """Dyson Sensor component test class.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name @@ -62,9 +62,9 @@ def tearDown(self): # pylint: disable=invalid-name def test_setup_component_with_no_devices(self): """Test setup component with no devices.""" self.hass.data[dyson.DYSON_DEVICES] = [] - add_devices = mock.MagicMock() - dyson.setup_platform(self.hass, None, add_devices) - add_devices.assert_called_with([]) + add_entities = mock.MagicMock() + dyson.setup_platform(self.hass, None, add_entities) + add_entities.assert_called_with([]) def test_setup_component(self): """Test setup component with devices.""" diff --git a/tests/components/sensor/test_efergy.py b/tests/components/sensor/test_efergy.py index 9a79ab5b81c473..fdcaa415483dd6 100644 --- a/tests/components/sensor/test_efergy.py +++ b/tests/components/sensor/test_efergy.py @@ -61,7 +61,7 @@ class TestEfergySensor(unittest.TestCase): DEVICES = [] @requests_mock.Mocker() - def add_devices(self, devices, mock): + def add_entities(self, devices, mock): """Mock add devices.""" mock_responses(mock) for device in devices: diff --git a/tests/components/sensor/test_fido.py b/tests/components/sensor/test_fido.py index 1eca7be7544296..c0bbadc043b3b7 100644 --- a/tests/components/sensor/test_fido.py +++ b/tests/components/sensor/test_fido.py @@ -59,8 +59,8 @@ class PyFidoFakeModule(): FidoClient = FidoClientMockError -def fake_async_add_devices(component, update_before_add=False): - """Fake async_add_devices function.""" +def fake_async_add_entities(component, update_before_add=False): + """Fake async_add_entities function.""" pass @@ -103,7 +103,7 @@ def test_error(hass, caplog): sys.modules['pyfido.client'] = PyFidoClientFakeModule() config = {} - fake_async_add_devices = MagicMock() + fake_async_add_entities = MagicMock() yield from fido.async_setup_platform(hass, config, - fake_async_add_devices) - assert fake_async_add_devices.called is False + fake_async_add_entities) + assert fake_async_add_entities.called is False diff --git a/tests/components/sensor/test_filter.py b/tests/components/sensor/test_filter.py index cf2cc9c42054db..b0683b04aa05dd 100644 --- a/tests/components/sensor/test_filter.py +++ b/tests/components/sensor/test_filter.py @@ -17,7 +17,7 @@ class TestFilterSensor(unittest.TestCase): """Test the Data Filter sensor.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() raw_values = [20, 19, 18, 21, 22, 0] self.values = [] diff --git a/tests/components/sensor/test_foobot.py b/tests/components/sensor/test_foobot.py index 322f2b3f2a893a..f9382a04b6bd04 100644 --- a/tests/components/sensor/test_foobot.py +++ b/tests/components/sensor/test_foobot.py @@ -44,18 +44,18 @@ async def test_default_setup(hass, aioclient_mock): async def test_setup_timeout_error(hass, aioclient_mock): """Expected failures caused by a timeout in API response.""" - fake_async_add_devices = MagicMock() + fake_async_add_entities = MagicMock() aioclient_mock.get(re.compile('api.foobot.io/v2/owner/.*'), exc=asyncio.TimeoutError()) with pytest.raises(PlatformNotReady): await foobot.async_setup_platform(hass, {'sensor': VALID_CONFIG}, - fake_async_add_devices) + fake_async_add_entities) async def test_setup_permanent_error(hass, aioclient_mock): """Expected failures caused by permanent errors in API response.""" - fake_async_add_devices = MagicMock() + fake_async_add_entities = MagicMock() errors = [400, 401, 403] for error in errors: @@ -63,13 +63,13 @@ async def test_setup_permanent_error(hass, aioclient_mock): status=error) result = await foobot.async_setup_platform(hass, {'sensor': VALID_CONFIG}, - fake_async_add_devices) + fake_async_add_entities) assert result is None async def test_setup_temporary_error(hass, aioclient_mock): """Expected failures caused by temporary errors in API response.""" - fake_async_add_devices = MagicMock() + fake_async_add_entities = MagicMock() errors = [429, 500] for error in errors: @@ -78,4 +78,4 @@ async def test_setup_temporary_error(hass, aioclient_mock): with pytest.raises(PlatformNotReady): await foobot.async_setup_platform(hass, {'sensor': VALID_CONFIG}, - fake_async_add_devices) + fake_async_add_entities) diff --git a/tests/components/sensor/test_google_wifi.py b/tests/components/sensor/test_google_wifi.py index 1004c20b314db0..55afedab53692d 100644 --- a/tests/components/sensor/test_google_wifi.py +++ b/tests/components/sensor/test_google_wifi.py @@ -36,7 +36,7 @@ class TestGoogleWifiSetup(unittest.TestCase): """Tests for setting up the Google Wifi sensor platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): @@ -83,7 +83,7 @@ class TestGoogleWifiSensor(unittest.TestCase): """Tests for Google Wifi sensor platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() with requests_mock.Mocker() as mock_req: self.setup_api(MOCK_DATA, mock_req) @@ -93,7 +93,7 @@ def tearDown(self): self.hass.stop() def setup_api(self, data, mock_req): - """Setup API with fake data.""" + """Set up API with fake data.""" resource = '{}{}{}'.format( 'http://', 'localhost', google_wifi.ENDPOINT) now = datetime(1970, month=1, day=1) diff --git a/tests/components/sensor/test_hydroquebec.py b/tests/components/sensor/test_hydroquebec.py index debd6ef6167cca..eed5ab4a6a6d1f 100644 --- a/tests/components/sensor/test_hydroquebec.py +++ b/tests/components/sensor/test_hydroquebec.py @@ -99,7 +99,7 @@ def test_error(hass, caplog): sys.modules['pyhydroquebec.client'] = PyHydroQuebecClientFakeModule() config = {} - fake_async_add_devices = MagicMock() + fake_async_add_entities = MagicMock() yield from hydroquebec.async_setup_platform(hass, config, - fake_async_add_devices) - assert fake_async_add_devices.called is False + fake_async_add_entities) + assert fake_async_add_entities.called is False diff --git a/tests/components/sensor/test_imap_email_content.py b/tests/components/sensor/test_imap_email_content.py index cd5c079a431f62..a07d94e3dcd547 100644 --- a/tests/components/sensor/test_imap_email_content.py +++ b/tests/components/sensor/test_imap_email_content.py @@ -17,7 +17,7 @@ class FakeEMailReader: """A test class for sending test emails.""" def __init__(self, messages): - """Setup the fake email reader.""" + """Set up the fake email reader.""" self._messages = messages def connect(self): @@ -35,7 +35,7 @@ class EmailContentSensor(unittest.TestCase): """Test the IMAP email content sensor.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/sensor/test_kira.py b/tests/components/sensor/test_kira.py index 093158cb25cfc8..76aba46d514465 100644 --- a/tests/components/sensor/test_kira.py +++ b/tests/components/sensor/test_kira.py @@ -22,7 +22,7 @@ class TestKiraSensor(unittest.TestCase): # pylint: disable=invalid-name DEVICES = [] - def add_devices(self, devices): + def add_entities(self, devices): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -41,7 +41,7 @@ def tearDown(self): # pylint: disable=protected-access def test_kira_sensor_callback(self): """Ensure Kira sensor properly updates its attributes from callback.""" - kira.setup_platform(self.hass, TEST_CONFIG, self.add_devices, + kira.setup_platform(self.hass, TEST_CONFIG, self.add_entities, DISCOVERY_INFO) assert len(self.DEVICES) == 1 sensor = self.DEVICES[0] diff --git a/tests/components/sensor/test_melissa.py b/tests/components/sensor/test_melissa.py index 55b3e7f70f4da4..7ac90221f16c60 100644 --- a/tests/components/sensor/test_melissa.py +++ b/tests/components/sensor/test_melissa.py @@ -42,10 +42,10 @@ def test_setup_platform(self): self.hass.data[DATA_MELISSA] = self.api config = {} - add_devices = Mock() + add_entities = Mock() discovery_info = {} - melissa.setup_platform(self.hass, config, add_devices, discovery_info) + melissa.setup_platform(self.hass, config, add_entities, discovery_info) def test_name(self): """Test name property.""" diff --git a/tests/components/sensor/test_mfi.py b/tests/components/sensor/test_mfi.py index ae967449ef2147..a10246ad777a7e 100644 --- a/tests/components/sensor/test_mfi.py +++ b/tests/components/sensor/test_mfi.py @@ -31,7 +31,7 @@ class TestMfiSensorSetup(unittest.TestCase): } def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): @@ -131,7 +131,7 @@ class TestMfiSensor(unittest.TestCase): """Test for mFi sensor platform.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.port = mock.MagicMock() self.sensor = mfi.MfiSensor(self.port, self.hass) diff --git a/tests/components/sensor/test_mhz19.py b/tests/components/sensor/test_mhz19.py index 6d0714896911b3..421035995dc119 100644 --- a/tests/components/sensor/test_mhz19.py +++ b/tests/components/sensor/test_mhz19.py @@ -15,7 +15,7 @@ class TestMHZ19Sensor(unittest.TestCase): hass = None def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): diff --git a/tests/components/sensor/test_moldindicator.py b/tests/components/sensor/test_moldindicator.py index 32cd0206decab0..4f1b40bf9eff40 100644 --- a/tests/components/sensor/test_moldindicator.py +++ b/tests/components/sensor/test_moldindicator.py @@ -15,7 +15,7 @@ class TestSensorMoldIndicator(unittest.TestCase): """Test the MoldIndicator sensor.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.states.set('test.indoortemp', '20', {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}) diff --git a/tests/components/sensor/test_mqtt.py b/tests/components/sensor/test_mqtt.py index 2583f52b3d2289..feef647b7b7992 100644 --- a/tests/components/sensor/test_mqtt.py +++ b/tests/components/sensor/test_mqtt.py @@ -5,13 +5,14 @@ from unittest.mock import patch import homeassistant.core as ha -from homeassistant.setup import setup_component +from homeassistant.setup import setup_component, async_setup_component import homeassistant.components.sensor as sensor from homeassistant.const import EVENT_STATE_CHANGED, STATE_UNAVAILABLE import homeassistant.util.dt as dt_util from tests.common import mock_mqtt_component, fire_mqtt_message, \ - assert_setup_component + assert_setup_component, async_fire_mqtt_message, \ + async_mock_mqtt_component from tests.common import get_test_home_assistant, mock_component @@ -19,7 +20,7 @@ class TestSensorMQTT(unittest.TestCase): """Test the MQTT sensor.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_mqtt_component(self.hass) @@ -331,27 +332,6 @@ def test_update_with_json_attrs_and_template(self): state.attributes.get('val')) self.assertEqual('100', state.state) - def test_unique_id(self): - """Test unique id option only creates one sensor per unique_id.""" - assert setup_component(self.hass, sensor.DOMAIN, { - sensor.DOMAIN: [{ - 'platform': 'mqtt', - 'name': 'Test 1', - 'state_topic': 'test-topic', - 'unique_id': 'TOTALLY_UNIQUE' - }, { - 'platform': 'mqtt', - 'name': 'Test 2', - 'state_topic': 'test-topic', - 'unique_id': 'TOTALLY_UNIQUE' - }] - }) - - fire_mqtt_message(self.hass, 'test-topic', 'payload') - self.hass.block_till_done() - - assert len(self.hass.states.all()) == 1 - def test_invalid_device_class(self): """Test device_class option with invalid value.""" with assert_setup_component(0): @@ -384,3 +364,26 @@ def test_valid_device_class(self): assert state.attributes['device_class'] == 'temperature' state = self.hass.states.get('sensor.test_2') assert 'device_class' not in state.attributes + + +async def test_unique_id(hass): + """Test unique id option only creates one sensor per unique_id.""" + await async_mock_mqtt_component(hass) + assert await async_setup_component(hass, sensor.DOMAIN, { + sensor.DOMAIN: [{ + 'platform': 'mqtt', + 'name': 'Test 1', + 'state_topic': 'test-topic', + 'unique_id': 'TOTALLY_UNIQUE' + }, { + 'platform': 'mqtt', + 'name': 'Test 2', + 'state_topic': 'test-topic', + 'unique_id': 'TOTALLY_UNIQUE' + }] + }) + + async_fire_mqtt_message(hass, 'test-topic', 'payload') + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 1 diff --git a/tests/components/sensor/test_mqtt_room.py b/tests/components/sensor/test_mqtt_room.py index c79017338e166f..88fa611b2a6139 100644 --- a/tests/components/sensor/test_mqtt_room.py +++ b/tests/components/sensor/test_mqtt_room.py @@ -50,7 +50,7 @@ class TestMQTTRoomSensor(unittest.TestCase): """Test the room presence sensor.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_mqtt_component(self.hass) self.assertTrue(setup_component(self.hass, sensor.DOMAIN, { diff --git a/tests/components/sensor/test_radarr.py b/tests/components/sensor/test_radarr.py index 0d6aca9d0b7067..30195b73a13e02 100644 --- a/tests/components/sensor/test_radarr.py +++ b/tests/components/sensor/test_radarr.py @@ -193,7 +193,7 @@ class TestRadarrSetup(unittest.TestCase): # pylint: disable=invalid-name DEVICES = [] - def add_devices(self, devices, update): + def add_entities(self, devices, update): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -221,7 +221,7 @@ def test_diskspace_no_paths(self, req_mock): 'diskspace' ] } - radarr.setup_platform(self.hass, config, self.add_devices, None) + radarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual('263.10', device.state) @@ -248,7 +248,7 @@ def test_diskspace_paths(self, req_mock): 'diskspace' ] } - radarr.setup_platform(self.hass, config, self.add_devices, None) + radarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual('263.10', device.state) @@ -275,7 +275,7 @@ def test_commands(self, req_mock): 'commands' ] } - radarr.setup_platform(self.hass, config, self.add_devices, None) + radarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -302,7 +302,7 @@ def test_movies(self, req_mock): 'movies' ] } - radarr.setup_platform(self.hass, config, self.add_devices, None) + radarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -329,7 +329,7 @@ def test_upcoming_multiple_days(self, req_mock): 'upcoming' ] } - radarr.setup_platform(self.hass, config, self.add_devices, None) + radarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -360,7 +360,7 @@ def test_upcoming_today(self, req_mock): 'upcoming' ] } - radarr.setup_platform(self.hass, config, self.add_devices, None) + radarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -387,7 +387,7 @@ def test_system_status(self, req_mock): 'status' ] } - radarr.setup_platform(self.hass, config, self.add_devices, None) + radarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual('0.2.0.210', device.state) @@ -413,7 +413,7 @@ def test_ssl(self, req_mock): ], "ssl": "true" } - radarr.setup_platform(self.hass, config, self.add_devices, None) + radarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -441,7 +441,7 @@ def test_exception_handling(self, req_mock): 'upcoming' ] } - radarr.setup_platform(self.hass, config, self.add_devices, None) + radarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(None, device.state) diff --git a/tests/components/sensor/test_rest.py b/tests/components/sensor/test_rest.py index f2362867979429..4d40ad394cdaab 100644 --- a/tests/components/sensor/test_rest.py +++ b/tests/components/sensor/test_rest.py @@ -19,7 +19,7 @@ class TestRestSensorSetup(unittest.TestCase): """Tests for setting up the REST sensor platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): @@ -121,7 +121,7 @@ class TestRestSensor(unittest.TestCase): """Tests for REST sensor platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.initial_state = 'initial_state' self.rest = Mock('rest.RestData') @@ -267,7 +267,7 @@ class TestRestData(unittest.TestCase): """Tests for RestData.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.method = "GET" self.resource = "http://localhost" self.verify_ssl = True diff --git a/tests/components/sensor/test_rfxtrx.py b/tests/components/sensor/test_rfxtrx.py index e049eabbe565a5..3f577127a115f9 100644 --- a/tests/components/sensor/test_rfxtrx.py +++ b/tests/components/sensor/test_rfxtrx.py @@ -15,7 +15,7 @@ class TestSensorRfxtrx(unittest.TestCase): """Test the Rfxtrx sensor platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'rfxtrx') diff --git a/tests/components/sensor/test_ring.py b/tests/components/sensor/test_ring.py index 4d34018ce52c85..05685376ef9e14 100644 --- a/tests/components/sensor/test_ring.py +++ b/tests/components/sensor/test_ring.py @@ -16,7 +16,7 @@ class TestRingSensorSetup(unittest.TestCase): DEVICES = [] - def add_devices(self, devices, action): + def add_entities(self, devices, action): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -66,7 +66,7 @@ def test_sensor(self, mock): base_ring.setup(self.hass, VALID_CONFIG) ring.setup_platform(self.hass, self.config, - self.add_devices, + self.add_entities, None) for device in self.DEVICES: diff --git a/tests/components/sensor/test_season.py b/tests/components/sensor/test_season.py index 5c071982f7fc8b..21e18a00c144d8 100644 --- a/tests/components/sensor/test_season.py +++ b/tests/components/sensor/test_season.py @@ -60,13 +60,13 @@ class TestSeason(unittest.TestCase): CONFIG_ASTRONOMICAL = {'type': 'astronomical'} CONFIG_METEOROLOGICAL = {'type': 'meteorological'} - def add_devices(self, devices): + def add_entities(self, devices): """Mock add devices.""" for device in devices: self.DEVICE = device def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/sensor/test_sleepiq.py b/tests/components/sensor/test_sleepiq.py index a79db86dc79b4e..646f8e5d8888d1 100644 --- a/tests/components/sensor/test_sleepiq.py +++ b/tests/components/sensor/test_sleepiq.py @@ -16,7 +16,7 @@ class TestSleepIQSensorSetup(unittest.TestCase): DEVICES = [] - def add_devices(self, devices): + def add_entities(self, devices): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -49,7 +49,7 @@ def test_setup(self, mock): sleepiq.setup_platform(self.hass, self.config, - self.add_devices, + self.add_entities, MagicMock()) self.assertEqual(2, len(self.DEVICES)) diff --git a/tests/components/sensor/test_sonarr.py b/tests/components/sensor/test_sonarr.py index 275bb4a1e8b16a..e44d3d9a99f8f6 100644 --- a/tests/components/sensor/test_sonarr.py +++ b/tests/components/sensor/test_sonarr.py @@ -579,7 +579,7 @@ class TestSonarrSetup(unittest.TestCase): # pylint: disable=invalid-name DEVICES = [] - def add_devices(self, devices, update): + def add_entities(self, devices, update): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -607,7 +607,7 @@ def test_diskspace_no_paths(self, req_mock): 'diskspace' ] } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual('263.10', device.state) @@ -634,7 +634,7 @@ def test_diskspace_paths(self, req_mock): 'diskspace' ] } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual('263.10', device.state) @@ -661,7 +661,7 @@ def test_commands(self, req_mock): 'commands' ] } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -688,7 +688,7 @@ def test_queue(self, req_mock): 'queue' ] } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -715,7 +715,7 @@ def test_series(self, req_mock): 'series' ] } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -742,7 +742,7 @@ def test_wanted(self, req_mock): 'wanted' ] } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -769,7 +769,7 @@ def test_upcoming_multiple_days(self, req_mock): 'upcoming' ] } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -800,7 +800,7 @@ def test_upcoming_today(self, req_mock): 'upcoming' ] } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -827,7 +827,7 @@ def test_system_status(self, req_mock): 'status' ] } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual('2.0.0.1121', device.state) @@ -854,7 +854,7 @@ def test_ssl(self, req_mock): ], "ssl": "true" } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(1, device.state) @@ -882,7 +882,7 @@ def test_exception_handling(self, req_mock): 'upcoming' ] } - sonarr.setup_platform(self.hass, config, self.add_devices, None) + sonarr.setup_platform(self.hass, config, self.add_entities, None) for device in self.DEVICES: device.update() self.assertEqual(None, device.state) diff --git a/tests/components/sensor/test_statistics.py b/tests/components/sensor/test_statistics.py index 48ebf720633d02..466b89cc0d1922 100644 --- a/tests/components/sensor/test_statistics.py +++ b/tests/components/sensor/test_statistics.py @@ -17,7 +17,7 @@ class TestStatisticsSensor(unittest.TestCase): """Test the Statistics sensor.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.values = [17, 20, 15.2, 5, 3.8, 9.2, 6.7, 14, 6] self.count = len(self.values) diff --git a/tests/components/sensor/test_tcp.py b/tests/components/sensor/test_tcp.py index 4c1e976ea51d5f..e89e8db861a8ce 100644 --- a/tests/components/sensor/test_tcp.py +++ b/tests/components/sensor/test_tcp.py @@ -39,7 +39,7 @@ class TestTCPSensor(unittest.TestCase): """Test the TCP Sensor.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): @@ -48,14 +48,14 @@ def teardown_method(self, method): @patch('homeassistant.components.sensor.tcp.TcpSensor.update') def test_setup_platform_valid_config(self, mock_update): - """Check a valid configuration and call add_devices with sensor.""" + """Check a valid configuration and call add_entities with sensor.""" with assert_setup_component(0, 'sensor'): assert setup_component(self.hass, 'sensor', TEST_CONFIG) - add_devices = Mock() - tcp.setup_platform(None, TEST_CONFIG['sensor'], add_devices) - assert add_devices.called - assert isinstance(add_devices.call_args[0][0][0], tcp.TcpSensor) + add_entities = Mock() + tcp.setup_platform(None, TEST_CONFIG['sensor'], add_entities) + assert add_entities.called + assert isinstance(add_entities.call_args[0][0][0], tcp.TcpSensor) def test_setup_platform_invalid_config(self): """Check an invalid configuration.""" diff --git a/tests/components/sensor/test_template.py b/tests/components/sensor/test_template.py index 6861d3a5070d0c..2211f092d7b3fc 100644 --- a/tests/components/sensor/test_template.py +++ b/tests/components/sensor/test_template.py @@ -11,7 +11,7 @@ class TestTemplateSensor: # pylint: disable=invalid-name def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): diff --git a/tests/components/sensor/test_time_date.py b/tests/components/sensor/test_time_date.py index 1b3ab68988e7ff..98368e997d577d 100644 --- a/tests/components/sensor/test_time_date.py +++ b/tests/components/sensor/test_time_date.py @@ -14,7 +14,7 @@ class TestTimeDateSensor(unittest.TestCase): # pylint: disable=invalid-name DEVICES = [] - def add_devices(self, devices): + def add_entities(self, devices): """Mock add devices.""" for device in devices: self.DEVICES.append(device) diff --git a/tests/components/sensor/test_vultr.py b/tests/components/sensor/test_vultr.py index 6b2e2341c52113..ee2dd35dc8f3b5 100644 --- a/tests/components/sensor/test_vultr.py +++ b/tests/components/sensor/test_vultr.py @@ -23,7 +23,7 @@ class TestVultrSensorSetup(unittest.TestCase): DEVICES = [] - def add_devices(self, devices, action): + def add_entities(self, devices, action): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -71,7 +71,7 @@ def test_sensor(self, mock): for config in self.configs: setup = vultr.setup_platform( - self.hass, config, self.add_devices, None) + self.hass, config, self.add_entities, None) self.assertIsNone(setup) @@ -160,7 +160,7 @@ def test_invalid_sensors(self, mock): } # No subs at all no_sub_setup = vultr.setup_platform( - self.hass, bad_conf, self.add_devices, None) + self.hass, bad_conf, self.add_entities, None) self.assertIsNone(no_sub_setup) self.assertEqual(0, len(self.DEVICES)) diff --git a/tests/components/sensor/test_yweather.py b/tests/components/sensor/test_yweather.py index aeee47bfa807c8..2912229d712caa 100644 --- a/tests/components/sensor/test_yweather.py +++ b/tests/components/sensor/test_yweather.py @@ -100,7 +100,7 @@ def Units(self): # pylint: disable=invalid-name @property def Now(self): # pylint: disable=invalid-name - """Current weather data.""" + """Return current weather data.""" if self.woeid == '111': raise ValueError return self._data['query']['results']['channel']['item']['condition'] @@ -129,7 +129,7 @@ class TestWeather(unittest.TestCase): """Test the Yahoo weather component.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/switch/test_command_line.py b/tests/components/switch/test_command_line.py index 40f999fa43bab2..9ec0507627df8c 100644 --- a/tests/components/switch/test_command_line.py +++ b/tests/components/switch/test_command_line.py @@ -17,7 +17,7 @@ class TestCommandSwitch(unittest.TestCase): """Test the command switch.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/switch/test_flux.py b/tests/components/switch/test_flux.py index 155ed85dac2d4b..f9ea88c525467a 100644 --- a/tests/components/switch/test_flux.py +++ b/tests/components/switch/test_flux.py @@ -17,7 +17,7 @@ class TestSwitchFlux(unittest.TestCase): """Test the Flux switch platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/switch/test_init.py b/tests/components/switch/test_init.py index 55e44299294828..579898437ca9db 100644 --- a/tests/components/switch/test_init.py +++ b/tests/components/switch/test_init.py @@ -15,7 +15,7 @@ class TestSwitch(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() platform = loader.get_component(self.hass, 'switch.test') platform.init() diff --git a/tests/components/switch/test_litejet.py b/tests/components/switch/test_litejet.py index e0d6e290def26b..45e5509c169d35 100644 --- a/tests/components/switch/test_litejet.py +++ b/tests/components/switch/test_litejet.py @@ -21,7 +21,7 @@ class TestLiteJetSwitch(unittest.TestCase): @mock.patch('pylitejet.LiteJet') def setup_method(self, method, mock_pylitejet): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.start() diff --git a/tests/components/switch/test_mfi.py b/tests/components/switch/test_mfi.py index ae3edec2102434..d2bf3c57ab6488 100644 --- a/tests/components/switch/test_mfi.py +++ b/tests/components/switch/test_mfi.py @@ -49,7 +49,7 @@ class TestMfiSwitch(unittest.TestCase): """Test for mFi switch platform.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.port = mock.MagicMock() self.switch = mfi.MfiSwitch(self.port) diff --git a/tests/components/switch/test_mochad.py b/tests/components/switch/test_mochad.py index a5e6e2c9ae641a..bfbd67e6b0c34a 100644 --- a/tests/components/switch/test_mochad.py +++ b/tests/components/switch/test_mochad.py @@ -29,7 +29,7 @@ class TestMochadSwitchSetup(unittest.TestCase): THING = 'switch' def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): @@ -58,7 +58,7 @@ class TestMochadSwitch(unittest.TestCase): """Test for mochad switch platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() controller_mock = mock.MagicMock() dev_dict = {'address': 'a1', 'name': 'fake_switch'} @@ -76,9 +76,9 @@ def test_name(self): def test_turn_on(self): """Test turn_on.""" self.switch.turn_on() - self.switch.device.send_cmd.assert_called_once_with('on') + self.switch.switch.send_cmd.assert_called_once_with('on') def test_turn_off(self): """Test turn_off.""" self.switch.turn_off() - self.switch.device.send_cmd.assert_called_once_with('off') + self.switch.switch.send_cmd.assert_called_once_with('off') diff --git a/tests/components/switch/test_mqtt.py b/tests/components/switch/test_mqtt.py index 7cd5a42b4a3e51..c9bfd02156f63a 100644 --- a/tests/components/switch/test_mqtt.py +++ b/tests/components/switch/test_mqtt.py @@ -15,7 +15,7 @@ class TestSwitchMQTT(unittest.TestCase): """Test the MQTT switch.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_publish = mock_mqtt_component(self.hass) diff --git a/tests/components/switch/test_rest.py b/tests/components/switch/test_rest.py index e3f11ec19a0b31..cb27ab40855473 100644 --- a/tests/components/switch/test_rest.py +++ b/tests/components/switch/test_rest.py @@ -14,7 +14,7 @@ class TestRestSwitchSetup: """Tests for setting up the REST switch platform.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self): @@ -95,7 +95,7 @@ class TestRestSwitch: """Tests for REST switch platform.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.name = 'foo' self.method = 'post' diff --git a/tests/components/switch/test_rfxtrx.py b/tests/components/switch/test_rfxtrx.py index 938093aa95ba2f..ae242a1dafb221 100644 --- a/tests/components/switch/test_rfxtrx.py +++ b/tests/components/switch/test_rfxtrx.py @@ -14,7 +14,7 @@ class TestSwitchRfxtrx(unittest.TestCase): """Test the Rfxtrx switch platform.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'rfxtrx') diff --git a/tests/components/switch/test_template.py b/tests/components/switch/test_template.py index 8f7bbda8e98702..47766e31f4d8aa 100644 --- a/tests/components/switch/test_template.py +++ b/tests/components/switch/test_template.py @@ -16,7 +16,7 @@ class TestTemplateSwitch: # pylint: disable=invalid-name def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.calls = [] diff --git a/tests/components/switch/test_vultr.py b/tests/components/switch/test_vultr.py index 222a044a5236c9..ce8740e9bff27f 100644 --- a/tests/components/switch/test_vultr.py +++ b/tests/components/switch/test_vultr.py @@ -26,7 +26,7 @@ class TestVultrSwitchSetup(unittest.TestCase): DEVICES = [] - def add_devices(self, devices, action): + def add_entities(self, devices, action): """Mock add devices.""" for device in devices: self.DEVICES.append(device) @@ -71,7 +71,7 @@ def test_switch(self, mock): for config in self.configs: vultr.setup_platform(self.hass, config, - self.add_devices, + self.add_entities, None) self.assertEqual(len(self.DEVICES), 3) @@ -181,7 +181,7 @@ def test_invalid_switches(self, mock): no_subs_setup = vultr.setup_platform(self.hass, bad_conf, - self.add_devices, + self.add_entities, None) self.assertIsNotNone(no_subs_setup) @@ -193,7 +193,7 @@ def test_invalid_switches(self, mock): wrong_subs_setup = vultr.setup_platform(self.hass, bad_conf, - self.add_devices, + self.add_entities, None) self.assertIsNotNone(wrong_subs_setup) diff --git a/tests/components/switch/test_wake_on_lan.py b/tests/components/switch/test_wake_on_lan.py index 167c3bb35ac204..abe1532cec738b 100644 --- a/tests/components/switch/test_wake_on_lan.py +++ b/tests/components/switch/test_wake_on_lan.py @@ -33,7 +33,7 @@ class TestWOLSwitch(unittest.TestCase): """Test the wol switch.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/test_alert.py b/tests/components/test_alert.py index d9eb33be37d8fa..90d732ac38e136 100644 --- a/tests/components/test_alert.py +++ b/tests/components/test_alert.py @@ -36,7 +36,7 @@ class TestAlert(unittest.TestCase): """Test the alert module.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/test_api.py b/tests/components/test_api.py index 2be1168b86a829..6f6b4e93068e21 100644 --- a/tests/components/test_api.py +++ b/tests/components/test_api.py @@ -154,7 +154,7 @@ def test_api_fire_event_with_no_data(hass, mock_api_client): @ha.callback def listener(event): - """Helper method that will verify our event got called.""" + """Record that our event got called.""" test_value.append(1) hass.bus.async_listen_once("test.event_no_data", listener) @@ -174,7 +174,7 @@ def test_api_fire_event_with_data(hass, mock_api_client): @ha.callback def listener(event): - """Helper method that will verify that our event got called. + """Record that our event got called. Also test if our data came through. """ @@ -200,7 +200,7 @@ def test_api_fire_event_with_invalid_json(hass, mock_api_client): @ha.callback def listener(event): - """Helper method that will verify our event got called.""" + """Record that our event got called.""" test_value.append(1) hass.bus.async_listen_once("test_event_bad_data", listener) @@ -281,7 +281,7 @@ def test_api_call_service_no_data(hass, mock_api_client): @ha.callback def listener(service_call): - """Helper method that will verify that our service got called.""" + """Record that our service got called.""" test_value.append(1) hass.services.async_register("test_domain", "test_service", listener) @@ -300,7 +300,7 @@ def test_api_call_service_with_data(hass, mock_api_client): @ha.callback def listener(service_call): - """Helper method that will verify that our service got called. + """Record that our service got called. Also test if our data came through. """ @@ -440,7 +440,7 @@ async def test_api_fire_event_context(hass, mock_api_client, @ha.callback def listener(event): - """Helper method that will verify our event got called.""" + """Record that our event got called.""" test_value.append(event) hass.bus.async_listen("test.event", listener) diff --git a/tests/components/test_configurator.py b/tests/components/test_configurator.py index 809c02548dc6fe..22f0d6646aa27c 100644 --- a/tests/components/test_configurator.py +++ b/tests/components/test_configurator.py @@ -13,7 +13,7 @@ class TestConfigurator(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name diff --git a/tests/components/test_conversation.py b/tests/components/test_conversation.py index 6a1d5a55c47eb9..61247b5bdde1dc 100644 --- a/tests/components/test_conversation.py +++ b/tests/components/test_conversation.py @@ -290,11 +290,11 @@ async def test_http_api_wrong_data(hass, aiohttp_client): def test_create_matcher(): """Test the create matcher method.""" # Basic sentence - pattern = conversation._create_matcher('Hello world') + pattern = conversation.create_matcher('Hello world') assert pattern.match('Hello world') is not None # Match a part - pattern = conversation._create_matcher('Hello {name}') + pattern = conversation.create_matcher('Hello {name}') match = pattern.match('hello world') assert match is not None assert match.groupdict()['name'] == 'world' @@ -302,7 +302,7 @@ def test_create_matcher(): assert no_match is None # Optional and matching part - pattern = conversation._create_matcher('Turn on [the] {name}') + pattern = conversation.create_matcher('Turn on [the] {name}') match = pattern.match('turn on the kitchen lights') assert match is not None assert match.groupdict()['name'] == 'kitchen lights' @@ -313,7 +313,7 @@ def test_create_matcher(): assert match is None # Two different optional parts, 1 matching part - pattern = conversation._create_matcher('Turn on [the] [a] {name}') + pattern = conversation.create_matcher('Turn on [the] [a] {name}') match = pattern.match('turn on the kitchen lights') assert match is not None assert match.groupdict()['name'] == 'kitchen lights' @@ -325,13 +325,13 @@ def test_create_matcher(): assert match.groupdict()['name'] == 'kitchen light' # Strip plural - pattern = conversation._create_matcher('Turn {name}[s] on') + pattern = conversation.create_matcher('Turn {name}[s] on') match = pattern.match('turn kitchen lights on') assert match is not None assert match.groupdict()['name'] == 'kitchen light' # Optional 2 words - pattern = conversation._create_matcher('Turn [the great] {name} on') + pattern = conversation.create_matcher('Turn [the great] {name} on') match = pattern.match('turn the great kitchen lights on') assert match is not None assert match.groupdict()['name'] == 'kitchen lights' diff --git a/tests/components/test_datadog.py b/tests/components/test_datadog.py index f1820c4d2504b7..f9724989f97e67 100644 --- a/tests/components/test_datadog.py +++ b/tests/components/test_datadog.py @@ -20,7 +20,7 @@ class TestDatadog(unittest.TestCase): """Test the Datadog component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/test_demo.py b/tests/components/test_demo.py index 258e3d96297998..b0b2524180fea2 100644 --- a/tests/components/test_demo.py +++ b/tests/components/test_demo.py @@ -7,7 +7,7 @@ from homeassistant.setup import async_setup_component from homeassistant.components import demo, device_tracker -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder @pytest.fixture(autouse=True) diff --git a/tests/components/test_device_sun_light_trigger.py b/tests/components/test_device_sun_light_trigger.py index 774185c51c1cdc..35d53f9a5c859f 100644 --- a/tests/components/test_device_sun_light_trigger.py +++ b/tests/components/test_device_sun_light_trigger.py @@ -18,7 +18,7 @@ class TestDeviceSunLightTrigger(unittest.TestCase): """Test the device sun light trigger module.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.scanner = loader.get_component( diff --git a/tests/components/test_discovery.py b/tests/components/test_discovery.py index 8b997cb911cf62..d4566bc0b03b2f 100644 --- a/tests/components/test_discovery.py +++ b/tests/components/test_discovery.py @@ -47,7 +47,7 @@ def netdisco_mock(): async def mock_discovery(hass, discoveries, config=BASE_CONFIG): - """Helper to mock discoveries.""" + """Mock discoveries.""" result = await async_setup_component(hass, 'discovery', config) assert result diff --git a/tests/components/test_dyson.py b/tests/components/test_dyson.py index 38f3e60dcf4731..19c39754eb2b18 100644 --- a/tests/components/test_dyson.py +++ b/tests/components/test_dyson.py @@ -36,7 +36,7 @@ class DysonTest(unittest.TestCase): """Dyson parent component test class.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/test_feedreader.py b/tests/components/test_feedreader.py index 336d19664b42ff..dd98ebaf189c31 100644 --- a/tests/components/test_feedreader.py +++ b/tests/components/test_feedreader.py @@ -79,7 +79,7 @@ def test_setup_max_entries(self): VALID_CONFIG_3)) def setup_manager(self, feed_data, max_entries=DEFAULT_MAX_ENTRIES): - """Generic test setup method.""" + """Set up feed manager.""" events = [] @callback diff --git a/tests/components/test_ffmpeg.py b/tests/components/test_ffmpeg.py index 44c3a1dd69545a..76b1300774b8c9 100644 --- a/tests/components/test_ffmpeg.py +++ b/tests/components/test_ffmpeg.py @@ -42,7 +42,7 @@ class TestFFmpegSetup: """Test class for ffmpeg.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self): @@ -50,14 +50,14 @@ def teardown_method(self): self.hass.stop() def test_setup_component(self): - """Setup ffmpeg component.""" + """Set up ffmpeg component.""" with assert_setup_component(2): setup_component(self.hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}}) assert self.hass.data[ffmpeg.DATA_FFMPEG].binary == 'ffmpeg' def test_setup_component_test_service(self): - """Setup ffmpeg component test services.""" + """Set up ffmpeg component test services.""" with assert_setup_component(2): setup_component(self.hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}}) @@ -68,7 +68,7 @@ def test_setup_component_test_service(self): @asyncio.coroutine def test_setup_component_test_register(hass): - """Setup ffmpeg component test register.""" + """Set up ffmpeg component test register.""" with assert_setup_component(2): yield from async_setup_component( hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}}) @@ -83,7 +83,7 @@ def test_setup_component_test_register(hass): @asyncio.coroutine def test_setup_component_test_register_no_startup(hass): - """Setup ffmpeg component test register without startup.""" + """Set up ffmpeg component test register without startup.""" with assert_setup_component(2): yield from async_setup_component( hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}}) @@ -98,7 +98,7 @@ def test_setup_component_test_register_no_startup(hass): @asyncio.coroutine def test_setup_component_test_service_start(hass): - """Setup ffmpeg component test service start.""" + """Set up ffmpeg component test service start.""" with assert_setup_component(2): yield from async_setup_component( hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}}) @@ -114,7 +114,7 @@ def test_setup_component_test_service_start(hass): @asyncio.coroutine def test_setup_component_test_service_stop(hass): - """Setup ffmpeg component test service stop.""" + """Set up ffmpeg component test service stop.""" with assert_setup_component(2): yield from async_setup_component( hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}}) @@ -130,7 +130,7 @@ def test_setup_component_test_service_stop(hass): @asyncio.coroutine def test_setup_component_test_service_restart(hass): - """Setup ffmpeg component test service restart.""" + """Set up ffmpeg component test service restart.""" with assert_setup_component(2): yield from async_setup_component( hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}}) @@ -147,7 +147,7 @@ def test_setup_component_test_service_restart(hass): @asyncio.coroutine def test_setup_component_test_service_start_with_entity(hass): - """Setup ffmpeg component test service start.""" + """Set up ffmpeg component test service start.""" with assert_setup_component(2): yield from async_setup_component( hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}}) @@ -164,7 +164,7 @@ def test_setup_component_test_service_start_with_entity(hass): @asyncio.coroutine def test_setup_component_test_run_test_false(hass): - """Setup ffmpeg component test run_test false.""" + """Set up ffmpeg component test run_test false.""" with assert_setup_component(2): yield from async_setup_component( hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: { @@ -180,7 +180,7 @@ def test_setup_component_test_run_test_false(hass): @asyncio.coroutine def test_setup_component_test_run_test(hass): - """Setup ffmpeg component test run_test.""" + """Set up ffmpeg component test run_test.""" with assert_setup_component(2): yield from async_setup_component( hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}}) @@ -206,7 +206,7 @@ def test_setup_component_test_run_test(hass): @asyncio.coroutine def test_setup_component_test_run_test_test_fail(hass): - """Setup ffmpeg component test run_test.""" + """Set up ffmpeg component test run_test.""" with assert_setup_component(2): yield from async_setup_component( hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}}) diff --git a/tests/components/test_folder_watcher.py b/tests/components/test_folder_watcher.py index b5ac9cca9d9b9e..451d9ae3e0e3d7 100644 --- a/tests/components/test_folder_watcher.py +++ b/tests/components/test_folder_watcher.py @@ -8,7 +8,7 @@ async def test_invalid_path_setup(hass): - """Test that an invalid path is not setup.""" + """Test that an invalid path is not set up.""" assert not await async_setup_component( hass, folder_watcher.DOMAIN, { folder_watcher.DOMAIN: { diff --git a/tests/components/test_google.py b/tests/components/test_google.py index 0ee066fcfeecaa..b8dc29b5dea0cc 100644 --- a/tests/components/test_google.py +++ b/tests/components/test_google.py @@ -14,7 +14,7 @@ class TestGoogle(unittest.TestCase): """Test the Google component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/test_graphite.py b/tests/components/test_graphite.py index 892fe5b5f4d3e5..7ceda9e191e21f 100644 --- a/tests/components/test_graphite.py +++ b/tests/components/test_graphite.py @@ -17,7 +17,7 @@ class TestGraphite(unittest.TestCase): """Test the Graphite component.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.gf = graphite.GraphiteFeeder(self.hass, 'foo', 123, 'ha') diff --git a/tests/components/test_history.py b/tests/components/test_history.py index b348498b07e25f..ef2f7a17a19fdb 100644 --- a/tests/components/test_history.py +++ b/tests/components/test_history.py @@ -17,7 +17,7 @@ class TestComponentHistory(unittest.TestCase): """Test History component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/test_history_graph.py b/tests/components/test_history_graph.py index 554f7f29dd76f9..9b7733a7ec2a6f 100644 --- a/tests/components/test_history_graph.py +++ b/tests/components/test_history_graph.py @@ -10,7 +10,7 @@ class TestGraph(unittest.TestCase): """Test the Google component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/test_influxdb.py b/tests/components/test_influxdb.py index e2323aca8559da..7d1b7527612233 100644 --- a/tests/components/test_influxdb.py +++ b/tests/components/test_influxdb.py @@ -21,7 +21,7 @@ class TestInfluxDB(unittest.TestCase): """Test the InfluxDB component.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.handler_method = None self.hass.bus.listen = mock.Mock() @@ -96,7 +96,7 @@ def test_setup_query_fail(self, mock_client): assert not setup_component(self.hass, influxdb.DOMAIN, config) def _setup(self, **kwargs): - """Setup the client.""" + """Set up the client.""" config = { 'influxdb': { 'host': 'host', diff --git a/tests/components/test_init.py b/tests/components/test_init.py index 1e565054637766..355f3dc0e96910 100644 --- a/tests/components/test_init.py +++ b/tests/components/test_init.py @@ -25,7 +25,7 @@ class TestComponentsCore(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.assertTrue(run_coroutine_threadsafe( comps.async_setup(self.hass, {}), self.hass.loop diff --git a/tests/components/test_input_boolean.py b/tests/components/test_input_boolean.py index e39b12481bc5b3..999e7ac100f32e 100644 --- a/tests/components/test_input_boolean.py +++ b/tests/components/test_input_boolean.py @@ -4,7 +4,7 @@ import unittest import logging -from homeassistant.core import CoreState, State +from homeassistant.core import CoreState, State, Context from homeassistant.setup import setup_component, async_setup_component from homeassistant.components.input_boolean import ( DOMAIN, is_on, toggle, turn_off, turn_on, CONF_INITIAL) @@ -22,7 +22,7 @@ class TestInputBoolean(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name @@ -158,3 +158,24 @@ def test_initial_state_overrules_restore_state(hass): state = hass.states.get('input_boolean.b2') assert state assert state.state == 'on' + + +async def test_input_boolean_context(hass): + """Test that input_boolean context works.""" + assert await async_setup_component(hass, 'input_boolean', { + 'input_boolean': { + 'ac': {CONF_INITIAL: True}, + } + }) + + state = hass.states.get('input_boolean.ac') + assert state is not None + + await hass.services.async_call('input_boolean', 'turn_off', { + 'entity_id': state.entity_id, + }, True, Context(user_id='abcd')) + + state2 = hass.states.get('input_boolean.ac') + assert state2 is not None + assert state.state != state2.state + assert state2.context.user_id == 'abcd' diff --git a/tests/components/test_input_datetime.py b/tests/components/test_input_datetime.py index 5d3f1782831d1c..9ced2aaa072ba2 100644 --- a/tests/components/test_input_datetime.py +++ b/tests/components/test_input_datetime.py @@ -4,20 +4,29 @@ import unittest import datetime -from homeassistant.core import CoreState, State +from homeassistant.core import CoreState, State, Context from homeassistant.setup import setup_component, async_setup_component from homeassistant.components.input_datetime import ( - DOMAIN, async_set_datetime) + DOMAIN, ATTR_ENTITY_ID, ATTR_DATE, ATTR_TIME, SERVICE_SET_DATETIME) from tests.common import get_test_home_assistant, mock_restore_cache +async def async_set_datetime(hass, entity_id, dt_value): + """Set date and / or time of input_datetime.""" + await hass.services.async_call(DOMAIN, SERVICE_SET_DATETIME, { + ATTR_ENTITY_ID: entity_id, + ATTR_DATE: dt_value.date(), + ATTR_TIME: dt_value.time() + }, blocking=True) + + class TestInputDatetime(unittest.TestCase): """Test the input datetime component.""" # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name @@ -57,7 +66,6 @@ def test_set_datetime(hass): dt_obj = datetime.datetime(2017, 9, 7, 19, 46) yield from async_set_datetime(hass, entity_id, dt_obj) - yield from hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == str(dt_obj) @@ -89,7 +97,6 @@ def test_set_datetime_time(hass): time_portion = dt_obj.time() yield from async_set_datetime(hass, entity_id, dt_obj) - yield from hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == str(time_portion) @@ -144,7 +151,6 @@ def test_set_datetime_date(hass): date_portion = dt_obj.date() yield from async_set_datetime(hass, entity_id, dt_obj) - yield from hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == str(date_portion) @@ -202,3 +208,27 @@ def test_restore_state(hass): state_bogus = hass.states.get('input_datetime.test_bogus_data') assert state_bogus.state == str(initial) + + +async def test_input_datetime_context(hass): + """Test that input_datetime context works.""" + assert await async_setup_component(hass, 'input_datetime', { + 'input_datetime': { + 'only_date': { + 'has_date': True, + } + } + }) + + state = hass.states.get('input_datetime.only_date') + assert state is not None + + await hass.services.async_call('input_datetime', 'set_datetime', { + 'entity_id': state.entity_id, + 'date': '2018-01-02' + }, True, Context(user_id='abcd')) + + state2 = hass.states.get('input_datetime.only_date') + assert state2 is not None + assert state.state != state2.state + assert state2.context.user_id == 'abcd' diff --git a/tests/components/test_input_number.py b/tests/components/test_input_number.py index fde940efa1a32f..659aaa524d92a5 100644 --- a/tests/components/test_input_number.py +++ b/tests/components/test_input_number.py @@ -3,7 +3,7 @@ import asyncio import unittest -from homeassistant.core import CoreState, State +from homeassistant.core import CoreState, State, Context from homeassistant.setup import setup_component, async_setup_component from homeassistant.components.input_number import ( DOMAIN, set_value, increment, decrement) @@ -16,7 +16,7 @@ class TestInputNumber(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name @@ -236,3 +236,27 @@ def test_no_initial_state_and_no_restore_state(hass): state = hass.states.get('input_number.b1') assert state assert float(state.state) == 0 + + +async def test_input_number_context(hass): + """Test that input_number context works.""" + assert await async_setup_component(hass, 'input_number', { + 'input_number': { + 'b1': { + 'min': 0, + 'max': 100, + }, + } + }) + + state = hass.states.get('input_number.b1') + assert state is not None + + await hass.services.async_call('input_number', 'increment', { + 'entity_id': state.entity_id, + }, True, Context(user_id='abcd')) + + state2 = hass.states.get('input_number.b1') + assert state2 is not None + assert state.state != state2.state + assert state2.context.user_id == 'abcd' diff --git a/tests/components/test_input_select.py b/tests/components/test_input_select.py index 20d9656d8b092e..1c73abfbb9431d 100644 --- a/tests/components/test_input_select.py +++ b/tests/components/test_input_select.py @@ -5,7 +5,7 @@ from tests.common import get_test_home_assistant, mock_restore_cache -from homeassistant.core import State +from homeassistant.core import State, Context from homeassistant.setup import setup_component, async_setup_component from homeassistant.components.input_select import ( ATTR_OPTIONS, DOMAIN, SERVICE_SET_OPTIONS, @@ -19,7 +19,7 @@ class TestInputSelect(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name @@ -276,3 +276,30 @@ def test_initial_state_overrules_restore_state(hass): state = hass.states.get('input_select.s2') assert state assert state.state == 'middle option' + + +async def test_input_select_context(hass): + """Test that input_select context works.""" + assert await async_setup_component(hass, 'input_select', { + 'input_select': { + 's1': { + 'options': [ + 'first option', + 'middle option', + 'last option', + ], + } + } + }) + + state = hass.states.get('input_select.s1') + assert state is not None + + await hass.services.async_call('input_select', 'select_next', { + 'entity_id': state.entity_id, + }, True, Context(user_id='abcd')) + + state2 = hass.states.get('input_select.s1') + assert state2 is not None + assert state.state != state2.state + assert state2.context.user_id == 'abcd' diff --git a/tests/components/test_input_text.py b/tests/components/test_input_text.py index c288375ec8fef0..7c8a0e65023ad4 100644 --- a/tests/components/test_input_text.py +++ b/tests/components/test_input_text.py @@ -3,7 +3,7 @@ import asyncio import unittest -from homeassistant.core import CoreState, State +from homeassistant.core import CoreState, State, Context from homeassistant.setup import setup_component, async_setup_component from homeassistant.components.input_text import (DOMAIN, set_value) @@ -15,7 +15,7 @@ class TestInputText(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name @@ -180,3 +180,27 @@ def test_no_initial_state_and_no_restore_state(hass): state = hass.states.get('input_text.b1') assert state assert str(state.state) == 'unknown' + + +async def test_input_text_context(hass): + """Test that input_text context works.""" + assert await async_setup_component(hass, 'input_text', { + 'input_text': { + 't1': { + 'initial': 'bla', + } + } + }) + + state = hass.states.get('input_text.t1') + assert state is not None + + await hass.services.async_call('input_text', 'set_value', { + 'entity_id': state.entity_id, + 'value': 'new_value', + }, True, Context(user_id='abcd')) + + state2 = hass.states.get('input_text.t1') + assert state2 is not None + assert state.state != state2.state + assert state2.context.user_id == 'abcd' diff --git a/tests/components/test_introduction.py b/tests/components/test_introduction.py index 99b373961cc1dd..b7099d048782bc 100644 --- a/tests/components/test_introduction.py +++ b/tests/components/test_introduction.py @@ -11,7 +11,7 @@ class TestIntroduction(unittest.TestCase): """Test Introduction.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/test_kira.py b/tests/components/test_kira.py index a80d766c3fd298..67ab679800f7eb 100644 --- a/tests/components/test_kira.py +++ b/tests/components/test_kira.py @@ -36,7 +36,7 @@ class TestKiraSetup(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() _base_mock = MagicMock() pykira = _base_mock.pykira diff --git a/tests/components/test_litejet.py b/tests/components/test_litejet.py index dfbcb9d99d88b8..3b46e9d274c277 100644 --- a/tests/components/test_litejet.py +++ b/tests/components/test_litejet.py @@ -12,7 +12,7 @@ class TestLiteJet(unittest.TestCase): """Test the litejet component.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.start() self.hass.block_till_done() diff --git a/tests/components/test_logbook.py b/tests/components/test_logbook.py index a3a5273ed4e1d1..cf78fbec352f82 100644 --- a/tests/components/test_logbook.py +++ b/tests/components/test_logbook.py @@ -26,7 +26,7 @@ class TestComponentLogbook(unittest.TestCase): EMPTY_CONFIG = logbook.CONFIG_SCHEMA({logbook.DOMAIN: {}}) def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() init_recorder_component(self.hass) # Force an in memory DB assert setup_component(self.hass, logbook.DOMAIN, self.EMPTY_CONFIG) diff --git a/tests/components/test_logentries.py b/tests/components/test_logentries.py index bff80c958f31a6..843a043cee6e7b 100644 --- a/tests/components/test_logentries.py +++ b/tests/components/test_logentries.py @@ -14,7 +14,7 @@ class TestLogentries(unittest.TestCase): """Test the Logentries component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/test_logger.py b/tests/components/test_logger.py index a55a66c6505fe5..f774af2169cccf 100644 --- a/tests/components/test_logger.py +++ b/tests/components/test_logger.py @@ -24,7 +24,7 @@ class TestUpdater(unittest.TestCase): """Test logger component.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.log_filter = None @@ -34,7 +34,7 @@ def tearDown(self): self.hass.stop() def setup_logger(self, config): - """Setup logger and save log filter.""" + """Set up logger and save log filter.""" setup_component(self.hass, logger.DOMAIN, config) self.log_filter = logging.root.handlers[-1].filters[0] diff --git a/tests/components/test_microsoft_face.py b/tests/components/test_microsoft_face.py index 92f840b8033d7f..601d5e7ebcc166 100644 --- a/tests/components/test_microsoft_face.py +++ b/tests/components/test_microsoft_face.py @@ -13,7 +13,7 @@ class TestMicrosoftFaceSetup: """Test the microsoft face component.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.config = { @@ -31,21 +31,21 @@ def teardown_method(self): @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_setup_component(self, mock_update): - """Setup component.""" + """Set up component.""" with assert_setup_component(3, mf.DOMAIN): setup_component(self.hass, mf.DOMAIN, self.config) @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_setup_component_wrong_api_key(self, mock_update): - """Setup component without api key.""" + """Set up component without api key.""" with assert_setup_component(0, mf.DOMAIN): setup_component(self.hass, mf.DOMAIN, {mf.DOMAIN: {}}) @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_setup_component_test_service(self, mock_update): - """Setup component.""" + """Set up component.""" with assert_setup_component(3, mf.DOMAIN): setup_component(self.hass, mf.DOMAIN, self.config) @@ -57,7 +57,7 @@ def test_setup_component_test_service(self, mock_update): assert self.hass.services.has_service(mf.DOMAIN, 'face_person') def test_setup_component_test_entities(self, aioclient_mock): - """Setup component.""" + """Set up component.""" aioclient_mock.get( self.endpoint_url.format("persongroups"), text=load_fixture('microsoft_face_persongroups.json') @@ -95,7 +95,7 @@ def test_setup_component_test_entities(self, aioclient_mock): @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_service_groups(self, mock_update, aioclient_mock): - """Setup component, test groups services.""" + """Set up component, test groups services.""" aioclient_mock.put( self.endpoint_url.format("persongroups/service_group"), status=200, text="{}" @@ -123,7 +123,7 @@ def test_service_groups(self, mock_update, aioclient_mock): assert len(aioclient_mock.mock_calls) == 2 def test_service_person(self, aioclient_mock): - """Setup component, test person services.""" + """Set up component, test person services.""" aioclient_mock.get( self.endpoint_url.format("persongroups"), text=load_fixture('microsoft_face_persongroups.json') @@ -175,7 +175,7 @@ def test_service_person(self, aioclient_mock): @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_service_train(self, mock_update, aioclient_mock): - """Setup component, test train groups services.""" + """Set up component, test train groups services.""" with assert_setup_component(3, mf.DOMAIN): setup_component(self.hass, mf.DOMAIN, self.config) @@ -192,7 +192,7 @@ def test_service_train(self, mock_update, aioclient_mock): @patch('homeassistant.components.camera.async_get_image', return_value=mock_coro(camera.Image('image/jpeg', b'Test'))) def test_service_face(self, camera_mock, aioclient_mock): - """Setup component, test person face services.""" + """Set up component, test person face services.""" aioclient_mock.get( self.endpoint_url.format("persongroups"), text=load_fixture('microsoft_face_persongroups.json') @@ -229,7 +229,7 @@ def test_service_face(self, camera_mock, aioclient_mock): @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_service_status_400(self, mock_update, aioclient_mock): - """Setup component, test groups services with error.""" + """Set up component, test groups services with error.""" aioclient_mock.put( self.endpoint_url.format("persongroups/service_group"), status=400, text="{'error': {'message': 'Error'}}" @@ -248,7 +248,7 @@ def test_service_status_400(self, mock_update, aioclient_mock): @patch('homeassistant.components.microsoft_face.' 'MicrosoftFace.update_store', return_value=mock_coro()) def test_service_status_timeout(self, mock_update, aioclient_mock): - """Setup component, test groups services with timeout.""" + """Set up component, test groups services with timeout.""" aioclient_mock.put( self.endpoint_url.format("persongroups/service_group"), status=400, exc=asyncio.TimeoutError() diff --git a/tests/components/test_mqtt_eventstream.py b/tests/components/test_mqtt_eventstream.py index 8da1311c87dc9e..1613198e4ced18 100644 --- a/tests/components/test_mqtt_eventstream.py +++ b/tests/components/test_mqtt_eventstream.py @@ -6,7 +6,7 @@ import homeassistant.components.mqtt_eventstream as eventstream from homeassistant.const import EVENT_STATE_CHANGED from homeassistant.core import State, callback -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder import homeassistant.util.dt as dt_util from tests.common import ( @@ -22,7 +22,7 @@ class TestMqttEventStream: """Test the MQTT eventstream module.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_mqtt = mock_mqtt_component(self.hass) diff --git a/tests/components/test_mqtt_statestream.py b/tests/components/test_mqtt_statestream.py index 4cf79e679cd470..97c4c0647e800e 100644 --- a/tests/components/test_mqtt_statestream.py +++ b/tests/components/test_mqtt_statestream.py @@ -16,7 +16,7 @@ class TestMqttStateStream: """Test the MQTT statestream module.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.mock_mqtt = mock_mqtt_component(self.hass) diff --git a/tests/components/test_panel_custom.py b/tests/components/test_panel_custom.py index 596aa1b3c0b25c..c265324179dc45 100644 --- a/tests/components/test_panel_custom.py +++ b/tests/components/test_panel_custom.py @@ -114,3 +114,73 @@ async def test_js_webcomponent(hass): assert panel.frontend_url_path == 'nice_url' assert panel.sidebar_icon == 'mdi:iconicon' assert panel.sidebar_title == 'Sidebar Title' + + +async def test_module_webcomponent(hass): + """Test if a js module is found in config panels dir.""" + config = { + 'panel_custom': { + 'name': 'todo-mvc', + 'module_url': '/local/bla.js', + 'sidebar_title': 'Sidebar Title', + 'sidebar_icon': 'mdi:iconicon', + 'url_path': 'nice_url', + 'config': { + 'hello': 'world', + }, + 'embed_iframe': True, + 'trust_external_script': True, + } + } + + result = await setup.async_setup_component( + hass, 'panel_custom', config + ) + assert result + + panels = hass.data.get(frontend.DATA_PANELS, []) + + assert panels + assert 'nice_url' in panels + + panel = panels['nice_url'] + + assert panel.config == { + 'hello': 'world', + '_panel_custom': { + 'module_url': '/local/bla.js', + 'name': 'todo-mvc', + 'embed_iframe': True, + 'trust_external': True, + } + } + assert panel.frontend_url_path == 'nice_url' + assert panel.sidebar_icon == 'mdi:iconicon' + assert panel.sidebar_title == 'Sidebar Title' + + +async def test_url_option_conflict(hass): + """Test config with multiple url options.""" + to_try = [ + {'panel_custom': { + 'name': 'todo-mvc', + 'module_url': '/local/bla.js', + 'js_url': '/local/bla.js', + } + }, {'panel_custom': { + 'name': 'todo-mvc', + 'webcomponent_path': '/local/bla.html', + 'js_url': '/local/bla.js', + }}, {'panel_custom': { + 'name': 'todo-mvc', + 'webcomponent_path': '/local/bla.html', + 'module_url': '/local/bla.js', + 'js_url': '/local/bla.js', + }} + ] + + for config in to_try: + result = await setup.async_setup_component( + hass, 'panel_custom', config + ) + assert not result diff --git a/tests/components/test_panel_iframe.py b/tests/components/test_panel_iframe.py index 214eda04ad8561..3ac06c09a2610f 100644 --- a/tests/components/test_panel_iframe.py +++ b/tests/components/test_panel_iframe.py @@ -11,7 +11,7 @@ class TestPanelIframe(unittest.TestCase): """Test the panel_iframe component.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/test_pilight.py b/tests/components/test_pilight.py index 24052a56839307..e630a354f45424 100644 --- a/tests/components/test_pilight.py +++ b/tests/components/test_pilight.py @@ -41,11 +41,11 @@ def __init__(self, host, port): pass def send_code(self, call): # pylint: disable=no-self-use - """Called pilight.send service is called.""" + """Handle pilight.send service callback.""" _LOGGER.error('PilightDaemonSim payload: ' + str(call)) def start(self): - """Called homeassistant.start is called. + """Handle homeassistant.start callback. Also sends one test message after start up """ @@ -56,11 +56,11 @@ def start(self): self.called = True def stop(self): # pylint: disable=no-self-use - """Called homeassistant.stop is called.""" + """Handle homeassistant.stop callback.""" _LOGGER.error('PilightDaemonSim stop') def set_callback(self, function): - """Callback called on event pilight.pilight_received.""" + """Handle pilight.pilight_received event callback.""" self.callback = function _LOGGER.error('PilightDaemonSim callback: ' + str(function)) @@ -70,7 +70,7 @@ class TestPilight(unittest.TestCase): """Test the Pilight component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.skip_teardown_stop = False @@ -351,7 +351,7 @@ class TestPilightCallrateThrottler(unittest.TestCase): """Test the Throttler used to throttle calls to send_code.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/test_proximity.py b/tests/components/test_proximity.py index 42f1cbf4b43b76..f69ace460148d0 100644 --- a/tests/components/test_proximity.py +++ b/tests/components/test_proximity.py @@ -12,7 +12,7 @@ class TestProximity(unittest.TestCase): """Test the Proximity component.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.states.set( 'zone.home', 'zoning', diff --git a/tests/components/test_rest_command.py b/tests/components/test_rest_command.py index 097fb799d4028c..b66628a35629fc 100644 --- a/tests/components/test_rest_command.py +++ b/tests/components/test_rest_command.py @@ -14,7 +14,7 @@ class TestRestCommandSetup: """Test the rest command component.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.config = { @@ -51,7 +51,7 @@ class TestRestCommandComponent: """Test the rest command component.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.url = "https://example.com/" self.config = { rc.DOMAIN: { @@ -81,7 +81,7 @@ def teardown_method(self): self.hass.stop() def test_setup_tests(self): - """Setup test config and test it.""" + """Set up test config and test it.""" with assert_setup_component(4): setup_component(self.hass, rc.DOMAIN, self.config) diff --git a/tests/components/test_rfxtrx.py b/tests/components/test_rfxtrx.py index 1730d3a5371d6f..93bf0b16dc5156 100644 --- a/tests/components/test_rfxtrx.py +++ b/tests/components/test_rfxtrx.py @@ -15,7 +15,7 @@ class TestRFXTRX(unittest.TestCase): """Test the Rfxtrx component.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/test_rss_feed_template.py b/tests/components/test_rss_feed_template.py index 36f68e57c9fd02..64876dbea44a60 100644 --- a/tests/components/test_rss_feed_template.py +++ b/tests/components/test_rss_feed_template.py @@ -9,7 +9,7 @@ @pytest.fixture def mock_http_client(loop, hass, aiohttp_client): - """Setup test fixture.""" + """Set up test fixture.""" config = { 'rss_feed_template': { 'testfeed': { diff --git a/tests/components/test_script.py b/tests/components/test_script.py index c4282cdfbaf201..8ad782cf6979de 100644 --- a/tests/components/test_script.py +++ b/tests/components/test_script.py @@ -18,7 +18,7 @@ class TestScriptComponent(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name diff --git a/tests/components/test_shell_command.py b/tests/components/test_shell_command.py index a1acffd62e591e..e945befbb848cf 100644 --- a/tests/components/test_shell_command.py +++ b/tests/components/test_shell_command.py @@ -33,7 +33,7 @@ class TestShellCommand(unittest.TestCase): """Test the shell_command component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started. + """Set up things to be run when tests are started. Also seems to require a child watcher attached to the loop when run from pytest. diff --git a/tests/components/test_splunk.py b/tests/components/test_splunk.py index 38143119112e23..173c822ddb6a0e 100644 --- a/tests/components/test_splunk.py +++ b/tests/components/test_splunk.py @@ -16,7 +16,7 @@ class TestSplunk(unittest.TestCase): """Test the Splunk component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/test_statsd.py b/tests/components/test_statsd.py index 5fd907fe0b10a4..6bd00e50646d69 100644 --- a/tests/components/test_statsd.py +++ b/tests/components/test_statsd.py @@ -16,7 +16,7 @@ class TestStatsd(unittest.TestCase): """Test the StatsD component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/test_sun.py b/tests/components/test_sun.py index d5a4ecfcb81a4a..aa94bf2bdd3dd5 100644 --- a/tests/components/test_sun.py +++ b/tests/components/test_sun.py @@ -17,7 +17,7 @@ class TestSun(unittest.TestCase): """Test the sun module.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/test_updater.py b/tests/components/test_updater.py index 28ffcac2b13cbb..23b669928f4aa0 100644 --- a/tests/components/test_updater.py +++ b/tests/components/test_updater.py @@ -46,7 +46,7 @@ def test_new_version_shows_entity_after_hour( res = yield from async_setup_component( hass, updater.DOMAIN, {updater.DOMAIN: {}}) - assert res, 'Updater failed to setup' + assert res, 'Updater failed to set up' with patch('homeassistant.components.updater.current_version', MOCK_VERSION): @@ -65,7 +65,7 @@ def test_same_version_not_show_entity( res = yield from async_setup_component( hass, updater.DOMAIN, {updater.DOMAIN: {}}) - assert res, 'Updater failed to setup' + assert res, 'Updater failed to set up' with patch('homeassistant.components.updater.current_version', MOCK_VERSION): @@ -85,7 +85,7 @@ def test_disable_reporting(hass, mock_get_uuid, mock_get_newest_version): hass, updater.DOMAIN, {updater.DOMAIN: { 'reporting': False }}) - assert res, 'Updater failed to setup' + assert res, 'Updater failed to set up' with patch('homeassistant.components.updater.current_version', MOCK_VERSION): @@ -187,7 +187,7 @@ def test_new_version_shows_entity_after_hour_hassio( res = yield from async_setup_component( hass, updater.DOMAIN, {updater.DOMAIN: {}}) - assert res, 'Updater failed to setup' + assert res, 'Updater failed to set up' with patch('homeassistant.components.updater.current_version', MOCK_VERSION): diff --git a/tests/components/test_upnp.py b/tests/components/test_upnp.py index 4956b8a62783fe..6089e6859f242f 100644 --- a/tests/components/test_upnp.py +++ b/tests/components/test_upnp.py @@ -47,7 +47,7 @@ async def get_device(self, *args, **kwargs): @pytest.fixture def mock_msearch_first(*args, **kwargs): - """Wrapper to async mock function.""" + """Wrap async mock msearch_first.""" async def async_mock_msearch_first(*args, **kwargs): """Mock msearch_first.""" return MockResp(*args, **kwargs) @@ -58,7 +58,7 @@ async def async_mock_msearch_first(*args, **kwargs): @pytest.fixture def mock_async_exception(*args, **kwargs): - """Wrapper to async mock function with exception.""" + """Wrap async mock exception.""" async def async_mock_exception(*args, **kwargs): return Exception @@ -102,7 +102,8 @@ async def test_setup_succeeds_if_specify_ip(hass, mock_msearch_first): return_value='127.0.0.1'): result = await async_setup_component(hass, 'upnp', { 'upnp': { - 'local_ip': '192.168.0.10' + 'local_ip': '192.168.0.10', + 'port_mapping': 'True' } }) @@ -118,7 +119,9 @@ async def test_no_config_maps_hass_local_to_remote_port(hass, mock_msearch_first): """Test by default we map local to remote port.""" result = await async_setup_component(hass, 'upnp', { - 'upnp': {} + 'upnp': { + 'port_mapping': 'True' + } }) assert result @@ -134,6 +137,7 @@ async def test_map_hass_to_remote_port(hass, """Test mapping hass to remote port.""" result = await async_setup_component(hass, 'upnp', { 'upnp': { + 'port_mapping': 'True', 'ports': { 'hass': 1000 } @@ -157,6 +161,7 @@ async def test_map_internal_to_remote_ports(hass, result = await async_setup_component(hass, 'upnp', { 'upnp': { + 'port_mapping': 'True', 'ports': ports } }) diff --git a/tests/components/test_weblink.py b/tests/components/test_weblink.py index f35398e034c08c..8e71c89cdd6b2f 100644 --- a/tests/components/test_weblink.py +++ b/tests/components/test_weblink.py @@ -11,7 +11,7 @@ class TestComponentWeblink(unittest.TestCase): """Test the Weblink component.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/components/tts/test_google.py b/tests/components/tts/test_google.py index cf9a7b2db295d2..f328e3e9f16a3e 100644 --- a/tests/components/tts/test_google.py +++ b/tests/components/tts/test_google.py @@ -19,7 +19,7 @@ class TestTTSGooglePlatform: """Test the Google speech component.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.url = "https://translate.google.com/translate_tts" diff --git a/tests/components/tts/test_init.py b/tests/components/tts/test_init.py index e8746ee762f6ae..719fe2716e774b 100644 --- a/tests/components/tts/test_init.py +++ b/tests/components/tts/test_init.py @@ -33,7 +33,7 @@ class TestTTS: """Test the Google speech component.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.demo_provider = DemoProvider('en') self.default_tts_cache = self.hass.config.path(tts.DEFAULT_CACHE_DIR) @@ -44,13 +44,13 @@ def setup_method(self): def teardown_method(self): """Stop everything that was started.""" + self.hass.stop() + if os.path.isdir(self.default_tts_cache): shutil.rmtree(self.default_tts_cache) - self.hass.stop() - def test_setup_component_demo(self): - """Setup the demo platform with defaults.""" + """Set up the demo platform with defaults.""" config = { tts.DOMAIN: { 'platform': 'demo', @@ -65,7 +65,7 @@ def test_setup_component_demo(self): @patch('os.mkdir', side_effect=OSError(2, "No access")) def test_setup_component_demo_no_access_cache_folder(self, mock_mkdir): - """Setup the demo platform with defaults.""" + """Set up the demo platform with defaults.""" config = { tts.DOMAIN: { 'platform': 'demo', @@ -78,7 +78,7 @@ def test_setup_component_demo_no_access_cache_folder(self, mock_mkdir): assert not self.hass.services.has_service(tts.DOMAIN, 'clear_cache') def test_setup_component_and_test_service(self): - """Setup the demo platform and call service.""" + """Set up the demo platform and call service.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -106,7 +106,7 @@ def test_setup_component_and_test_service(self): "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")) def test_setup_component_and_test_service_with_config_language(self): - """Setup the demo platform and call service.""" + """Set up the demo platform and call service.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -135,7 +135,7 @@ def test_setup_component_and_test_service_with_config_language(self): "265944c108cbb00b2a621be5930513e03a0bb2cd_de_-_demo.mp3")) def test_setup_component_and_test_service_with_wrong_conf_language(self): - """Setup the demo platform and call service with wrong config.""" + """Set up the demo platform and call service with wrong config.""" config = { tts.DOMAIN: { 'platform': 'demo', @@ -147,7 +147,7 @@ def test_setup_component_and_test_service_with_wrong_conf_language(self): setup_component(self.hass, tts.DOMAIN, config) def test_setup_component_and_test_service_with_service_language(self): - """Setup the demo platform and call service.""" + """Set up the demo platform and call service.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -176,7 +176,7 @@ def test_setup_component_and_test_service_with_service_language(self): "265944c108cbb00b2a621be5930513e03a0bb2cd_de_-_demo.mp3")) def test_setup_component_test_service_with_wrong_service_language(self): - """Setup the demo platform and call service.""" + """Set up the demo platform and call service.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -200,7 +200,7 @@ def test_setup_component_test_service_with_wrong_service_language(self): "265944c108cbb00b2a621be5930513e03a0bb2cd_lang_-_demo.mp3")) def test_setup_component_and_test_service_with_service_options(self): - """Setup the demo platform and call service with options.""" + """Set up the demo platform and call service with options.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -237,7 +237,7 @@ def test_setup_component_and_test_service_with_service_options(self): @patch('homeassistant.components.tts.demo.DemoProvider.default_options', new_callable=PropertyMock(return_value={'voice': 'alex'})) def test_setup_component_and_test_with_service_options_def(self, def_mock): - """Setup the demo platform and call service with default options.""" + """Set up the demo platform and call service with default options.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -269,7 +269,7 @@ def test_setup_component_and_test_with_service_options_def(self, def_mock): opt_hash))) def test_setup_component_and_test_service_with_service_options_wrong(self): - """Setup the demo platform and call service with wrong options.""" + """Set up the demo platform and call service with wrong options.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -299,7 +299,7 @@ def test_setup_component_and_test_service_with_service_options_wrong(self): opt_hash))) def test_setup_component_and_test_service_clear_cache(self): - """Setup the demo platform and call service clear cache.""" + """Set up the demo platform and call service clear cache.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -329,7 +329,7 @@ def test_setup_component_and_test_service_clear_cache(self): "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")) def test_setup_component_and_test_service_with_receive_voice(self): - """Setup the demo platform and call service and receive voice.""" + """Set up the demo platform and call service and receive voice.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -359,7 +359,7 @@ def test_setup_component_and_test_service_with_receive_voice(self): assert req.content == demo_data def test_setup_component_and_test_service_with_receive_voice_german(self): - """Setup the demo platform and call service and receive voice.""" + """Set up the demo platform and call service and receive voice.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -390,7 +390,7 @@ def test_setup_component_and_test_service_with_receive_voice_german(self): assert req.content == demo_data def test_setup_component_and_web_view_wrong_file(self): - """Setup the demo platform and receive wrong file from web.""" + """Set up the demo platform and receive wrong file from web.""" config = { tts.DOMAIN: { 'platform': 'demo', @@ -409,7 +409,7 @@ def test_setup_component_and_web_view_wrong_file(self): assert req.status_code == 404 def test_setup_component_and_web_view_wrong_filename(self): - """Setup the demo platform and receive wrong filename from web.""" + """Set up the demo platform and receive wrong filename from web.""" config = { tts.DOMAIN: { 'platform': 'demo', @@ -428,7 +428,7 @@ def test_setup_component_and_web_view_wrong_filename(self): assert req.status_code == 404 def test_setup_component_test_without_cache(self): - """Setup demo platform without cache.""" + """Set up demo platform without cache.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -452,7 +452,7 @@ def test_setup_component_test_without_cache(self): "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")) def test_setup_component_test_with_cache_call_service_without_cache(self): - """Setup demo platform with cache and call service without cache.""" + """Set up demo platform with cache and call service without cache.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -477,7 +477,7 @@ def test_setup_component_test_with_cache_call_service_without_cache(self): "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")) def test_setup_component_test_with_cache_dir(self): - """Setup demo platform with cache and call service without cache.""" + """Set up demo platform with cache and call service without cache.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) _, demo_data = self.demo_provider.get_tts_audio("bla", 'en') @@ -515,7 +515,7 @@ def test_setup_component_test_with_cache_dir(self): @patch('homeassistant.components.tts.demo.DemoProvider.get_tts_audio', return_value=(None, None)) def test_setup_component_test_with_error_on_get_tts(self, tts_mock): - """Setup demo platform with wrong get_tts_audio.""" + """Set up demo platform with wrong get_tts_audio.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) config = { @@ -535,7 +535,7 @@ def test_setup_component_test_with_error_on_get_tts(self, tts_mock): assert len(calls) == 0 def test_setup_component_load_cache_retrieve_without_mem_cache(self): - """Setup component and load cache and get without mem cache.""" + """Set up component and load cache and get without mem cache.""" _, demo_data = self.demo_provider.get_tts_audio("bla", 'en') cache_file = os.path.join( self.default_tts_cache, @@ -565,7 +565,7 @@ def test_setup_component_load_cache_retrieve_without_mem_cache(self): assert req.content == demo_data def test_setup_component_and_web_get_url(self): - """Setup the demo platform and receive wrong file from web.""" + """Set up the demo platform and receive wrong file from web.""" config = { tts.DOMAIN: { 'platform': 'demo', @@ -589,7 +589,7 @@ def test_setup_component_and_web_get_url(self): .format(self.hass.config.api.base_url)) def test_setup_component_and_web_get_url_bad_config(self): - """Setup the demo platform and receive wrong file from web.""" + """Set up the demo platform and receive wrong file from web.""" config = { tts.DOMAIN: { 'platform': 'demo', diff --git a/tests/components/tts/test_marytts.py b/tests/components/tts/test_marytts.py index 7ec2ae39cd6103..110473d75a8138 100644 --- a/tests/components/tts/test_marytts.py +++ b/tests/components/tts/test_marytts.py @@ -18,7 +18,7 @@ class TestTTSMaryTTSPlatform: """Test the speech component.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.url = "http://localhost:59125/process?" diff --git a/tests/components/tts/test_voicerss.py b/tests/components/tts/test_voicerss.py index 365cf1ff73ba32..334e35a9386a24 100644 --- a/tests/components/tts/test_voicerss.py +++ b/tests/components/tts/test_voicerss.py @@ -18,7 +18,7 @@ class TestTTSVoiceRSSPlatform: """Test the voicerss speech component.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.url = "https://api.voicerss.org/" diff --git a/tests/components/tts/test_yandextts.py b/tests/components/tts/test_yandextts.py index 82d203189287be..2675e3225071a9 100644 --- a/tests/components/tts/test_yandextts.py +++ b/tests/components/tts/test_yandextts.py @@ -17,7 +17,7 @@ class TestTTSYandexPlatform: """Test the speech component.""" def setup_method(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self._base_url = "https://tts.voicetech.yandex.net/generate?" diff --git a/tests/components/vacuum/test_demo.py b/tests/components/vacuum/test_demo.py index bd6f2ae543c01d..1fc8f8cd5c1d15 100644 --- a/tests/components/vacuum/test_demo.py +++ b/tests/components/vacuum/test_demo.py @@ -30,7 +30,7 @@ class TestVacuumDemo(unittest.TestCase): """Test the Demo vacuum.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.assertTrue(setup_component( self.hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: 'demo'}})) diff --git a/tests/components/vacuum/test_dyson.py b/tests/components/vacuum/test_dyson.py index 8a4e6d57b91540..e9e6aaa1b35d59 100644 --- a/tests/components/vacuum/test_dyson.py +++ b/tests/components/vacuum/test_dyson.py @@ -67,7 +67,7 @@ class DysonTest(unittest.TestCase): """Dyson 360 eye robot vacuum component test class.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name @@ -77,9 +77,9 @@ def tearDown(self): # pylint: disable=invalid-name def test_setup_component_with_no_devices(self): """Test setup component with no devices.""" self.hass.data[dyson.DYSON_DEVICES] = [] - add_devices = mock.MagicMock() - dyson.setup_platform(self.hass, {}, add_devices) - add_devices.assert_called_with([]) + add_entities = mock.MagicMock() + dyson.setup_platform(self.hass, {}, add_entities) + add_entities.assert_called_with([]) def test_setup_component(self): """Test setup component with devices.""" diff --git a/tests/components/weather/test_darksky.py b/tests/components/weather/test_darksky.py index 41687451cd6493..5423943e6fd8da 100644 --- a/tests/components/weather/test_darksky.py +++ b/tests/components/weather/test_darksky.py @@ -17,7 +17,7 @@ class TestDarkSky(unittest.TestCase): """Test the Dark Sky weather component.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.units = METRIC_SYSTEM self.lat = self.hass.config.latitude = 37.8267 diff --git a/tests/components/weather/test_ipma.py b/tests/components/weather/test_ipma.py index 7df6166a2b6a18..d438e1185736f3 100644 --- a/tests/components/weather/test_ipma.py +++ b/tests/components/weather/test_ipma.py @@ -52,7 +52,7 @@ class TestIPMA(unittest.TestCase): """Test the IPMA weather component.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.units = METRIC_SYSTEM self.lat = self.hass.config.latitude = 40.00 diff --git a/tests/components/weather/test_weather.py b/tests/components/weather/test_weather.py index a88e9979551c96..42b1dacc5f8a72 100644 --- a/tests/components/weather/test_weather.py +++ b/tests/components/weather/test_weather.py @@ -17,7 +17,7 @@ class TestWeather(unittest.TestCase): """Test the Weather component.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.units = METRIC_SYSTEM self.assertTrue(setup_component(self.hass, weather.DOMAIN, { diff --git a/tests/components/weather/test_yweather.py b/tests/components/weather/test_yweather.py index 3e5eff9dae71b1..a808eb4e468e63 100644 --- a/tests/components/weather/test_yweather.py +++ b/tests/components/weather/test_yweather.py @@ -41,31 +41,31 @@ def updateWeather(self): # pylint: disable=invalid-name @property def RawData(self): # pylint: disable=invalid-name - """Raw Data.""" + """Return raw Data.""" if self.woeid == '12345': return json.loads('[]') return self._data @property def Now(self): # pylint: disable=invalid-name - """Current weather data.""" + """Return current weather data.""" if self.woeid == '111': raise ValueError return self._data['query']['results']['channel']['item']['condition'] @property def Atmosphere(self): # pylint: disable=invalid-name - """Atmosphere weather data.""" + """Return atmosphere weather data.""" return self._data['query']['results']['channel']['atmosphere'] @property def Wind(self): # pylint: disable=invalid-name - """Wind weather data.""" + """Return wind weather data.""" return self._data['query']['results']['channel']['wind'] @property def Forecast(self): # pylint: disable=invalid-name - """Forecast data 0-5 Days.""" + """Return forecast data 0-5 Days.""" if self.woeid == '123123': raise ValueError return self._data['query']['results']['channel']['item']['forecast'] @@ -76,14 +76,14 @@ class TestWeather(unittest.TestCase): DEVICES = [] - def add_devices(self, devices): + def add_entities(self, devices): """Mock add devices.""" for device in devices: device.update() self.DEVICES.append(device) def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.units = METRIC_SYSTEM diff --git a/tests/components/zone/test_init.py b/tests/components/zone/test_init.py index 92dee05818dedb..ba98915e7778ff 100644 --- a/tests/components/zone/test_init.py +++ b/tests/components/zone/test_init.py @@ -42,7 +42,7 @@ class TestComponentZone(unittest.TestCase): """Test the zone component.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name diff --git a/tests/components/zwave/test_init.py b/tests/components/zwave/test_init.py index 39abf6f588f72f..c9224885bbcab8 100644 --- a/tests/components/zwave/test_init.py +++ b/tests/components/zwave/test_init.py @@ -128,24 +128,24 @@ def test_setup_platform(hass, mock_openzwave): mock_device = MagicMock() hass.data[DATA_NETWORK] = MagicMock() hass.data[zwave.DATA_DEVICES] = {456: mock_device} - async_add_devices = MagicMock() + async_add_entities = MagicMock() result = yield from zwave.async_setup_platform( - hass, None, async_add_devices, None) + hass, None, async_add_entities, None) assert not result - assert not async_add_devices.called + assert not async_add_entities.called result = yield from zwave.async_setup_platform( - hass, None, async_add_devices, {const.DISCOVERY_DEVICE: 123}) + hass, None, async_add_entities, {const.DISCOVERY_DEVICE: 123}) assert not result - assert not async_add_devices.called + assert not async_add_entities.called result = yield from zwave.async_setup_platform( - hass, None, async_add_devices, {const.DISCOVERY_DEVICE: 456}) + hass, None, async_add_entities, {const.DISCOVERY_DEVICE: 456}) assert result - assert async_add_devices.called - assert len(async_add_devices.mock_calls) == 1 - assert async_add_devices.mock_calls[0][1][0] == [mock_device] + assert async_add_entities.called + assert len(async_add_entities.mock_calls) == 1 + assert async_add_entities.mock_calls[0][1][0] == [mock_device] @asyncio.coroutine diff --git a/tests/conftest.py b/tests/conftest.py index 28c47948666569..61c5c1c7dd5e02 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -"""Setup some common test helper things.""" +"""Set up some common test helper things.""" import asyncio import functools import logging diff --git a/tests/helpers/test_aiohttp_client.py b/tests/helpers/test_aiohttp_client.py index ccfe1b1aff9315..699342381f9539 100644 --- a/tests/helpers/test_aiohttp_client.py +++ b/tests/helpers/test_aiohttp_client.py @@ -30,7 +30,7 @@ class TestHelpersAiohttpClient(unittest.TestCase): """Test homeassistant.helpers.aiohttp_client module.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): diff --git a/tests/helpers/test_condition.py b/tests/helpers/test_condition.py index aa7b5170648652..69fab77715cbfb 100644 --- a/tests/helpers/test_condition.py +++ b/tests/helpers/test_condition.py @@ -11,7 +11,7 @@ class TestConditionHelper: """Test condition helpers.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): diff --git a/tests/helpers/test_config_entry_flow.py b/tests/helpers/test_config_entry_flow.py index 9eede7dff9b8d6..a9b4dc158e0ecd 100644 --- a/tests/helpers/test_config_entry_flow.py +++ b/tests/helpers/test_config_entry_flow.py @@ -49,7 +49,7 @@ async def test_user_no_devices_found(hass, flow_conf): async def test_user_no_confirmation(hass, flow_conf): - """Test user requires no confirmation to setup.""" + """Test user requires no confirmation to set up.""" flow = config_entries.HANDLERS['test']() flow.hass = hass flow_conf['discovered'] = True @@ -118,7 +118,7 @@ async def test_user_init_trumps_discovery(hass, flow_conf): async def test_import_no_confirmation(hass, flow_conf): - """Test import requires no confirmation to setup.""" + """Test import requires no confirmation to set up.""" flow = config_entries.HANDLERS['test']() flow.hass = hass flow_conf['discovered'] = True diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index 28efcb3e868579..ab575c61789e5a 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -523,21 +523,6 @@ def test_has_at_least_one_key(): schema(value) -def test_has_at_least_one_key_value(): - """Test has_at_least_one_key_value validator.""" - schema = vol.Schema(cv.has_at_least_one_key_value(('drink', 'beer'), - ('drink', 'soda'), - ('food', 'maultaschen'))) - - for value in (None, [], {}, {'wine': None}, {'drink': 'water'}): - with pytest.raises(vol.MultipleInvalid): - schema(value) - - for value in ({'drink': 'beer'}, {'food': 'maultaschen'}, - {'drink': 'soda', 'food': 'maultaschen'}): - schema(value) - - def test_enum(): """Test enum validator.""" class TestEnum(enum.Enum): diff --git a/tests/helpers/test_device_registry.py b/tests/helpers/test_device_registry.py new file mode 100644 index 00000000000000..b2e7307182397e --- /dev/null +++ b/tests/helpers/test_device_registry.py @@ -0,0 +1,138 @@ +"""Tests for the Device Registry.""" +import pytest + +from homeassistant.helpers import device_registry + + +def mock_registry(hass, mock_entries=None): + """Mock the Device Registry.""" + registry = device_registry.DeviceRegistry(hass) + registry.devices = mock_entries or [] + + async def _get_reg(): + return registry + + hass.data[device_registry.DATA_REGISTRY] = \ + hass.loop.create_task(_get_reg()) + return registry + + +@pytest.fixture +def registry(hass): + """Return an empty, loaded, registry.""" + return mock_registry(hass) + + +async def test_get_or_create_returns_same_entry(registry): + """Make sure we do not duplicate entries.""" + entry = registry.async_get_or_create( + config_entry='1234', + connections={('ethernet', '12:34:56:78:90:AB:CD:EF')}, + identifiers={('bridgeid', '0123')}, + manufacturer='manufacturer', model='model') + entry2 = registry.async_get_or_create( + config_entry='1234', + connections={('ethernet', '11:22:33:44:55:66:77:88')}, + identifiers={('bridgeid', '0123')}, + manufacturer='manufacturer', model='model') + entry3 = registry.async_get_or_create( + config_entry='1234', + connections={('ethernet', '12:34:56:78:90:AB:CD:EF')}, + identifiers={('bridgeid', '1234')}, + manufacturer='manufacturer', model='model') + + assert len(registry.devices) == 1 + assert entry is entry2 + assert entry is entry3 + assert entry.identifiers == {('bridgeid', '0123')} + + +async def test_requirement_for_identifier_or_connection(registry): + """Make sure we do require some descriptor of device.""" + entry = registry.async_get_or_create( + config_entry='1234', + connections={('ethernet', '12:34:56:78:90:AB:CD:EF')}, + identifiers=set(), + manufacturer='manufacturer', model='model') + entry2 = registry.async_get_or_create( + config_entry='1234', + connections=set(), + identifiers={('bridgeid', '0123')}, + manufacturer='manufacturer', model='model') + entry3 = registry.async_get_or_create( + config_entry='1234', + connections=set(), + identifiers=set(), + manufacturer='manufacturer', model='model') + + assert len(registry.devices) == 2 + assert entry + assert entry2 + assert entry3 is None + + +async def test_multiple_config_entries(registry): + """Make sure we do not get duplicate entries.""" + entry = registry.async_get_or_create( + config_entry='123', + connections={('ethernet', '12:34:56:78:90:AB:CD:EF')}, + identifiers={('bridgeid', '0123')}, + manufacturer='manufacturer', model='model') + entry2 = registry.async_get_or_create( + config_entry='456', + connections={('ethernet', '12:34:56:78:90:AB:CD:EF')}, + identifiers={('bridgeid', '0123')}, + manufacturer='manufacturer', model='model') + entry3 = registry.async_get_or_create( + config_entry='123', + connections={('ethernet', '12:34:56:78:90:AB:CD:EF')}, + identifiers={('bridgeid', '0123')}, + manufacturer='manufacturer', model='model') + + assert len(registry.devices) == 1 + assert entry is entry2 + assert entry is entry3 + assert entry.config_entries == {'123', '456'} + + +async def test_loading_from_storage(hass, hass_storage): + """Test loading stored devices on start.""" + hass_storage[device_registry.STORAGE_KEY] = { + 'version': device_registry.STORAGE_VERSION, + 'data': { + 'devices': [ + { + 'config_entries': [ + '1234' + ], + 'connections': [ + [ + 'Zigbee', + '01.23.45.67.89' + ] + ], + 'id': 'abcdefghijklm', + 'identifiers': [ + [ + 'serial', + '12:34:56:78:90:AB:CD:EF' + ] + ], + 'manufacturer': 'manufacturer', + 'model': 'model', + 'name': 'name', + 'sw_version': 'version', + } + ] + } + } + + registry = await device_registry.async_get_registry(hass) + + entry = registry.async_get_or_create( + config_entry='1234', + connections={('Zigbee', '01.23.45.67.89')}, + identifiers={('serial', '12:34:56:78:90:AB:CD:EF')}, + manufacturer='manufacturer', model='model') + assert entry.id == 'abcdefghijklm' + assert isinstance(entry.config_entries, set) diff --git a/tests/helpers/test_discovery.py b/tests/helpers/test_discovery.py index c7b39954d85be6..a8d78bde1f40b6 100644 --- a/tests/helpers/test_discovery.py +++ b/tests/helpers/test_discovery.py @@ -16,7 +16,7 @@ class TestHelpersDiscovery: """Tests for discovery helper methods.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): @@ -117,15 +117,15 @@ def test_circular_import(self): platform_calls = [] def component_setup(hass, config): - """Setup mock component.""" + """Set up mock component.""" discovery.load_platform(hass, 'switch', 'test_circular', 'disc', config) component_calls.append(1) return True - def setup_platform(hass, config, add_devices_callback, + def setup_platform(hass, config, add_entities_callback, discovery_info=None): - """Setup mock platform.""" + """Set up mock platform.""" platform_calls.append('disc' if discovery_info else 'component') loader.set_component( @@ -158,21 +158,21 @@ def setup_platform(hass, config, add_devices_callback, def test_1st_discovers_2nd_component(self, mock_signal): """Test that we don't break if one component discovers the other. - If the first component fires a discovery event to setup the - second component while the second component is about to be setup, - it should not setup the second component twice. + If the first component fires a discovery event to set up the + second component while the second component is about to be set up, + it should not set up the second component twice. """ component_calls = [] def component1_setup(hass, config): - """Setup mock component.""" + """Set up mock component.""" print('component1 setup') discovery.discover(hass, 'test_component2', component='test_component2') return True def component2_setup(hass, config): - """Setup mock component.""" + """Set up mock component.""" component_calls.append(1) return True @@ -186,7 +186,7 @@ def component2_setup(hass, config): @callback def do_setup(): - """Setup 2 components.""" + """Set up 2 components.""" self.hass.async_add_job(setup.async_setup_component( self.hass, 'test_component1', {})) self.hass.async_add_job(setup.async_setup_component( diff --git a/tests/helpers/test_dispatcher.py b/tests/helpers/test_dispatcher.py index 55e67def2bc6c0..ef1ad2336eb398 100644 --- a/tests/helpers/test_dispatcher.py +++ b/tests/helpers/test_dispatcher.py @@ -12,7 +12,7 @@ class TestHelpersDispatcher: """Tests for discovery helper methods.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def teardown_method(self, method): diff --git a/tests/helpers/test_entity.py b/tests/helpers/test_entity.py index e24bec489f492f..a51787225ca5a4 100644 --- a/tests/helpers/test_entity.py +++ b/tests/helpers/test_entity.py @@ -1,11 +1,13 @@ """Test the entity helper.""" # pylint: disable=protected-access import asyncio -from unittest.mock import MagicMock, patch +from datetime import timedelta +from unittest.mock import MagicMock, patch, PropertyMock import pytest import homeassistant.helpers.entity as entity +from homeassistant.core import Context from homeassistant.const import ATTR_HIDDEN, ATTR_DEVICE_CLASS from homeassistant.config import DATA_CUSTOMIZE from homeassistant.helpers.entity_values import EntityValues @@ -75,7 +77,7 @@ class TestHelpersEntity: """Test homeassistant.helpers.entity module.""" def setup_method(self, method): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.entity = entity.Entity() self.entity.entity_id = 'test.overwrite_hidden_true' self.hass = self.entity.hass = get_test_home_assistant() @@ -412,3 +414,32 @@ async def test_async_remove_runs_callbacks(hass): ent.async_on_remove(lambda: result.append(1)) await ent.async_remove() assert len(result) == 1 + + +async def test_set_context(hass): + """Test setting context.""" + context = Context() + ent = entity.Entity() + ent.hass = hass + ent.entity_id = 'hello.world' + ent.async_set_context(context) + await ent.async_update_ha_state() + assert hass.states.get('hello.world').context == context + + +async def test_set_context_expired(hass): + """Test setting context.""" + context = Context() + + with patch.object(entity.Entity, 'context_recent_time', + new_callable=PropertyMock) as recent: + recent.return_value = timedelta(seconds=-5) + ent = entity.Entity() + ent.hass = hass + ent.entity_id = 'hello.world' + ent.async_set_context(context) + await ent.async_update_ha_state() + + assert hass.states.get('hello.world').context != context + assert ent._context is None + assert ent._context_set is None diff --git a/tests/helpers/test_entity_component.py b/tests/helpers/test_entity_component.py index b4910723c8dfa8..1a0c248383bff5 100644 --- a/tests/helpers/test_entity_component.py +++ b/tests/helpers/test_entity_component.py @@ -39,7 +39,7 @@ def tearDown(self): # pylint: disable=invalid-name self.hass.stop() def test_setting_up_group(self): - """Setup the setting of a group.""" + """Set up the setting of a group.""" setup_component(self.hass, 'group', {'group': {}}) component = EntityComponent(_LOGGER, DOMAIN, self.hass, group_name='everyone') @@ -143,9 +143,9 @@ def test_setup_does_discovery(self, mock_setup_component, mock_setup): 'async_track_time_interval') def test_set_scan_interval_via_config(self, mock_track): """Test the setting of the scan interval via configuration.""" - def platform_setup(hass, config, add_devices, discovery_info=None): + def platform_setup(hass, config, add_entities, discovery_info=None): """Test the platform setup.""" - add_devices([MockEntity(should_poll=True)]) + add_entities([MockEntity(should_poll=True)]) loader.set_component(self.hass, 'test_domain.platform', MockPlatform(platform_setup)) @@ -165,9 +165,9 @@ def platform_setup(hass, config, add_devices, discovery_info=None): def test_set_entity_namespace_via_config(self): """Test setting an entity namespace.""" - def platform_setup(hass, config, add_devices, discovery_info=None): + def platform_setup(hass, config, add_entities, discovery_info=None): """Test the platform setup.""" - add_devices([ + add_entities([ MockEntity(name='beer'), MockEntity(name=None), ]) diff --git a/tests/helpers/test_entity_platform.py b/tests/helpers/test_entity_platform.py index b52405aa8beae0..b51219ddbed089 100644 --- a/tests/helpers/test_entity_platform.py +++ b/tests/helpers/test_entity_platform.py @@ -142,9 +142,9 @@ def test_update_state_adds_entities_with_update_before_add_false(self): 'async_track_time_interval') def test_set_scan_interval_via_platform(self, mock_track): """Test the setting of the scan interval via platform.""" - def platform_setup(hass, config, add_devices, discovery_info=None): + def platform_setup(hass, config, add_entities, discovery_info=None): """Test the platform setup.""" - add_devices([MockEntity(should_poll=True)]) + add_entities([MockEntity(should_poll=True)]) platform = MockPlatform(platform_setup) platform.SCAN_INTERVAL = timedelta(seconds=30) @@ -336,7 +336,7 @@ def test_raise_error_on_update(hass): entity2 = MockEntity(name='test_2') def _raise(): - """Helper to raise an exception.""" + """Raise an exception.""" raise AssertionError entity1.update = _raise @@ -520,9 +520,9 @@ async def test_setup_entry(hass): """Test we can setup an entry.""" registry = mock_registry(hass) - async def async_setup_entry(hass, config_entry, async_add_devices): + async def async_setup_entry(hass, config_entry, async_add_entities): """Mock setup entry method.""" - async_add_devices([ + async_add_entities([ MockEntity(name='test1', unique_id='unique') ]) return True diff --git a/tests/helpers/test_entity_registry.py b/tests/helpers/test_entity_registry.py index 5a9efd5c041f7c..d0c088a6f6971b 100644 --- a/tests/helpers/test_entity_registry.py +++ b/tests/helpers/test_entity_registry.py @@ -1,6 +1,6 @@ """Tests for the Entity Registry.""" import asyncio -from unittest.mock import patch, mock_open +from unittest.mock import patch import pytest @@ -61,29 +61,13 @@ def test_get_or_create_suggested_object_id_conflict_existing(hass, registry): @asyncio.coroutine def test_create_triggers_save(hass, registry): """Test that registering entry triggers a save.""" - with patch.object(hass.loop, 'call_later') as mock_call_later: + with patch.object(registry, 'async_schedule_save') as mock_schedule_save: registry.async_get_or_create('light', 'hue', '1234') - assert len(mock_call_later.mock_calls) == 1 + assert len(mock_schedule_save.mock_calls) == 1 -@asyncio.coroutine -def test_save_timer_reset_on_subsequent_save(hass, registry): - """Test we reset the save timer on a new create.""" - with patch.object(hass.loop, 'call_later') as mock_call_later: - registry.async_get_or_create('light', 'hue', '1234') - - assert len(mock_call_later.mock_calls) == 1 - - with patch.object(hass.loop, 'call_later') as mock_call_later_2: - registry.async_get_or_create('light', 'hue', '5678') - - assert len(mock_call_later().cancel.mock_calls) == 1 - assert len(mock_call_later_2.mock_calls) == 1 - - -@asyncio.coroutine -def test_loading_saving_data(hass, registry): +async def test_loading_saving_data(hass, registry): """Test that we load/save data correctly.""" orig_entry1 = registry.async_get_or_create('light', 'hue', '1234') orig_entry2 = registry.async_get_or_create( @@ -91,18 +75,11 @@ def test_loading_saving_data(hass, registry): assert len(registry.entities) == 2 - with patch(YAML__OPEN_PATH, mock_open(), create=True) as mock_write: - yield from registry._async_save() - - # Mock open calls are: open file, context enter, write, context leave - written = mock_write.mock_calls[2][1][0] - # Now load written data in new registry registry2 = entity_registry.EntityRegistry(hass) + registry2._store = registry._store - with patch('os.path.isfile', return_value=True), \ - patch(YAML__OPEN_PATH, mock_open(read_data=written), create=True): - yield from registry2._async_load() + await registry2.async_load() # Ensure same order assert list(registry.entities) == list(registry2.entities) @@ -139,32 +116,37 @@ def test_is_registered(registry): assert not registry.async_is_registered('light.non_existing') -@asyncio.coroutine -def test_loading_extra_values(hass): +async def test_loading_extra_values(hass, hass_storage): """Test we load extra data from the registry.""" - written = """ -test.named: - platform: super_platform - unique_id: with-name - name: registry override -test.no_name: - platform: super_platform - unique_id: without-name -test.disabled_user: - platform: super_platform - unique_id: disabled-user - disabled_by: user -test.disabled_hass: - platform: super_platform - unique_id: disabled-hass - disabled_by: hass -""" - - registry = entity_registry.EntityRegistry(hass) - - with patch('os.path.isfile', return_value=True), \ - patch(YAML__OPEN_PATH, mock_open(read_data=written), create=True): - yield from registry._async_load() + hass_storage[entity_registry.STORAGE_KEY] = { + 'version': entity_registry.STORAGE_VERSION, + 'data': { + 'entities': [ + { + 'entity_id': 'test.named', + 'platform': 'super_platform', + 'unique_id': 'with-name', + 'name': 'registry override', + }, { + 'entity_id': 'test.no_name', + 'platform': 'super_platform', + 'unique_id': 'without-name', + }, { + 'entity_id': 'test.disabled_user', + 'platform': 'super_platform', + 'unique_id': 'disabled-user', + 'disabled_by': 'user', + }, { + 'entity_id': 'test.disabled_hass', + 'platform': 'super_platform', + 'unique_id': 'disabled-hass', + 'disabled_by': 'hass', + } + ] + } + } + + registry = await entity_registry.async_get_registry(hass) entry_with_name = registry.async_get_or_create( 'test', 'super_platform', 'with-name') @@ -202,3 +184,31 @@ async def test_updating_config_entry_id(registry): 'light', 'hue', '5678', config_entry_id='mock-id-2') assert entry.entity_id == entry2.entity_id assert entry2.config_entry_id == 'mock-id-2' + + +async def test_migration(hass): + """Test migration from old data to new.""" + old_conf = { + 'light.kitchen': { + 'config_entry_id': 'test-config-id', + 'unique_id': 'test-unique', + 'platform': 'test-platform', + 'name': 'Test Name', + 'disabled_by': 'hass', + } + } + with patch('os.path.isfile', return_value=True), patch('os.remove'), \ + patch('homeassistant.helpers.entity_registry.load_yaml', + return_value=old_conf): + registry = await entity_registry.async_get_registry(hass) + + assert registry.async_is_registered('light.kitchen') + entry = registry.async_get_or_create( + domain='light', + platform='test-platform', + unique_id='test-unique', + config_entry_id='test-config-id', + ) + assert entry.name == 'Test Name' + assert entry.disabled_by == 'hass' + assert entry.config_entry_id == 'test-config-id' diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index 73f2b9ff5a4c98..deefcec773a291 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -37,7 +37,7 @@ class TestEventHelpers(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name diff --git a/tests/helpers/test_json.py b/tests/helpers/test_json.py new file mode 100644 index 00000000000000..1d6e7eb6edeb60 --- /dev/null +++ b/tests/helpers/test_json.py @@ -0,0 +1,21 @@ +"""Test Home Assistant remote methods and classes.""" +import pytest + +from homeassistant import core +from homeassistant.helpers.json import JSONEncoder +from homeassistant.util import dt as dt_util + + +def test_json_encoder(hass): + """Test the JSON Encoder.""" + ha_json_enc = JSONEncoder() + state = core.State('test.test', 'hello') + + assert ha_json_enc.default(state) == state.as_dict() + + # Default method raises TypeError if non HA object + with pytest.raises(TypeError): + ha_json_enc.default(1) + + now = dt_util.utcnow() + assert ha_json_enc.default(now) == now.isoformat() diff --git a/tests/helpers/test_location.py b/tests/helpers/test_location.py index 068e1a58ac2b08..22f69c18326877 100644 --- a/tests/helpers/test_location.py +++ b/tests/helpers/test_location.py @@ -7,15 +7,15 @@ class TestHelpersLocation(unittest.TestCase): - """Setup the tests.""" + """Set up the tests.""" def test_has_location_with_invalid_states(self): - """Setup the tests.""" + """Set up the tests.""" for state in (None, 1, "hello", object): self.assertFalse(location.has_location(state)) def test_has_location_with_states_with_invalid_locations(self): - """Setup the tests.""" + """Set up the tests.""" state = State('hello.world', 'invalid', { ATTR_LATITUDE: 'no number', ATTR_LONGITUDE: 123.12 @@ -23,7 +23,7 @@ def test_has_location_with_states_with_invalid_locations(self): self.assertFalse(location.has_location(state)) def test_has_location_with_states_with_valid_location(self): - """Setup the tests.""" + """Set up the tests.""" state = State('hello.world', 'invalid', { ATTR_LATITUDE: 123.12, ATTR_LONGITUDE: 123.12 @@ -31,7 +31,7 @@ def test_has_location_with_states_with_valid_location(self): self.assertTrue(location.has_location(state)) def test_closest_with_no_states_with_location(self): - """Setup the tests.""" + """Set up the tests.""" state = State('light.test', 'on') state2 = State('light.test', 'on', { ATTR_LATITUDE: 'invalid', diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index 4297ca26e7dcac..ba5bdbcdc6eeb2 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -21,7 +21,7 @@ class TestScriptHelper(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name @@ -375,8 +375,8 @@ def record_event(event): assert script_obj.can_cancel assert len(events) == 2 - def test_wait_template_timeout(self): - """Test the wait template.""" + def test_wait_template_timeout_halt(self): + """Test the wait template, halt on timeout.""" event = 'test_event' events = [] @@ -393,6 +393,7 @@ def record_event(event): {'event': event}, { 'wait_template': "{{states.switch.test.state == 'off'}}", + 'continue_on_timeout': False, 'timeout': 5 }, {'event': event}])) @@ -412,6 +413,81 @@ def record_event(event): assert not script_obj.is_running assert len(events) == 1 + def test_wait_template_timeout_continue(self): + """Test the wait template with continuing the script.""" + event = 'test_event' + events = [] + + @callback + def record_event(event): + """Add recorded event to set.""" + events.append(event) + + self.hass.bus.listen(event, record_event) + + self.hass.states.set('switch.test', 'on') + + script_obj = script.Script(self.hass, cv.SCRIPT_SCHEMA([ + {'event': event}, + { + 'wait_template': "{{states.switch.test.state == 'off'}}", + 'timeout': 5, + 'continue_on_timeout': True + }, + {'event': event}])) + + script_obj.run() + self.hass.block_till_done() + + assert script_obj.is_running + assert script_obj.can_cancel + assert script_obj.last_action == event + assert len(events) == 1 + + future = dt_util.utcnow() + timedelta(seconds=5) + fire_time_changed(self.hass, future) + self.hass.block_till_done() + + assert not script_obj.is_running + assert len(events) == 2 + + def test_wait_template_timeout_default(self): + """Test the wait template with default contiune.""" + event = 'test_event' + events = [] + + @callback + def record_event(event): + """Add recorded event to set.""" + events.append(event) + + self.hass.bus.listen(event, record_event) + + self.hass.states.set('switch.test', 'on') + + script_obj = script.Script(self.hass, cv.SCRIPT_SCHEMA([ + {'event': event}, + { + 'wait_template': "{{states.switch.test.state == 'off'}}", + 'timeout': 5 + }, + {'event': event}])) + + script_obj.run() + self.hass.block_till_done() + + assert script_obj.is_running + assert script_obj.can_cancel + assert script_obj.last_action == event + assert len(events) == 1 + + future = dt_util.utcnow() + timedelta(seconds=5) + fire_time_changed(self.hass, future) + self.hass.block_till_done() + + assert not script_obj.is_running + assert len(events) == 2 + def test_wait_template_variables(self): """Test the wait template with variables.""" event = 'test_event' diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index 79054726c0312f..529804bd307df6 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -19,7 +19,7 @@ class TestServiceHelpers(unittest.TestCase): """Test the Home Assistant service helpers.""" def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.calls = mock_service(self.hass, 'test_domain', 'test_service') diff --git a/tests/helpers/test_storage.py b/tests/helpers/test_storage.py index f414eaec97c844..6cb75899d3552a 100644 --- a/tests/helpers/test_storage.py +++ b/tests/helpers/test_storage.py @@ -56,7 +56,7 @@ async def test_loading_parallel(hass, store, hass_storage, caplog): async def test_saving_with_delay(hass, store, hass_storage): """Test saving data after a delay.""" - await store.async_save(MOCK_DATA, delay=1) + store.async_delay_save(lambda: MOCK_DATA, 1) assert store.key not in hass_storage async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=1)) @@ -71,7 +71,7 @@ async def test_saving_with_delay(hass, store, hass_storage): async def test_saving_on_stop(hass, hass_storage): """Test delayed saves trigger when we quit Home Assistant.""" store = storage.Store(hass, MOCK_VERSION, MOCK_KEY) - await store.async_save(MOCK_DATA, delay=1) + store.async_delay_save(lambda: MOCK_DATA, 1) assert store.key not in hass_storage hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) @@ -92,7 +92,7 @@ async def test_loading_while_delay(hass, store, hass_storage): 'data': {'delay': 'no'}, } - await store.async_save({'delay': 'yes'}, delay=1) + store.async_delay_save(lambda: {'delay': 'yes'}, 1) assert hass_storage[store.key] == { 'version': MOCK_VERSION, 'key': MOCK_KEY, @@ -105,7 +105,7 @@ async def test_loading_while_delay(hass, store, hass_storage): async def test_writing_while_writing_delay(hass, store, hass_storage): """Test a write while a write with delay is active.""" - await store.async_save({'delay': 'yes'}, delay=1) + store.async_delay_save(lambda: {'delay': 'yes'}, 1) assert store.key not in hass_storage await store.async_save({'delay': 'no'}) assert hass_storage[store.key] == { @@ -141,11 +141,10 @@ async def test_migrator_no_existing_config(hass, store, hass_storage): async def test_migrator_existing_config(hass, store, hass_storage): """Test migrating existing config.""" with patch('os.path.isfile', return_value=True), \ - patch('os.remove') as mock_remove, \ - patch('homeassistant.util.json.load_json', - return_value={'old': 'config'}): + patch('os.remove') as mock_remove: data = await storage.async_migrator( - hass, 'old-path', store) + hass, 'old-path', store, + old_conf_load_func=lambda _: {'old': 'config'}) assert len(mock_remove.mock_calls) == 1 assert data == {'old': 'config'} @@ -163,12 +162,11 @@ async def old_conf_migrate_func(old_config): return {'new': old_config['old']} with patch('os.path.isfile', return_value=True), \ - patch('os.remove') as mock_remove, \ - patch('homeassistant.util.json.load_json', - return_value={'old': 'config'}): + patch('os.remove') as mock_remove: data = await storage.async_migrator( hass, 'old-path', store, - old_conf_migrate_func=old_conf_migrate_func) + old_conf_migrate_func=old_conf_migrate_func, + old_conf_load_func=lambda _: {'old': 'config'}) assert len(mock_remove.mock_calls) == 1 assert data == {'new': 'config'} diff --git a/tests/helpers/test_sun.py b/tests/helpers/test_sun.py index 2cfe28e5178cd4..b1c7f62e776c74 100644 --- a/tests/helpers/test_sun.py +++ b/tests/helpers/test_sun.py @@ -15,7 +15,7 @@ class TestSun(unittest.TestCase): """Test the sun helpers.""" def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): diff --git a/tests/helpers/test_temperature.py b/tests/helpers/test_temperature.py index 96e7bd6c74ff43..e2366d8866a733 100644 --- a/tests/helpers/test_temperature.py +++ b/tests/helpers/test_temperature.py @@ -13,10 +13,10 @@ class TestHelpersTemperature(unittest.TestCase): - """Setup the temperature tests.""" + """Set up the temperature tests.""" def setUp(self): - """Setup the tests.""" + """Set up the tests.""" self.hass = get_test_home_assistant() self.hass.config.unit_system = METRIC_SYSTEM diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 2dfcb2a58e5088..6f426c290c5eb2 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -27,7 +27,7 @@ class TestHelpersTemplate(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup the tests.""" + """Set up the tests.""" self.hass = get_test_home_assistant() self.hass.config.units = UnitSystem('custom', TEMP_CELSIUS, LENGTH_METERS, VOLUME_LITERS, @@ -511,8 +511,8 @@ def test_utcnow(self, mock_is_safe): def test_regex_match(self): """Test regex_match method.""" - tpl = template.Template(""" -{{ '123-456-7890' | regex_match('(\d{3})-(\d{3})-(\d{4})') }} + tpl = template.Template(r""" +{{ '123-456-7890' | regex_match('(\\d{3})-(\\d{3})-(\\d{4})') }} """, self.hass) self.assertEqual('True', tpl.render()) @@ -528,8 +528,8 @@ def test_regex_match(self): def test_regex_search(self): """Test regex_search method.""" - tpl = template.Template(""" -{{ '123-456-7890' | regex_search('(\d{3})-(\d{3})-(\d{4})') }} + tpl = template.Template(r""" +{{ '123-456-7890' | regex_search('(\\d{3})-(\\d{3})-(\\d{4})') }} """, self.hass) self.assertEqual('True', tpl.render()) @@ -545,8 +545,8 @@ def test_regex_search(self): def test_regex_replace(self): """Test regex_replace method.""" - tpl = template.Template(""" -{{ 'Hello World' | regex_replace('(Hello\s)',) }} + tpl = template.Template(r""" +{{ 'Hello World' | regex_replace('(Hello\\s)',) }} """, self.hass) self.assertEqual('World', tpl.render()) diff --git a/tests/scripts/test_check_config.py b/tests/scripts/test_check_config.py index 532197b407227b..28438a5e4b3625 100644 --- a/tests/scripts/test_check_config.py +++ b/tests/scripts/test_check_config.py @@ -22,6 +22,12 @@ '\n\n' ) +BAD_CORE_CONFIG = ( + 'homeassistant:\n' + ' unit_system: bad\n' + '\n\n' +) + def normalize_yaml_files(check_dict): """Remove configuration path from ['yaml_files'].""" @@ -47,6 +53,17 @@ def setUp(self): self.maxDiff = None # pylint: disable=invalid-name # pylint: disable=no-self-use,invalid-name + @patch('os.path.isfile', return_value=True) + def test_bad_core_config(self, isfile_patch): + """Test a bad core config setup.""" + files = { + YAML_CONFIG_FILE: BAD_CORE_CONFIG, + } + with patch_yaml_files(files): + res = check_config.check(get_test_config_dir()) + assert res['except'].keys() == {'homeassistant'} + assert res['except']['homeassistant'][1] == {'unit_system': 'bad'} + @patch('os.path.isfile', return_value=True) def test_config_platform_valid(self, isfile_patch): """Test a valid platform setup.""" diff --git a/tests/test_config.py b/tests/test_config.py index 435d3a00ec2146..e4a6798093ffbb 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -16,7 +16,7 @@ CONF_LATITUDE, CONF_LONGITUDE, CONF_UNIT_SYSTEM, CONF_NAME, CONF_TIME_ZONE, CONF_ELEVATION, CONF_CUSTOMIZE, __version__, CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, - CONF_AUTH_PROVIDERS) + CONF_AUTH_PROVIDERS, CONF_AUTH_MFA_MODULES) from homeassistant.util import location as location_util, dt as dt_util from homeassistant.util.yaml import SECRET_YAML from homeassistant.util.async_ import run_coroutine_threadsafe @@ -805,6 +805,10 @@ async def test_auth_provider_config(hass): CONF_AUTH_PROVIDERS: [ {'type': 'homeassistant'}, {'type': 'legacy_api_password'}, + ], + CONF_AUTH_MFA_MODULES: [ + {'type': 'totp'}, + {'type': 'totp', 'id': 'second'}, ] } if hasattr(hass, 'auth'): @@ -812,6 +816,73 @@ async def test_auth_provider_config(hass): await config_util.async_process_ha_core_config(hass, core_config) assert len(hass.auth.auth_providers) == 2 + assert hass.auth.auth_providers[0].type == 'homeassistant' + assert hass.auth.auth_providers[1].type == 'legacy_api_password' + assert hass.auth.active is True + assert len(hass.auth.auth_mfa_modules) == 2 + assert hass.auth.auth_mfa_modules[0].id == 'totp' + assert hass.auth.auth_mfa_modules[1].id == 'second' + + +async def test_auth_provider_config_default(hass): + """Test loading default auth provider config.""" + core_config = { + 'latitude': 60, + 'longitude': 50, + 'elevation': 25, + 'name': 'Huis', + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + 'time_zone': 'GMT', + } + if hasattr(hass, 'auth'): + del hass.auth + await config_util.async_process_ha_core_config(hass, core_config) + + assert len(hass.auth.auth_providers) == 1 + assert hass.auth.auth_providers[0].type == 'homeassistant' + assert hass.auth.active is True + assert len(hass.auth.auth_mfa_modules) == 1 + assert hass.auth.auth_mfa_modules[0].id == 'totp' + + +async def test_auth_provider_config_default_api_password(hass): + """Test loading default auth provider config with api password.""" + core_config = { + 'latitude': 60, + 'longitude': 50, + 'elevation': 25, + 'name': 'Huis', + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + 'time_zone': 'GMT', + } + if hasattr(hass, 'auth'): + del hass.auth + await config_util.async_process_ha_core_config(hass, core_config, True) + + assert len(hass.auth.auth_providers) == 2 + assert hass.auth.auth_providers[0].type == 'homeassistant' + assert hass.auth.auth_providers[1].type == 'legacy_api_password' + assert hass.auth.active is True + + +async def test_auth_provider_config_default_trusted_networks(hass): + """Test loading default auth provider config with trusted networks.""" + core_config = { + 'latitude': 60, + 'longitude': 50, + 'elevation': 25, + 'name': 'Huis', + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + 'time_zone': 'GMT', + } + if hasattr(hass, 'auth'): + del hass.auth + await config_util.async_process_ha_core_config(hass, core_config, + has_trusted_networks=True) + + assert len(hass.auth.auth_providers) == 2 + assert hass.auth.auth_providers[0].type == 'homeassistant' + assert hass.auth.auth_providers[1].type == 'trusted_networks' assert hass.auth.active is True @@ -824,9 +895,73 @@ async def test_disallowed_auth_provider_config(hass): 'name': 'Huis', CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, 'time_zone': 'GMT', - CONF_AUTH_PROVIDERS: [ - {'type': 'insecure_example'}, - ] + CONF_AUTH_PROVIDERS: [{ + 'type': 'insecure_example', + 'users': [{ + 'username': 'test-user', + 'password': 'test-pass', + 'name': 'Test Name' + }], + }] + } + with pytest.raises(Invalid): + await config_util.async_process_ha_core_config(hass, core_config) + + +async def test_disallowed_duplicated_auth_provider_config(hass): + """Test loading insecure example auth provider is disallowed.""" + core_config = { + 'latitude': 60, + 'longitude': 50, + 'elevation': 25, + 'name': 'Huis', + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + 'time_zone': 'GMT', + CONF_AUTH_PROVIDERS: [{ + 'type': 'homeassistant', + }, { + 'type': 'homeassistant', + }] + } + with pytest.raises(Invalid): + await config_util.async_process_ha_core_config(hass, core_config) + + +async def test_disallowed_auth_mfa_module_config(hass): + """Test loading insecure example auth mfa module is disallowed.""" + core_config = { + 'latitude': 60, + 'longitude': 50, + 'elevation': 25, + 'name': 'Huis', + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + 'time_zone': 'GMT', + CONF_AUTH_MFA_MODULES: [{ + 'type': 'insecure_example', + 'data': [{ + 'user_id': 'mock-user', + 'pin': 'test-pin' + }] + }] + } + with pytest.raises(Invalid): + await config_util.async_process_ha_core_config(hass, core_config) + + +async def test_disallowed_duplicated_auth_mfa_module_config(hass): + """Test loading insecure example auth mfa module is disallowed.""" + core_config = { + 'latitude': 60, + 'longitude': 50, + 'elevation': 25, + 'name': 'Huis', + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + 'time_zone': 'GMT', + CONF_AUTH_MFA_MODULES: [{ + 'type': 'totp', + }, { + 'type': 'totp', + }] } with pytest.raises(Invalid): await config_util.async_process_ha_core_config(hass, core_config) diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 1f6fd8756e6521..d8756d87a19f98 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -231,7 +231,7 @@ async def test_forward_entry_sets_up_component(hass): async def test_forward_entry_does_not_setup_entry_if_setup_fails(hass): - """Test we do not setup entry if component setup fails.""" + """Test we do not set up entry if component setup fails.""" entry = MockConfigEntry(domain='original') mock_setup = MagicMock(return_value=mock_coro(False)) diff --git a/tests/test_core.py b/tests/test_core.py index f23bed6bc8a028..ce066135709f1f 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -130,7 +130,7 @@ class TestHomeAssistant(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name @@ -291,7 +291,7 @@ class TestEventBus(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.bus = self.hass.bus @@ -495,7 +495,7 @@ class TestStateMachine(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.states = self.hass.states self.states.set("light.Bowl", "on") @@ -620,7 +620,7 @@ class TestServiceRegistry(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.services = self.hass.services @@ -775,7 +775,7 @@ class TestConfig(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup things to be run when tests are started.""" + """Set up things to be run when tests are started.""" self.config = ha.Config() self.assertIsNone(self.config.config_dir) diff --git a/tests/test_data_entry_flow.py b/tests/test_data_entry_flow.py index c5d5bbb50bfa03..aa8240ff56754b 100644 --- a/tests/test_data_entry_flow.py +++ b/tests/test_data_entry_flow.py @@ -25,11 +25,12 @@ async def async_create_flow(handler_name, *, context, data): if context is not None else 'user_input' return flow - async def async_add_entry(context, result): - if (result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY): - result['source'] = context.get('source') \ - if context is not None else 'user' + async def async_add_entry(flow, result): + if result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + result['source'] = flow.context.get('source') \ + if flow.context is not None else 'user' entries.append(result) + return result manager = data_entry_flow.FlowManager( None, async_create_flow, async_add_entry) @@ -198,3 +199,49 @@ async def async_step_init(self, info): assert entry['title'] == 'hello' assert entry['data'] == data assert entry['source'] == 'discovery' + + +async def test_finish_callback_change_result_type(hass): + """Test finish callback can change result type.""" + class TestFlow(data_entry_flow.FlowHandler): + VERSION = 1 + + async def async_step_init(self, input): + """Return init form with one input field 'count'.""" + if input is not None: + return self.async_create_entry(title='init', data=input) + return self.async_show_form( + step_id='init', + data_schema=vol.Schema({'count': int})) + + async def async_create_flow(handler_name, *, context, data): + """Create a test flow.""" + return TestFlow() + + async def async_finish_flow(flow, result): + """Redirect to init form if count <= 1.""" + if result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + if (result['data'] is None or + result['data'].get('count', 0) <= 1): + return flow.async_show_form( + step_id='init', + data_schema=vol.Schema({'count': int})) + else: + result['result'] = result['data']['count'] + return result + + manager = data_entry_flow.FlowManager( + hass, async_create_flow, async_finish_flow) + + result = await manager.async_init('test') + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'init' + + result = await manager.async_configure(result['flow_id'], {'count': 0}) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'init' + assert 'result' not in result + + result = await manager.async_configure(result['flow_id'], {'count': 2}) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['result'] == 2 diff --git a/tests/test_loader.py b/tests/test_loader.py index d87201fb61bdce..4beb7db570e4cd 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -17,7 +17,7 @@ class TestLoader(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): - """Setup tests.""" + """Set up tests.""" self.hass = get_test_home_assistant() # pylint: disable=invalid-name diff --git a/tests/test_remote.py b/tests/test_remote.py deleted file mode 100644 index 9aa730d6eb6564..00000000000000 --- a/tests/test_remote.py +++ /dev/null @@ -1,205 +0,0 @@ -"""Test Home Assistant remote methods and classes.""" -# pylint: disable=protected-access -import unittest - -from homeassistant import remote, setup, core as ha -import homeassistant.components.http as http -from homeassistant.const import HTTP_HEADER_HA_AUTH, EVENT_STATE_CHANGED -import homeassistant.util.dt as dt_util - -from tests.common import ( - get_test_instance_port, get_test_home_assistant) - -API_PASSWORD = 'test1234' -MASTER_PORT = get_test_instance_port() -BROKEN_PORT = get_test_instance_port() -HTTP_BASE_URL = 'http://127.0.0.1:{}'.format(MASTER_PORT) - -HA_HEADERS = {HTTP_HEADER_HA_AUTH: API_PASSWORD} - -broken_api = remote.API('127.0.0.1', "bladybla", port=get_test_instance_port()) -hass, master_api = None, None - - -def _url(path=''): - """Helper method to generate URLs.""" - return HTTP_BASE_URL + path - - -# pylint: disable=invalid-name -def setUpModule(): - """Initialization of a Home Assistant server instance.""" - global hass, master_api - - hass = get_test_home_assistant() - - hass.bus.listen('test_event', lambda _: _) - hass.states.set('test.test', 'a_state') - - setup.setup_component( - hass, http.DOMAIN, - {http.DOMAIN: {http.CONF_API_PASSWORD: API_PASSWORD, - http.CONF_SERVER_PORT: MASTER_PORT}}) - - setup.setup_component(hass, 'api') - - hass.start() - - master_api = remote.API('127.0.0.1', API_PASSWORD, MASTER_PORT) - - -# pylint: disable=invalid-name -def tearDownModule(): - """Stop the Home Assistant server.""" - hass.stop() - - -class TestRemoteMethods(unittest.TestCase): - """Test the homeassistant.remote module.""" - - def tearDown(self): - """Stop everything that was started.""" - hass.block_till_done() - - def test_validate_api(self): - """Test Python API validate_api.""" - self.assertEqual(remote.APIStatus.OK, remote.validate_api(master_api)) - - self.assertEqual( - remote.APIStatus.INVALID_PASSWORD, - remote.validate_api( - remote.API('127.0.0.1', API_PASSWORD + 'A', MASTER_PORT))) - - self.assertEqual( - remote.APIStatus.CANNOT_CONNECT, remote.validate_api(broken_api)) - - def test_get_event_listeners(self): - """Test Python API get_event_listeners.""" - local_data = hass.bus.listeners - remote_data = remote.get_event_listeners(master_api) - - for event in remote_data: - self.assertEqual(local_data.pop(event["event"]), - event["listener_count"]) - - self.assertEqual(len(local_data), 0) - - self.assertEqual({}, remote.get_event_listeners(broken_api)) - - def test_fire_event(self): - """Test Python API fire_event.""" - test_value = [] - - @ha.callback - def listener(event): - """Helper method that will verify our event got called.""" - test_value.append(1) - - hass.bus.listen("test.event_no_data", listener) - remote.fire_event(master_api, "test.event_no_data") - hass.block_till_done() - self.assertEqual(1, len(test_value)) - - # Should not trigger any exception - remote.fire_event(broken_api, "test.event_no_data") - - def test_get_state(self): - """Test Python API get_state.""" - self.assertEqual( - hass.states.get('test.test'), - remote.get_state(master_api, 'test.test')) - - self.assertEqual(None, remote.get_state(broken_api, 'test.test')) - - def test_get_states(self): - """Test Python API get_state_entity_ids.""" - self.assertEqual(hass.states.all(), remote.get_states(master_api)) - self.assertEqual([], remote.get_states(broken_api)) - - def test_remove_state(self): - """Test Python API set_state.""" - hass.states.set('test.remove_state', 'set_test') - - self.assertIn('test.remove_state', hass.states.entity_ids()) - remote.remove_state(master_api, 'test.remove_state') - self.assertNotIn('test.remove_state', hass.states.entity_ids()) - - def test_set_state(self): - """Test Python API set_state.""" - remote.set_state(master_api, 'test.test', 'set_test') - - state = hass.states.get('test.test') - - self.assertIsNotNone(state) - self.assertEqual('set_test', state.state) - - self.assertFalse(remote.set_state(broken_api, 'test.test', 'set_test')) - - def test_set_state_with_push(self): - """Test Python API set_state with push option.""" - events = [] - hass.bus.listen(EVENT_STATE_CHANGED, lambda ev: events.append(ev)) - - remote.set_state(master_api, 'test.test', 'set_test_2') - remote.set_state(master_api, 'test.test', 'set_test_2') - hass.block_till_done() - self.assertEqual(1, len(events)) - - remote.set_state( - master_api, 'test.test', 'set_test_2', force_update=True) - hass.block_till_done() - self.assertEqual(2, len(events)) - - def test_is_state(self): - """Test Python API is_state.""" - self.assertTrue( - remote.is_state(master_api, 'test.test', - hass.states.get('test.test').state)) - - self.assertFalse( - remote.is_state(broken_api, 'test.test', - hass.states.get('test.test').state)) - - def test_get_services(self): - """Test Python API get_services.""" - local_services = hass.services.services - - for serv_domain in remote.get_services(master_api): - local = local_services.pop(serv_domain["domain"]) - - self.assertEqual(local, serv_domain["services"]) - - self.assertEqual({}, remote.get_services(broken_api)) - - def test_call_service(self): - """Test Python API services.call.""" - test_value = [] - - @ha.callback - def listener(service_call): - """Helper method that will verify that our service got called.""" - test_value.append(1) - - hass.services.register("test_domain", "test_service", listener) - - remote.call_service(master_api, "test_domain", "test_service") - - hass.block_till_done() - - self.assertEqual(1, len(test_value)) - - # Should not raise an exception - remote.call_service(broken_api, "test_domain", "test_service") - - def test_json_encoder(self): - """Test the JSON Encoder.""" - ha_json_enc = remote.JSONEncoder() - state = hass.states.get('test.test') - - self.assertEqual(state.as_dict(), ha_json_enc.default(state)) - - # Default method raises TypeError if non HA object - self.assertRaises(TypeError, ha_json_enc.default, 1) - - now = dt_util.utcnow() - self.assertEqual(now.isoformat(), ha_json_enc.default(now)) diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 8ae0f6c11deb70..e3ef797df4d982 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -16,7 +16,7 @@ class TestRequirements: # pylint: disable=invalid-name, no-self-use def setup_method(self, method): - """Setup the test.""" + """Set up the test.""" self.hass = get_test_home_assistant() def teardown_method(self, method): diff --git a/tests/test_setup.py b/tests/test_setup.py index 6f0c282e01657b..29712f40ebc018 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -34,7 +34,7 @@ class TestSetup: # pylint: disable=invalid-name, no-self-use def setup_method(self, method): - """Setup the test.""" + """Set up the test.""" self.hass = get_test_home_assistant() def teardown_method(self, method): @@ -179,7 +179,7 @@ def test_component_not_found(self): assert not setup.setup_component(self.hass, 'non_existing') def test_component_not_double_initialized(self): - """Test we do not setup a component twice.""" + """Test we do not set up a component twice.""" mock_setup = mock.MagicMock(return_value=True) loader.set_component( @@ -206,7 +206,7 @@ def test_component_not_installed_if_requirement_fails(self, mock_install): assert 'comp' not in self.hass.config.components def test_component_not_setup_twice_if_loaded_during_other_setup(self): - """Test component setup while waiting for lock is not setup twice.""" + """Test component setup while waiting for lock is not set up twice.""" result = [] @asyncio.coroutine @@ -219,7 +219,7 @@ def async_setup(hass, config): 'comp', MockModule('comp', async_setup=async_setup)) def setup_component(): - """Setup the component.""" + """Set up the component.""" setup.setup_component(self.hass, 'comp') thread = threading.Thread(target=setup_component) @@ -231,7 +231,7 @@ def setup_component(): assert len(result) == 1 def test_component_not_setup_missing_dependencies(self): - """Test we do not setup a component if not all dependencies loaded.""" + """Test we do not set up a component if not all dependencies loaded.""" deps = ['non_existing'] loader.set_component( self.hass, 'comp', MockModule('comp', dependencies=deps)) @@ -257,7 +257,7 @@ def test_component_failing_setup(self): def test_component_exception_setup(self): """Test component that raises exception during setup.""" def exception_setup(hass, config): - """Setup that raises exception.""" + """Raise exception.""" raise Exception('fail!') loader.set_component( @@ -269,7 +269,7 @@ def exception_setup(hass, config): def test_component_setup_with_validation_and_dependency(self): """Test all config is passed to dependencies.""" def config_check_setup(hass, config): - """Setup method that tests config is passed in.""" + """Test that config is passed in.""" if config.get('comp_a', {}).get('valid', False): return True raise Exception('Config not passed in: {}'.format(config)) @@ -377,7 +377,7 @@ def test_all_work_done_before_start(self): call_order = [] def component1_setup(hass, config): - """Setup mock component.""" + """Set up mock component.""" discovery.discover(hass, 'test_component2', component='test_component2') discovery.discover(hass, 'test_component3', @@ -385,7 +385,7 @@ def component1_setup(hass, config): return True def component_track_setup(hass, config): - """Setup mock component.""" + """Set up mock component.""" call_order.append(1) return True diff --git a/tests/test_util/aiohttp.py b/tests/test_util/aiohttp.py index 813eb84707c3c3..d662f3b195521c 100644 --- a/tests/test_util/aiohttp.py +++ b/tests/test_util/aiohttp.py @@ -12,6 +12,8 @@ from aiohttp.client_exceptions import ClientResponseError +from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE + retype = type(re.compile('')) @@ -216,7 +218,18 @@ def mock_aiohttp_client(): """Context manager to mock aiohttp client.""" mocker = AiohttpClientMocker() + def create_session(hass, *args): + session = mocker.create_session(hass.loop) + + async def close_session(event): + """Close session.""" + await session.close() + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_CLOSE, close_session) + + return session + with mock.patch( 'homeassistant.helpers.aiohttp_client.async_create_clientsession', - side_effect=lambda hass, *args: mocker.create_session(hass.loop)): + side_effect=create_session): yield mocker diff --git a/tests/testing_config/custom_components/image_processing/test.py b/tests/testing_config/custom_components/image_processing/test.py index b50050ed68e5e7..c8cdc998ea03d4 100644 --- a/tests/testing_config/custom_components/image_processing/test.py +++ b/tests/testing_config/custom_components/image_processing/test.py @@ -3,10 +3,10 @@ from homeassistant.components.image_processing import ImageProcessingEntity -async def async_setup_platform(hass, config, async_add_devices_callback, +async def async_setup_platform(hass, config, async_add_entities_callback, discovery_info=None): """Set up the test image_processing platform.""" - async_add_devices_callback([ + async_add_entities_callback([ TestImageProcessing('camera.demo_camera', "Test")]) diff --git a/tests/testing_config/custom_components/light/test.py b/tests/testing_config/custom_components/light/test.py index fbf79f9e77054a..798051ef90cd70 100644 --- a/tests/testing_config/custom_components/light/test.py +++ b/tests/testing_config/custom_components/light/test.py @@ -21,7 +21,7 @@ def init(empty=False): ] -async def async_setup_platform(hass, config, async_add_devices_callback, +async def async_setup_platform(hass, config, async_add_entities_callback, discovery_info=None): """Return mock devices.""" - async_add_devices_callback(DEVICES) + async_add_entities_callback(DEVICES) diff --git a/tests/testing_config/custom_components/switch/test.py b/tests/testing_config/custom_components/switch/test.py index 79126b7b52ae45..33d607e011be2e 100644 --- a/tests/testing_config/custom_components/switch/test.py +++ b/tests/testing_config/custom_components/switch/test.py @@ -21,7 +21,7 @@ def init(empty=False): ] -async def async_setup_platform(hass, config, async_add_devices_callback, +async def async_setup_platform(hass, config, async_add_entities_callback, discovery_info=None): """Find and return test switches.""" - async_add_devices_callback(DEVICES) + async_add_entities_callback(DEVICES) diff --git a/tests/util/test_dt.py b/tests/util/test_dt.py index feee69ad3c85c5..d670917c0550ba 100644 --- a/tests/util/test_dt.py +++ b/tests/util/test_dt.py @@ -11,7 +11,7 @@ class TestDateUtil(unittest.TestCase): """Test util date methods.""" def setUp(self): - """Setup the tests.""" + """Set up the tests.""" self.orig_default_time_zone = dt_util.DEFAULT_TIME_ZONE def tearDown(self): diff --git a/tests/util/test_package.py b/tests/util/test_package.py index ab9f9f0ad2c5e3..1e93a078bd925f 100644 --- a/tests/util/test_package.py +++ b/tests/util/test_package.py @@ -30,8 +30,8 @@ def mock_sys(): @pytest.fixture def mock_exists(): - """Mock check_package_exists.""" - with patch('homeassistant.util.package.check_package_exists') as mock: + """Mock package_loadable.""" + with patch('homeassistant.util.package.package_loadable') as mock: mock.return_value = False yield mock @@ -193,12 +193,12 @@ def test_install_constraint( def test_check_package_global(): """Test for an installed package.""" installed_package = list(pkg_resources.working_set)[0].project_name - assert package.check_package_exists(installed_package) + assert package.package_loadable(installed_package) def test_check_package_zip(): """Test for an installed zip package.""" - assert not package.check_package_exists(TEST_ZIP_REQ) + assert not package.package_loadable(TEST_ZIP_REQ) @asyncio.coroutine @@ -217,3 +217,28 @@ def test_async_get_user_site(mock_env_copy): stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.DEVNULL, env=env) assert ret == os.path.join(deps_dir, 'lib_dir') + + +def test_package_loadable_installed_twice(): + """Test that a package is loadable when installed twice. + + If a package is installed twice, only the first version will be imported. + Test that package_loadable will only compare with the first package. + """ + v1 = pkg_resources.Distribution(project_name='hello', version='1.0.0') + v2 = pkg_resources.Distribution(project_name='hello', version='2.0.0') + + with patch('pkg_resources.find_distributions', side_effect=[[v1]]): + assert not package.package_loadable('hello==2.0.0') + + with patch('pkg_resources.find_distributions', side_effect=[[v1], [v2]]): + assert not package.package_loadable('hello==2.0.0') + + with patch('pkg_resources.find_distributions', side_effect=[[v2], [v1]]): + assert package.package_loadable('hello==2.0.0') + + with patch('pkg_resources.find_distributions', side_effect=[[v2]]): + assert package.package_loadable('hello==2.0.0') + + with patch('pkg_resources.find_distributions', side_effect=[[v2]]): + assert package.package_loadable('Hello==2.0.0') diff --git a/tox.ini b/tox.ini index d6ef1981bef000..e1261457c47bd5 100644 --- a/tox.ini +++ b/tox.ini @@ -58,4 +58,4 @@ whitelist_externals=/bin/bash deps = -r{toxinidir}/requirements_test.txt commands = - /bin/bash -c 'mypy homeassistant/*.py homeassistant/util/' + /bin/bash -c 'mypy homeassistant/*.py homeassistant/auth/ homeassistant/util/' diff --git a/virtualization/Docker/scripts/libcec b/virtualization/Docker/scripts/libcec index 56cbaeb16d7b6e..481b3e700accb7 100755 --- a/virtualization/Docker/scripts/libcec +++ b/virtualization/Docker/scripts/libcec @@ -41,7 +41,7 @@ git submodule update --init src/platform -DPYTHON_LIBRARY="${PYTHON_LIBRARY}" \ -DPYTHON_INCLUDE_DIR="${PYTHON_INCLUDE_DIR}" \ .. - make + make -j$(nproc) make install ldconfig -) \ No newline at end of file +) diff --git a/virtualization/Docker/scripts/openalpr b/virtualization/Docker/scripts/openalpr index 5ee36ecd4ed66a..b9e403710f1044 100755 --- a/virtualization/Docker/scripts/openalpr +++ b/virtualization/Docker/scripts/openalpr @@ -26,7 +26,7 @@ cd build cmake -DWITH_TEST=FALSE -DWITH_BINDING_JAVA=FALSE --DWITH_BINDING_PYTHON=FALSE --DWITH_BINDING_GO=FALSE -DWITH_DAEMON=FALSE -DCMAKE_INSTALL_PREFIX:PATH=/usr/local .. # compile the library -make +make -j$(nproc) # Install the binaries/libraries to your local system (prefix is /usr/local) -make install \ No newline at end of file +make install diff --git a/virtualization/Docker/scripts/ssocr b/virtualization/Docker/scripts/ssocr index 7054951407a844..6778bcab90d010 100755 --- a/virtualization/Docker/scripts/ssocr +++ b/virtualization/Docker/scripts/ssocr @@ -18,7 +18,7 @@ git clone --depth 1 https://github.com/auerswal/ssocr.git ssocr cd ssocr/ # Compile the library -make +make -j$(nproc) # Install the binaries/libraries to your local system (prefix is /usr/local) -make install \ No newline at end of file +make install diff --git a/virtualization/Docker/scripts/tellstick b/virtualization/Docker/scripts/tellstick index d4bf8b331f34ea..c9658d14029f23 100755 --- a/virtualization/Docker/scripts/tellstick +++ b/virtualization/Docker/scripts/tellstick @@ -14,4 +14,4 @@ echo "deb http://download.telldus.com/debian/ stable main" >> /etc/apt/sources.l wget -qO - http://download.telldus.com/debian/telldus-public.key | apt-key add - apt-get update -apt-get install -y --no-install-recommends ${PACKAGES[@]} \ No newline at end of file +apt-get install -y --no-install-recommends ${PACKAGES[@]}