mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Add strict typing to abode (#57673)
This commit is contained in:
parent
bc2f4e82e3
commit
7c51d2f159
@ -4,6 +4,7 @@
|
||||
|
||||
homeassistant.core
|
||||
homeassistant.components
|
||||
homeassistant.components.abode.*
|
||||
homeassistant.components.acer_projector.*
|
||||
homeassistant.components.accuweather.*
|
||||
homeassistant.components.actiontec.*
|
||||
|
@ -1,7 +1,10 @@
|
||||
"""Support for the Abode Security System."""
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import partial
|
||||
|
||||
from abodepy import Abode
|
||||
from abodepy import Abode, AbodeAutomation as AbodeAuto
|
||||
from abodepy.devices import AbodeDevice as AbodeDev
|
||||
from abodepy.exceptions import AbodeAuthenticationException, AbodeException
|
||||
import abodepy.helpers.timeline as TIMELINE
|
||||
from requests.exceptions import ConnectTimeout, HTTPError
|
||||
@ -18,15 +21,12 @@ from homeassistant.const import (
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.core import Event, HomeAssistant, ServiceCall
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers import config_validation as cv, entity
|
||||
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
|
||||
from .const import ATTRIBUTION, DEFAULT_CACHEDB, DOMAIN, LOGGER
|
||||
|
||||
CONF_POLLING = "polling"
|
||||
from .const import ATTRIBUTION, CONF_POLLING, DEFAULT_CACHEDB, DOMAIN, LOGGER
|
||||
|
||||
SERVICE_SETTINGS = "change_setting"
|
||||
SERVICE_CAPTURE_IMAGE = "capture_image"
|
||||
@ -69,25 +69,25 @@ PLATFORMS = [
|
||||
class AbodeSystem:
|
||||
"""Abode System class."""
|
||||
|
||||
def __init__(self, abode, polling):
|
||||
def __init__(self, abode: Abode, polling: bool) -> None:
|
||||
"""Initialize the system."""
|
||||
self.abode = abode
|
||||
self.polling = polling
|
||||
self.entity_ids = set()
|
||||
self.entity_ids: set[str | None] = set()
|
||||
self.logout_listener = None
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Abode integration from a config entry."""
|
||||
username = config_entry.data.get(CONF_USERNAME)
|
||||
password = config_entry.data.get(CONF_PASSWORD)
|
||||
polling = config_entry.data.get(CONF_POLLING)
|
||||
username = entry.data[CONF_USERNAME]
|
||||
password = entry.data[CONF_PASSWORD]
|
||||
polling = entry.data[CONF_POLLING]
|
||||
cache = hass.config.path(DEFAULT_CACHEDB)
|
||||
|
||||
# For previous config entries where unique_id is None
|
||||
if config_entry.unique_id is None:
|
||||
if entry.unique_id is None:
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry, unique_id=config_entry.data[CONF_USERNAME]
|
||||
entry, unique_id=entry.data[CONF_USERNAME]
|
||||
)
|
||||
|
||||
try:
|
||||
@ -103,7 +103,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
|
||||
hass.data[DOMAIN] = AbodeSystem(abode, polling)
|
||||
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
await setup_hass_events(hass)
|
||||
await hass.async_add_executor_job(setup_hass_services, hass)
|
||||
@ -112,15 +112,13 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
hass.services.async_remove(DOMAIN, SERVICE_SETTINGS)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_CAPTURE_IMAGE)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_TRIGGER_AUTOMATION)
|
||||
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
await hass.async_add_executor_job(hass.data[DOMAIN].abode.events.stop)
|
||||
await hass.async_add_executor_job(hass.data[DOMAIN].abode.logout)
|
||||
@ -188,7 +186,7 @@ def setup_hass_services(hass: HomeAssistant) -> None:
|
||||
async def setup_hass_events(hass: HomeAssistant) -> None:
|
||||
"""Home Assistant start and stop callbacks."""
|
||||
|
||||
def logout(event):
|
||||
def logout(event: Event) -> None:
|
||||
"""Logout of Abode."""
|
||||
if not hass.data[DOMAIN].polling:
|
||||
hass.data[DOMAIN].abode.events.stop()
|
||||
@ -207,7 +205,7 @@ async def setup_hass_events(hass: HomeAssistant) -> None:
|
||||
def setup_abode_events(hass: HomeAssistant) -> None:
|
||||
"""Event callbacks."""
|
||||
|
||||
def event_callback(event, event_json):
|
||||
def event_callback(event: str, event_json: dict[str, str]) -> None:
|
||||
"""Handle an event callback from Abode."""
|
||||
data = {
|
||||
ATTR_DEVICE_ID: event_json.get(ATTR_DEVICE_ID, ""),
|
||||
@ -246,17 +244,17 @@ def setup_abode_events(hass: HomeAssistant) -> None:
|
||||
)
|
||||
|
||||
|
||||
class AbodeEntity(Entity):
|
||||
class AbodeEntity(entity.Entity):
|
||||
"""Representation of an Abode entity."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
|
||||
def __init__(self, data):
|
||||
def __init__(self, data: AbodeSystem) -> None:
|
||||
"""Initialize Abode entity."""
|
||||
self._data = data
|
||||
self._attr_should_poll = data.polling
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to Abode connection status updates."""
|
||||
await self.hass.async_add_executor_job(
|
||||
self._data.abode.events.add_connection_status_callback,
|
||||
@ -266,13 +264,13 @@ class AbodeEntity(Entity):
|
||||
|
||||
self.hass.data[DOMAIN].entity_ids.add(self.entity_id)
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Unsubscribe from Abode connection status updates."""
|
||||
await self.hass.async_add_executor_job(
|
||||
self._data.abode.events.remove_connection_status_callback, self.unique_id
|
||||
)
|
||||
|
||||
def _update_connection_status(self):
|
||||
def _update_connection_status(self) -> None:
|
||||
"""Update the entity available property."""
|
||||
self._attr_available = self._data.abode.events.connected
|
||||
self.schedule_update_ha_state()
|
||||
@ -281,14 +279,14 @@ class AbodeEntity(Entity):
|
||||
class AbodeDevice(AbodeEntity):
|
||||
"""Representation of an Abode device."""
|
||||
|
||||
def __init__(self, data, device):
|
||||
def __init__(self, data: AbodeSystem, device: AbodeDev) -> None:
|
||||
"""Initialize Abode device."""
|
||||
super().__init__(data)
|
||||
self._device = device
|
||||
self._attr_name = device.name
|
||||
self._attr_unique_id = device.device_uuid
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to device events."""
|
||||
await super().async_added_to_hass()
|
||||
await self.hass.async_add_executor_job(
|
||||
@ -297,19 +295,19 @@ class AbodeDevice(AbodeEntity):
|
||||
self._update_callback,
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Unsubscribe from device events."""
|
||||
await super().async_will_remove_from_hass()
|
||||
await self.hass.async_add_executor_job(
|
||||
self._data.abode.events.remove_all_device_callbacks, self._device.device_id
|
||||
)
|
||||
|
||||
def update(self):
|
||||
def update(self) -> None:
|
||||
"""Update device state."""
|
||||
self._device.refresh()
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
def extra_state_attributes(self) -> dict[str, str]:
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
"device_id": self._device.device_id,
|
||||
@ -319,16 +317,16 @@ class AbodeDevice(AbodeEntity):
|
||||
}
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
def device_info(self) -> entity.DeviceInfo:
|
||||
"""Return device registry information for this entity."""
|
||||
return DeviceInfo(
|
||||
return entity.DeviceInfo(
|
||||
identifiers={(DOMAIN, self._device.device_id)},
|
||||
manufacturer="Abode",
|
||||
model=self._device.type,
|
||||
name=self._device.name,
|
||||
)
|
||||
|
||||
def _update_callback(self, device):
|
||||
def _update_callback(self, device: AbodeDev) -> None:
|
||||
"""Update the device state."""
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
@ -336,7 +334,7 @@ class AbodeDevice(AbodeEntity):
|
||||
class AbodeAutomation(AbodeEntity):
|
||||
"""Representation of an Abode automation."""
|
||||
|
||||
def __init__(self, data, automation):
|
||||
def __init__(self, data: AbodeSystem, automation: AbodeAuto) -> None:
|
||||
"""Initialize for Abode automation."""
|
||||
super().__init__(data)
|
||||
self._automation = automation
|
||||
@ -346,6 +344,6 @@ class AbodeAutomation(AbodeEntity):
|
||||
"type": "CUE automation",
|
||||
}
|
||||
|
||||
def update(self):
|
||||
def update(self) -> None:
|
||||
"""Update automation state."""
|
||||
self._automation.refresh()
|
||||
|
@ -1,4 +1,8 @@
|
||||
"""Support for Abode Security System alarm control panels."""
|
||||
from __future__ import annotations
|
||||
|
||||
from abodepy.devices.alarm import AbodeAlarm as AbodeAl
|
||||
|
||||
import homeassistant.components.alarm_control_panel as alarm
|
||||
from homeassistant.components.alarm_control_panel.const import (
|
||||
SUPPORT_ALARM_ARM_AWAY,
|
||||
@ -13,19 +17,17 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AbodeDevice
|
||||
from . import AbodeDevice, AbodeSystem
|
||||
from .const import DOMAIN
|
||||
|
||||
ICON = "mdi:security"
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Abode alarm control panel device."""
|
||||
data = hass.data[DOMAIN]
|
||||
data: AbodeSystem = hass.data[DOMAIN]
|
||||
async_add_entities(
|
||||
[AbodeAlarm(data, await hass.async_add_executor_job(data.abode.get_alarm))]
|
||||
)
|
||||
@ -37,34 +39,33 @@ class AbodeAlarm(AbodeDevice, alarm.AlarmControlPanelEntity):
|
||||
_attr_icon = ICON
|
||||
_attr_code_arm_required = False
|
||||
_attr_supported_features = SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY
|
||||
_device: AbodeAl
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def state(self) -> str | None:
|
||||
"""Return the state of the device."""
|
||||
if self._device.is_standby:
|
||||
state = STATE_ALARM_DISARMED
|
||||
elif self._device.is_away:
|
||||
state = STATE_ALARM_ARMED_AWAY
|
||||
elif self._device.is_home:
|
||||
state = STATE_ALARM_ARMED_HOME
|
||||
else:
|
||||
state = None
|
||||
return state
|
||||
return STATE_ALARM_DISARMED
|
||||
if self._device.is_away:
|
||||
return STATE_ALARM_ARMED_AWAY
|
||||
if self._device.is_home:
|
||||
return STATE_ALARM_ARMED_HOME
|
||||
return None
|
||||
|
||||
def alarm_disarm(self, code=None):
|
||||
def alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Send disarm command."""
|
||||
self._device.set_standby()
|
||||
|
||||
def alarm_arm_home(self, code=None):
|
||||
def alarm_arm_home(self, code: str | None = None) -> None:
|
||||
"""Send arm home command."""
|
||||
self._device.set_home()
|
||||
|
||||
def alarm_arm_away(self, code=None):
|
||||
def alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Send arm away command."""
|
||||
self._device.set_away()
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
def extra_state_attributes(self) -> dict[str, str]:
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
"device_id": self._device.device_id,
|
||||
|
@ -1,4 +1,7 @@
|
||||
"""Support for Abode Security System binary sensors."""
|
||||
from typing import cast
|
||||
|
||||
from abodepy.devices.binary_sensor import AbodeBinarySensor as ABBinarySensor
|
||||
import abodepy.helpers.constants as CONST
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
@ -9,17 +12,15 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AbodeDevice
|
||||
from . import AbodeDevice, AbodeSystem
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Abode binary sensor devices."""
|
||||
data = hass.data[DOMAIN]
|
||||
data: AbodeSystem = hass.data[DOMAIN]
|
||||
|
||||
device_types = [
|
||||
CONST.TYPE_CONNECTIVITY,
|
||||
@ -40,14 +41,16 @@ async def async_setup_entry(
|
||||
class AbodeBinarySensor(AbodeDevice, BinarySensorEntity):
|
||||
"""A binary sensor implementation for Abode device."""
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return True if the binary sensor is on."""
|
||||
return self._device.is_on
|
||||
_device: ABBinarySensor
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
def is_on(self) -> bool:
|
||||
"""Return True if the binary sensor is on."""
|
||||
return cast(bool, self._device.is_on)
|
||||
|
||||
@property
|
||||
def device_class(self) -> str:
|
||||
"""Return the class of the binary sensor."""
|
||||
if self._device.get_value("is_window") == "1":
|
||||
return BinarySensorDeviceClass.WINDOW
|
||||
return self._device.generic_type
|
||||
return cast(str, self._device.generic_type)
|
||||
|
@ -2,32 +2,32 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import Any, cast
|
||||
|
||||
import abodepy.helpers.constants as CONST
|
||||
from abodepy.devices import CONST, AbodeDevice as AbodeDev
|
||||
from abodepy.devices.camera import AbodeCamera as AbodeCam
|
||||
import abodepy.helpers.timeline as TIMELINE
|
||||
import requests
|
||||
from requests.models import Response
|
||||
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import Event, HomeAssistant
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
from . import AbodeDevice
|
||||
from . import AbodeDevice, AbodeSystem
|
||||
from .const import DOMAIN, LOGGER
|
||||
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Abode camera devices."""
|
||||
data = hass.data[DOMAIN]
|
||||
|
||||
data: AbodeSystem = hass.data[DOMAIN]
|
||||
entities = []
|
||||
|
||||
for device in data.abode.get_devices(generic_type=CONST.TYPE_CAMERA):
|
||||
@ -39,14 +39,16 @@ async def async_setup_entry(
|
||||
class AbodeCamera(AbodeDevice, Camera):
|
||||
"""Representation of an Abode camera."""
|
||||
|
||||
def __init__(self, data, device, event):
|
||||
_device: AbodeCam
|
||||
|
||||
def __init__(self, data: AbodeSystem, device: AbodeDev, event: Event) -> None:
|
||||
"""Initialize the Abode device."""
|
||||
AbodeDevice.__init__(self, data, device)
|
||||
Camera.__init__(self)
|
||||
self._event = event
|
||||
self._response = None
|
||||
self._response: Response | None = None
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe Abode events."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
@ -59,17 +61,17 @@ class AbodeCamera(AbodeDevice, Camera):
|
||||
signal = f"abode_camera_capture_{self.entity_id}"
|
||||
self.async_on_remove(async_dispatcher_connect(self.hass, signal, self.capture))
|
||||
|
||||
def capture(self):
|
||||
def capture(self) -> bool:
|
||||
"""Request a new image capture."""
|
||||
return self._device.capture()
|
||||
return cast(bool, self._device.capture())
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def refresh_image(self):
|
||||
def refresh_image(self) -> None:
|
||||
"""Find a new image on the timeline."""
|
||||
if self._device.refresh_image():
|
||||
self.get_image()
|
||||
|
||||
def get_image(self):
|
||||
def get_image(self) -> None:
|
||||
"""Attempt to download the most recent capture."""
|
||||
if self._device.image_url:
|
||||
try:
|
||||
@ -93,21 +95,21 @@ class AbodeCamera(AbodeDevice, Camera):
|
||||
|
||||
return None
|
||||
|
||||
def turn_on(self):
|
||||
def turn_on(self) -> None:
|
||||
"""Turn on camera."""
|
||||
self._device.privacy_mode(False)
|
||||
|
||||
def turn_off(self):
|
||||
def turn_off(self) -> None:
|
||||
"""Turn off camera."""
|
||||
self._device.privacy_mode(True)
|
||||
|
||||
def _capture_callback(self, capture):
|
||||
def _capture_callback(self, capture: Any) -> None:
|
||||
"""Update the image with the device then refresh device."""
|
||||
self._device.update_image_location(capture)
|
||||
self.get_image()
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if on."""
|
||||
return self._device.is_on
|
||||
return cast(bool, self._device.is_on)
|
||||
|
@ -1,5 +1,8 @@
|
||||
"""Config flow for the Abode Security System component."""
|
||||
from __future__ import annotations
|
||||
|
||||
from http import HTTPStatus
|
||||
from typing import Any, cast
|
||||
|
||||
from abodepy import Abode
|
||||
from abodepy.exceptions import AbodeAuthenticationException, AbodeException
|
||||
@ -9,11 +12,11 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
|
||||
from .const import DEFAULT_CACHEDB, DOMAIN, LOGGER
|
||||
from .const import CONF_POLLING, DEFAULT_CACHEDB, DOMAIN, LOGGER
|
||||
|
||||
CONF_MFA = "mfa_code"
|
||||
CONF_POLLING = "polling"
|
||||
|
||||
|
||||
class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
@ -21,7 +24,7 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
"""Initialize."""
|
||||
self.data_schema = {
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
@ -31,13 +34,13 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
vol.Required(CONF_MFA): str,
|
||||
}
|
||||
|
||||
self._cache = None
|
||||
self._mfa_code = None
|
||||
self._password = None
|
||||
self._polling = False
|
||||
self._username = None
|
||||
self._cache: str | None = None
|
||||
self._mfa_code: str | None = None
|
||||
self._password: str | None = None
|
||||
self._polling: bool = False
|
||||
self._username: str | None = None
|
||||
|
||||
async def _async_abode_login(self, step_id):
|
||||
async def _async_abode_login(self, step_id: str) -> FlowResult:
|
||||
"""Handle login with Abode."""
|
||||
self._cache = self.hass.config.path(DEFAULT_CACHEDB)
|
||||
errors = {}
|
||||
@ -47,7 +50,7 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
Abode, self._username, self._password, True, False, False, self._cache
|
||||
)
|
||||
|
||||
except (AbodeException, ConnectTimeout, HTTPError) as ex:
|
||||
except AbodeException as ex:
|
||||
if ex.errcode == MFA_CODE_REQUIRED[0]:
|
||||
return await self.async_step_mfa()
|
||||
|
||||
@ -59,6 +62,9 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
else:
|
||||
errors = {"base": "cannot_connect"}
|
||||
|
||||
except (ConnectTimeout, HTTPError):
|
||||
errors = {"base": "cannot_connect"}
|
||||
|
||||
if errors:
|
||||
return self.async_show_form(
|
||||
step_id=step_id, data_schema=vol.Schema(self.data_schema), errors=errors
|
||||
@ -66,7 +72,7 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
return await self._async_create_entry()
|
||||
|
||||
async def _async_abode_mfa_login(self):
|
||||
async def _async_abode_mfa_login(self) -> FlowResult:
|
||||
"""Handle multi-factor authentication (MFA) login with Abode."""
|
||||
try:
|
||||
# Create instance to access login method for passing MFA code
|
||||
@ -89,7 +95,7 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
return await self._async_create_entry()
|
||||
|
||||
async def _async_create_entry(self):
|
||||
async def _async_create_entry(self) -> FlowResult:
|
||||
"""Create the config entry."""
|
||||
config_data = {
|
||||
CONF_USERNAME: self._username,
|
||||
@ -109,9 +115,13 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
|
||||
return self.async_create_entry(title=self._username, data=config_data)
|
||||
return self.async_create_entry(
|
||||
title=cast(str, self._username), data=config_data
|
||||
)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle a flow initialized by the user."""
|
||||
if self._async_current_entries():
|
||||
return self.async_abort(reason="single_instance_allowed")
|
||||
@ -126,7 +136,9 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
return await self._async_abode_login(step_id="user")
|
||||
|
||||
async def async_step_mfa(self, user_input=None):
|
||||
async def async_step_mfa(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle a multi-factor authentication (MFA) flow."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
@ -137,13 +149,15 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
return await self._async_abode_mfa_login()
|
||||
|
||||
async def async_step_reauth(self, config):
|
||||
async def async_step_reauth(self, config: dict[str, Any]) -> FlowResult:
|
||||
"""Handle reauthorization request from Abode."""
|
||||
self._username = config[CONF_USERNAME]
|
||||
|
||||
return await self.async_step_reauth_confirm()
|
||||
|
||||
async def async_step_reauth_confirm(self, user_input=None):
|
||||
async def async_step_reauth_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle reauthorization flow."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
|
@ -7,3 +7,4 @@ DOMAIN = "abode"
|
||||
ATTRIBUTION = "Data provided by goabode.com"
|
||||
|
||||
DEFAULT_CACHEDB = "abodepy_cache.pickle"
|
||||
CONF_POLLING = "polling"
|
||||
|
@ -1,4 +1,7 @@
|
||||
"""Support for Abode Security System covers."""
|
||||
from typing import Any
|
||||
|
||||
from abodepy.devices.cover import AbodeCover as AbodeCV
|
||||
import abodepy.helpers.constants as CONST
|
||||
|
||||
from homeassistant.components.cover import CoverEntity
|
||||
@ -6,17 +9,15 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AbodeDevice
|
||||
from . import AbodeDevice, AbodeSystem
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Abode cover devices."""
|
||||
data = hass.data[DOMAIN]
|
||||
data: AbodeSystem = hass.data[DOMAIN]
|
||||
|
||||
entities = []
|
||||
|
||||
@ -29,15 +30,17 @@ async def async_setup_entry(
|
||||
class AbodeCover(AbodeDevice, CoverEntity):
|
||||
"""Representation of an Abode cover."""
|
||||
|
||||
_device: AbodeCV
|
||||
|
||||
@property
|
||||
def is_closed(self):
|
||||
def is_closed(self) -> bool:
|
||||
"""Return true if cover is closed, else False."""
|
||||
return not self._device.is_open
|
||||
|
||||
def close_cover(self, **kwargs):
|
||||
def close_cover(self, **kwargs: Any) -> None:
|
||||
"""Issue close command to cover."""
|
||||
self._device.close_cover()
|
||||
|
||||
def open_cover(self, **kwargs):
|
||||
def open_cover(self, **kwargs: Any) -> None:
|
||||
"""Issue open command to cover."""
|
||||
self._device.open_cover()
|
||||
|
@ -1,6 +1,10 @@
|
||||
"""Support for Abode Security System lights."""
|
||||
from math import ceil
|
||||
from __future__ import annotations
|
||||
|
||||
from math import ceil
|
||||
from typing import Any
|
||||
|
||||
from abodepy.devices.light import AbodeLight as AbodeLT
|
||||
import abodepy.helpers.constants as CONST
|
||||
|
||||
from homeassistant.components.light import (
|
||||
@ -20,17 +24,15 @@ from homeassistant.util.color import (
|
||||
color_temperature_mired_to_kelvin,
|
||||
)
|
||||
|
||||
from . import AbodeDevice
|
||||
from . import AbodeDevice, AbodeSystem
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Abode light devices."""
|
||||
data = hass.data[DOMAIN]
|
||||
data: AbodeSystem = hass.data[DOMAIN]
|
||||
|
||||
entities = []
|
||||
|
||||
@ -43,7 +45,9 @@ async def async_setup_entry(
|
||||
class AbodeLight(AbodeDevice, LightEntity):
|
||||
"""Representation of an Abode light."""
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
_device: AbodeLT
|
||||
|
||||
def turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on the light."""
|
||||
if ATTR_COLOR_TEMP in kwargs and self._device.is_color_capable:
|
||||
self._device.set_color_temp(
|
||||
@ -63,40 +67,42 @@ class AbodeLight(AbodeDevice, LightEntity):
|
||||
|
||||
self._device.switch_on()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
def turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off the light."""
|
||||
self._device.switch_off()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if device is on."""
|
||||
return self._device.is_on
|
||||
return bool(self._device.is_on)
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
def brightness(self) -> int | None:
|
||||
"""Return the brightness of the light."""
|
||||
if self._device.is_dimmable and self._device.has_brightness:
|
||||
brightness = int(self._device.brightness)
|
||||
# Abode returns 100 during device initialization and device refresh
|
||||
if brightness == 100:
|
||||
return 255
|
||||
# Convert Abode brightness (0-99) to Home Assistant brightness (0-255)
|
||||
return ceil(brightness * 255 / 99.0)
|
||||
return 255 if brightness == 100 else ceil(brightness * 255 / 99.0)
|
||||
return None
|
||||
|
||||
@property
|
||||
def color_temp(self):
|
||||
def color_temp(self) -> int | None:
|
||||
"""Return the color temp of the light."""
|
||||
if self._device.has_color:
|
||||
return color_temperature_kelvin_to_mired(self._device.color_temp)
|
||||
return None
|
||||
|
||||
@property
|
||||
def hs_color(self):
|
||||
def hs_color(self) -> tuple[float, float] | None:
|
||||
"""Return the color of the light."""
|
||||
_hs = None
|
||||
if self._device.has_color:
|
||||
return self._device.color
|
||||
_hs = self._device.color
|
||||
return _hs
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
def supported_features(self) -> int:
|
||||
"""Flag supported features."""
|
||||
if self._device.is_dimmable and self._device.is_color_capable:
|
||||
return SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_COLOR_TEMP
|
||||
|
@ -1,4 +1,7 @@
|
||||
"""Support for the Abode Security System locks."""
|
||||
from typing import Any
|
||||
|
||||
from abodepy.devices.lock import AbodeLock as AbodeLK
|
||||
import abodepy.helpers.constants as CONST
|
||||
|
||||
from homeassistant.components.lock import LockEntity
|
||||
@ -6,17 +9,15 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AbodeDevice
|
||||
from . import AbodeDevice, AbodeSystem
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Abode lock devices."""
|
||||
data = hass.data[DOMAIN]
|
||||
data: AbodeSystem = hass.data[DOMAIN]
|
||||
|
||||
entities = []
|
||||
|
||||
@ -29,15 +30,17 @@ async def async_setup_entry(
|
||||
class AbodeLock(AbodeDevice, LockEntity):
|
||||
"""Representation of an Abode lock."""
|
||||
|
||||
def lock(self, **kwargs):
|
||||
_device: AbodeLK
|
||||
|
||||
def lock(self, **kwargs: Any) -> None:
|
||||
"""Lock the device."""
|
||||
self._device.lock()
|
||||
|
||||
def unlock(self, **kwargs):
|
||||
def unlock(self, **kwargs: Any) -> None:
|
||||
"""Unlock the device."""
|
||||
self._device.unlock()
|
||||
|
||||
@property
|
||||
def is_locked(self):
|
||||
def is_locked(self) -> bool:
|
||||
"""Return true if device is on."""
|
||||
return self._device.is_locked
|
||||
return bool(self._device.is_locked)
|
||||
|
@ -1,7 +1,9 @@
|
||||
"""Support for Abode Security System sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
import abodepy.helpers.constants as CONST
|
||||
from typing import cast
|
||||
|
||||
from abodepy.devices.sensor import CONST, AbodeSensor as AbodeSense
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
@ -12,7 +14,7 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AbodeDevice
|
||||
from . import AbodeDevice, AbodeSystem
|
||||
from .const import DOMAIN
|
||||
|
||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
@ -35,12 +37,10 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Abode sensor devices."""
|
||||
data = hass.data[DOMAIN]
|
||||
data: AbodeSystem = hass.data[DOMAIN]
|
||||
|
||||
entities = []
|
||||
|
||||
@ -60,7 +60,14 @@ async def async_setup_entry(
|
||||
class AbodeSensor(AbodeDevice, SensorEntity):
|
||||
"""A sensor implementation for Abode devices."""
|
||||
|
||||
def __init__(self, data, device, description: SensorEntityDescription):
|
||||
_device: AbodeSense
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: AbodeSystem,
|
||||
device: AbodeSense,
|
||||
description: SensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize a sensor for an Abode device."""
|
||||
super().__init__(data, device)
|
||||
self.entity_description = description
|
||||
@ -74,11 +81,12 @@ class AbodeSensor(AbodeDevice, SensorEntity):
|
||||
self._attr_native_unit_of_measurement = device.lux_unit
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
def native_value(self) -> float | None:
|
||||
"""Return the state of the sensor."""
|
||||
if self.entity_description.key == CONST.TEMP_STATUS_KEY:
|
||||
return self._device.temp
|
||||
return cast(float, self._device.temp)
|
||||
if self.entity_description.key == CONST.HUMI_STATUS_KEY:
|
||||
return self._device.humidity
|
||||
return cast(float, self._device.humidity)
|
||||
if self.entity_description.key == CONST.LUX_STATUS_KEY:
|
||||
return self._device.lux
|
||||
return cast(float, self._device.lux)
|
||||
return None
|
||||
|
@ -1,7 +1,9 @@
|
||||
"""Support for Abode Security System switches."""
|
||||
from __future__ import annotations
|
||||
|
||||
import abodepy.helpers.constants as CONST
|
||||
from typing import Any, cast
|
||||
|
||||
from abodepy.devices.switch import CONST, AbodeSwitch as AbodeSW
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@ -9,7 +11,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AbodeAutomation, AbodeDevice
|
||||
from . import AbodeAutomation, AbodeDevice, AbodeSystem
|
||||
from .const import DOMAIN
|
||||
|
||||
DEVICE_TYPES = [CONST.TYPE_SWITCH, CONST.TYPE_VALVE]
|
||||
@ -18,12 +20,10 @@ ICON = "mdi:robot"
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Abode switch devices."""
|
||||
data = hass.data[DOMAIN]
|
||||
data: AbodeSystem = hass.data[DOMAIN]
|
||||
|
||||
entities: list[SwitchEntity] = []
|
||||
|
||||
@ -40,18 +40,20 @@ async def async_setup_entry(
|
||||
class AbodeSwitch(AbodeDevice, SwitchEntity):
|
||||
"""Representation of an Abode switch."""
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
_device: AbodeSW
|
||||
|
||||
def turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on the device."""
|
||||
self._device.switch_on()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
def turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off the device."""
|
||||
self._device.switch_off()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if device is on."""
|
||||
return self._device.is_on
|
||||
return cast(bool, self._device.is_on)
|
||||
|
||||
|
||||
class AbodeAutomationSwitch(AbodeAutomation, SwitchEntity):
|
||||
@ -59,28 +61,28 @@ class AbodeAutomationSwitch(AbodeAutomation, SwitchEntity):
|
||||
|
||||
_attr_icon = ICON
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Set up trigger automation service."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
signal = f"abode_trigger_automation_{self.entity_id}"
|
||||
self.async_on_remove(async_dispatcher_connect(self.hass, signal, self.trigger))
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
def turn_on(self, **kwargs: Any) -> None:
|
||||
"""Enable the automation."""
|
||||
if self._automation.enable(True):
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
def turn_off(self, **kwargs: Any) -> None:
|
||||
"""Disable the automation."""
|
||||
if self._automation.enable(False):
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def trigger(self):
|
||||
def trigger(self) -> None:
|
||||
"""Trigger the automation."""
|
||||
self._automation.trigger()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
def is_on(self) -> bool:
|
||||
"""Return True if the automation is enabled."""
|
||||
return self._automation.is_enabled
|
||||
return bool(self._automation.is_enabled)
|
||||
|
11
mypy.ini
11
mypy.ini
@ -47,6 +47,17 @@ no_implicit_optional = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.abode.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
disallow_subclassing_any = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_decorators = true
|
||||
disallow_untyped_defs = true
|
||||
no_implicit_optional = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.acer_projector.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
|
@ -2,17 +2,23 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.abode import DOMAIN as ABODE_DOMAIN
|
||||
from homeassistant.components.abode.const import CONF_POLLING
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def setup_platform(hass, platform):
|
||||
async def setup_platform(hass: HomeAssistant, platform: str) -> MockConfigEntry:
|
||||
"""Set up the Abode platform."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=ABODE_DOMAIN,
|
||||
data={CONF_USERNAME: "user@email.com", CONF_PASSWORD: "password"},
|
||||
data={
|
||||
CONF_USERNAME: "user@email.com",
|
||||
CONF_PASSWORD: "password",
|
||||
CONF_POLLING: False,
|
||||
},
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
|
@ -7,7 +7,7 @@ from tests.components.light.conftest import mock_light_profiles # noqa: F401
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def requests_mock_fixture(requests_mock):
|
||||
def requests_mock_fixture(requests_mock) -> None:
|
||||
"""Fixture to provide a requests mocker."""
|
||||
# Mocks the login response for abodepy.
|
||||
requests_mock.post(CONST.LOGIN_URL, text=load_fixture("login.json", "abode"))
|
||||
|
@ -16,6 +16,7 @@ from homeassistant.const import (
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_DISARMED,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import setup_platform
|
||||
@ -23,7 +24,7 @@ from .common import setup_platform
|
||||
DEVICE_ID = "alarm_control_panel.abode_alarm"
|
||||
|
||||
|
||||
async def test_entity_registry(hass):
|
||||
async def test_entity_registry(hass: HomeAssistant) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
@ -33,7 +34,7 @@ async def test_entity_registry(hass):
|
||||
assert entry.unique_id == "001122334455"
|
||||
|
||||
|
||||
async def test_attributes(hass):
|
||||
async def test_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test the alarm control panel attributes are correct."""
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
|
||||
@ -46,7 +47,7 @@ async def test_attributes(hass):
|
||||
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 3
|
||||
|
||||
|
||||
async def test_set_alarm_away(hass):
|
||||
async def test_set_alarm_away(hass: HomeAssistant) -> None:
|
||||
"""Test the alarm control panel can be set to away."""
|
||||
with patch("abodepy.AbodeEventController.add_device_callback") as mock_callback:
|
||||
with patch("abodepy.ALARM.AbodeAlarm.set_away") as mock_set_away:
|
||||
@ -75,7 +76,7 @@ async def test_set_alarm_away(hass):
|
||||
assert state.state == STATE_ALARM_ARMED_AWAY
|
||||
|
||||
|
||||
async def test_set_alarm_home(hass):
|
||||
async def test_set_alarm_home(hass: HomeAssistant) -> None:
|
||||
"""Test the alarm control panel can be set to home."""
|
||||
with patch("abodepy.AbodeEventController.add_device_callback") as mock_callback:
|
||||
with patch("abodepy.ALARM.AbodeAlarm.set_home") as mock_set_home:
|
||||
@ -103,7 +104,7 @@ async def test_set_alarm_home(hass):
|
||||
assert state.state == STATE_ALARM_ARMED_HOME
|
||||
|
||||
|
||||
async def test_set_alarm_standby(hass):
|
||||
async def test_set_alarm_standby(hass: HomeAssistant) -> None:
|
||||
"""Test the alarm control panel can be set to standby."""
|
||||
with patch("abodepy.AbodeEventController.add_device_callback") as mock_callback:
|
||||
with patch("abodepy.ALARM.AbodeAlarm.set_standby") as mock_set_standby:
|
||||
@ -130,7 +131,7 @@ async def test_set_alarm_standby(hass):
|
||||
assert state.state == STATE_ALARM_DISARMED
|
||||
|
||||
|
||||
async def test_state_unknown(hass):
|
||||
async def test_state_unknown(hass: HomeAssistant) -> None:
|
||||
"""Test an unknown alarm control panel state."""
|
||||
with patch("abodepy.ALARM.AbodeAlarm.mode", new_callable=PropertyMock) as mock_mode:
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
|
@ -11,12 +11,13 @@ from homeassistant.const import (
|
||||
ATTR_FRIENDLY_NAME,
|
||||
STATE_OFF,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import setup_platform
|
||||
|
||||
|
||||
async def test_entity_registry(hass):
|
||||
async def test_entity_registry(hass: HomeAssistant) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, BINARY_SENSOR_DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
@ -25,7 +26,7 @@ async def test_entity_registry(hass):
|
||||
assert entry.unique_id == "2834013428b6035fba7d4054aa7b25a3"
|
||||
|
||||
|
||||
async def test_attributes(hass):
|
||||
async def test_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test the binary sensor attributes are correct."""
|
||||
await setup_platform(hass, BINARY_SENSOR_DOMAIN)
|
||||
|
||||
|
@ -4,12 +4,13 @@ from unittest.mock import patch
|
||||
from homeassistant.components.abode.const import DOMAIN as ABODE_DOMAIN
|
||||
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_IDLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import setup_platform
|
||||
|
||||
|
||||
async def test_entity_registry(hass):
|
||||
async def test_entity_registry(hass: HomeAssistant) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, CAMERA_DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
@ -18,7 +19,7 @@ async def test_entity_registry(hass):
|
||||
assert entry.unique_id == "d0a3a1c316891ceb00c20118aae2a133"
|
||||
|
||||
|
||||
async def test_attributes(hass):
|
||||
async def test_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test the camera attributes are correct."""
|
||||
await setup_platform(hass, CAMERA_DOMAIN)
|
||||
|
||||
@ -26,7 +27,7 @@ async def test_attributes(hass):
|
||||
assert state.state == STATE_IDLE
|
||||
|
||||
|
||||
async def test_capture_image(hass):
|
||||
async def test_capture_image(hass: HomeAssistant) -> None:
|
||||
"""Test the camera capture image service."""
|
||||
await setup_platform(hass, CAMERA_DOMAIN)
|
||||
|
||||
@ -41,7 +42,7 @@ async def test_capture_image(hass):
|
||||
mock_capture.assert_called_once()
|
||||
|
||||
|
||||
async def test_camera_on(hass):
|
||||
async def test_camera_on(hass: HomeAssistant) -> None:
|
||||
"""Test the camera turn on service."""
|
||||
await setup_platform(hass, CAMERA_DOMAIN)
|
||||
|
||||
@ -56,7 +57,7 @@ async def test_camera_on(hass):
|
||||
mock_capture.assert_called_once_with(False)
|
||||
|
||||
|
||||
async def test_camera_off(hass):
|
||||
async def test_camera_off(hass: HomeAssistant) -> None:
|
||||
"""Test the camera turn off service."""
|
||||
await setup_platform(hass, CAMERA_DOMAIN)
|
||||
|
||||
|
@ -4,19 +4,19 @@ from unittest.mock import patch
|
||||
|
||||
from abodepy.exceptions import AbodeAuthenticationException
|
||||
from abodepy.helpers.errors import MFA_CODE_REQUIRED
|
||||
from requests.exceptions import ConnectTimeout
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.abode import config_flow
|
||||
from homeassistant.components.abode.const import DOMAIN
|
||||
from homeassistant.components.abode.const import CONF_POLLING, DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
CONF_POLLING = "polling"
|
||||
|
||||
|
||||
async def test_show_form(hass):
|
||||
async def test_show_form(hass: HomeAssistant) -> None:
|
||||
"""Test that the form is served with no input."""
|
||||
flow = config_flow.AbodeFlowHandler()
|
||||
flow.hass = hass
|
||||
@ -27,7 +27,7 @@ async def test_show_form(hass):
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
|
||||
async def test_one_config_allowed(hass):
|
||||
async def test_one_config_allowed(hass: HomeAssistant) -> None:
|
||||
"""Test that only one Abode configuration is allowed."""
|
||||
flow = config_flow.AbodeFlowHandler()
|
||||
flow.hass = hass
|
||||
@ -43,7 +43,7 @@ async def test_one_config_allowed(hass):
|
||||
assert step_user_result["reason"] == "single_instance_allowed"
|
||||
|
||||
|
||||
async def test_invalid_credentials(hass):
|
||||
async def test_invalid_credentials(hass: HomeAssistant) -> None:
|
||||
"""Test that invalid credentials throws an error."""
|
||||
conf = {CONF_USERNAME: "user@email.com", CONF_PASSWORD: "password"}
|
||||
|
||||
@ -60,7 +60,7 @@ async def test_invalid_credentials(hass):
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_connection_error(hass):
|
||||
async def test_connection_auth_error(hass: HomeAssistant) -> None:
|
||||
"""Test other than invalid credentials throws an error."""
|
||||
conf = {CONF_USERNAME: "user@email.com", CONF_PASSWORD: "password"}
|
||||
|
||||
@ -77,7 +77,22 @@ async def test_connection_error(hass):
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_step_user(hass):
|
||||
async def test_connection_error(hass: HomeAssistant) -> None:
|
||||
"""Test login throws an error if connection times out."""
|
||||
conf = {CONF_USERNAME: "user@email.com", CONF_PASSWORD: "password"}
|
||||
|
||||
flow = config_flow.AbodeFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.abode.config_flow.Abode",
|
||||
side_effect=ConnectTimeout,
|
||||
):
|
||||
result = await flow.async_step_user(user_input=conf)
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_step_user(hass: HomeAssistant) -> None:
|
||||
"""Test that the user step works."""
|
||||
conf = {CONF_USERNAME: "user@email.com", CONF_PASSWORD: "password"}
|
||||
|
||||
@ -98,7 +113,7 @@ async def test_step_user(hass):
|
||||
}
|
||||
|
||||
|
||||
async def test_step_mfa(hass):
|
||||
async def test_step_mfa(hass: HomeAssistant) -> None:
|
||||
"""Test that the MFA step works."""
|
||||
conf = {CONF_USERNAME: "user@email.com", CONF_PASSWORD: "password"}
|
||||
|
||||
@ -141,7 +156,7 @@ async def test_step_mfa(hass):
|
||||
}
|
||||
|
||||
|
||||
async def test_step_reauth(hass):
|
||||
async def test_step_reauth(hass: HomeAssistant) -> None:
|
||||
"""Test the reauth flow."""
|
||||
conf = {CONF_USERNAME: "user@email.com", CONF_PASSWORD: "password"}
|
||||
|
||||
|
@ -10,6 +10,7 @@ from homeassistant.const import (
|
||||
SERVICE_OPEN_COVER,
|
||||
STATE_CLOSED,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import setup_platform
|
||||
@ -17,7 +18,7 @@ from .common import setup_platform
|
||||
DEVICE_ID = "cover.garage_door"
|
||||
|
||||
|
||||
async def test_entity_registry(hass):
|
||||
async def test_entity_registry(hass: HomeAssistant) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, COVER_DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
@ -26,7 +27,7 @@ async def test_entity_registry(hass):
|
||||
assert entry.unique_id == "61cbz3b542d2o33ed2fz02721bda3324"
|
||||
|
||||
|
||||
async def test_attributes(hass):
|
||||
async def test_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test the cover attributes are correct."""
|
||||
await setup_platform(hass, COVER_DOMAIN)
|
||||
|
||||
@ -39,7 +40,7 @@ async def test_attributes(hass):
|
||||
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "Garage Door"
|
||||
|
||||
|
||||
async def test_open(hass):
|
||||
async def test_open(hass: HomeAssistant) -> None:
|
||||
"""Test the cover can be opened."""
|
||||
await setup_platform(hass, COVER_DOMAIN)
|
||||
|
||||
@ -51,7 +52,7 @@ async def test_open(hass):
|
||||
mock_open.assert_called_once()
|
||||
|
||||
|
||||
async def test_close(hass):
|
||||
async def test_close(hass: HomeAssistant) -> None:
|
||||
"""Test the cover can be closed."""
|
||||
await setup_platform(hass, COVER_DOMAIN)
|
||||
|
||||
|
@ -14,11 +14,12 @@ from homeassistant.components.abode import (
|
||||
from homeassistant.components.alarm_control_panel import DOMAIN as ALARM_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import setup_platform
|
||||
|
||||
|
||||
async def test_change_settings(hass):
|
||||
async def test_change_settings(hass: HomeAssistant) -> None:
|
||||
"""Test change_setting service."""
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
|
||||
@ -33,7 +34,7 @@ async def test_change_settings(hass):
|
||||
mock_set_setting.assert_called_once()
|
||||
|
||||
|
||||
async def test_add_unique_id(hass):
|
||||
async def test_add_unique_id(hass: HomeAssistant) -> None:
|
||||
"""Test unique_id is set to Abode username."""
|
||||
mock_entry = await setup_platform(hass, ALARM_DOMAIN)
|
||||
# Set unique_id to None to match previous config entries
|
||||
@ -49,7 +50,7 @@ async def test_add_unique_id(hass):
|
||||
assert mock_entry.unique_id == mock_entry.data[CONF_USERNAME]
|
||||
|
||||
|
||||
async def test_unload_entry(hass):
|
||||
async def test_unload_entry(hass: HomeAssistant) -> None:
|
||||
"""Test unloading the Abode entry."""
|
||||
mock_entry = await setup_platform(hass, ALARM_DOMAIN)
|
||||
|
||||
@ -65,7 +66,7 @@ async def test_unload_entry(hass):
|
||||
assert not hass.services.has_service(ABODE_DOMAIN, SERVICE_TRIGGER_AUTOMATION)
|
||||
|
||||
|
||||
async def test_invalid_credentials(hass):
|
||||
async def test_invalid_credentials(hass: HomeAssistant) -> None:
|
||||
"""Test Abode credentials changing."""
|
||||
with patch(
|
||||
"homeassistant.components.abode.Abode",
|
||||
@ -81,7 +82,7 @@ async def test_invalid_credentials(hass):
|
||||
mock_async_step_reauth.assert_called_once()
|
||||
|
||||
|
||||
async def test_raise_config_entry_not_ready_when_offline(hass):
|
||||
async def test_raise_config_entry_not_ready_when_offline(hass: HomeAssistant) -> None:
|
||||
"""Config entry state is SETUP_RETRY when abode is offline."""
|
||||
with patch(
|
||||
"homeassistant.components.abode.Abode",
|
||||
|
@ -16,6 +16,7 @@ from homeassistant.const import (
|
||||
SERVICE_TURN_ON,
|
||||
STATE_ON,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import setup_platform
|
||||
@ -23,7 +24,7 @@ from .common import setup_platform
|
||||
DEVICE_ID = "light.living_room_lamp"
|
||||
|
||||
|
||||
async def test_entity_registry(hass):
|
||||
async def test_entity_registry(hass: HomeAssistant) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, LIGHT_DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
@ -32,7 +33,7 @@ async def test_entity_registry(hass):
|
||||
assert entry.unique_id == "741385f4388b2637df4c6b398fe50581"
|
||||
|
||||
|
||||
async def test_attributes(hass):
|
||||
async def test_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test the light attributes are correct."""
|
||||
await setup_platform(hass, LIGHT_DOMAIN)
|
||||
|
||||
@ -49,7 +50,7 @@ async def test_attributes(hass):
|
||||
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 19
|
||||
|
||||
|
||||
async def test_switch_off(hass):
|
||||
async def test_switch_off(hass: HomeAssistant) -> None:
|
||||
"""Test the light can be turned off."""
|
||||
await setup_platform(hass, LIGHT_DOMAIN)
|
||||
|
||||
@ -61,7 +62,7 @@ async def test_switch_off(hass):
|
||||
mock_switch_off.assert_called_once()
|
||||
|
||||
|
||||
async def test_switch_on(hass):
|
||||
async def test_switch_on(hass: HomeAssistant) -> None:
|
||||
"""Test the light can be turned on."""
|
||||
await setup_platform(hass, LIGHT_DOMAIN)
|
||||
|
||||
@ -73,7 +74,7 @@ async def test_switch_on(hass):
|
||||
mock_switch_on.assert_called_once()
|
||||
|
||||
|
||||
async def test_set_brightness(hass):
|
||||
async def test_set_brightness(hass: HomeAssistant) -> None:
|
||||
"""Test the brightness can be set."""
|
||||
await setup_platform(hass, LIGHT_DOMAIN)
|
||||
|
||||
@ -89,7 +90,7 @@ async def test_set_brightness(hass):
|
||||
mock_set_level.assert_called_once_with(39)
|
||||
|
||||
|
||||
async def test_set_color(hass):
|
||||
async def test_set_color(hass: HomeAssistant) -> None:
|
||||
"""Test the color can be set."""
|
||||
await setup_platform(hass, LIGHT_DOMAIN)
|
||||
|
||||
@ -104,7 +105,7 @@ async def test_set_color(hass):
|
||||
mock_set_color.assert_called_once_with((240.0, 100.0))
|
||||
|
||||
|
||||
async def test_set_color_temp(hass):
|
||||
async def test_set_color_temp(hass: HomeAssistant) -> None:
|
||||
"""Test the color temp can be set."""
|
||||
await setup_platform(hass, LIGHT_DOMAIN)
|
||||
|
||||
|
@ -10,6 +10,7 @@ from homeassistant.const import (
|
||||
SERVICE_UNLOCK,
|
||||
STATE_LOCKED,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import setup_platform
|
||||
@ -17,7 +18,7 @@ from .common import setup_platform
|
||||
DEVICE_ID = "lock.test_lock"
|
||||
|
||||
|
||||
async def test_entity_registry(hass):
|
||||
async def test_entity_registry(hass: HomeAssistant) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, LOCK_DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
@ -26,7 +27,7 @@ async def test_entity_registry(hass):
|
||||
assert entry.unique_id == "51cab3b545d2o34ed7fz02731bda5324"
|
||||
|
||||
|
||||
async def test_attributes(hass):
|
||||
async def test_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test the lock attributes are correct."""
|
||||
await setup_platform(hass, LOCK_DOMAIN)
|
||||
|
||||
@ -39,7 +40,7 @@ async def test_attributes(hass):
|
||||
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "Test Lock"
|
||||
|
||||
|
||||
async def test_lock(hass):
|
||||
async def test_lock(hass: HomeAssistant) -> None:
|
||||
"""Test the lock can be locked."""
|
||||
await setup_platform(hass, LOCK_DOMAIN)
|
||||
|
||||
@ -51,7 +52,7 @@ async def test_lock(hass):
|
||||
mock_lock.assert_called_once()
|
||||
|
||||
|
||||
async def test_unlock(hass):
|
||||
async def test_unlock(hass: HomeAssistant) -> None:
|
||||
"""Test the lock can be unlocked."""
|
||||
await setup_platform(hass, LOCK_DOMAIN)
|
||||
|
||||
|
@ -8,12 +8,13 @@ from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import setup_platform
|
||||
|
||||
|
||||
async def test_entity_registry(hass):
|
||||
async def test_entity_registry(hass: HomeAssistant) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, SENSOR_DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
@ -22,7 +23,7 @@ async def test_entity_registry(hass):
|
||||
assert entry.unique_id == "13545b21f4bdcd33d9abd461f8443e65-humidity"
|
||||
|
||||
|
||||
async def test_attributes(hass):
|
||||
async def test_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test the sensor attributes are correct."""
|
||||
await setup_platform(hass, SENSOR_DOMAIN)
|
||||
|
||||
|
@ -13,6 +13,7 @@ from homeassistant.const import (
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import setup_platform
|
||||
@ -23,7 +24,7 @@ DEVICE_ID = "switch.test_switch"
|
||||
DEVICE_UID = "0012a4d3614cb7e2b8c9abea31d2fb2a"
|
||||
|
||||
|
||||
async def test_entity_registry(hass):
|
||||
async def test_entity_registry(hass: HomeAssistant) -> None:
|
||||
"""Tests that the devices are registered in the entity registry."""
|
||||
await setup_platform(hass, SWITCH_DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
@ -35,7 +36,7 @@ async def test_entity_registry(hass):
|
||||
assert entry.unique_id == DEVICE_UID
|
||||
|
||||
|
||||
async def test_attributes(hass):
|
||||
async def test_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test the switch attributes are correct."""
|
||||
await setup_platform(hass, SWITCH_DOMAIN)
|
||||
|
||||
@ -43,7 +44,7 @@ async def test_attributes(hass):
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
|
||||
async def test_switch_on(hass):
|
||||
async def test_switch_on(hass: HomeAssistant) -> None:
|
||||
"""Test the switch can be turned on."""
|
||||
await setup_platform(hass, SWITCH_DOMAIN)
|
||||
|
||||
@ -56,7 +57,7 @@ async def test_switch_on(hass):
|
||||
mock_switch_on.assert_called_once()
|
||||
|
||||
|
||||
async def test_switch_off(hass):
|
||||
async def test_switch_off(hass: HomeAssistant) -> None:
|
||||
"""Test the switch can be turned off."""
|
||||
await setup_platform(hass, SWITCH_DOMAIN)
|
||||
|
||||
@ -69,7 +70,7 @@ async def test_switch_off(hass):
|
||||
mock_switch_off.assert_called_once()
|
||||
|
||||
|
||||
async def test_automation_attributes(hass):
|
||||
async def test_automation_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test the automation attributes are correct."""
|
||||
await setup_platform(hass, SWITCH_DOMAIN)
|
||||
|
||||
@ -78,7 +79,7 @@ async def test_automation_attributes(hass):
|
||||
assert state.state == STATE_ON
|
||||
|
||||
|
||||
async def test_turn_automation_off(hass):
|
||||
async def test_turn_automation_off(hass: HomeAssistant) -> None:
|
||||
"""Test the automation can be turned off."""
|
||||
with patch("abodepy.AbodeAutomation.enable") as mock_trigger:
|
||||
await setup_platform(hass, SWITCH_DOMAIN)
|
||||
@ -94,7 +95,7 @@ async def test_turn_automation_off(hass):
|
||||
mock_trigger.assert_called_once_with(False)
|
||||
|
||||
|
||||
async def test_turn_automation_on(hass):
|
||||
async def test_turn_automation_on(hass: HomeAssistant) -> None:
|
||||
"""Test the automation can be turned on."""
|
||||
with patch("abodepy.AbodeAutomation.enable") as mock_trigger:
|
||||
await setup_platform(hass, SWITCH_DOMAIN)
|
||||
@ -110,7 +111,7 @@ async def test_turn_automation_on(hass):
|
||||
mock_trigger.assert_called_once_with(True)
|
||||
|
||||
|
||||
async def test_trigger_automation(hass, requests_mock):
|
||||
async def test_trigger_automation(hass: HomeAssistant) -> None:
|
||||
"""Test the trigger automation service."""
|
||||
await setup_platform(hass, SWITCH_DOMAIN)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user