Compare commits

..

19 Commits

Author SHA1 Message Date
Bram Kragten 422de2c56d Bumped version to 2021.7.0b4 2021-07-06 11:57:50 +02:00
Bram Kragten 2356c1e52a Update frontend to 20210706.0 (#52577) 2021-07-06 11:54:32 +02:00
Raman Gupta 2220c8cd3f Bump pyeight version to 0.1.9 (#52568) 2021-07-06 11:54:31 +02:00
Franck Nijhof 979d37dc19 Fix unavailable entity capable of triggering non-numerical warning in Threshold sensor (#52563) 2021-07-06 11:54:30 +02:00
J. Nick Koston 701fa06584 Bump aiohomekit to 0.4.2 (#52560)
- Changelog: https://github.com/Jc2k/aiohomekit/compare/0.4.1...0.4.2

- Fixes: #52548
2021-07-06 11:54:29 +02:00
Alexei Chetroi 1c9053fef6 Bump up zha dependencies (#52555) 2021-07-06 11:54:28 +02:00
J. Nick Koston 777cf116aa Update the ip/port in the homekit_controller config entry when it changes (#52554) 2021-07-06 11:54:27 +02:00
René Klomp a52b4b0f62 Bump pysma version to 0.6.2 (#52553) 2021-07-06 11:54:26 +02:00
J. Nick Koston dfce89f2c7 Bump zeroconf to 0.32.1 (#52547)
- Changelog: https://github.com/jstasiak/python-zeroconf/compare/0.32.0...0.32.1

- Fixes #52384
2021-07-06 11:54:25 +02:00
Tom Brien 0cd097cd12 Update list of supported Coinbase wallet currencies (#52545) 2021-07-06 11:54:24 +02:00
Franck Nijhof 9368f75cec Bumped version to 2021.7.0b3 2021-07-05 13:41:25 +02:00
Franck Nijhof d5b419eeda Remove problematic/redudant db migration happning schema 15 (#52541) 2021-07-05 13:41:01 +02:00
J. Nick Koston e140cd9b6a Bump HAP-python to 3.5.1 (#52508)
- Fixes additional cases of invalid mdns hostnames
2021-07-05 13:40:57 +02:00
jan iversen 206437b10c Fix MODBUS connection type rtuovertcp does not connect (#52505)
* Correct host -> framer.

* Use function pointer
2021-07-05 13:40:54 +02:00
J. Nick Koston 36eec7ddbc Remove empty hosts and excludes from nmap configuration (#52489) 2021-07-05 13:40:51 +02:00
Aaron Bach afb187942a Revert "Force SimpliSafe to reauthenticate with a password (#51528)" (#52484)
This reverts commit 549f779b06.
2021-07-05 13:40:47 +02:00
J. Nick Koston 070991c160 Bump aiohomekit to 0.4.1 (#52472)
- Fixes mdns queries being sent with the original case received on the wire
  Some responders were case sensitive and needed the original case sent

- Reduces mdns traffic
2021-07-05 13:40:44 +02:00
Franck Nijhof ebc3e1f658 Fix Statistics recorder migration path by dropping in pairs (#52453) 2021-07-05 13:40:40 +02:00
Maciej Bieniek d2cef65b63 Bump gios library to version 1.0.2 (#52527) 2021-07-05 09:23:02 +02:00
26 changed files with 391 additions and 107 deletions
+199 -2
View File
@@ -19,50 +19,247 @@ API_ACCOUNTS_DATA = "data"
API_RATES = "rates"
WALLETS = {
"1INCH": "1INCH",
"AAVE": "AAVE",
"ADA": "ADA",
"AED": "AED",
"AFN": "AFN",
"ALGO": "ALGO",
"ALL": "ALL",
"AMD": "AMD",
"AMP": "AMP",
"ANG": "ANG",
"ANKR": "ANKR",
"AOA": "AOA",
"ARS": "ARS",
"ATOM": "ATOM",
"AUD": "AUD",
"AWG": "AWG",
"AZN": "AZN",
"BAL": "BAL",
"BAM": "BAM",
"BAND": "BAND",
"BAT": "BAT",
"BBD": "BBD",
"BCH": "BCH",
"BDT": "BDT",
"BGN": "BGN",
"BHD": "BHD",
"BIF": "BIF",
"BMD": "BMD",
"BND": "BND",
"BNT": "BNT",
"BOB": "BOB",
"BOND": "BOND",
"BRL": "BRL",
"BSD": "BSD",
"BSV": "BSV",
"BTC": "BTC",
"CGLD": "CLGD",
"CVC": "CVC",
"BTN": "BTN",
"BWP": "BWP",
"BYN": "BYN",
"BYR": "BYR",
"BZD": "BZD",
"CAD": "CAD",
"CDF": "CDF",
"CGLD": "CGLD",
"CHF": "CHF",
"CHZ": "CHZ",
"CLF": "CLF",
"CLP": "CLP",
"CNH": "CNH",
"CNY": "CNY",
"COMP": "COMP",
"COP": "COP",
"CRC": "CRC",
"CRV": "CRV",
"CTSI": "CTSI",
"CUC": "CUC",
"CVC": "CVC",
"CVE": "CVE",
"CZK": "CZK",
"DAI": "DAI",
"DASH": "DASH",
"DJF": "DJF",
"DKK": "DKK",
"DNT": "DNT",
"DOGE": "DOGE",
"DOP": "DOP",
"DOT": "DOT",
"DZD": "DZD",
"EGP": "EGP",
"ENJ": "ENJ",
"EOS": "EOS",
"ERN": "ERN",
"ETB": "ETB",
"ETC": "ETC",
"ETH": "ETH",
"ETH2": "ETH2",
"EUR": "EUR",
"FIL": "FIL",
"FJD": "FJD",
"FKP": "FKP",
"FORTH": "FORTH",
"GBP": "GBP",
"GBX": "GBX",
"GEL": "GEL",
"GGP": "GGP",
"GHS": "GHS",
"GIP": "GIP",
"GMD": "GMD",
"GNF": "GNF",
"GRT": "GRT",
"GTC": "GTC",
"GTQ": "GTQ",
"GYD": "GYD",
"HKD": "HKD",
"HNL": "HNL",
"HRK": "HRK",
"HTG": "HTG",
"HUF": "HUF",
"ICP": "ICP",
"IDR": "IDR",
"ILS": "ILS",
"IMP": "IMP",
"INR": "INR",
"IQD": "IQD",
"ISK": "ISK",
"JEP": "JEP",
"JMD": "JMD",
"JOD": "JOD",
"JPY": "JPY",
"KEEP": "KEEP",
"KES": "KES",
"KGS": "KGS",
"KHR": "KHR",
"KMF": "KMF",
"KNC": "KNC",
"KRW": "KRW",
"KWD": "KWD",
"KYD": "KYD",
"KZT": "KZT",
"LAK": "LAK",
"LBP": "LBP",
"LINK": "LINK",
"LKR": "LKR",
"LPT": "LPT",
"LRC": "LRC",
"LRD": "LRD",
"LSL": "LSL",
"LTC": "LTC",
"LYD": "LYD",
"MAD": "MAD",
"MANA": "MANA",
"MATIC": "MATIC",
"MDL": "MDL",
"MGA": "MGA",
"MIR": "MIR",
"MKD": "MKD",
"MKR": "MKR",
"MLN": "MLN",
"MMK": "MMK",
"MNT": "MNT",
"MOP": "MOP",
"MRO": "MRO",
"MTL": "MTL",
"MUR": "MUR",
"MVR": "MVR",
"MWK": "MWK",
"MXN": "MXN",
"MYR": "MYR",
"MZN": "MZN",
"NAD": "NAD",
"NGN": "NGN",
"NIO": "NIO",
"NKN": "NKN",
"NMR": "NMR",
"NOK": "NOK",
"NPR": "NPR",
"NU": "NU",
"NZD": "NZD",
"OGN": "OGN",
"OMG": "OMG",
"OMR": "OMR",
"OXT": "OXT",
"PAB": "PAB",
"PEN": "PEN",
"PGK": "PGK",
"PHP": "PHP",
"PKR": "PKR",
"PLN": "PLN",
"PYG": "PYG",
"QAR": "QAR",
"QNT": "QNT",
"REN": "REN",
"REP": "REP",
"REPV2": "REPV2",
"RLC": "RLC",
"RON": "RON",
"RSD": "RSD",
"RUB": "RUB",
"RWF": "RWF",
"SAR": "SAR",
"SBD": "SBD",
"SCR": "SCR",
"SEK": "SEK",
"SGD": "SGD",
"SHP": "SHP",
"SKL": "SKL",
"SLL": "SLL",
"SNX": "SNX",
"SOL": "SOL",
"SOS": "SOS",
"SRD": "SRD",
"SSP": "SSP",
"STD": "STD",
"STORJ": "STORJ",
"SUSHI": "SUSHI",
"SVC": "SVC",
"SZL": "SZL",
"THB": "THB",
"TJS": "TJS",
"TMM": "TMM",
"TMT": "TMT",
"TND": "TND",
"TOP": "TOP",
"TRB": "TRB",
"TRY": "TRY",
"TTD": "TTD",
"TWD": "TWD",
"TZS": "TZS",
"UAH": "UAH",
"UGX": "UGX",
"UMA": "UMA",
"UNI": "UNI",
"USD": "USD",
"USDC": "USDC",
"USDT": "USDT",
"UYU": "UYU",
"UZS": "UZS",
"VES": "VES",
"VND": "VND",
"VUV": "VUV",
"WBTC": "WBTC",
"WST": "WST",
"XAF": "XAF",
"XAG": "XAG",
"XAU": "XAU",
"XCD": "XCD",
"XDR": "XDR",
"XLM": "XLM",
"XOF": "XOF",
"XPD": "XPD",
"XPF": "XPF",
"XPT": "XPT",
"XRP": "XRP",
"XTZ": "XTZ",
"YER": "YER",
"YFI": "YFI",
"ZAR": "ZAR",
"ZEC": "ZEC",
"ZMW": "ZMW",
"ZRX": "ZRX",
"ZWL": "ZWL",
}
RATES = {
@@ -2,7 +2,7 @@
"domain": "eight_sleep",
"name": "Eight Sleep",
"documentation": "https://www.home-assistant.io/integrations/eight_sleep",
"requirements": ["pyeight==0.1.8"],
"requirements": ["pyeight==0.1.9"],
"codeowners": ["@mezz64"],
"iot_class": "cloud_polling"
}
@@ -3,7 +3,7 @@
"name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/integrations/frontend",
"requirements": [
"home-assistant-frontend==20210630.0"
"home-assistant-frontend==20210706.0"
],
"dependencies": [
"api",
+2 -2
View File
@@ -80,7 +80,7 @@ class GiosAirQuality(CoordinatorEntity, AirQualityEntity):
@property
def air_quality_index(self) -> str | None:
"""Return the air quality index."""
return cast(Optional[str], self.coordinator.data.get(API_AQI, {}).get("value"))
return cast(Optional[str], self.coordinator.data.get(API_AQI).get("value"))
@property
def particulate_matter_2_5(self) -> float | None:
@@ -141,7 +141,7 @@ class GiosAirQuality(CoordinatorEntity, AirQualityEntity):
if sensor in self.coordinator.data:
self._attrs[f"{SENSOR_MAP[sensor]}_index"] = self.coordinator.data[
sensor
]["index"]
].get("index")
self._attrs[ATTR_STATION] = self.coordinator.gios.station_name
return self._attrs
+1 -1
View File
@@ -3,7 +3,7 @@
"name": "GIO\u015a",
"documentation": "https://www.home-assistant.io/integrations/gios",
"codeowners": ["@bieniu"],
"requirements": ["gios==1.0.1"],
"requirements": ["gios==1.0.2"],
"config_flow": true,
"quality_scale": "platinum",
"iot_class": "cloud_polling"
@@ -3,7 +3,7 @@
"name": "HomeKit",
"documentation": "https://www.home-assistant.io/integrations/homekit",
"requirements": [
"HAP-python==3.5.0",
"HAP-python==3.5.1",
"fnvhash==0.1.0",
"PyQRCode==1.2.1",
"base36==0.1.1",
@@ -236,9 +236,20 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
)
config_num = None
# Set unique-id and error out if it's already configured
existing_entry = await self.async_set_unique_id(normalize_hkid(hkid))
updated_ip_port = {
"AccessoryIP": discovery_info["host"],
"AccessoryPort": discovery_info["port"],
}
# If the device is already paired and known to us we should monitor c#
# (config_num) for changes. If it changes, we check for new entities
if paired and hkid in self.hass.data.get(KNOWN_DEVICES, {}):
if existing_entry:
self.hass.config_entries.async_update_entry(
existing_entry, data={**existing_entry.data, **updated_ip_port}
)
conn = self.hass.data[KNOWN_DEVICES][hkid]
# When we rediscover the device, let aiohomekit know
# that the device is available and we should not wait
@@ -262,8 +273,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
await self.hass.config_entries.async_remove(existing.entry_id)
# Set unique-id and error out if it's already configured
await self.async_set_unique_id(normalize_hkid(hkid))
self._abort_if_unique_id_configured()
self._abort_if_unique_id_configured(updates=updated_ip_port)
self.context["hkid"] = hkid
@@ -3,7 +3,7 @@
"name": "HomeKit Controller",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
"requirements": ["aiohomekit==0.4.0"],
"requirements": ["aiohomekit==0.4.2"],
"zeroconf": ["_hap._tcp.local."],
"after_dependencies": ["zeroconf"],
"codeowners": ["@Jc2k", "@bdraco"],
+2 -1
View File
@@ -5,6 +5,7 @@ import logging
from pymodbus.client.sync import ModbusSerialClient, ModbusTcpClient, ModbusUdpClient
from pymodbus.constants import Defaults
from pymodbus.exceptions import ModbusException
from pymodbus.transaction import ModbusRtuFramer
from homeassistant.const import (
CONF_DELAY,
@@ -224,7 +225,7 @@ class ModbusHub:
# network configuration
self._pb_params["host"] = client_config[CONF_HOST]
if self._config_type == CONF_RTUOVERTCP:
self._pb_params["host"] = "ModbusRtuFramer"
self._pb_params["framer"] = ModbusRtuFramer
Defaults.Timeout = client_config[CONF_TIMEOUT]
@@ -166,8 +166,10 @@ class NmapDeviceScanner:
self._scan_interval = timedelta(
seconds=config.get(CONF_SCAN_INTERVAL, TRACKER_SCAN_INTERVAL)
)
self._hosts = cv.ensure_list_csv(config[CONF_HOSTS])
self._exclude = cv.ensure_list_csv(config[CONF_EXCLUDE])
hosts_list = cv.ensure_list_csv(config[CONF_HOSTS])
self._hosts = [host for host in hosts_list if host != ""]
excludes_list = cv.ensure_list_csv(config[CONF_EXCLUDE])
self._exclude = [exclude for exclude in excludes_list if exclude != ""]
self._options = config[CONF_OPTIONS]
self.home_interval = timedelta(
minutes=cv.positive_int(config[CONF_HOME_INTERVAL])
+15 -12
View File
@@ -347,7 +347,7 @@ def _drop_foreign_key_constraints(connection, engine, table, columns):
)
def _apply_update(engine, session, new_version, old_version): # noqa: C901
def _apply_update(engine, session, new_version, old_version):
"""Perform operations to bring schema up to date."""
connection = session.connection()
if new_version == 1:
@@ -451,10 +451,8 @@ def _apply_update(engine, session, new_version, old_version): # noqa: C901
elif new_version == 14:
_modify_columns(connection, engine, "events", ["event_type VARCHAR(64)"])
elif new_version == 15:
if sqlalchemy.inspect(engine).has_table(Statistics.__tablename__):
# Recreate the statistics table
Statistics.__table__.drop(engine)
Statistics.__table__.create(engine)
# This dropped the statistics table, done again in version 18.
pass
elif new_version == 16:
_drop_foreign_key_constraints(
connection, engine, TABLE_STATES, ["old_state_id"]
@@ -463,14 +461,19 @@ def _apply_update(engine, session, new_version, old_version): # noqa: C901
# This dropped the statistics table, done again in version 18.
pass
elif new_version == 18:
# Recreate the statisticsmeta tables
if sqlalchemy.inspect(engine).has_table(StatisticsMeta.__tablename__):
StatisticsMeta.__table__.drop(engine)
StatisticsMeta.__table__.create(engine)
# Recreate the statistics and statistics meta tables.
#
# Order matters! Statistics has a relation with StatisticsMeta,
# so statistics need to be deleted before meta (or in pair depending
# on the SQL backend); and meta needs to be created before statistics.
if sqlalchemy.inspect(engine).has_table(
StatisticsMeta.__tablename__
) or sqlalchemy.inspect(engine).has_table(Statistics.__tablename__):
Base.metadata.drop_all(
bind=engine, tables=[Statistics.__table__, StatisticsMeta.__table__]
)
# Recreate the statistics table
if sqlalchemy.inspect(engine).has_table(Statistics.__tablename__):
Statistics.__table__.drop(engine)
StatisticsMeta.__table__.create(engine)
Statistics.__table__.create(engine)
else:
raise ValueError(f"No schema migration defined for version {new_version}")
+45 -28
View File
@@ -6,7 +6,7 @@ from simplipy import API
from simplipy.errors import EndpointUnavailable, InvalidCredentialsError, SimplipyError
import voluptuous as vol
from homeassistant.const import ATTR_CODE, CONF_CODE, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import ATTR_CODE, CONF_CODE, CONF_TOKEN, CONF_USERNAME
from homeassistant.core import CoreState, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import (
@@ -107,6 +107,14 @@ SERVICE_SET_SYSTEM_PROPERTIES_SCHEMA = SERVICE_BASE_SCHEMA.extend(
CONFIG_SCHEMA = cv.deprecated(DOMAIN)
@callback
def _async_save_refresh_token(hass, config_entry, token):
"""Save a refresh token to the config entry."""
hass.config_entries.async_update_entry(
config_entry, data={**config_entry.data, CONF_TOKEN: token}
)
async def async_get_client_id(hass):
"""Get a client ID (based on the HASS unique ID) for the SimpliSafe API.
@@ -134,9 +142,6 @@ async def async_setup_entry(hass, config_entry): # noqa: C901
hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = []
hass.data[DOMAIN][DATA_LISTENER][config_entry.entry_id] = []
if CONF_PASSWORD not in config_entry.data:
raise ConfigEntryAuthFailed("Config schema change requires re-authentication")
entry_updates = {}
if not config_entry.unique_id:
# If the config entry doesn't already have a unique ID, set one:
@@ -158,24 +163,20 @@ async def async_setup_entry(hass, config_entry): # noqa: C901
client_id = await async_get_client_id(hass)
websession = aiohttp_client.async_get_clientsession(hass)
async def async_get_api():
"""Define a helper to get an authenticated SimpliSafe API object."""
return await API.login_via_credentials(
config_entry.data[CONF_USERNAME],
config_entry.data[CONF_PASSWORD],
client_id=client_id,
session=websession,
)
try:
api = await async_get_api()
except InvalidCredentialsError as err:
raise ConfigEntryAuthFailed from err
api = await API.login_via_token(
config_entry.data[CONF_TOKEN], client_id=client_id, session=websession
)
except InvalidCredentialsError:
LOGGER.error("Invalid credentials provided")
return False
except SimplipyError as err:
LOGGER.error("Config entry failed: %s", err)
raise ConfigEntryNotReady from err
simplisafe = SimpliSafe(hass, config_entry, api, async_get_api)
_async_save_refresh_token(hass, config_entry, api.refresh_token)
simplisafe = SimpliSafe(hass, api, config_entry)
try:
await simplisafe.async_init()
@@ -302,10 +303,10 @@ async def async_reload_entry(hass, config_entry):
class SimpliSafe:
"""Define a SimpliSafe data object."""
def __init__(self, hass, config_entry, api, async_get_api):
def __init__(self, hass, api, config_entry):
"""Initialize."""
self._api = api
self._async_get_api = async_get_api
self._emergency_refresh_token_used = False
self._hass = hass
self._system_notifications = {}
self.config_entry = config_entry
@@ -382,17 +383,23 @@ class SimpliSafe:
for result in results:
if isinstance(result, InvalidCredentialsError):
try:
self._api = await self._async_get_api()
return
except InvalidCredentialsError as err:
if self._emergency_refresh_token_used:
raise ConfigEntryAuthFailed(
"Unable to re-authenticate with SimpliSafe"
) from err
"Update failed with stored refresh token"
)
LOGGER.warning("SimpliSafe cloud error; trying stored refresh token")
self._emergency_refresh_token_used = True
try:
await self._api.refresh_access_token(
self.config_entry.data[CONF_TOKEN]
)
return
except SimplipyError as err:
raise UpdateFailed(
f"SimpliSafe error while updating: {err}"
) from err
raise UpdateFailed( # pylint: disable=raise-missing-from
f"Error while using stored refresh token: {err}"
)
if isinstance(result, EndpointUnavailable):
# In case the user attempts an action not allowed in their current plan,
@@ -403,6 +410,16 @@ class SimpliSafe:
if isinstance(result, SimplipyError):
raise UpdateFailed(f"SimpliSafe error while updating: {result}")
if self._api.refresh_token != self.config_entry.data[CONF_TOKEN]:
_async_save_refresh_token(
self._hass, self.config_entry, self._api.refresh_token
)
# If we've reached this point using an emergency refresh token, we're in the
# clear and we can discard it:
if self._emergency_refresh_token_used:
self._emergency_refresh_token_used = False
class SimpliSafeEntity(CoordinatorEntity):
"""Define a base SimpliSafe entity."""
@@ -8,7 +8,7 @@ from simplipy.errors import (
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_CODE, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_CODE, CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME
from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client
@@ -59,7 +59,7 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
errors = {}
try:
await self._async_get_simplisafe_api()
simplisafe = await self._async_get_simplisafe_api()
except PendingAuthorizationError:
LOGGER.info("Awaiting confirmation of MFA email click")
return await self.async_step_mfa()
@@ -79,7 +79,7 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return await self.async_step_finish(
{
CONF_USERNAME: self._username,
CONF_PASSWORD: self._password,
CONF_TOKEN: simplisafe.refresh_token,
CONF_CODE: self._code,
}
)
@@ -89,9 +89,6 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
existing_entry = await self.async_set_unique_id(self._username)
if existing_entry:
self.hass.config_entries.async_update_entry(existing_entry, data=user_input)
self.hass.async_create_task(
self.hass.config_entries.async_reload(existing_entry.entry_id)
)
return self.async_abort(reason="reauth_successful")
return self.async_create_entry(title=self._username, data=user_input)
@@ -101,7 +98,7 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_show_form(step_id="mfa")
try:
await self._async_get_simplisafe_api()
simplisafe = await self._async_get_simplisafe_api()
except PendingAuthorizationError:
LOGGER.error("Still awaiting confirmation of MFA email click")
return self.async_show_form(
@@ -111,7 +108,7 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return await self.async_step_finish(
{
CONF_USERNAME: self._username,
CONF_PASSWORD: self._password,
CONF_TOKEN: simplisafe.refresh_token,
CONF_CODE: self._code,
}
)
@@ -7,7 +7,7 @@
},
"reauth_confirm": {
"title": "[%key:common::config_flow::title::reauth%]",
"description": "Your access has expired or been revoked. Enter your password to re-link your account.",
"description": "Your access token has expired or been revoked. Enter your password to re-link your account.",
"data": {
"password": "[%key:common::config_flow::data::password%]"
}
@@ -19,7 +19,7 @@
"data": {
"password": "Password"
},
"description": "Your access has expired or been revoked. Enter your password to re-link your account.",
"description": "Your access token has expired or been revoked. Enter your password to re-link your account.",
"title": "Reauthenticate Integration"
},
"user": {
+1 -1
View File
@@ -3,7 +3,7 @@
"name": "SMA Solar",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/sma",
"requirements": ["pysma==0.6.1"],
"requirements": ["pysma==0.6.2"],
"codeowners": ["@kellerza", "@rklomp"],
"iot_class": "local_polling"
}
@@ -13,6 +13,7 @@ from homeassistant.const import (
CONF_DEVICE_CLASS,
CONF_ENTITY_ID,
CONF_NAME,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import callback
@@ -100,7 +101,9 @@ class ThresholdSensor(BinarySensorEntity):
try:
self.sensor_value = (
None if new_state.state == STATE_UNKNOWN else float(new_state.state)
None
if new_state.state in [STATE_UNKNOWN, STATE_UNAVAILABLE]
else float(new_state.state)
)
except (ValueError, TypeError):
self.sensor_value = None
@@ -2,7 +2,7 @@
"domain": "zeroconf",
"name": "Zero-configuration networking (zeroconf)",
"documentation": "https://www.home-assistant.io/integrations/zeroconf",
"requirements": ["zeroconf==0.32.0"],
"requirements": ["zeroconf==0.32.1"],
"dependencies": ["network", "api"],
"codeowners": ["@bdraco"],
"quality_scale": "internal",
+1 -1
View File
@@ -10,7 +10,7 @@
"zha-quirks==0.0.58",
"zigpy-cc==0.5.2",
"zigpy-deconz==0.12.0",
"zigpy==0.35.0",
"zigpy==0.35.1",
"zigpy-xbee==0.13.0",
"zigpy-zigate==0.7.3",
"zigpy-znp==0.5.1"
+1 -1
View File
@@ -5,7 +5,7 @@ from typing import Final
MAJOR_VERSION: Final = 2021
MINOR_VERSION: Final = 7
PATCH_VERSION: Final = "0b2"
PATCH_VERSION: Final = "0b4"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)
+2 -2
View File
@@ -17,7 +17,7 @@ defusedxml==0.7.1
distro==1.5.0
emoji==1.2.0
hass-nabucasa==0.44.0
home-assistant-frontend==20210630.0
home-assistant-frontend==20210706.0
httpx==0.18.0
ifaddr==0.1.7
jinja2==3.0.1
@@ -33,7 +33,7 @@ sqlalchemy==1.4.17
voluptuous-serialize==2.4.0
voluptuous==0.12.1
yarl==1.6.3
zeroconf==0.32.0
zeroconf==0.32.1
pycryptodome>=3.6.6
+8 -8
View File
@@ -14,7 +14,7 @@ Adafruit-SHT31==1.0.2
# Adafruit_BBIO==1.1.1
# homeassistant.components.homekit
HAP-python==3.5.0
HAP-python==3.5.1
# homeassistant.components.mastodon
Mastodon.py==1.5.1
@@ -175,7 +175,7 @@ aioguardian==1.0.4
aioharmony==0.2.7
# homeassistant.components.homekit_controller
aiohomekit==0.4.0
aiohomekit==0.4.2
# homeassistant.components.emulated_hue
# homeassistant.components.http
@@ -678,7 +678,7 @@ georss_qld_bushfire_alert_client==0.5
getmac==0.8.2
# homeassistant.components.gios
gios==1.0.1
gios==1.0.2
# homeassistant.components.gitter
gitterpy==0.1.7
@@ -780,7 +780,7 @@ hole==0.5.1
holidays==0.11.1
# homeassistant.components.frontend
home-assistant-frontend==20210630.0
home-assistant-frontend==20210706.0
# homeassistant.components.zwave
homeassistant-pyozw==0.1.10
@@ -1409,7 +1409,7 @@ pyeconet==0.1.14
pyedimax==0.2.1
# homeassistant.components.eight_sleep
pyeight==0.1.8
pyeight==0.1.9
# homeassistant.components.emby
pyemby==1.7
@@ -1756,7 +1756,7 @@ pysignalclirestapi==0.3.4
pyskyqhub==0.1.3
# homeassistant.components.sma
pysma==0.6.1
pysma==0.6.2
# homeassistant.components.smappee
pysmappee==0.2.25
@@ -2428,7 +2428,7 @@ zeep[async]==4.0.0
zengge==0.2
# homeassistant.components.zeroconf
zeroconf==0.32.0
zeroconf==0.32.1
# homeassistant.components.zha
zha-quirks==0.0.58
@@ -2455,7 +2455,7 @@ zigpy-zigate==0.7.3
zigpy-znp==0.5.1
# homeassistant.components.zha
zigpy==0.35.0
zigpy==0.35.1
# homeassistant.components.zoneminder
zm-py==0.5.2
+7 -7
View File
@@ -7,7 +7,7 @@
AEMET-OpenData==0.2.1
# homeassistant.components.homekit
HAP-python==3.5.0
HAP-python==3.5.1
# homeassistant.components.flick_electric
PyFlick==0.0.2
@@ -112,7 +112,7 @@ aioguardian==1.0.4
aioharmony==0.2.7
# homeassistant.components.homekit_controller
aiohomekit==0.4.0
aiohomekit==0.4.2
# homeassistant.components.emulated_hue
# homeassistant.components.http
@@ -384,7 +384,7 @@ georss_qld_bushfire_alert_client==0.5
getmac==0.8.2
# homeassistant.components.gios
gios==1.0.1
gios==1.0.2
# homeassistant.components.glances
glances_api==0.2.0
@@ -447,7 +447,7 @@ hole==0.5.1
holidays==0.11.1
# homeassistant.components.frontend
home-assistant-frontend==20210630.0
home-assistant-frontend==20210706.0
# homeassistant.components.zwave
homeassistant-pyozw==0.1.10
@@ -998,7 +998,7 @@ pysiaalarm==3.0.0
pysignalclirestapi==0.3.4
# homeassistant.components.sma
pysma==0.6.1
pysma==0.6.2
# homeassistant.components.smappee
pysmappee==0.2.25
@@ -1331,7 +1331,7 @@ yeelight==0.6.3
zeep[async]==4.0.0
# homeassistant.components.zeroconf
zeroconf==0.32.0
zeroconf==0.32.1
# homeassistant.components.zha
zha-quirks==0.0.58
@@ -1352,7 +1352,7 @@ zigpy-zigate==0.7.3
zigpy-znp==0.5.1
# homeassistant.components.zha
zigpy==0.35.0
zigpy==0.35.1
# homeassistant.components.zwave_js
zwave-js-server-python==0.27.0
@@ -1,7 +1,7 @@
"""Tests for homekit_controller config flow."""
from unittest import mock
import unittest.mock
from unittest.mock import patch
from unittest.mock import AsyncMock, patch
import aiohomekit
from aiohomekit.model import Accessories, Accessory
@@ -11,6 +11,7 @@ import pytest
from homeassistant import config_entries
from homeassistant.components.homekit_controller import config_flow
from homeassistant.components.homekit_controller.const import KNOWN_DEVICES
from homeassistant.helpers import device_registry
from tests.common import MockConfigEntry, mock_device_registry
@@ -383,11 +384,16 @@ async def test_discovery_invalid_config_entry(hass, controller):
async def test_discovery_already_configured(hass, controller):
"""Already configured."""
MockConfigEntry(
entry = MockConfigEntry(
domain="homekit_controller",
data={"AccessoryPairingID": "00:00:00:00:00:00"},
data={
"AccessoryIP": "4.4.4.4",
"AccessoryPort": 66,
"AccessoryPairingID": "00:00:00:00:00:00",
},
unique_id="00:00:00:00:00:00",
).add_to_hass(hass)
)
entry.add_to_hass(hass)
device = setup_mock_accessory(controller)
discovery_info = get_device_discovery_info(device)
@@ -403,6 +409,49 @@ async def test_discovery_already_configured(hass, controller):
)
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
assert entry.data["AccessoryIP"] == discovery_info["host"]
assert entry.data["AccessoryPort"] == discovery_info["port"]
async def test_discovery_already_configured_update_csharp(hass, controller):
"""Already configured and csharp changes."""
entry = MockConfigEntry(
domain="homekit_controller",
data={
"AccessoryIP": "4.4.4.4",
"AccessoryPort": 66,
"AccessoryPairingID": "AA:BB:CC:DD:EE:FF",
},
unique_id="aa:bb:cc:dd:ee:ff",
)
entry.add_to_hass(hass)
connection_mock = AsyncMock()
connection_mock.pairing.connect.reconnect_soon = AsyncMock()
connection_mock.async_refresh_entity_map = AsyncMock()
hass.data[KNOWN_DEVICES] = {"AA:BB:CC:DD:EE:FF": connection_mock}
device = setup_mock_accessory(controller)
discovery_info = get_device_discovery_info(device)
# Set device as already paired
discovery_info["properties"]["sf"] = 0x00
discovery_info["properties"]["c#"] = 99999
discovery_info["properties"]["id"] = "AA:BB:CC:DD:EE:FF"
# Device is discovered
result = await hass.config_entries.flow.async_init(
"homekit_controller",
context={"source": config_entries.SOURCE_ZEROCONF},
data=discovery_info,
)
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
await hass.async_block_till_done()
assert entry.data["AccessoryIP"] == discovery_info["host"]
assert entry.data["AccessoryPort"] == discovery_info["port"]
assert connection_mock.async_refresh_entity_map.await_count == 1
@pytest.mark.parametrize("exception,expected", PAIRING_START_ABORT_ERRORS)
@@ -10,7 +10,7 @@ from simplipy.errors import (
from homeassistant import data_entry_flow
from homeassistant.components.simplisafe import DOMAIN
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER
from homeassistant.const import CONF_CODE, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_CODE, CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME
from tests.common import MockConfigEntry
@@ -33,11 +33,7 @@ async def test_duplicate_error(hass):
MockConfigEntry(
domain=DOMAIN,
unique_id="user@email.com",
data={
CONF_USERNAME: "user@email.com",
CONF_PASSWORD: "password",
CONF_CODE: "1234",
},
data={CONF_USERNAME: "user@email.com", CONF_TOKEN: "12345", CONF_CODE: "1234"},
).add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
@@ -106,11 +102,7 @@ async def test_step_reauth(hass):
MockConfigEntry(
domain=DOMAIN,
unique_id="user@email.com",
data={
CONF_USERNAME: "user@email.com",
CONF_PASSWORD: "password",
CONF_CODE: "1234",
},
data={CONF_USERNAME: "user@email.com", CONF_TOKEN: "12345", CONF_CODE: "1234"},
).add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
@@ -128,8 +120,6 @@ async def test_step_reauth(hass):
"homeassistant.components.simplisafe.async_setup_entry", return_value=True
), patch(
"simplipy.API.login_via_credentials", new=AsyncMock(return_value=mock_api())
), patch(
"homeassistant.config_entries.ConfigEntries.async_reload"
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={CONF_PASSWORD: "password"}
@@ -161,7 +151,7 @@ async def test_step_user(hass):
assert result["title"] == "user@email.com"
assert result["data"] == {
CONF_USERNAME: "user@email.com",
CONF_PASSWORD: "password",
CONF_TOKEN: "12345abc",
CONF_CODE: "1234",
}
@@ -207,7 +197,7 @@ async def test_step_user_mfa(hass):
assert result["title"] == "user@email.com"
assert result["data"] == {
CONF_USERNAME: "user@email.com",
CONF_PASSWORD: "password",
CONF_TOKEN: "12345abc",
CONF_CODE: "1234",
}
@@ -1,6 +1,11 @@
"""The test for the threshold sensor platform."""
from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, STATE_UNKNOWN, TEMP_CELSIUS
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
TEMP_CELSIUS,
)
from homeassistant.setup import async_setup_component
@@ -283,7 +288,7 @@ async def test_sensor_in_range_with_hysteresis(hass):
assert state.state == "on"
async def test_sensor_in_range_unknown_state(hass):
async def test_sensor_in_range_unknown_state(hass, caplog):
"""Test if source is within the range."""
config = {
"binary_sensor": {
@@ -322,6 +327,16 @@ async def test_sensor_in_range_unknown_state(hass):
assert state.attributes.get("position") == "unknown"
assert state.state == "off"
hass.states.async_set("sensor.test_monitored", STATE_UNAVAILABLE)
await hass.async_block_till_done()
state = hass.states.get("binary_sensor.threshold")
assert state.attributes.get("position") == "unknown"
assert state.state == "off"
assert "State is not numerical" not in caplog.text
async def test_sensor_lower_zero_threshold(hass):
"""Test if a lower threshold of zero is set."""