pull/90641/head 2023.3.1
Paulus Schoutsen 2023-03-02 15:53:50 -05:00 committed by GitHub
commit f0f12fd14a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 112 additions and 44 deletions

View File

@ -11,5 +11,5 @@
"documentation": "https://www.home-assistant.io/integrations/dormakaba_dkey",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["py-dormakaba-dkey==1.0.3"]
"requirements": ["py-dormakaba-dkey==1.0.4"]
}

View File

@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20230301.0"]
"requirements": ["home-assistant-frontend==20230302.0"]
}

View File

@ -4,6 +4,7 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from collections.abc import AsyncGenerator
from pathlib import Path
import shutil
from typing import Any, Final
import voluptuous as vol
@ -549,9 +550,12 @@ class KNXCommonFlow(ABC, FlowHandler):
),
None,
)
_tunnel_identifier = selected_tunnel_ia or self.new_entry_data.get(
CONF_HOST
)
_tunnel_suffix = f" @ {_tunnel_identifier}" if _tunnel_identifier else ""
self.new_title = (
f"{'Secure ' if _if_user_id else ''}"
f"Tunneling @ {selected_tunnel_ia or self.new_entry_data[CONF_HOST]}"
f"{'Secure ' if _if_user_id else ''}Tunneling{_tunnel_suffix}"
)
return self.finish_flow()
@ -708,7 +712,8 @@ class KNXCommonFlow(ABC, FlowHandler):
else:
dest_path = Path(self.hass.config.path(STORAGE_DIR, DOMAIN))
dest_path.mkdir(exist_ok=True)
file_path.rename(dest_path / DEFAULT_KNX_KEYRING_FILENAME)
dest_file = dest_path / DEFAULT_KNX_KEYRING_FILENAME
shutil.move(file_path, dest_file)
return keyring, errors
keyring, errors = await self.hass.async_add_executor_job(_process_upload)

View File

@ -12,5 +12,5 @@
"documentation": "https://www.home-assistant.io/integrations/nuheat",
"iot_class": "cloud_polling",
"loggers": ["nuheat"],
"requirements": ["nuheat==1.0.0"]
"requirements": ["nuheat==1.0.1"]
}

View File

@ -271,15 +271,20 @@ class SensorEntity(Entity):
@property
def _numeric_state_expected(self) -> bool:
"""Return true if the sensor must be numeric."""
# Note: the order of the checks needs to be kept aligned
# with the checks in `state` property.
device_class = try_parse_enum(SensorDeviceClass, self.device_class)
if device_class in NON_NUMERIC_DEVICE_CLASSES:
return False
if (
self.state_class is not None
or self.native_unit_of_measurement is not None
or self.suggested_display_precision is not None
):
return True
# Sensors with custom device classes are not considered numeric
device_class = try_parse_enum(SensorDeviceClass, self.device_class)
return device_class not in {None, *NON_NUMERIC_DEVICE_CLASSES}
# Sensors with custom device classes will have the device class
# converted to None and are not considered numeric
return device_class is not None
@property
def options(self) -> list[str] | None:

View File

@ -53,17 +53,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
try:
await tibber_connection.update_info()
if not tibber_connection.name:
raise ConfigEntryNotReady("Could not fetch Tibber data.")
except asyncio.TimeoutError as err:
raise ConfigEntryNotReady from err
except aiohttp.ClientError as err:
_LOGGER.error("Error connecting to Tibber: %s ", err)
return False
except (
asyncio.TimeoutError,
aiohttp.ClientError,
tibber.RetryableHttpException,
) as err:
raise ConfigEntryNotReady("Unable to connect") from err
except tibber.InvalidLogin as exp:
_LOGGER.error("Failed to login. %s", exp)
return False
except tibber.FatalHttpException:
return False
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@ -44,10 +44,14 @@ class TibberConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
await tibber_connection.update_info()
except asyncio.TimeoutError:
errors[CONF_ACCESS_TOKEN] = "timeout"
except aiohttp.ClientError:
errors[CONF_ACCESS_TOKEN] = "cannot_connect"
except tibber.InvalidLogin:
errors[CONF_ACCESS_TOKEN] = "invalid_access_token"
except (
aiohttp.ClientError,
tibber.RetryableHttpException,
tibber.FatalHttpException,
):
errors[CONF_ACCESS_TOKEN] = "cannot_connect"
if errors:
return self.async_show_form(

View File

@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["tibber"],
"quality_scale": "silver",
"requirements": ["pyTibber==0.26.13"]
"requirements": ["pyTibber==0.27.0"]
}

