mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 23:57:06 +00:00
Merge pull request #59397 from home-assistant/rc
This commit is contained in:
commit
435f278053
@ -3,7 +3,7 @@
|
|||||||
"name": "Flux LED/MagicHome",
|
"name": "Flux LED/MagicHome",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/flux_led",
|
"documentation": "https://www.home-assistant.io/integrations/flux_led",
|
||||||
"requirements": ["flux_led==0.24.14"],
|
"requirements": ["flux_led==0.24.17"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"codeowners": ["@icemanch"],
|
"codeowners": ["@icemanch"],
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
|
@ -370,7 +370,7 @@ class FritzBoxTools:
|
|||||||
device_reg = async_get(self.hass)
|
device_reg = async_get(self.hass)
|
||||||
device_list = async_entries_for_config_entry(device_reg, config_entry.entry_id)
|
device_list = async_entries_for_config_entry(device_reg, config_entry.entry_id)
|
||||||
for device_entry in device_list:
|
for device_entry in device_list:
|
||||||
if async_entries_for_device(
|
if not async_entries_for_device(
|
||||||
entity_reg,
|
entity_reg,
|
||||||
device_entry.id,
|
device_entry.id,
|
||||||
include_disabled_entities=True,
|
include_disabled_entities=True,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Home Assistant Frontend",
|
"name": "Home Assistant Frontend",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"home-assistant-frontend==20211103.0"
|
"home-assistant-frontend==20211108.0"
|
||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"api",
|
"api",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Elexa Guardian",
|
"name": "Elexa Guardian",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/guardian",
|
"documentation": "https://www.home-assistant.io/integrations/guardian",
|
||||||
"requirements": ["aioguardian==1.0.8"],
|
"requirements": ["aioguardian==2021.11.0"],
|
||||||
"zeroconf": ["_api._udp.local."],
|
"zeroconf": ["_api._udp.local."],
|
||||||
"codeowners": ["@bachya"],
|
"codeowners": ["@bachya"],
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "MQTT",
|
"name": "MQTT",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/mqtt",
|
"documentation": "https://www.home-assistant.io/integrations/mqtt",
|
||||||
"requirements": ["paho-mqtt==1.5.1"],
|
"requirements": ["paho-mqtt==1.6.1"],
|
||||||
"dependencies": ["http"],
|
"dependencies": ["http"],
|
||||||
"codeowners": ["@emontnemery"],
|
"codeowners": ["@emontnemery"],
|
||||||
"iot_class": "local_push"
|
"iot_class": "local_push"
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
"""Support for ReCollect Waste sensors."""
|
"""Support for ReCollect Waste sensors."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import date, datetime, time
|
|
||||||
|
|
||||||
from aiorecollect.client import PickupType
|
from aiorecollect.client import PickupType
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity
|
from homeassistant.components.sensor import SensorEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import ATTR_ATTRIBUTION, CONF_FRIENDLY_NAME, DEVICE_CLASS_DATE
|
||||||
ATTR_ATTRIBUTION,
|
|
||||||
CONF_FRIENDLY_NAME,
|
|
||||||
DEVICE_CLASS_TIMESTAMP,
|
|
||||||
)
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
@ -19,7 +13,6 @@ from homeassistant.helpers.update_coordinator import (
|
|||||||
CoordinatorEntity,
|
CoordinatorEntity,
|
||||||
DataUpdateCoordinator,
|
DataUpdateCoordinator,
|
||||||
)
|
)
|
||||||
from homeassistant.util.dt import as_utc
|
|
||||||
|
|
||||||
from .const import CONF_PLACE_ID, CONF_SERVICE_ID, DATA_COORDINATOR, DOMAIN
|
from .const import CONF_PLACE_ID, CONF_SERVICE_ID, DATA_COORDINATOR, DOMAIN
|
||||||
|
|
||||||
@ -47,12 +40,6 @@ def async_get_pickup_type_names(
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_get_utc_midnight(target_date: date) -> datetime:
|
|
||||||
"""Get UTC midnight for a given date."""
|
|
||||||
return as_utc(datetime.combine(target_date, time(0)))
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -64,7 +51,7 @@ async def async_setup_entry(
|
|||||||
class ReCollectWasteSensor(CoordinatorEntity, SensorEntity):
|
class ReCollectWasteSensor(CoordinatorEntity, SensorEntity):
|
||||||
"""ReCollect Waste Sensor."""
|
"""ReCollect Waste Sensor."""
|
||||||
|
|
||||||
_attr_device_class = DEVICE_CLASS_TIMESTAMP
|
_attr_device_class = DEVICE_CLASS_DATE
|
||||||
|
|
||||||
def __init__(self, coordinator: DataUpdateCoordinator, entry: ConfigEntry) -> None:
|
def __init__(self, coordinator: DataUpdateCoordinator, entry: ConfigEntry) -> None:
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
@ -91,8 +78,13 @@ class ReCollectWasteSensor(CoordinatorEntity, SensorEntity):
|
|||||||
@callback
|
@callback
|
||||||
def update_from_latest_data(self) -> None:
|
def update_from_latest_data(self) -> None:
|
||||||
"""Update the state."""
|
"""Update the state."""
|
||||||
pickup_event = self.coordinator.data[0]
|
try:
|
||||||
next_pickup_event = self.coordinator.data[1]
|
pickup_event = self.coordinator.data[0]
|
||||||
|
next_pickup_event = self.coordinator.data[1]
|
||||||
|
except IndexError:
|
||||||
|
self._attr_native_value = None
|
||||||
|
self._attr_extra_state_attributes = {}
|
||||||
|
return
|
||||||
|
|
||||||
self._attr_extra_state_attributes.update(
|
self._attr_extra_state_attributes.update(
|
||||||
{
|
{
|
||||||
@ -103,9 +95,7 @@ class ReCollectWasteSensor(CoordinatorEntity, SensorEntity):
|
|||||||
ATTR_NEXT_PICKUP_TYPES: async_get_pickup_type_names(
|
ATTR_NEXT_PICKUP_TYPES: async_get_pickup_type_names(
|
||||||
self._entry, next_pickup_event.pickup_types
|
self._entry, next_pickup_event.pickup_types
|
||||||
),
|
),
|
||||||
ATTR_NEXT_PICKUP_DATE: async_get_utc_midnight(
|
ATTR_NEXT_PICKUP_DATE: next_pickup_event.date.isoformat(),
|
||||||
next_pickup_event.date
|
|
||||||
).isoformat(),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self._attr_native_value = async_get_utc_midnight(pickup_event.date).isoformat()
|
self._attr_native_value = pickup_event.date.isoformat()
|
||||||
|
@ -282,9 +282,6 @@ class ShellyBlockEntity(entity.Entity):
|
|||||||
self.wrapper = wrapper
|
self.wrapper = wrapper
|
||||||
self.block = block
|
self.block = block
|
||||||
self._name = get_block_entity_name(wrapper.device, block)
|
self._name = get_block_entity_name(wrapper.device, block)
|
||||||
self._attr_device_info = DeviceInfo(
|
|
||||||
connections={(device_registry.CONNECTION_NETWORK_MAC, wrapper.mac)}
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
@ -296,6 +293,13 @@ class ShellyBlockEntity(entity.Entity):
|
|||||||
"""If device should be polled."""
|
"""If device should be polled."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self) -> DeviceInfo:
|
||||||
|
"""Device info."""
|
||||||
|
return {
|
||||||
|
"connections": {(device_registry.CONNECTION_NETWORK_MAC, self.wrapper.mac)}
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Available."""
|
"""Available."""
|
||||||
@ -344,9 +348,9 @@ class ShellyRpcEntity(entity.Entity):
|
|||||||
self.wrapper = wrapper
|
self.wrapper = wrapper
|
||||||
self.key = key
|
self.key = key
|
||||||
self._attr_should_poll = False
|
self._attr_should_poll = False
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = {
|
||||||
connections={(device_registry.CONNECTION_NETWORK_MAC, wrapper.mac)}
|
"connections": {(device_registry.CONNECTION_NETWORK_MAC, wrapper.mac)}
|
||||||
)
|
}
|
||||||
self._attr_unique_id = f"{wrapper.mac}-{key}"
|
self._attr_unique_id = f"{wrapper.mac}-{key}"
|
||||||
self._attr_name = get_rpc_entity_name(wrapper.device, key)
|
self._attr_name = get_rpc_entity_name(wrapper.device, key)
|
||||||
|
|
||||||
@ -490,15 +494,19 @@ class ShellyRestAttributeEntity(update_coordinator.CoordinatorEntity):
|
|||||||
self.description = description
|
self.description = description
|
||||||
self._name = get_block_entity_name(wrapper.device, None, self.description.name)
|
self._name = get_block_entity_name(wrapper.device, None, self.description.name)
|
||||||
self._last_value = None
|
self._last_value = None
|
||||||
self._attr_device_info = DeviceInfo(
|
|
||||||
connections={(device_registry.CONNECTION_NETWORK_MAC, wrapper.mac)}
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
"""Name of sensor."""
|
"""Name of sensor."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self) -> DeviceInfo:
|
||||||
|
"""Device info."""
|
||||||
|
return {
|
||||||
|
"connections": {(device_registry.CONNECTION_NETWORK_MAC, self.wrapper.mac)}
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def entity_registry_enabled_default(self) -> bool:
|
def entity_registry_enabled_default(self) -> bool:
|
||||||
"""Return if it should be enabled by default."""
|
"""Return if it should be enabled by default."""
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Shelly",
|
"name": "Shelly",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/shelly",
|
"documentation": "https://www.home-assistant.io/integrations/shelly",
|
||||||
"requirements": ["aioshelly==1.0.2"],
|
"requirements": ["aioshelly==1.0.4"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_http._tcp.local.",
|
"type": "_http._tcp.local.",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "shiftr",
|
"domain": "shiftr",
|
||||||
"name": "shiftr.io",
|
"name": "shiftr.io",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/shiftr",
|
"documentation": "https://www.home-assistant.io/integrations/shiftr",
|
||||||
"requirements": ["paho-mqtt==1.5.1"],
|
"requirements": ["paho-mqtt==1.6.1"],
|
||||||
"codeowners": ["@fabaff"],
|
"codeowners": ["@fabaff"],
|
||||||
"iot_class": "cloud_push"
|
"iot_class": "cloud_push"
|
||||||
}
|
}
|
||||||
|
@ -103,9 +103,11 @@ ATTR_TIMESTAMP = "timestamp"
|
|||||||
|
|
||||||
DEFAULT_ENTITY_MODEL = "alarm_control_panel"
|
DEFAULT_ENTITY_MODEL = "alarm_control_panel"
|
||||||
DEFAULT_ENTITY_NAME = "Alarm Control Panel"
|
DEFAULT_ENTITY_NAME = "Alarm Control Panel"
|
||||||
|
DEFAULT_REST_API_ERROR_COUNT = 2
|
||||||
DEFAULT_SCAN_INTERVAL = timedelta(seconds=30)
|
DEFAULT_SCAN_INTERVAL = timedelta(seconds=30)
|
||||||
DEFAULT_SOCKET_MIN_RETRY = 15
|
DEFAULT_SOCKET_MIN_RETRY = 15
|
||||||
|
|
||||||
|
|
||||||
DISPATCHER_TOPIC_WEBSOCKET_EVENT = "simplisafe_websocket_event_{0}"
|
DISPATCHER_TOPIC_WEBSOCKET_EVENT = "simplisafe_websocket_event_{0}"
|
||||||
|
|
||||||
EVENT_SIMPLISAFE_EVENT = "SIMPLISAFE_EVENT"
|
EVENT_SIMPLISAFE_EVENT = "SIMPLISAFE_EVENT"
|
||||||
@ -556,6 +558,8 @@ class SimpliSafeEntity(CoordinatorEntity):
|
|||||||
assert simplisafe.coordinator
|
assert simplisafe.coordinator
|
||||||
super().__init__(simplisafe.coordinator)
|
super().__init__(simplisafe.coordinator)
|
||||||
|
|
||||||
|
self._rest_api_errors = 0
|
||||||
|
|
||||||
if device:
|
if device:
|
||||||
model = device.type.name
|
model = device.type.name
|
||||||
device_name = device.name
|
device_name = device.name
|
||||||
@ -618,11 +622,24 @@ class SimpliSafeEntity(CoordinatorEntity):
|
|||||||
else:
|
else:
|
||||||
system_offline = False
|
system_offline = False
|
||||||
|
|
||||||
return super().available and self._online and not system_offline
|
return (
|
||||||
|
self._rest_api_errors < DEFAULT_REST_API_ERROR_COUNT
|
||||||
|
and self._online
|
||||||
|
and not system_offline
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _handle_coordinator_update(self) -> None:
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Update the entity with new REST API data."""
|
"""Update the entity with new REST API data."""
|
||||||
|
# SimpliSafe can incorrectly return an error state when there isn't any
|
||||||
|
# error. This can lead to the system having an unknown state frequently.
|
||||||
|
# To protect against that, we measure how many "error states" we receive
|
||||||
|
# and only alter the state if we detect a few in a row:
|
||||||
|
if self.coordinator.last_update_success:
|
||||||
|
self._rest_api_errors = 0
|
||||||
|
else:
|
||||||
|
self._rest_api_errors += 1
|
||||||
|
|
||||||
self.async_update_from_rest_api()
|
self.async_update_from_rest_api()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@ -72,8 +72,6 @@ ATTR_RF_JAMMING = "rf_jamming"
|
|||||||
ATTR_WALL_POWER_LEVEL = "wall_power_level"
|
ATTR_WALL_POWER_LEVEL = "wall_power_level"
|
||||||
ATTR_WIFI_STRENGTH = "wifi_strength"
|
ATTR_WIFI_STRENGTH = "wifi_strength"
|
||||||
|
|
||||||
DEFAULT_ERRORS_TO_ACCOMMODATE = 2
|
|
||||||
|
|
||||||
VOLUME_STRING_MAP = {
|
VOLUME_STRING_MAP = {
|
||||||
VOLUME_HIGH: "high",
|
VOLUME_HIGH: "high",
|
||||||
VOLUME_LOW: "low",
|
VOLUME_LOW: "low",
|
||||||
@ -141,8 +139,6 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
|
|||||||
additional_websocket_events=WEBSOCKET_EVENTS_TO_LISTEN_FOR,
|
additional_websocket_events=WEBSOCKET_EVENTS_TO_LISTEN_FOR,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._errors = 0
|
|
||||||
|
|
||||||
if code := self._simplisafe.entry.options.get(CONF_CODE):
|
if code := self._simplisafe.entry.options.get(CONF_CODE):
|
||||||
if code.isdigit():
|
if code.isdigit():
|
||||||
self._attr_code_format = FORMAT_NUMBER
|
self._attr_code_format = FORMAT_NUMBER
|
||||||
@ -249,19 +245,6 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# SimpliSafe can incorrectly return an error state when there isn't any
|
|
||||||
# error. This can lead to the system having an unknown state frequently.
|
|
||||||
# To protect against that, we measure how many "error states" we receive
|
|
||||||
# and only alter the state if we detect a few in a row:
|
|
||||||
if self._system.state == SystemStates.error:
|
|
||||||
if self._errors > DEFAULT_ERRORS_TO_ACCOMMODATE:
|
|
||||||
self._attr_state = None
|
|
||||||
else:
|
|
||||||
self._errors += 1
|
|
||||||
return
|
|
||||||
|
|
||||||
self._errors = 0
|
|
||||||
|
|
||||||
self._set_state_from_system_data()
|
self._set_state_from_system_data()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -102,18 +102,18 @@ class SegmentBuffer:
|
|||||||
# The LL-HLS spec allows for a fragment's duration to be within the range [0.85x,1.0x]
|
# The LL-HLS spec allows for a fragment's duration to be within the range [0.85x,1.0x]
|
||||||
# of the part target duration. We use the frag_duration option to tell ffmpeg to try to
|
# of the part target duration. We use the frag_duration option to tell ffmpeg to try to
|
||||||
# cut the fragments when they reach frag_duration. However, the resulting fragments can
|
# cut the fragments when they reach frag_duration. However, the resulting fragments can
|
||||||
# have variability in their durations and can end up being too short or too long. If
|
# have variability in their durations and can end up being too short or too long. With a
|
||||||
# there are two tracks, as in the case of a video feed with audio, the fragment cut seems
|
|
||||||
# to be done on the first track that crosses the desired threshold, and cutting on the
|
|
||||||
# audio track may result in a shorter video fragment than desired. Conversely, with a
|
|
||||||
# video track with no audio, the discrete nature of frames means that the frame at the
|
# video track with no audio, the discrete nature of frames means that the frame at the
|
||||||
# end of a fragment will sometimes extend slightly beyond the desired frag_duration.
|
# end of a fragment will sometimes extend slightly beyond the desired frag_duration.
|
||||||
# Given this, our approach is to use a frag_duration near the upper end of the range for
|
# If there are two tracks, as in the case of a video feed with audio, there is an added
|
||||||
# outputs with audio using a frag_duration at the lower end of the range for outputs with
|
# wrinkle as the fragment cut seems to be done on the first track that crosses the desired
|
||||||
# only video.
|
# threshold, and cutting on the audio track may also result in a shorter video fragment
|
||||||
|
# than desired.
|
||||||
|
# Given this, our approach is to give ffmpeg a frag_duration somewhere in the middle
|
||||||
|
# of the range, hoping that the parts stay pretty well bounded, and we adjust the part
|
||||||
|
# durations a bit in the hls metadata so that everything "looks" ok.
|
||||||
"frag_duration": str(
|
"frag_duration": str(
|
||||||
self._stream_settings.part_target_duration
|
self._stream_settings.part_target_duration * 9e5
|
||||||
* (98e4 if add_audio else 9e5)
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
if self._stream_settings.ll_hls
|
if self._stream_settings.ll_hls
|
||||||
|
@ -233,7 +233,7 @@ async def async_setup_entry( # noqa: C901
|
|||||||
surveillance_station = api.surveillance_station
|
surveillance_station = api.surveillance_station
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with async_timeout.timeout(10):
|
async with async_timeout.timeout(30):
|
||||||
await hass.async_add_executor_job(surveillance_station.update)
|
await hass.async_add_executor_job(surveillance_station.update)
|
||||||
except SynologyDSMAPIErrorException as err:
|
except SynologyDSMAPIErrorException as err:
|
||||||
raise UpdateFailed(f"Error communicating with API: {err}") from err
|
raise UpdateFailed(f"Error communicating with API: {err}") from err
|
||||||
|
@ -38,7 +38,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
TotalConnectClient, username, password, usercodes
|
TotalConnectClient, username, password, usercodes
|
||||||
)
|
)
|
||||||
|
|
||||||
if not client.is_valid_credentials():
|
if not client.is_logged_in():
|
||||||
raise ConfigEntryAuthFailed("TotalConnect authentication failed")
|
raise ConfigEntryAuthFailed("TotalConnect authentication failed")
|
||||||
|
|
||||||
coordinator = TotalConnectDataUpdateCoordinator(hass, client)
|
coordinator = TotalConnectDataUpdateCoordinator(hass, client)
|
||||||
@ -88,5 +88,3 @@ class TotalConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
raise UpdateFailed(exception) from exception
|
raise UpdateFailed(exception) from exception
|
||||||
except ValueError as exception:
|
except ValueError as exception:
|
||||||
raise UpdateFailed("Unknown state from TotalConnect") from exception
|
raise UpdateFailed("Unknown state from TotalConnect") from exception
|
||||||
|
|
||||||
return True
|
|
||||||
|
@ -40,7 +40,7 @@ class TotalConnectConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
TotalConnectClient, username, password, None
|
TotalConnectClient, username, password, None
|
||||||
)
|
)
|
||||||
|
|
||||||
if client.is_valid_credentials():
|
if client.is_logged_in():
|
||||||
# username/password valid so show user locations
|
# username/password valid so show user locations
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
@ -136,7 +136,7 @@ class TotalConnectConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self.usercodes,
|
self.usercodes,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not client.is_valid_credentials():
|
if not client.is_logged_in():
|
||||||
errors["base"] = "invalid_auth"
|
errors["base"] = "invalid_auth"
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="reauth_confirm",
|
step_id="reauth_confirm",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "totalconnect",
|
"domain": "totalconnect",
|
||||||
"name": "Total Connect",
|
"name": "Total Connect",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/totalconnect",
|
"documentation": "https://www.home-assistant.io/integrations/totalconnect",
|
||||||
"requirements": ["total_connect_client==2021.8.3"],
|
"requirements": ["total_connect_client==2021.11.2"],
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@austinmroczek"],
|
"codeowners": ["@austinmroczek"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
@ -60,7 +60,6 @@ class TradfriBaseClass(Entity):
|
|||||||
"""Initialize a device."""
|
"""Initialize a device."""
|
||||||
self._api = handle_error(api)
|
self._api = handle_error(api)
|
||||||
self._attr_name = device.name
|
self._attr_name = device.name
|
||||||
self._attr_available = device.reachable
|
|
||||||
self._device: Device = device
|
self._device: Device = device
|
||||||
self._device_control: BlindControl | LightControl | SocketControl | SignalRepeaterControl | AirPurifierControl | None = (
|
self._device_control: BlindControl | LightControl | SocketControl | SignalRepeaterControl | AirPurifierControl | None = (
|
||||||
None
|
None
|
||||||
@ -105,7 +104,6 @@ class TradfriBaseClass(Entity):
|
|||||||
"""Refresh the device data."""
|
"""Refresh the device data."""
|
||||||
self._device = device
|
self._device = device
|
||||||
self._attr_name = device.name
|
self._attr_name = device.name
|
||||||
self._attr_available = device.reachable
|
|
||||||
if write_ha:
|
if write_ha:
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -116,6 +114,16 @@ class TradfriBaseDevice(TradfriBaseClass):
|
|||||||
All devices should inherit from this class.
|
All devices should inherit from this class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
device: Device,
|
||||||
|
api: Callable[[Command | list[Command]], Any],
|
||||||
|
gateway_id: str,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize a device."""
|
||||||
|
self._attr_available = device.reachable
|
||||||
|
super().__init__(device, api, gateway_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self) -> DeviceInfo:
|
def device_info(self) -> DeviceInfo:
|
||||||
"""Return the device info."""
|
"""Return the device info."""
|
||||||
@ -128,3 +136,11 @@ class TradfriBaseDevice(TradfriBaseClass):
|
|||||||
sw_version=info.firmware_version,
|
sw_version=info.firmware_version,
|
||||||
via_device=(DOMAIN, self._gateway_id),
|
via_device=(DOMAIN, self._gateway_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _refresh(self, device: Device, write_ha: bool = True) -> None:
|
||||||
|
"""Refresh the device data."""
|
||||||
|
# The base class _refresh cannot be used, because
|
||||||
|
# there are devices (group) that do not have .reachable
|
||||||
|
# so set _attr_available here and let the base class do the rest.
|
||||||
|
self._attr_available = device.reachable
|
||||||
|
super()._refresh(device, write_ha)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "velbus",
|
"domain": "velbus",
|
||||||
"name": "Velbus",
|
"name": "Velbus",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/velbus",
|
"documentation": "https://www.home-assistant.io/integrations/velbus",
|
||||||
"requirements": ["velbus-aio==2021.11.0"],
|
"requirements": ["velbus-aio==2021.11.6"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"codeowners": ["@Cereal2nd", "@brefra"],
|
"codeowners": ["@Cereal2nd", "@brefra"],
|
||||||
"iot_class": "local_push"
|
"iot_class": "local_push"
|
||||||
|
@ -66,6 +66,8 @@ from .const import (
|
|||||||
MODELS_PURIFIER_MIOT,
|
MODELS_PURIFIER_MIOT,
|
||||||
MODELS_SWITCH,
|
MODELS_SWITCH,
|
||||||
MODELS_VACUUM,
|
MODELS_VACUUM,
|
||||||
|
ROBOROCK_GENERIC,
|
||||||
|
ROCKROBO_GENERIC,
|
||||||
AuthException,
|
AuthException,
|
||||||
SetupException,
|
SetupException,
|
||||||
)
|
)
|
||||||
@ -267,7 +269,7 @@ async def async_create_miio_device_and_coordinator(
|
|||||||
hass: core.HomeAssistant, entry: config_entries.ConfigEntry
|
hass: core.HomeAssistant, entry: config_entries.ConfigEntry
|
||||||
):
|
):
|
||||||
"""Set up a data coordinator and one miio device to service multiple entities."""
|
"""Set up a data coordinator and one miio device to service multiple entities."""
|
||||||
model = entry.data[CONF_MODEL]
|
model: str = entry.data[CONF_MODEL]
|
||||||
host = entry.data[CONF_HOST]
|
host = entry.data[CONF_HOST]
|
||||||
token = entry.data[CONF_TOKEN]
|
token = entry.data[CONF_TOKEN]
|
||||||
name = entry.title
|
name = entry.title
|
||||||
@ -280,6 +282,8 @@ async def async_create_miio_device_and_coordinator(
|
|||||||
model not in MODELS_HUMIDIFIER
|
model not in MODELS_HUMIDIFIER
|
||||||
and model not in MODELS_FAN
|
and model not in MODELS_FAN
|
||||||
and model not in MODELS_VACUUM
|
and model not in MODELS_VACUUM
|
||||||
|
and not model.startswith(ROBOROCK_GENERIC)
|
||||||
|
and not model.startswith(ROCKROBO_GENERIC)
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -304,7 +308,11 @@ async def async_create_miio_device_and_coordinator(
|
|||||||
device = AirPurifier(host, token)
|
device = AirPurifier(host, token)
|
||||||
elif model.startswith("zhimi.airfresh."):
|
elif model.startswith("zhimi.airfresh."):
|
||||||
device = AirFresh(host, token)
|
device = AirFresh(host, token)
|
||||||
elif model in MODELS_VACUUM:
|
elif (
|
||||||
|
model in MODELS_VACUUM
|
||||||
|
or model.startswith(ROBOROCK_GENERIC)
|
||||||
|
or model.startswith(ROCKROBO_GENERIC)
|
||||||
|
):
|
||||||
device = Vacuum(host, token)
|
device = Vacuum(host, token)
|
||||||
update_method = _async_update_data_vacuum
|
update_method = _async_update_data_vacuum
|
||||||
coordinator_class = DataUpdateCoordinator[VacuumCoordinatorData]
|
coordinator_class = DataUpdateCoordinator[VacuumCoordinatorData]
|
||||||
|
@ -202,7 +202,8 @@ ROCKROBO_S4_MAX = "roborock.vacuum.a19"
|
|||||||
ROCKROBO_S5_MAX = "roborock.vacuum.s5e"
|
ROCKROBO_S5_MAX = "roborock.vacuum.s5e"
|
||||||
ROCKROBO_S6_PURE = "roborock.vacuum.a08"
|
ROCKROBO_S6_PURE = "roborock.vacuum.a08"
|
||||||
ROCKROBO_E2 = "roborock.vacuum.e2"
|
ROCKROBO_E2 = "roborock.vacuum.e2"
|
||||||
ROCKROBO_GENERIC = "roborock.vacuum"
|
ROBOROCK_GENERIC = "roborock.vacuum"
|
||||||
|
ROCKROBO_GENERIC = "rockrobo.vacuum"
|
||||||
MODELS_VACUUM = [
|
MODELS_VACUUM = [
|
||||||
ROCKROBO_V1,
|
ROCKROBO_V1,
|
||||||
ROCKROBO_E2,
|
ROCKROBO_E2,
|
||||||
@ -214,6 +215,7 @@ MODELS_VACUUM = [
|
|||||||
ROCKROBO_S6_MAXV,
|
ROCKROBO_S6_MAXV,
|
||||||
ROCKROBO_S6_PURE,
|
ROCKROBO_S6_PURE,
|
||||||
ROCKROBO_S7,
|
ROCKROBO_S7,
|
||||||
|
ROBOROCK_GENERIC,
|
||||||
ROCKROBO_GENERIC,
|
ROCKROBO_GENERIC,
|
||||||
]
|
]
|
||||||
MODELS_VACUUM_WITH_MOP = [
|
MODELS_VACUUM_WITH_MOP = [
|
||||||
|
@ -81,6 +81,8 @@ from .const import (
|
|||||||
MODELS_PURIFIER_MIIO,
|
MODELS_PURIFIER_MIIO,
|
||||||
MODELS_PURIFIER_MIOT,
|
MODELS_PURIFIER_MIOT,
|
||||||
MODELS_VACUUM,
|
MODELS_VACUUM,
|
||||||
|
ROBOROCK_GENERIC,
|
||||||
|
ROCKROBO_GENERIC,
|
||||||
)
|
)
|
||||||
from .device import XiaomiCoordinatedMiioEntity, XiaomiMiioEntity
|
from .device import XiaomiCoordinatedMiioEntity, XiaomiMiioEntity
|
||||||
from .gateway import XiaomiGatewayDevice
|
from .gateway import XiaomiGatewayDevice
|
||||||
@ -374,7 +376,6 @@ AIRFRESH_SENSORS = (
|
|||||||
ATTR_FILTER_LIFE_REMAINING,
|
ATTR_FILTER_LIFE_REMAINING,
|
||||||
ATTR_FILTER_USE,
|
ATTR_FILTER_USE,
|
||||||
ATTR_HUMIDITY,
|
ATTR_HUMIDITY,
|
||||||
ATTR_ILLUMINANCE_LUX,
|
|
||||||
ATTR_PM25,
|
ATTR_PM25,
|
||||||
ATTR_TEMPERATURE,
|
ATTR_TEMPERATURE,
|
||||||
ATTR_USE_TIME,
|
ATTR_USE_TIME,
|
||||||
@ -593,7 +594,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
elif config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
|
elif config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
|
||||||
host = config_entry.data[CONF_HOST]
|
host = config_entry.data[CONF_HOST]
|
||||||
token = config_entry.data[CONF_TOKEN]
|
token = config_entry.data[CONF_TOKEN]
|
||||||
model = config_entry.data[CONF_MODEL]
|
model: str = config_entry.data[CONF_MODEL]
|
||||||
|
|
||||||
if model in (MODEL_FAN_ZA1, MODEL_FAN_ZA3, MODEL_FAN_ZA4, MODEL_FAN_P5):
|
if model in (MODEL_FAN_ZA1, MODEL_FAN_ZA3, MODEL_FAN_ZA4, MODEL_FAN_P5):
|
||||||
return
|
return
|
||||||
@ -625,7 +626,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
sensors = PURIFIER_MIIO_SENSORS
|
sensors = PURIFIER_MIIO_SENSORS
|
||||||
elif model in MODELS_PURIFIER_MIOT:
|
elif model in MODELS_PURIFIER_MIOT:
|
||||||
sensors = PURIFIER_MIOT_SENSORS
|
sensors = PURIFIER_MIOT_SENSORS
|
||||||
elif model in MODELS_VACUUM:
|
elif (
|
||||||
|
model in MODELS_VACUUM
|
||||||
|
or model.startswith(ROBOROCK_GENERIC)
|
||||||
|
or model.startswith(ROCKROBO_GENERIC)
|
||||||
|
):
|
||||||
return _setup_vacuum_sensors(hass, config_entry, async_add_entities)
|
return _setup_vacuum_sensors(hass, config_entry, async_add_entities)
|
||||||
|
|
||||||
for sensor, description in SENSOR_TYPES.items():
|
for sensor, description in SENSOR_TYPES.items():
|
||||||
|
@ -226,6 +226,22 @@ class Battery(Sensor):
|
|||||||
_unit = PERCENTAGE
|
_unit = PERCENTAGE
|
||||||
_attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC
|
_attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_entity(
|
||||||
|
cls,
|
||||||
|
unique_id: str,
|
||||||
|
zha_device: ZhaDeviceType,
|
||||||
|
channels: list[ChannelType],
|
||||||
|
**kwargs,
|
||||||
|
) -> ZhaEntity | None:
|
||||||
|
"""Entity Factory.
|
||||||
|
|
||||||
|
Unlike any other entity, PowerConfiguration cluster may not support
|
||||||
|
battery_percent_remaining attribute, but zha-device-handlers takes care of it
|
||||||
|
so create the entity regardless
|
||||||
|
"""
|
||||||
|
return cls(unique_id, zha_device, channels, **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def formatter(value: int) -> int:
|
def formatter(value: int) -> int:
|
||||||
"""Return the state of the entity."""
|
"""Return the state of the entity."""
|
||||||
|
@ -5,7 +5,7 @@ from typing import Final
|
|||||||
|
|
||||||
MAJOR_VERSION: Final = 2021
|
MAJOR_VERSION: Final = 2021
|
||||||
MINOR_VERSION: Final = 11
|
MINOR_VERSION: Final = 11
|
||||||
PATCH_VERSION: Final = "1"
|
PATCH_VERSION: Final = "2"
|
||||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)
|
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)
|
||||||
|
@ -15,11 +15,11 @@ ciso8601==2.2.0
|
|||||||
cryptography==3.4.8
|
cryptography==3.4.8
|
||||||
emoji==1.5.0
|
emoji==1.5.0
|
||||||
hass-nabucasa==0.50.0
|
hass-nabucasa==0.50.0
|
||||||
home-assistant-frontend==20211103.0
|
home-assistant-frontend==20211108.0
|
||||||
httpx==0.19.0
|
httpx==0.19.0
|
||||||
ifaddr==0.1.7
|
ifaddr==0.1.7
|
||||||
jinja2==3.0.2
|
jinja2==3.0.2
|
||||||
paho-mqtt==1.5.1
|
paho-mqtt==1.6.1
|
||||||
pillow==8.2.0
|
pillow==8.2.0
|
||||||
pip>=8.0.3,<20.3
|
pip>=8.0.3,<20.3
|
||||||
pyserial==3.5
|
pyserial==3.5
|
||||||
|
@ -173,7 +173,7 @@ aioftp==0.12.0
|
|||||||
aiogithubapi==21.8.0
|
aiogithubapi==21.8.0
|
||||||
|
|
||||||
# homeassistant.components.guardian
|
# homeassistant.components.guardian
|
||||||
aioguardian==1.0.8
|
aioguardian==2021.11.0
|
||||||
|
|
||||||
# homeassistant.components.harmony
|
# homeassistant.components.harmony
|
||||||
aioharmony==0.2.8
|
aioharmony==0.2.8
|
||||||
@ -243,7 +243,7 @@ aiopylgtv==0.4.0
|
|||||||
aiorecollect==1.0.8
|
aiorecollect==1.0.8
|
||||||
|
|
||||||
# homeassistant.components.shelly
|
# homeassistant.components.shelly
|
||||||
aioshelly==1.0.2
|
aioshelly==1.0.4
|
||||||
|
|
||||||
# homeassistant.components.switcher_kis
|
# homeassistant.components.switcher_kis
|
||||||
aioswitcher==2.0.6
|
aioswitcher==2.0.6
|
||||||
@ -652,7 +652,7 @@ fjaraskupan==1.0.2
|
|||||||
flipr-api==1.4.1
|
flipr-api==1.4.1
|
||||||
|
|
||||||
# homeassistant.components.flux_led
|
# homeassistant.components.flux_led
|
||||||
flux_led==0.24.14
|
flux_led==0.24.17
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
# homeassistant.components.homekit
|
||||||
fnvhash==0.1.0
|
fnvhash==0.1.0
|
||||||
@ -813,7 +813,7 @@ hole==0.5.1
|
|||||||
holidays==0.11.3.1
|
holidays==0.11.3.1
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20211103.0
|
home-assistant-frontend==20211108.0
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
homeassistant-pyozw==0.1.10
|
homeassistant-pyozw==0.1.10
|
||||||
@ -1163,7 +1163,7 @@ p1monitor==1.0.0
|
|||||||
|
|
||||||
# homeassistant.components.mqtt
|
# homeassistant.components.mqtt
|
||||||
# homeassistant.components.shiftr
|
# homeassistant.components.shiftr
|
||||||
paho-mqtt==1.5.1
|
paho-mqtt==1.6.1
|
||||||
|
|
||||||
# homeassistant.components.panasonic_bluray
|
# homeassistant.components.panasonic_bluray
|
||||||
panacotta==0.1
|
panacotta==0.1
|
||||||
@ -2314,7 +2314,7 @@ todoist-python==8.0.0
|
|||||||
toonapi==0.2.1
|
toonapi==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.totalconnect
|
# homeassistant.components.totalconnect
|
||||||
total_connect_client==2021.8.3
|
total_connect_client==2021.11.2
|
||||||
|
|
||||||
# homeassistant.components.tplink_lte
|
# homeassistant.components.tplink_lte
|
||||||
tp-connected==0.0.4
|
tp-connected==0.0.4
|
||||||
@ -2360,7 +2360,7 @@ uvcclient==0.11.0
|
|||||||
vallox-websocket-api==2.8.1
|
vallox-websocket-api==2.8.1
|
||||||
|
|
||||||
# homeassistant.components.velbus
|
# homeassistant.components.velbus
|
||||||
velbus-aio==2021.11.0
|
velbus-aio==2021.11.6
|
||||||
|
|
||||||
# homeassistant.components.venstar
|
# homeassistant.components.venstar
|
||||||
venstarcolortouch==0.14
|
venstarcolortouch==0.14
|
||||||
|
@ -115,7 +115,7 @@ aioesphomeapi==10.2.0
|
|||||||
aioflo==0.4.1
|
aioflo==0.4.1
|
||||||
|
|
||||||
# homeassistant.components.guardian
|
# homeassistant.components.guardian
|
||||||
aioguardian==1.0.8
|
aioguardian==2021.11.0
|
||||||
|
|
||||||
# homeassistant.components.harmony
|
# homeassistant.components.harmony
|
||||||
aioharmony==0.2.8
|
aioharmony==0.2.8
|
||||||
@ -170,7 +170,7 @@ aiopylgtv==0.4.0
|
|||||||
aiorecollect==1.0.8
|
aiorecollect==1.0.8
|
||||||
|
|
||||||
# homeassistant.components.shelly
|
# homeassistant.components.shelly
|
||||||
aioshelly==1.0.2
|
aioshelly==1.0.4
|
||||||
|
|
||||||
# homeassistant.components.switcher_kis
|
# homeassistant.components.switcher_kis
|
||||||
aioswitcher==2.0.6
|
aioswitcher==2.0.6
|
||||||
@ -387,7 +387,7 @@ fjaraskupan==1.0.2
|
|||||||
flipr-api==1.4.1
|
flipr-api==1.4.1
|
||||||
|
|
||||||
# homeassistant.components.flux_led
|
# homeassistant.components.flux_led
|
||||||
flux_led==0.24.14
|
flux_led==0.24.17
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
# homeassistant.components.homekit
|
||||||
fnvhash==0.1.0
|
fnvhash==0.1.0
|
||||||
@ -500,7 +500,7 @@ hole==0.5.1
|
|||||||
holidays==0.11.3.1
|
holidays==0.11.3.1
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20211103.0
|
home-assistant-frontend==20211108.0
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
homeassistant-pyozw==0.1.10
|
homeassistant-pyozw==0.1.10
|
||||||
@ -689,7 +689,7 @@ p1monitor==1.0.0
|
|||||||
|
|
||||||
# homeassistant.components.mqtt
|
# homeassistant.components.mqtt
|
||||||
# homeassistant.components.shiftr
|
# homeassistant.components.shiftr
|
||||||
paho-mqtt==1.5.1
|
paho-mqtt==1.6.1
|
||||||
|
|
||||||
# homeassistant.components.panasonic_viera
|
# homeassistant.components.panasonic_viera
|
||||||
panasonic_viera==0.3.6
|
panasonic_viera==0.3.6
|
||||||
@ -1330,7 +1330,7 @@ tesla-powerwall==0.3.12
|
|||||||
toonapi==0.2.1
|
toonapi==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.totalconnect
|
# homeassistant.components.totalconnect
|
||||||
total_connect_client==2021.8.3
|
total_connect_client==2021.11.2
|
||||||
|
|
||||||
# homeassistant.components.transmission
|
# homeassistant.components.transmission
|
||||||
transmissionrpc==0.11
|
transmissionrpc==0.11
|
||||||
@ -1364,7 +1364,7 @@ url-normalize==1.4.1
|
|||||||
uvcclient==0.11.0
|
uvcclient==0.11.0
|
||||||
|
|
||||||
# homeassistant.components.velbus
|
# homeassistant.components.velbus
|
||||||
velbus-aio==2021.11.0
|
velbus-aio==2021.11.6
|
||||||
|
|
||||||
# homeassistant.components.venstar
|
# homeassistant.components.venstar
|
||||||
venstarcolortouch==0.14
|
venstarcolortouch==0.14
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
"""Common methods used across tests for TotalConnect."""
|
"""Common methods used across tests for TotalConnect."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from total_connect_client.client import TotalConnectClient
|
from total_connect_client import ArmingState, ResultCode, ZoneStatus, ZoneType
|
||||||
from total_connect_client.const import ArmingState
|
|
||||||
from total_connect_client.zone import ZoneStatus, ZoneType
|
|
||||||
|
|
||||||
from homeassistant.components.totalconnect.const import CONF_USERCODES, DOMAIN
|
from homeassistant.components.totalconnect.const import CONF_USERCODES, DOMAIN
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||||
@ -44,7 +42,7 @@ USER = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RESPONSE_AUTHENTICATE = {
|
RESPONSE_AUTHENTICATE = {
|
||||||
"ResultCode": TotalConnectClient.SUCCESS,
|
"ResultCode": ResultCode.SUCCESS.value,
|
||||||
"SessionID": 1,
|
"SessionID": 1,
|
||||||
"Locations": LOCATIONS,
|
"Locations": LOCATIONS,
|
||||||
"ModuleFlags": MODULE_FLAGS,
|
"ModuleFlags": MODULE_FLAGS,
|
||||||
@ -52,7 +50,7 @@ RESPONSE_AUTHENTICATE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RESPONSE_AUTHENTICATE_FAILED = {
|
RESPONSE_AUTHENTICATE_FAILED = {
|
||||||
"ResultCode": TotalConnectClient.BAD_USER_OR_PASSWORD,
|
"ResultCode": ResultCode.BAD_USER_OR_PASSWORD.value,
|
||||||
"ResultData": "test bad authentication",
|
"ResultData": "test bad authentication",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,18 +253,18 @@ RESPONSE_UNKNOWN = {
|
|||||||
"ArmingState": ArmingState.DISARMED,
|
"ArmingState": ArmingState.DISARMED,
|
||||||
}
|
}
|
||||||
|
|
||||||
RESPONSE_ARM_SUCCESS = {"ResultCode": TotalConnectClient.ARM_SUCCESS}
|
RESPONSE_ARM_SUCCESS = {"ResultCode": ResultCode.ARM_SUCCESS.value}
|
||||||
RESPONSE_ARM_FAILURE = {"ResultCode": TotalConnectClient.COMMAND_FAILED}
|
RESPONSE_ARM_FAILURE = {"ResultCode": ResultCode.COMMAND_FAILED.value}
|
||||||
RESPONSE_DISARM_SUCCESS = {"ResultCode": TotalConnectClient.DISARM_SUCCESS}
|
RESPONSE_DISARM_SUCCESS = {"ResultCode": ResultCode.DISARM_SUCCESS.value}
|
||||||
RESPONSE_DISARM_FAILURE = {
|
RESPONSE_DISARM_FAILURE = {
|
||||||
"ResultCode": TotalConnectClient.COMMAND_FAILED,
|
"ResultCode": ResultCode.COMMAND_FAILED.value,
|
||||||
"ResultData": "Command Failed",
|
"ResultData": "Command Failed",
|
||||||
}
|
}
|
||||||
RESPONSE_USER_CODE_INVALID = {
|
RESPONSE_USER_CODE_INVALID = {
|
||||||
"ResultCode": TotalConnectClient.USER_CODE_INVALID,
|
"ResultCode": ResultCode.USER_CODE_INVALID.value,
|
||||||
"ResultData": "testing user code invalid",
|
"ResultData": "testing user code invalid",
|
||||||
}
|
}
|
||||||
RESPONSE_SUCCESS = {"ResultCode": TotalConnectClient.SUCCESS}
|
RESPONSE_SUCCESS = {"ResultCode": ResultCode.SUCCESS.value}
|
||||||
|
|
||||||
USERNAME = "username@me.com"
|
USERNAME = "username@me.com"
|
||||||
PASSWORD = "password"
|
PASSWORD = "password"
|
||||||
@ -292,7 +290,7 @@ PARTITION_DETAILS_2 = {
|
|||||||
|
|
||||||
PARTITION_DETAILS = {"PartitionDetails": [PARTITION_DETAILS_1, PARTITION_DETAILS_2]}
|
PARTITION_DETAILS = {"PartitionDetails": [PARTITION_DETAILS_1, PARTITION_DETAILS_2]}
|
||||||
RESPONSE_PARTITION_DETAILS = {
|
RESPONSE_PARTITION_DETAILS = {
|
||||||
"ResultCode": TotalConnectClient.SUCCESS,
|
"ResultCode": ResultCode.SUCCESS.value,
|
||||||
"ResultData": "testing partition details",
|
"ResultData": "testing partition details",
|
||||||
"PartitionsInfoList": PARTITION_DETAILS,
|
"PartitionsInfoList": PARTITION_DETAILS,
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ async def test_abort_if_already_setup(hass):
|
|||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.totalconnect.config_flow.TotalConnectClient"
|
"homeassistant.components.totalconnect.config_flow.TotalConnectClient"
|
||||||
) as client_mock:
|
) as client_mock:
|
||||||
client_mock.return_value.is_valid_credentials.return_value = True
|
client_mock.return_value.is_logged_in.return_value = True
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": SOURCE_USER},
|
context={"source": SOURCE_USER},
|
||||||
@ -111,7 +111,7 @@ async def test_login_failed(hass):
|
|||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.totalconnect.config_flow.TotalConnectClient"
|
"homeassistant.components.totalconnect.config_flow.TotalConnectClient"
|
||||||
) as client_mock:
|
) as client_mock:
|
||||||
client_mock.return_value.is_valid_credentials.return_value = False
|
client_mock.return_value.is_logged_in.return_value = False
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": SOURCE_USER},
|
context={"source": SOURCE_USER},
|
||||||
@ -143,7 +143,7 @@ async def test_reauth(hass):
|
|||||||
"homeassistant.components.totalconnect.async_setup_entry", return_value=True
|
"homeassistant.components.totalconnect.async_setup_entry", return_value=True
|
||||||
):
|
):
|
||||||
# first test with an invalid password
|
# first test with an invalid password
|
||||||
client_mock.return_value.is_valid_credentials.return_value = False
|
client_mock.return_value.is_logged_in.return_value = False
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], user_input={CONF_PASSWORD: "password"}
|
result["flow_id"], user_input={CONF_PASSWORD: "password"}
|
||||||
@ -153,7 +153,7 @@ async def test_reauth(hass):
|
|||||||
assert result["errors"] == {"base": "invalid_auth"}
|
assert result["errors"] == {"base": "invalid_auth"}
|
||||||
|
|
||||||
# now test with the password valid
|
# now test with the password valid
|
||||||
client_mock.return_value.is_valid_credentials.return_value = True
|
client_mock.return_value.is_logged_in.return_value = True
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], user_input={CONF_PASSWORD: "password"}
|
result["flow_id"], user_input={CONF_PASSWORD: "password"}
|
||||||
|
@ -22,7 +22,7 @@ async def test_reauth_started(hass):
|
|||||||
"homeassistant.components.totalconnect.TotalConnectClient",
|
"homeassistant.components.totalconnect.TotalConnectClient",
|
||||||
autospec=True,
|
autospec=True,
|
||||||
) as mock_client:
|
) as mock_client:
|
||||||
mock_client.return_value.is_valid_credentials.return_value = False
|
mock_client.return_value.is_logged_in.return_value = False
|
||||||
assert await async_setup_component(hass, DOMAIN, {})
|
assert await async_setup_component(hass, DOMAIN, {})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user