View File

@ -44,6 +44,7 @@ from homeassistant.helpers.entity_registry import async_get as async_get_entity_
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
UpdateFailed,
)
from homeassistant.util import Throttle, dt as dt_util
@ -559,6 +560,8 @@ class TibberRtDataCoordinator(DataUpdateCoordinator):
class TibberDataCoordinator(DataUpdateCoordinator[None]):
"""Handle Tibber data and insert statistics."""
config_entry: ConfigEntry
def __init__(self, hass: HomeAssistant, tibber_connection: tibber.Tibber) -> None:
"""Initialize the data handler."""
super().__init__(
@ -571,9 +574,17 @@ class TibberDataCoordinator(DataUpdateCoordinator[None]):
async def _async_update_data(self) -> None:
"""Update data via API."""
await self._tibber_connection.fetch_consumption_data_active_homes()
await self._tibber_connection.fetch_production_data_active_homes()
await self._insert_statistics()
try:
await self._tibber_connection.fetch_consumption_data_active_homes()
await self._tibber_connection.fetch_production_data_active_homes()
await self._insert_statistics()
except tibber.RetryableHttpException as err:
raise UpdateFailed(f"Error communicating with API ({err.status})") from err
except tibber.FatalHttpException:
# Fatal error. Reload config entry to show correct error.
self.hass.async_create_task(
self.hass.config_entries.async_reload(self.config_entry.entry_id)
)
async def _insert_statistics(self) -> None:
"""Insert Tibber statistics."""

View File

@ -8,7 +8,7 @@ from .backports.enum import StrEnum
APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2023
MINOR_VERSION: Final = 3
PATCH_VERSION: Final = "0"
PATCH_VERSION: Final = "1"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 10, 0)

View File

@ -23,14 +23,14 @@ fnvhash==0.1.0
hass-nabucasa==0.61.0
hassil==1.0.6
home-assistant-bluetooth==1.9.3
home-assistant-frontend==20230301.0
home-assistant-frontend==20230302.0
home-assistant-intents==2023.2.28
httpx==0.23.3
ifaddr==0.1.7
janus==1.0.0
jinja2==3.1.2
lru-dict==1.1.8
orjson==3.8.6
orjson==3.8.7
paho-mqtt==1.6.1
pillow==9.4.0
pip>=21.0,<23.1
@ -40,7 +40,7 @@ pyserial==3.5
python-slugify==4.0.1
pyudev==0.23.2
pyyaml==6.0
requests==2.28.1
requests==2.28.2
scapy==2.5.0
sqlalchemy==2.0.4
typing-extensions>=4.5.0,<5.0

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "homeassistant"
version = "2023.3.0"
version = "2023.3.1"
license = {text = "Apache-2.0"}
description = "Open-source home automation platform running on Python 3."
readme = "README.rst"
@ -44,11 +44,11 @@ dependencies = [
"cryptography==39.0.1",
# pyOpenSSL 23.0.0 is required to work with cryptography 39+
"pyOpenSSL==23.0.0",
"orjson==3.8.6",
"orjson==3.8.7",
"pip>=21.0,<23.1",
"python-slugify==4.0.1",
"pyyaml==6.0",
"requests==2.28.1",
"requests==2.28.2",
"typing-extensions>=4.5.0,<5.0",
"voluptuous==0.13.1",
"voluptuous-serialize==2.6.0",

View File

@ -18,11 +18,11 @@ lru-dict==1.1.8
PyJWT==2.5.0
cryptography==39.0.1
pyOpenSSL==23.0.0
orjson==3.8.6
orjson==3.8.7
pip>=21.0,<23.1
python-slugify==4.0.1
pyyaml==6.0
requests==2.28.1
requests==2.28.2
typing-extensions>=4.5.0,<5.0
voluptuous==0.13.1
voluptuous-serialize==2.6.0

View File

@ -907,7 +907,7 @@ hole==0.8.0
holidays==0.18.0
# homeassistant.components.frontend
home-assistant-frontend==20230301.0
home-assistant-frontend==20230302.0
# homeassistant.components.conversation
home-assistant-intents==2023.2.28
@ -1225,7 +1225,7 @@ nsapi==3.0.5
nsw-fuel-api-client==1.1.0
# homeassistant.components.nuheat
nuheat==1.0.0
nuheat==1.0.1
# homeassistant.components.numato
numato-gpio==0.10.0
@ -1430,7 +1430,7 @@ py-canary==0.5.3
py-cpuinfo==8.0.0
# homeassistant.components.dormakaba_dkey
py-dormakaba-dkey==1.0.3
py-dormakaba-dkey==1.0.4
# homeassistant.components.melissa
py-melissa-climate==2.1.4
@ -1473,7 +1473,7 @@ pyRFXtrx==0.30.1
pySwitchmate==0.5.1
# homeassistant.components.tibber
pyTibber==0.26.13
pyTibber==0.27.0
# homeassistant.components.dlink
pyW215==0.7.0

View File

@ -690,7 +690,7 @@ hole==0.8.0
holidays==0.18.0
# homeassistant.components.frontend
home-assistant-frontend==20230301.0
home-assistant-frontend==20230302.0
# homeassistant.components.conversation
home-assistant-intents==2023.2.28
@ -903,7 +903,7 @@ notify-events==1.0.4
nsw-fuel-api-client==1.1.0
# homeassistant.components.nuheat
nuheat==1.0.0
nuheat==1.0.1
# homeassistant.components.numato
numato-gpio==0.10.0
@ -1045,7 +1045,7 @@ py-canary==0.5.3
py-cpuinfo==8.0.0
# homeassistant.components.dormakaba_dkey
py-dormakaba-dkey==1.0.3
py-dormakaba-dkey==1.0.4
# homeassistant.components.melissa
py-melissa-climate==2.1.4
@ -1076,7 +1076,7 @@ pyMetno==0.9.0
pyRFXtrx==0.30.1
# homeassistant.components.tibber
pyTibber==0.26.13
pyTibber==0.27.0
# homeassistant.components.dlink
pyW215==0.7.0

View File

@ -77,16 +77,17 @@ def patch_file_upload(return_value=FIXTURE_KEYRING, side_effect=None):
side_effect=side_effect,
), patch(
"pathlib.Path.mkdir"
) as mkdir_mock:
file_path_mock = Mock()
file_upload_mock.return_value.__enter__.return_value = file_path_mock
) as mkdir_mock, patch(
"shutil.move"
) as shutil_move_mock:
file_upload_mock.return_value.__enter__.return_value = Mock()
yield return_value
if side_effect:
mkdir_mock.assert_not_called()
file_path_mock.rename.assert_not_called()
shutil_move_mock.assert_not_called()
else:
mkdir_mock.assert_called_once()
file_path_mock.rename.assert_called_once()
shutil_move_mock.assert_called_once()
def _gateway_descriptor(

View File

@ -205,6 +205,47 @@ async def test_datetime_conversion(
assert state.state == test_timestamp.isoformat()
async def test_a_sensor_with_a_non_numeric_device_class(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
enable_custom_integrations: None,
) -> None:
"""Test that a sensor with a non numeric device class will be non numeric.
A non numeric sensor with a valid device class should never be
handled as numeric because it has a device class.
"""
test_timestamp = datetime(2017, 12, 19, 18, 29, 42, tzinfo=timezone.utc)
test_local_timestamp = test_timestamp.astimezone(
dt_util.get_time_zone("Europe/Amsterdam")
)
platform = getattr(hass.components, "test.sensor")
platform.init(empty=True)
platform.ENTITIES["0"] = platform.MockSensor(
name="Test",
native_value=test_local_timestamp,
native_unit_of_measurement="",
device_class=SensorDeviceClass.TIMESTAMP,
)
platform.ENTITIES["1"] = platform.MockSensor(
name="Test",
native_value=test_local_timestamp,
state_class="",
device_class=SensorDeviceClass.TIMESTAMP,
)
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
await hass.async_block_till_done()
state = hass.states.get(platform.ENTITIES["0"].entity_id)
assert state.state == test_timestamp.isoformat()
state = hass.states.get(platform.ENTITIES["1"].entity_id)
assert state.state == test_timestamp.isoformat()
@pytest.mark.parametrize(
("device_class", "state_value", "provides"),
[