Compare commits

..

2 Commits

Author SHA1 Message Date
jbouwh
5f3bcee97e Refactor url fetch code to use base platform 2023-06-27 08:42:38 +02:00
jbouwh
51edc007fe Add url support for mqtt image platform 2023-06-27 08:42:38 +02:00
805 changed files with 6177 additions and 17204 deletions

View File

@@ -124,6 +124,7 @@ omit =
homeassistant/components/bluetooth_tracker/*
homeassistant/components/bmw_connected_drive/__init__.py
homeassistant/components/bmw_connected_drive/binary_sensor.py
homeassistant/components/bmw_connected_drive/button.py
homeassistant/components/bmw_connected_drive/coordinator.py
homeassistant/components/bmw_connected_drive/lock.py
homeassistant/components/bmw_connected_drive/notify.py
@@ -182,6 +183,7 @@ omit =
homeassistant/components/crownstone/listeners.py
homeassistant/components/cups/sensor.py
homeassistant/components/currencylayer/sensor.py
homeassistant/components/daikin/__init__.py
homeassistant/components/daikin/climate.py
homeassistant/components/daikin/sensor.py
homeassistant/components/daikin/switch.py
@@ -309,6 +311,7 @@ omit =
homeassistant/components/esphome/camera.py
homeassistant/components/esphome/domain_data.py
homeassistant/components/esphome/entry_data.py
homeassistant/components/esphome/light.py
homeassistant/components/etherscan/sensor.py
homeassistant/components/eufy/*
homeassistant/components/eufylife_ble/__init__.py
@@ -354,13 +357,10 @@ omit =
homeassistant/components/fitbit/*
homeassistant/components/fivem/__init__.py
homeassistant/components/fivem/binary_sensor.py
homeassistant/components/fivem/coordinator.py
homeassistant/components/fivem/entity.py
homeassistant/components/fivem/sensor.py
homeassistant/components/fixer/sensor.py
homeassistant/components/fjaraskupan/__init__.py
homeassistant/components/fjaraskupan/binary_sensor.py
homeassistant/components/fjaraskupan/coordinator.py
homeassistant/components/fjaraskupan/fan.py
homeassistant/components/fjaraskupan/light.py
homeassistant/components/fjaraskupan/number.py
@@ -940,8 +940,6 @@ omit =
homeassistant/components/pyload/sensor.py
homeassistant/components/qbittorrent/__init__.py
homeassistant/components/qbittorrent/sensor.py
homeassistant/components/qnap/__init__.py
homeassistant/components/qnap/coordinator.py
homeassistant/components/qnap/sensor.py
homeassistant/components/qrcode/image_processing.py
homeassistant/components/quantum_gateway/device_tracker.py
@@ -1046,6 +1044,12 @@ omit =
homeassistant/components/sense/__init__.py
homeassistant/components/sense/binary_sensor.py
homeassistant/components/sense/sensor.py
homeassistant/components/senseme/__init__.py
homeassistant/components/senseme/discovery.py
homeassistant/components/senseme/entity.py
homeassistant/components/senseme/fan.py
homeassistant/components/senseme/light.py
homeassistant/components/senseme/switch.py
homeassistant/components/senz/__init__.py
homeassistant/components/senz/api.py
homeassistant/components/senz/climate.py

View File

@@ -10,7 +10,7 @@ on:
env:
BUILD_TYPE: core
DEFAULT_PYTHON: "3.11"
DEFAULT_PYTHON: "3.10"
jobs:
init:
@@ -197,7 +197,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build base image
uses: home-assistant/builder@2023.06.1
uses: home-assistant/builder@2023.06.0
with:
args: |
$BUILD_ARGS \
@@ -274,7 +274,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build base image
uses: home-assistant/builder@2023.06.1
uses: home-assistant/builder@2023.06.0
with:
args: |
$BUILD_ARGS \
@@ -324,16 +324,12 @@ jobs:
if: github.repository_owner == 'home-assistant'
needs: ["init", "build_base"]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
- name: Install Cosign
uses: sigstore/cosign-installer@v3.1.1
uses: sigstore/cosign-installer@v3.0.5
with:
cosign-release: "v2.0.2"

View File

@@ -495,7 +495,7 @@ jobs:
pip install --cache-dir=$PIP_CACHE -U "pip>=21.3.1,<23.2" setuptools wheel
pip install --cache-dir=$PIP_CACHE -r requirements_all.txt
pip install --cache-dir=$PIP_CACHE -r requirements_test.txt
pip install -e . --config-settings editable_mode=compat
pip install .
hassfest:
name: Check hassfest

View File

@@ -10,7 +10,7 @@ on:
- "**strings.json"
env:
DEFAULT_PYTHON: "3.11"
DEFAULT_PYTHON: "3.10"
jobs:
upload:

View File

@@ -47,7 +47,10 @@ jobs:
echo "GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=true"
echo "GRPC_PYTHON_BUILD_WITH_CYTHON=true"
echo "GRPC_PYTHON_DISABLE_LIBC_COMPATIBILITY=true"
echo "GRPC_PYTHON_LDFLAGS=-lpthread -Wl,-wrap,memcpy -static-libgcc"
# GRPC on armv7 needs -lexecinfo (issue #56669) since home assistant installs
# execinfo-dev when building wheels. The setuptools build setup does not have an option for
# adding a single LDFLAG so copy all relevant linux flags here (as of 1.43.0)
echo "GRPC_PYTHON_LDFLAGS=-lpthread -Wl,-wrap,memcpy -static-libgcc -lexecinfo"
# Fix out of memory issues with rust
echo "CARGO_NET_GIT_FETCH_WITH_CLI=true"
@@ -80,7 +83,7 @@ jobs:
strategy:
fail-fast: false
matrix:
abi: ["cp311"]
abi: ["cp310", "cp311"]
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
@@ -110,7 +113,7 @@ jobs:
requirements-diff: "requirements_diff.txt"
requirements: "requirements.txt"
integrations_cp311:
integrations_cp310:
name: Build wheels ${{ matrix.abi }} for ${{ matrix.arch }}
if: github.repository_owner == 'home-assistant'
needs: init
@@ -118,7 +121,7 @@ jobs:
strategy:
fail-fast: false
matrix:
abi: ["cp311"]
abi: ["cp310"]
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
@@ -134,30 +137,20 @@ jobs:
with:
name: requirements_diff
- name: (Un)comment packages
- name: Uncomment packages
run: |
requirement_files="requirements_all.txt requirements_diff.txt"
for requirement_file in ${requirement_files}; do
sed -i "s|# pybluez|pybluez|g" ${requirement_file}
sed -i "s|# beacontools|beacontools|g" ${requirement_file}
sed -i "s|# fritzconnection|fritzconnection|g" ${requirement_file}
sed -i "s|# pyuserinput|pyuserinput|g" ${requirement_file}
sed -i "s|# evdev|evdev|g" ${requirement_file}
sed -i "s|# pycups|pycups|g" ${requirement_file}
sed -i "s|# homekit|homekit|g" ${requirement_file}
sed -i "s|# decora-wifi|decora-wifi|g" ${requirement_file}
sed -i "s|# decora_wifi|decora_wifi|g" ${requirement_file}
sed -i "s|# python-gammu|python-gammu|g" ${requirement_file}
# Some packages are not buildable on armhf anymore
if [ "${{ matrix.arch }}" = "armhf" ]; then
# Pandas has issues building on armhf, it is expected they
# will drop the platform in the near future (they consider it
# "flimsy" on 386). The following packages depend on pandas,
# so we comment them out.
sed -i "s|env-canada|# env-canada|g" ${requirement_file}
sed -i "s|noaa-coops|# noaa-coops|g" ${requirement_file}
sed -i "s|pyezviz|# pyezviz|g" ${requirement_file}
sed -i "s|pykrakenapi|# pykrakenapi|g" ${requirement_file}
fi
sed -i "s|# opencv-python-headless|opencv-python-headless|g" ${requirement_file}
done
- name: Split requirements all
@@ -174,6 +167,165 @@ jobs:
echo "NPY_DISABLE_SVML=1" >> .env_file
fi
(
# cmake > 3.22.2 have issue on arm
# Tested until 3.22.5
echo "cmake==3.22.2"
) >> homeassistant/package_constraints.txt
# Do not pin numpy in wheels building
sed -i "/numpy/d" homeassistant/package_constraints.txt
- name: Build wheels (part 1)
uses: home-assistant/wheels@2023.04.0
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
arch: ${{ matrix.arch }}
wheels-key: ${{ secrets.WHEELS_KEY }}
env-file: true
apk: "bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev;yaml-dev;openblas-dev;fftw-dev;lapack-dev;gfortran;blas-dev;eigen-dev;freetype-dev;glew-dev;harfbuzz-dev;hdf5-dev;libdc1394-dev;libtbb-dev;mesa-dev;openexr-dev;openjpeg-dev;uchardet-dev"
skip-binary: aiohttp;grpcio;sqlalchemy;protobuf
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
requirements: "requirements_all.txtaa"
- name: Build wheels (part 2)
uses: home-assistant/wheels@2023.04.0
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
arch: ${{ matrix.arch }}
wheels-key: ${{ secrets.WHEELS_KEY }}
env-file: true
apk: "bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev;yaml-dev;openblas-dev;fftw-dev;lapack-dev;gfortran;blas-dev;eigen-dev;freetype-dev;glew-dev;harfbuzz-dev;hdf5-dev;libdc1394-dev;libtbb-dev;mesa-dev;openexr-dev;openjpeg-dev;uchardet-dev"
skip-binary: aiohttp;grpcio;sqlalchemy;protobuf
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
requirements: "requirements_all.txtab"
- name: Build wheels (part 3)
uses: home-assistant/wheels@2023.04.0
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
arch: ${{ matrix.arch }}
wheels-key: ${{ secrets.WHEELS_KEY }}
env-file: true
apk: "bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev;yaml-dev;openblas-dev;fftw-dev;lapack-dev;gfortran;blas-dev;eigen-dev;freetype-dev;glew-dev;harfbuzz-dev;hdf5-dev;libdc1394-dev;libtbb-dev;mesa-dev;openexr-dev;openjpeg-dev;uchardet-dev"
skip-binary: aiohttp;grpcio;sqlalchemy;protobuf
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
requirements: "requirements_all.txtac"
# Wheels building for the cp311 ABI is currently split
# This is mainly until we have figured out to get all wheels built.
# Without harming our current workflow.
integrations_cp311:
name: Build wheels ${{ matrix.abi }} for ${{ matrix.arch }}
if: github.repository_owner == 'home-assistant'
needs: init
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
abi: ["cp311"]
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
- name: Write alternative env-file for cp311
run: |
(
echo "GRPC_BUILD_WITH_BORING_SSL_ASM=false"
echo "GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=true"
echo "GRPC_PYTHON_BUILD_WITH_CYTHON=true"
echo "GRPC_PYTHON_DISABLE_LIBC_COMPATIBILITY=true"
# GRPC on armv7 needed -lexecinfo (issue #56669) since home assistant installed
# execinfo-dev when building wheels. However, this package is no longer available
# Alpine 3.17, which we use for the cp311 ABI, so the flag should no longer be needed.
echo "GRPC_PYTHON_LDFLAGS=-lpthread -Wl,-wrap,memcpy -static-libgcc" # -lexecinfo
# Fix out of memory issues with rust
echo "CARGO_NET_GIT_FETCH_WITH_CLI=true"
# OpenCV headless installation
echo "CI_BUILD=1"
echo "ENABLE_HEADLESS=1"
# Use C-Extension for sqlalchemy
echo "REQUIRE_SQLALCHEMY_CEXT=1"
) > .env_file
- name: Download requirements_diff
uses: actions/download-artifact@v3
with:
name: requirements_diff
- name: (Un)comment packages
run: |
requirement_files="requirements_all.txt requirements_diff.txt"
for requirement_file in ${requirement_files}; do
# PyBluez no longer compiles. Commented it out for now.
# It need further cleanup down the line, as all machine images
# try to install it.
# sed -i "s|# pybluez|pybluez|g" ${requirement_file}
# beacontools requires PyBluez.
# sed -i "s|# beacontools|beacontools|g" ${requirement_file}
# It doesn't build for some reason, so we skip it for now.
# Bumping to the latest version (4.7.0.72) supporting Python 3.11
# doesn't help. Reverted bump in #91871. There are 8 registered
# instances using this integration according to analytics.
# sed -i "s|# opencv-python-headless|opencv-python-headless|g" ${requirement_file}
sed -i "s|# fritzconnection|fritzconnection|g" ${requirement_file}
sed -i "s|# pyuserinput|pyuserinput|g" ${requirement_file}
sed -i "s|# evdev|evdev|g" ${requirement_file}
sed -i "s|# pycups|pycups|g" ${requirement_file}
sed -i "s|# homekit|homekit|g" ${requirement_file}
sed -i "s|# decora-wifi|decora-wifi|g" ${requirement_file}
sed -i "s|# python-gammu|python-gammu|g" ${requirement_file}
# Some packages are not buildable on armhf anymore
if [ "${{ matrix.arch }}" = "armhf" ]; then
# Pandas has issues building on armhf, it is expected they
# will drop the platform in the near future (they consider it
# "flimsy" on 386). The following packages depend on pandas,
# so we comment them out.
sed -i "s|env-canada|# env-canada|g" ${requirement_file}
sed -i "s|noaa-coops|# noaa-coops|g" ${requirement_file}
sed -i "s|pyezviz|# pyezviz|g" ${requirement_file}
sed -i "s|pykrakenapi|# pykrakenapi|g" ${requirement_file}
fi
done
- name: Split requirements all
run: |
# We split requirements all into two different files.
# This is to prevent the build from running out of memory when
# resolving packages on 32-bits systems (like armhf, armv7).
split -l $(expr $(expr $(cat requirements_all.txt | wc -l) + 1) / 3) requirements_all.txt requirements_all.txt
- name: Adjust build env
run: |
if [ "${{ matrix.arch }}" = "i386" ]; then
echo "NPY_DISABLE_SVML=1" >> .env_file
fi
# Probably not an issue anymore. Removing for now.
# (
# # cmake > 3.22.2 have issue on arm
# # Tested until 3.22.5
# echo "cmake==3.22.2"
# ) >> homeassistant/package_constraints.txt
# Do not pin numpy in wheels building
sed -i "/numpy/d" homeassistant/package_constraints.txt

View File

@@ -277,6 +277,7 @@ homeassistant.components.scene.*
homeassistant.components.schedule.*
homeassistant.components.scrape.*
homeassistant.components.select.*
homeassistant.components.senseme.*
homeassistant.components.sensibo.*
homeassistant.components.sensirion_ble.*
homeassistant.components.sensor.*

View File

@@ -703,8 +703,6 @@ build.json @home-assistant/supervisor
/tests/components/logi_circle/ @evanjd
/homeassistant/components/lookin/ @ANMalko @bdraco
/tests/components/lookin/ @ANMalko @bdraco
/homeassistant/components/loqed/ @mikewoudenberg
/tests/components/loqed/ @mikewoudenberg
/homeassistant/components/lovelace/ @home-assistant/frontend
/tests/components/lovelace/ @home-assistant/frontend
/homeassistant/components/luci/ @mzdrale
@@ -972,7 +970,6 @@ build.json @home-assistant/supervisor
/homeassistant/components/qld_bushfire/ @exxamalte
/tests/components/qld_bushfire/ @exxamalte
/homeassistant/components/qnap/ @disforw
/tests/components/qnap/ @disforw
/homeassistant/components/qnap_qsw/ @Noltari
/tests/components/qnap_qsw/ @Noltari
/homeassistant/components/quantum_gateway/ @cisasteelersfan
@@ -1082,6 +1079,8 @@ build.json @home-assistant/supervisor
/tests/components/select/ @home-assistant/core
/homeassistant/components/sense/ @kbickar
/tests/components/sense/ @kbickar
/homeassistant/components/senseme/ @mikelawrence @bdraco
/tests/components/senseme/ @mikelawrence @bdraco
/homeassistant/components/sensibo/ @andrey-git @gjohansson-ST
/tests/components/sensibo/ @andrey-git @gjohansson-ST
/homeassistant/components/sensirion_ble/ @akx

View File

@@ -34,7 +34,6 @@ class AbodeAlarm(AbodeDevice, alarm.AlarmControlPanelEntity):
"""An alarm_control_panel implementation for Abode."""
_attr_icon = ICON
_attr_name = None
_attr_code_arm_required = False
_attr_supported_features = (
AlarmControlPanelEntityFeature.ARM_HOME

View File

@@ -39,7 +39,6 @@ class AbodeCamera(AbodeDevice, Camera):
"""Representation of an Abode camera."""
_device: AbodeCam
_attr_name = None
def __init__(self, data: AbodeSystem, device: AbodeDev, event: Event) -> None:
"""Initialize the Abode device."""

View File

@@ -29,7 +29,6 @@ class AbodeCover(AbodeDevice, CoverEntity):
"""Representation of an Abode cover."""
_device: AbodeCV
_attr_name = None
@property
def is_closed(self) -> bool:

View File

@@ -42,7 +42,6 @@ class AbodeLight(AbodeDevice, LightEntity):
"""Representation of an Abode light."""
_device: AbodeLT
_attr_name = None
def turn_on(self, **kwargs: Any) -> None:
"""Turn on the light."""

View File

@@ -29,7 +29,6 @@ class AbodeLock(AbodeDevice, LockEntity):
"""Representation of an Abode lock."""
_device: AbodeLK
_attr_name = None
def lock(self, **kwargs: Any) -> None:
"""Lock the device."""

View File

@@ -53,6 +53,7 @@ class AbodeSensor(AbodeDevice, SensorEntity):
"""A sensor implementation for Abode devices."""
_device: AbodeSense
_attr_has_entity_name = True
def __init__(
self,

View File

@@ -44,7 +44,6 @@ class AbodeSwitch(AbodeDevice, SwitchEntity):
"""Representation of an Abode switch."""
_device: AbodeSW
_attr_name = None
def turn_on(self, **kwargs: Any) -> None:
"""Turn on the device."""

View File

@@ -90,7 +90,6 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
_attr_target_temperature_step = PRECISION_WHOLE
_attr_max_temp = 32
_attr_min_temp = 16
_attr_name = None
_attr_hvac_modes = [
HVACMode.OFF,

View File

@@ -9,30 +9,17 @@ from homeassistant.core import HomeAssistant
from .const import DOMAIN as ADVANTAGE_AIR_DOMAIN
TO_REDACT = [
"dealerPhoneNumber",
"latitude",
"logoPIN",
"longitude",
"postCode",
"rid",
"deviceNames",
"deviceIds",
"deviceIdsV2",
"backupId",
]
TO_REDACT = ["dealerPhoneNumber", "latitude", "logoPIN", "longitude", "postCode"]
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
data = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id].coordinator.data
data = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]["coordinator"].data
# Return only the relevant children
return {
"aircons": data.get("aircons"),
"myLights": data.get("myLights"),
"myThings": data.get("myThings"),
"aircons": data["aircons"],
"system": async_redact_data(data["system"], TO_REDACT),
}

View File

@@ -84,8 +84,6 @@ class AdvantageAirZoneEntity(AdvantageAirAcEntity):
class AdvantageAirThingEntity(AdvantageAirEntity):
"""Parent class for Advantage Air Things Entities."""
_attr_name = None
def __init__(self, instance: AdvantageAirData, thing: dict[str, Any]) -> None:
"""Initialize common aspects of an Advantage Air Things entity."""
super().__init__(instance)

View File

@@ -41,7 +41,6 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
"""Representation of Advantage Air Light."""
_attr_supported_color_modes = {ColorMode.ONOFF}
_attr_name = None
def __init__(self, instance: AdvantageAirData, light: dict[str, Any]) -> None:
"""Initialize an Advantage Air Light."""

View File

@@ -17,7 +17,7 @@
"api_key": "[%key:common::config_flow::data::api_key%]",
"city": "City",
"country": "Country",
"state": "State"
"state": "state"
}
},
"reauth_confirm": {

View File

@@ -193,8 +193,6 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity):
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set hvac mode."""
slave_raise = False
params = {}
if hvac_mode == HVACMode.OFF:
params[API_ON] = 0
@@ -204,13 +202,12 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity):
if self.get_airzone_value(AZD_MASTER):
params[API_MODE] = mode
else:
slave_raise = True
raise HomeAssistantError(
f"Mode can't be changed on slave zone {self.name}"
)
params[API_ON] = 1
await self._async_update_hvac_params(params)
if slave_raise:
raise HomeAssistantError(f"Mode can't be changed on slave zone {self.name}")
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
params = {}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/airzone_cloud",
"iot_class": "cloud_polling",
"loggers": ["aioairzone_cloud"],
"requirements": ["aioairzone-cloud==0.2.0"]
"requirements": ["aioairzone-cloud==0.1.9"]
}

View File

@@ -21,22 +21,12 @@ from homeassistant.components.recorder import (
DOMAIN as RECORDER_DOMAIN,
get_instance as get_recorder_instance,
)
import homeassistant.config as conf_util
from homeassistant.config_entries import (
SOURCE_IGNORE,
)
from homeassistant.const import ATTR_DOMAIN, __version__ as HA_VERSION
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.entity_registry as er
from homeassistant.helpers.storage import Store
from homeassistant.helpers.system_info import async_get_system_info
from homeassistant.loader import (
Integration,
IntegrationNotFound,
async_get_integrations,
)
from homeassistant.loader import IntegrationNotFound, async_get_integrations
from homeassistant.setup import async_get_loaded_integrations
from .const import (
@@ -216,25 +206,8 @@ class Analytics:
if self.preferences.get(ATTR_USAGE, False) or self.preferences.get(
ATTR_STATISTICS, False
):
ent_reg = er.async_get(self.hass)
try:
yaml_configuration = await conf_util.async_hass_config_yaml(self.hass)
except HomeAssistantError as err:
LOGGER.error(err)
return
configuration_set = set(yaml_configuration)
er_platforms = {
entity.platform
for entity in ent_reg.entities.values()
if not entity.disabled
}
domains = async_get_loaded_integrations(self.hass)
configured_integrations = await async_get_integrations(self.hass, domains)
enabled_domains = set(configured_integrations)
for integration in configured_integrations.values():
if isinstance(integration, IntegrationNotFound):
continue
@@ -242,11 +215,7 @@ class Analytics:
if isinstance(integration, BaseException):
raise integration
if not self._async_should_report_integration(
integration=integration,
yaml_domains=configuration_set,
entity_registry_platforms=er_platforms,
):
if integration.disabled:
continue
if not integration.is_built_in:
@@ -284,12 +253,12 @@ class Analytics:
if supervisor_info is not None:
payload[ATTR_ADDONS] = addons
if ENERGY_DOMAIN in enabled_domains:
if ENERGY_DOMAIN in integrations:
payload[ATTR_ENERGY] = {
ATTR_CONFIGURED: await energy_is_configured(self.hass)
}
if RECORDER_DOMAIN in enabled_domains:
if RECORDER_DOMAIN in integrations:
instance = get_recorder_instance(self.hass)
engine = instance.database_engine
if engine and engine.version is not None:
@@ -337,34 +306,3 @@ class Analytics:
LOGGER.error(
"Error sending analytics to %s: %r", ANALYTICS_ENDPOINT_URL, err
)
@callback
def _async_should_report_integration(
self,
integration: Integration,
yaml_domains: set[str],
entity_registry_platforms: set[str],
) -> bool:
"""Return a bool to indicate if this integration should be reported."""
if integration.disabled:
return False
# Check if the integration is defined in YAML or in the entity registry
if (
integration.domain in yaml_domains
or integration.domain in entity_registry_platforms
):
return True
# Check if the integration provide a config flow
if not integration.config_flow:
return False
entries = self.hass.config_entries.async_entries(integration.domain)
# Filter out ignored and disabled entries
return any(
entry
for entry in entries
if entry.source != SOURCE_IGNORE and entry.disabled_by is None
)

View File

@@ -296,6 +296,7 @@ class ADBDevice(MediaPlayerEntity):
self._process_config,
)
)
return
@property
def media_image_hash(self) -> str | None:

View File

@@ -1,7 +1,6 @@
"""The Android TV Remote integration."""
from __future__ import annotations
import asyncio
import logging
from androidtvremote2 import (
@@ -10,7 +9,6 @@ from androidtvremote2 import (
ConnectionClosed,
InvalidAuth,
)
import async_timeout
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_NAME, EVENT_HOMEASSISTANT_STOP, Platform
@@ -45,12 +43,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
api.add_is_available_updated_callback(is_available_updated)
try:
async with async_timeout.timeout(5.0):
await api.async_connect()
await api.async_connect()
except InvalidAuth as exc:
# The Android TV is hard reset or the certificate and key files were deleted.
raise ConfigEntryAuthFailed from exc
except (CannotConnect, ConnectionClosed, asyncio.TimeoutError) as exc:
except (CannotConnect, ConnectionClosed) as exc:
# The Android TV is network unreachable. Raise exception and let Home Assistant retry
# later. If device gets a new IP address the zeroconf flow will update the config.
raise ConfigEntryNotReady from exc

View File

@@ -16,7 +16,6 @@ from .const import DOMAIN
class AndroidTVRemoteBaseEntity(Entity):
"""Android TV Remote Base Entity."""
_attr_name = None
_attr_has_entity_name = True
_attr_should_poll = False

View File

@@ -80,7 +80,6 @@ class AnthemAVR(MediaPlayerEntity):
self._attr_name = f"zone {zone_number}"
self._attr_unique_id = f"{mac_address}_{zone_number}"
else:
self._attr_name = None
self._attr_unique_id = mac_address
self._attr_device_info = DeviceInfo(

View File

@@ -7,7 +7,7 @@
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
"iot_class": "local_push",
"loggers": ["pyatv", "srptools"],
"requirements": ["pyatv==0.13.2"],
"requirements": ["pyatv==0.13.0"],
"zeroconf": [
"_mediaremotetv._tcp.local.",
"_companion-link._tcp.local.",
@@ -16,24 +16,7 @@
"_touch-able._tcp.local.",
"_appletv-v2._tcp.local.",
"_hscp._tcp.local.",
{
"type": "_airplay._tcp.local.",
"properties": {
"model": "appletv*"
}
},
{
"type": "_airplay._tcp.local.",
"properties": {
"model": "audioaccessory*"
}
},
{
"type": "_airplay._tcp.local.",
"properties": {
"am": "airport*"
}
},
"_airplay._tcp.local.",
{
"type": "_raop._tcp.local.",
"properties": {

View File

@@ -25,7 +25,6 @@ from homeassistant.const import (
ATTR_SW_VERSION,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
EntityCategory,
UnitOfPressure,
UnitOfTemperature,
UnitOfTime,
@@ -82,7 +81,6 @@ SENSOR_DESCRIPTIONS = {
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
),
"interval": AranetSensorEntityDescription(
key="update_interval",
@@ -92,7 +90,6 @@ SENSOR_DESCRIPTIONS = {
state_class=SensorStateClass.MEASUREMENT,
# The interval setting is not a generally useful entity for most users.
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
}

View File

@@ -3,9 +3,7 @@ from __future__ import annotations
import voluptuous as vol
from homeassistant.components.device_automation import (
DEVICE_TRIGGER_BASE_SCHEMA,
)
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_DEVICE_ID,
@@ -24,7 +22,7 @@ from .const import DOMAIN, EVENT_TURN_ON
TRIGGER_TYPES = {"turn_on"}
TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
{
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_TYPE): vol.In(TRIGGER_TYPES),
}
)
@@ -45,7 +43,7 @@ async def async_get_triggers(
CONF_PLATFORM: "device",
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_ENTITY_ID: entry.id,
CONF_ENTITY_ID: entry.entity_id,
CONF_TYPE: "turn_on",
}
)
@@ -64,8 +62,7 @@ async def async_attach_trigger(
job = HassJob(action)
if config[CONF_TYPE] == "turn_on":
registry = er.async_get(hass)
entity_id = er.async_resolve_entity_id(registry, config[ATTR_ENTITY_ID])
entity_id = config[CONF_ENTITY_ID]
@callback
def _handle_event(event: Event) -> None:
@@ -77,7 +74,6 @@ async def async_attach_trigger(
**trigger_data,
**config,
"description": f"{DOMAIN} - {entity_id}",
"entity_id": entity_id,
}
},
event.context,

View File

@@ -137,15 +137,16 @@ class VoiceCommandSegmenter:
self._reset_seconds_left -= self._seconds_per_chunk
if self._reset_seconds_left <= 0:
self._speech_seconds_left = self.speech_seconds
elif not is_speech:
self._reset_seconds_left = self.reset_seconds
self._silence_seconds_left -= self._seconds_per_chunk
if self._silence_seconds_left <= 0:
return False
else:
# Reset if enough speech
self._reset_seconds_left -= self._seconds_per_chunk
if self._reset_seconds_left <= 0:
self._silence_seconds_left = self.silence_seconds
if not is_speech:
self._reset_seconds_left = self.reset_seconds
self._silence_seconds_left -= self._seconds_per_chunk
if self._silence_seconds_left <= 0:
return False
else:
# Reset if enough speech
self._reset_seconds_left -= self._seconds_per_chunk
if self._reset_seconds_left <= 0:
self._silence_seconds_left = self.silence_seconds
return True

View File

@@ -34,7 +34,7 @@ SENSOR_TYPES = [
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
translation_key="power_output",
name="Power Output",
),
SensorEntityDescription(
key="temp",
@@ -42,13 +42,14 @@ SENSOR_TYPES = [
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
name="Temperature",
),
SensorEntityDescription(
key="totalenergy",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
translation_key="total_energy",
name="Total Energy",
),
]
@@ -74,8 +75,6 @@ async def async_setup_entry(
class AuroraSensor(AuroraEntity, SensorEntity):
"""Representation of a Sensor on a Aurora ABB PowerOne Solar inverter."""
_attr_has_entity_name = True
def __init__(
self,
client: AuroraSerialClient,

View File

@@ -18,15 +18,5 @@
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"no_serial_ports": "No com ports found. Need a valid RS485 device to communicate."
}
},
"entity": {
"sensor": {
"power_output": {
"name": "Power Output"
},
"total_energy": {
"name": "Total Energy"
}
}
}
}

View File

@@ -1,7 +1,6 @@
"""Allow to set up simple automation rules via the config file."""
from __future__ import annotations
from abc import ABC, abstractmethod
import asyncio
from collections.abc import Callable, Mapping
from dataclasses import dataclass
@@ -154,7 +153,7 @@ def _automations_with_x(
if DOMAIN not in hass.data:
return []
component: EntityComponent[BaseAutomationEntity] = hass.data[DOMAIN]
component: EntityComponent[AutomationEntity] = hass.data[DOMAIN]
return [
automation_entity.entity_id
@@ -170,7 +169,7 @@ def _x_in_automation(
if DOMAIN not in hass.data:
return []
component: EntityComponent[BaseAutomationEntity] = hass.data[DOMAIN]
component: EntityComponent[AutomationEntity] = hass.data[DOMAIN]
if (automation_entity := component.get_entity(entity_id)) is None:
return []
@@ -220,7 +219,7 @@ def automations_with_blueprint(hass: HomeAssistant, blueprint_path: str) -> list
if DOMAIN not in hass.data:
return []
component: EntityComponent[BaseAutomationEntity] = hass.data[DOMAIN]
component: EntityComponent[AutomationEntity] = hass.data[DOMAIN]
return [
automation_entity.entity_id
@@ -235,7 +234,7 @@ def blueprint_in_automation(hass: HomeAssistant, entity_id: str) -> str | None:
if DOMAIN not in hass.data:
return None
component: EntityComponent[BaseAutomationEntity] = hass.data[DOMAIN]
component: EntityComponent[AutomationEntity] = hass.data[DOMAIN]
if (automation_entity := component.get_entity(entity_id)) is None:
return None
@@ -245,7 +244,7 @@ def blueprint_in_automation(hass: HomeAssistant, entity_id: str) -> str | None:
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up all automations."""
hass.data[DOMAIN] = component = EntityComponent[BaseAutomationEntity](
hass.data[DOMAIN] = component = EntityComponent[AutomationEntity](
LOGGER, DOMAIN, hass
)
@@ -263,7 +262,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
await async_get_blueprints(hass).async_populate()
async def trigger_service_handler(
entity: BaseAutomationEntity, service_call: ServiceCall
entity: AutomationEntity, service_call: ServiceCall
) -> None:
"""Handle forced automation trigger, e.g. from frontend."""
await entity.async_trigger(
@@ -311,103 +310,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return True
class BaseAutomationEntity(ToggleEntity, ABC):
"""Base class for automation entities."""
raw_config: ConfigType | None
@property
def capability_attributes(self) -> dict[str, Any] | None:
"""Return capability attributes."""
if self.unique_id is not None:
return {CONF_ID: self.unique_id}
return None
@property
@abstractmethod
def referenced_areas(self) -> set[str]:
"""Return a set of referenced areas."""
@property
@abstractmethod
def referenced_blueprint(self) -> str | None:
"""Return referenced blueprint or None."""
@property
@abstractmethod
def referenced_devices(self) -> set[str]:
"""Return a set of referenced devices."""
@property
@abstractmethod
def referenced_entities(self) -> set[str]:
"""Return a set of referenced entities."""
@abstractmethod
async def async_trigger(
self,
run_variables: dict[str, Any],
context: Context | None = None,
skip_condition: bool = False,
) -> None:
"""Trigger automation."""
class UnavailableAutomationEntity(BaseAutomationEntity):
"""A non-functional automation entity with its state set to unavailable.
This class is instatiated when an automation fails to validate.
"""
_attr_should_poll = False
_attr_available = False
def __init__(
self,
automation_id: str | None,
name: str,
raw_config: ConfigType | None,
) -> None:
"""Initialize an automation entity."""
self._name = name
self._attr_unique_id = automation_id
self.raw_config = raw_config
@property
def name(self) -> str:
"""Return the name of the entity."""
return self._name
@property
def referenced_areas(self) -> set[str]:
"""Return a set of referenced areas."""
return set()
@property
def referenced_blueprint(self) -> str | None:
"""Return referenced blueprint or None."""
return None
@property
def referenced_devices(self) -> set[str]:
"""Return a set of referenced devices."""
return set()
@property
def referenced_entities(self) -> set[str]:
"""Return a set of referenced entities."""
return set()
async def async_trigger(
self,
run_variables: dict[str, Any],
context: Context | None = None,
skip_condition: bool = False,
) -> None:
"""Trigger automation."""
class AutomationEntity(BaseAutomationEntity, RestoreEntity):
class AutomationEntity(ToggleEntity, RestoreEntity):
"""Entity to show status of entity."""
_attr_should_poll = False
@@ -460,6 +363,8 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
}
if self.action_script.supports_max:
attrs[ATTR_MAX] = self.action_script.max_runs
if self.unique_id is not None:
attrs[CONF_ID] = self.unique_id
return attrs
@property
@@ -781,7 +686,6 @@ class AutomationEntityConfig:
list_no: int
raw_blueprint_inputs: ConfigType | None
raw_config: ConfigType | None
validation_failed: bool
async def _prepare_automation_config(
@@ -796,14 +700,9 @@ async def _prepare_automation_config(
for list_no, config_block in enumerate(conf):
raw_config = cast(AutomationConfig, config_block).raw_config
raw_blueprint_inputs = cast(AutomationConfig, config_block).raw_blueprint_inputs
validation_failed = cast(AutomationConfig, config_block).validation_failed
automation_configs.append(
AutomationEntityConfig(
config_block,
list_no,
raw_blueprint_inputs,
raw_config,
validation_failed,
config_block, list_no, raw_blueprint_inputs, raw_config
)
)
@@ -819,9 +718,9 @@ def _automation_name(automation_config: AutomationEntityConfig) -> str:
async def _create_automation_entities(
hass: HomeAssistant, automation_configs: list[AutomationEntityConfig]
) -> list[BaseAutomationEntity]:
) -> list[AutomationEntity]:
"""Create automation entities from prepared configuration."""
entities: list[BaseAutomationEntity] = []
entities: list[AutomationEntity] = []
for automation_config in automation_configs:
config_block = automation_config.config_block
@@ -829,16 +728,6 @@ async def _create_automation_entities(
automation_id: str | None = config_block.get(CONF_ID)
name = _automation_name(automation_config)
if automation_config.validation_failed:
entities.append(
UnavailableAutomationEntity(
automation_id,
name,
automation_config.raw_config,
)
)
continue
initial_state: bool | None = config_block.get(CONF_INITIAL_STATE)
action_script = Script(
@@ -897,18 +786,18 @@ async def _create_automation_entities(
async def _async_process_config(
hass: HomeAssistant,
config: dict[str, Any],
component: EntityComponent[BaseAutomationEntity],
component: EntityComponent[AutomationEntity],
) -> None:
"""Process config and add automations."""
def automation_matches_config(
automation: BaseAutomationEntity, config: AutomationEntityConfig
automation: AutomationEntity, config: AutomationEntityConfig
) -> bool:
name = _automation_name(config)
return automation.name == name and automation.raw_config == config.raw_config
def find_matches(
automations: list[BaseAutomationEntity],
automations: list[AutomationEntity],
automation_configs: list[AutomationEntityConfig],
) -> tuple[set[int], set[int]]:
"""Find matches between a list of automation entities and a list of configurations.
@@ -954,7 +843,7 @@ async def _async_process_config(
return automation_matches, config_matches
automation_configs = await _prepare_automation_config(hass, config)
automations: list[BaseAutomationEntity] = list(component.entities)
automations: list[AutomationEntity] = list(component.entities)
# Find automations and configurations which have matches
automation_matches, config_matches = find_matches(automations, automation_configs)
@@ -976,6 +865,8 @@ async def _async_process_config(
entities = await _create_automation_entities(hass, updated_automation_configs)
await component.async_add_entities(entities)
return
async def _async_process_if(
hass: HomeAssistant, name: str, config: dict[str, Any]
@@ -1079,7 +970,7 @@ def websocket_config(
msg: dict[str, Any],
) -> None:
"""Get automation config."""
component: EntityComponent[BaseAutomationEntity] = hass.data[DOMAIN]
component: EntityComponent[AutomationEntity] = hass.data[DOMAIN]
automation = component.get_entity(msg["entity_id"])

View File

@@ -41,15 +41,7 @@ from .helpers import async_get_blueprints
PACKAGE_MERGE_HINT = "list"
_MINIMAL_PLATFORM_SCHEMA = vol.Schema(
{
CONF_ID: str,
CONF_ALIAS: cv.string,
vol.Optional(CONF_DESCRIPTION): cv.string,
},
extra=vol.ALLOW_EXTRA,
)
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
PLATFORM_SCHEMA = vol.All(
cv.deprecated(CONF_HIDE_ENTITY),
@@ -63,7 +55,7 @@ PLATFORM_SCHEMA = vol.All(
vol.Optional(CONF_INITIAL_STATE): cv.boolean,
vol.Optional(CONF_HIDE_ENTITY): cv.boolean,
vol.Required(CONF_TRIGGER): cv.TRIGGER_SCHEMA,
vol.Optional(CONF_CONDITION): cv.CONDITIONS_SCHEMA,
vol.Optional(CONF_CONDITION): _CONDITION_SCHEMA,
vol.Optional(CONF_VARIABLES): cv.SCRIPT_VARIABLES_SCHEMA,
vol.Optional(CONF_TRIGGER_VARIABLES): cv.SCRIPT_VARIABLES_SCHEMA,
vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA,
@@ -76,7 +68,6 @@ PLATFORM_SCHEMA = vol.All(
async def _async_validate_config_item(
hass: HomeAssistant,
config: ConfigType,
raise_on_errors: bool,
warn_on_errors: bool,
) -> AutomationConfig:
"""Validate config item."""
@@ -113,15 +104,6 @@ async def _async_validate_config_item(
)
return
def _minimal_config() -> AutomationConfig:
"""Try validating id, alias and description."""
minimal_config = _MINIMAL_PLATFORM_SCHEMA(config)
automation_config = AutomationConfig(minimal_config)
automation_config.raw_blueprint_inputs = raw_blueprint_inputs
automation_config.raw_config = raw_config
automation_config.validation_failed = True
return automation_config
if blueprint.is_blueprint_instance_config(config):
uses_blueprint = True
blueprints = async_get_blueprints(hass)
@@ -133,9 +115,7 @@ async def _async_validate_config_item(
"Failed to generate automation from blueprint: %s",
err,
)
if raise_on_errors:
raise
return _minimal_config()
raise
raw_blueprint_inputs = blueprint_inputs.config_with_inputs
@@ -150,9 +130,7 @@ async def _async_validate_config_item(
blueprint_inputs.inputs,
err,
)
if raise_on_errors:
raise HomeAssistantError(err) from err
return _minimal_config()
raise HomeAssistantError from err
automation_name = "Unnamed automation"
if isinstance(config, Mapping):
@@ -165,16 +143,10 @@ async def _async_validate_config_item(
validated_config = PLATFORM_SCHEMA(config)
except vol.Invalid as err:
_log_invalid_automation(err, automation_name, "could not be validated", config)
if raise_on_errors:
raise
return _minimal_config()
automation_config = AutomationConfig(validated_config)
automation_config.raw_blueprint_inputs = raw_blueprint_inputs
automation_config.raw_config = raw_config
raise
try:
automation_config[CONF_TRIGGER] = await async_validate_trigger_config(
validated_config[CONF_TRIGGER] = await async_validate_trigger_config(
hass, validated_config[CONF_TRIGGER]
)
except (
@@ -184,14 +156,11 @@ async def _async_validate_config_item(
_log_invalid_automation(
err, automation_name, "failed to setup triggers", validated_config
)
if raise_on_errors:
raise
automation_config.validation_failed = True
return automation_config
raise
if CONF_CONDITION in validated_config:
try:
automation_config[CONF_CONDITION] = await async_validate_conditions_config(
validated_config[CONF_CONDITION] = await async_validate_conditions_config(
hass, validated_config[CONF_CONDITION]
)
except (
@@ -201,13 +170,10 @@ async def _async_validate_config_item(
_log_invalid_automation(
err, automation_name, "failed to setup conditions", validated_config
)
if raise_on_errors:
raise
automation_config.validation_failed = True
return automation_config
raise
try:
automation_config[CONF_ACTION] = await script.async_validate_actions_config(
validated_config[CONF_ACTION] = await script.async_validate_actions_config(
hass, validated_config[CONF_ACTION]
)
except (
@@ -217,11 +183,11 @@ async def _async_validate_config_item(
_log_invalid_automation(
err, automation_name, "failed to setup actions", validated_config
)
if raise_on_errors:
raise
automation_config.validation_failed = True
return automation_config
raise
automation_config = AutomationConfig(validated_config)
automation_config.raw_blueprint_inputs = raw_blueprint_inputs
automation_config.raw_config = raw_config
return automation_config
@@ -230,7 +196,6 @@ class AutomationConfig(dict):
raw_config: dict[str, Any] | None = None
raw_blueprint_inputs: dict[str, Any] | None = None
validation_failed: bool = False
async def _try_async_validate_config_item(
@@ -239,7 +204,7 @@ async def _try_async_validate_config_item(
) -> AutomationConfig | None:
"""Validate config item."""
try:
return await _async_validate_config_item(hass, config, False, True)
return await _async_validate_config_item(hass, config, True)
except (vol.Invalid, HomeAssistantError):
return None
@@ -250,7 +215,7 @@ async def async_validate_config_item(
config: dict[str, Any],
) -> AutomationConfig | None:
"""Validate config item, called by EditAutomationConfigView."""
return await _async_validate_config_item(hass, config, True, False)
return await _async_validate_config_item(hass, config, False)
async def async_validate_config(hass: HomeAssistant, config: ConfigType) -> ConfigType:

View File

@@ -112,8 +112,8 @@ ENTITY_TRIGGERS = {
{CONF_TYPE: CONF_NO_LIGHT},
],
BinarySensorDeviceClass.LOCK: [
{CONF_TYPE: CONF_NOT_LOCKED},
{CONF_TYPE: CONF_LOCKED},
{CONF_TYPE: CONF_NOT_LOCKED},
],
BinarySensorDeviceClass.MOISTURE: [
{CONF_TYPE: CONF_MOIST},

View File

@@ -302,7 +302,7 @@
}
},
"device_class": {
"co": "carbon monoxide",
"co": "carbon_monoxide",
"cold": "cold",
"gas": "gas",
"heat": "heat",

View File

@@ -42,7 +42,6 @@ class BlinkSyncModule(AlarmControlPanelEntity):
_attr_icon = ICON
_attr_supported_features = AlarmControlPanelEntityFeature.ARM_AWAY
_attr_name = None
_attr_has_entity_name = True
def __init__(self, data, name, sync):
"""Initialize the alarm control panel."""

View File

@@ -58,14 +58,13 @@ async def async_setup_entry(
class BlinkBinarySensor(BinarySensorEntity):
"""Representation of a Blink binary sensor."""
_attr_has_entity_name = True
def __init__(
self, data, camera, description: BinarySensorEntityDescription
) -> None:
"""Initialize the sensor."""
self.data = data
self.entity_description = description
self._attr_name = f"{DOMAIN} {camera} {description.name}"
self._camera = data.cameras[camera]
self._attr_unique_id = f"{self._camera.serial}-{description.key}"
self._attr_device_info = DeviceInfo(

View File

@@ -38,7 +38,6 @@ async def async_setup_entry(
class BlinkCamera(Camera):
"""An implementation of a Blink Camera."""
_attr_has_entity_name = True
_attr_name = None
def __init__(self, data, name, camera):

View File

@@ -1,8 +1,6 @@
"""Import logic for blueprint."""
from __future__ import annotations
from contextlib import suppress
from dataclasses import dataclass
import html
import re
@@ -30,10 +28,6 @@ GITHUB_FILE_PATTERN = re.compile(
r"^https://github.com/(?P<repository>.+)/blob/(?P<path>.+)$"
)
WEBSITE_PATTERN = re.compile(
r"^https://(?P<subdomain>[a-z0-9-]+)\.home-assistant\.io/(?P<path>.+).yaml$"
)
COMMUNITY_TOPIC_SCHEMA = vol.Schema(
{
"slug": str,
@@ -225,37 +219,18 @@ async def fetch_blueprint_from_github_gist_url(
)
async def fetch_blueprint_from_website_url(
hass: HomeAssistant, url: str
) -> ImportedBlueprint:
"""Get a blueprint from our website."""
if (WEBSITE_PATTERN.match(url)) is None:
raise UnsupportedUrl("Not a Home Assistant website URL")
session = aiohttp_client.async_get_clientsession(hass)
resp = await session.get(url, raise_for_status=True)
raw_yaml = await resp.text()
data = yaml.parse_yaml(raw_yaml)
assert isinstance(data, dict)
blueprint = Blueprint(data)
parsed_import_url = yarl.URL(url)
suggested_filename = f"homeassistant/{parsed_import_url.parts[-1][:-5]}"
return ImportedBlueprint(suggested_filename, raw_yaml, blueprint)
async def fetch_blueprint_from_url(hass: HomeAssistant, url: str) -> ImportedBlueprint:
"""Get a blueprint from a url."""
for func in (
fetch_blueprint_from_community_post,
fetch_blueprint_from_github_url,
fetch_blueprint_from_github_gist_url,
fetch_blueprint_from_website_url,
):
with suppress(UnsupportedUrl):
try:
imported_bp = await func(hass, url)
imported_bp.blueprint.update_metadata(source_url=url)
return imported_bp
except UnsupportedUrl:
pass
raise HomeAssistantError("Unsupported URL")
raise HomeAssistantError("Unsupported url")

View File

@@ -18,7 +18,7 @@
"bleak-retry-connector==3.0.2",
"bluetooth-adapters==0.15.3",
"bluetooth-auto-recovery==1.2.0",
"bluetooth-data-tools==1.3.0",
"bluetooth-data-tools==1.2.0",
"dbus-fast==1.86.0"
]
}

View File

@@ -34,7 +34,6 @@ class BMWButtonEntityDescription(ButtonEntityDescription):
[MyBMWVehicle], Coroutine[Any, Any, RemoteServiceStatus]
] | None = None
account_function: Callable[[BMWDataUpdateCoordinator], Coroutine] | None = None
is_available: Callable[[MyBMWVehicle], bool] = lambda _: True
BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = (
@@ -56,13 +55,6 @@ BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = (
icon="mdi:hvac",
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning(),
),
BMWButtonEntityDescription(
key="deactivate_air_conditioning",
icon="mdi:hvac-off",
name="Deactivate air conditioning",
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning_stop(),
is_available=lambda vehicle: vehicle.is_remote_climate_stop_enabled,
),
BMWButtonEntityDescription(
key="find_vehicle",
translation_key="find_vehicle",
@@ -94,7 +86,7 @@ async def async_setup_entry(
[
BMWButton(coordinator, vehicle, description)
for description in BUTTON_TYPES
if (not coordinator.read_only and description.is_available(vehicle))
if not coordinator.read_only
or (coordinator.read_only and description.enabled_when_read_only)
]
)

View File

@@ -21,9 +21,3 @@ UNIT_MAP = {
"LITERS": UnitOfVolume.LITERS,
"GALLONS": UnitOfVolume.GALLONS,
}
SCAN_INTERVALS = {
"china": 300,
"north_america": 600,
"rest_of_world": 300,
}

View File

@@ -15,8 +15,10 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_GCID, CONF_READ_ONLY, CONF_REFRESH_TOKEN, DOMAIN, SCAN_INTERVALS
from .const import CONF_GCID, CONF_READ_ONLY, CONF_REFRESH_TOKEN, DOMAIN
DEFAULT_SCAN_INTERVAL_SECONDS = 300
SCAN_INTERVAL = timedelta(seconds=DEFAULT_SCAN_INTERVAL_SECONDS)
_LOGGER = logging.getLogger(__name__)
@@ -48,7 +50,7 @@ class BMWDataUpdateCoordinator(DataUpdateCoordinator[None]):
hass,
_LOGGER,
name=f"{DOMAIN}-{entry.data['username']}",
update_interval=timedelta(seconds=SCAN_INTERVALS[entry.data[CONF_REGION]]),
update_interval=SCAN_INTERVAL,
)
async def _async_update_data(self) -> None:

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"iot_class": "cloud_polling",
"loggers": ["bimmer_connected"],
"requirements": ["bimmer-connected==0.13.8"]
"requirements": ["bimmer-connected==0.13.7"]
}

View File

@@ -107,7 +107,6 @@ class BroadlinkRemote(BroadlinkEntity, RemoteEntity, RestoreEntity):
"""Representation of a Broadlink remote."""
_attr_has_entity_name = True
_attr_name = None
def __init__(self, device, codes, flags):
"""Initialize the remote."""

View File

@@ -221,7 +221,6 @@ class BroadlinkSP2Switch(BroadlinkSP1Switch):
_attr_assumed_state = False
_attr_has_entity_name = True
_attr_name = None
def __init__(self, device, *args, **kwargs):
"""Initialize the switch."""

View File

@@ -34,21 +34,6 @@ class BPKConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
async def async_step_import(self, config: dict[str, Any]) -> FlowResult:
"""Import a configuration from config.yaml."""
if config.get(CONF_LATITUDE):
config[CONF_LOCATION] = {
CONF_LATITUDE: config[CONF_LATITUDE],
CONF_LONGITUDE: config[CONF_LONGITUDE],
}
if not config.get(CONF_AREA):
config[CONF_AREA] = "none"
else:
config[CONF_AREA] = config[CONF_AREA][0]
return await self.async_step_user(user_input=config)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:

View File

@@ -5,62 +5,19 @@ from collections import defaultdict
from datetime import timedelta
from brottsplatskartan import ATTRIBUTION, BrottsplatsKartan
import voluptuous as vol
from homeassistant.components.sensor import (
PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
SensorEntity,
)
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import AREAS, CONF_APP_ID, CONF_AREA, DEFAULT_NAME, DOMAIN, LOGGER
from .const import CONF_APP_ID, CONF_AREA, DOMAIN, LOGGER
SCAN_INTERVAL = timedelta(minutes=30)
PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(
{
vol.Inclusive(CONF_LATITUDE, "coordinates"): cv.latitude,
vol.Inclusive(CONF_LONGITUDE, "coordinates"): cv.longitude,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_AREA, default=[]): vol.All(cv.ensure_list, [vol.In(AREAS)]),
}
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Brottsplatskartan platform."""
async_create_issue(
hass,
DOMAIN,
"deprecated_yaml",
breaks_in_ha_version="2023.11.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_yaml",
)
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=config,
)
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
@@ -83,7 +40,6 @@ class BrottsplatskartanSensor(SensorEntity):
_attr_attribution = ATTRIBUTION
_attr_has_entity_name = True
_attr_name = None
def __init__(self, bpk: BrottsplatsKartan, name: str, entry_id: str) -> None:
"""Initialize the Brottsplatskartan sensor."""

View File

@@ -16,12 +16,6 @@
}
}
},
"issues": {
"deprecated_yaml": {
"title": "The Brottsplatskartan YAML configuration is being removed",
"description": "Configuring Brottsplatskartan using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Brottsplatskartan YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
}
},
"selector": {
"areas": {
"options": {

View File

@@ -71,7 +71,6 @@ class BSBLANClimate(
"""Defines a BSBLAN climate device."""
_attr_has_entity_name = True
_attr_name = None
# Determine preset modes
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE

View File

@@ -20,5 +20,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/bthome",
"iot_class": "local_push",
"requirements": ["bthome-ble==2.12.1"]
"requirements": ["bthome-ble==2.12.0"]
}

View File

@@ -301,55 +301,55 @@
"name": "Condition 1d",
"state": {
"clear": "[%key:component::buienradar::entity::sensor::condition::state::clear%]",
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
"cloudy": "[%key:component::buienradar::entity::sensor::condition::state::cloudy%]",
"fog": "[%key:component::buienradar::entity::sensor::condition::state::fog%]",
"rainy": "[%key:component::buienradar::entity::sensor::condition::state::rainy%]",
"snowy": "[%key:component::buienradar::entity::sensor::condition::state::snowy%]",
"lightning": "[%key:component::buienradar::entity::sensor::condition::state::lightning%]"
}
},
"condition_2d": {
"name": "Condition 2d",
"state": {
"clear": "[%key:component::buienradar::entity::sensor::condition::state::clear%]",
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
"cloudy": "[%key:component::buienradar::entity::sensor::condition::state::cloudy%]",
"fog": "[%key:component::buienradar::entity::sensor::condition::state::fog%]",
"rainy": "[%key:component::buienradar::entity::sensor::condition::state::rainy%]",
"snowy": "[%key:component::buienradar::entity::sensor::condition::state::snowy%]",
"lightning": "[%key:component::buienradar::entity::sensor::condition::state::lightning%]"
}
},
"condition_3d": {
"name": "Condition 3d",
"state": {
"clear": "[%key:component::buienradar::entity::sensor::condition::state::clear%]",
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
"cloudy": "[%key:component::buienradar::entity::sensor::condition::state::cloudy%]",
"fog": "[%key:component::buienradar::entity::sensor::condition::state::fog%]",
"rainy": "[%key:component::buienradar::entity::sensor::condition::state::rainy%]",
"snowy": "[%key:component::buienradar::entity::sensor::condition::state::snowy%]",
"lightning": "[%key:component::buienradar::entity::sensor::condition::state::lightning%]"
}
},
"condition_4d": {
"name": "Condition 4d",
"state": {
"clear": "[%key:component::buienradar::entity::sensor::condition::state::clear%]",
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
"cloudy": "[%key:component::buienradar::entity::sensor::condition::state::cloudy%]",
"fog": "[%key:component::buienradar::entity::sensor::condition::state::fog%]",
"rainy": "[%key:component::buienradar::entity::sensor::condition::state::rainy%]",
"snowy": "[%key:component::buienradar::entity::sensor::condition::state::snowy%]",
"lightning": "[%key:component::buienradar::entity::sensor::condition::state::lightning%]"
}
},
"condition_5d": {
"name": "Condition 5d",
"state": {
"clear": "[%key:component::buienradar::entity::sensor::condition::state::clear%]",
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
"cloudy": "[%key:component::buienradar::entity::sensor::condition::state::cloudy%]",
"fog": "[%key:component::buienradar::entity::sensor::condition::state::fog%]",
"rainy": "[%key:component::buienradar::entity::sensor::condition::state::rainy%]",
"snowy": "[%key:component::buienradar::entity::sensor::condition::state::snowy%]",
"lightning": "[%key:component::buienradar::entity::sensor::condition::state::lightning%]"
}
},
"conditioncode_1d": {
@@ -371,76 +371,76 @@
"name": "Detailed condition 1d",
"state": {
"clear": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::clear%]",
"partlycloudy": "[%key:component::weather::entity_component::_::state::partlycloudy%]",
"partlycloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy%]",
"partlycloudy-fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-fog%]",
"partlycloudy-light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-rain%]",
"partlycloudy-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-rain%]",
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
"cloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::cloudy%]",
"fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::fog%]",
"rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::rainy%]",
"light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-rain%]",
"light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-snow%]",
"partlycloudy-light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-snow%]",
"partlycloudy-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-snow%]",
"partlycloudy-lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-lightning%]",
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
"snowy-rainy": "[%key:component::weather::entity_component::_::state::snowy-rainy%]",
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
"snowy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy%]",
"snowy-rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy-rainy%]",
"lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::lightning%]"
}
},
"conditiondetailed_2d": {
"name": "Detailed condition 2d",
"state": {
"clear": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::clear%]",
"partlycloudy": "[%key:component::weather::entity_component::_::state::partlycloudy%]",
"partlycloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy%]",
"partlycloudy-fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-fog%]",
"partlycloudy-light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-rain%]",
"partlycloudy-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-rain%]",
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
"cloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::cloudy%]",
"fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::fog%]",
"rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::rainy%]",
"light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-rain%]",
"light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-snow%]",
"partlycloudy-light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-snow%]",
"partlycloudy-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-snow%]",
"partlycloudy-lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-lightning%]",
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
"snowy-rainy": "[%key:component::weather::entity_component::_::state::snowy-rainy%]",
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
"snowy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy%]",
"snowy-rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy-rainy%]",
"lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::lightning%]"
}
},
"conditiondetailed_3d": {
"name": "Detailed condition 3d",
"state": {
"clear": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::clear%]",
"partlycloudy": "[%key:component::weather::entity_component::_::state::partlycloudy%]",
"partlycloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy%]",
"partlycloudy-fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-fog%]",
"partlycloudy-light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-rain%]",
"partlycloudy-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-rain%]",
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
"cloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::cloudy%]",
"fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::fog%]",
"rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::rainy%]",
"light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-rain%]",
"light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-snow%]",
"partlycloudy-light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-snow%]",
"partlycloudy-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-snow%]",
"partlycloudy-lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-lightning%]",
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
"snowy-rainy": "[%key:component::weather::entity_component::_::state::snowy-rainy%]",
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
"snowy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy%]",
"snowy-rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy-rainy%]",
"lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::lightning%]"
}
},
"conditiondetailed_4d": {
"name": "Detailed condition 4d",
"state": {
"clear": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::clear%]",
"partlycloudy": "[%key:component::weather::entity_component::_::state::partlycloudy%]",
"partlycloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy%]",
"partlycloudy-fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-fog%]",
"partlycloudy-light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-rain%]",
"partlycloudy-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-rain%]",
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
"cloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::cloudy%]",
"fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::fog%]",
"rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::rainy%]",
"light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-rain%]",
"light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-snow%]",
"partlycloudy-light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-snow%]",
@@ -455,21 +455,21 @@
"name": "Detailed condition 5d",
"state": {
"clear": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::clear%]",
"partlycloudy": "[%key:component::weather::entity_component::_::state::partlycloudy%]",
"partlycloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy%]",
"partlycloudy-fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-fog%]",
"partlycloudy-light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-rain%]",
"partlycloudy-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-rain%]",
"cloudy": "[%key:component::weather::entity_component::_::state::cloudy%]",
"fog": "[%key:component::weather::entity_component::_::state::fog%]",
"rainy": "[%key:component::weather::entity_component::_::state::rainy%]",
"cloudy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::cloudy%]",
"fog": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::fog%]",
"rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::rainy%]",
"light-rain": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-rain%]",
"light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::light-snow%]",
"partlycloudy-light-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-light-snow%]",
"partlycloudy-snow": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-snow%]",
"partlycloudy-lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::partlycloudy-lightning%]",
"snowy": "[%key:component::weather::entity_component::_::state::snowy%]",
"snowy-rainy": "[%key:component::weather::entity_component::_::state::snowy-rainy%]",
"lightning": "[%key:component::weather::entity_component::_::state::lightning%]"
"snowy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy%]",
"snowy-rainy": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::snowy-rainy%]",
"lightning": "[%key:component::buienradar::entity::sensor::conditiondetailed::state::lightning%]"
}
},
"conditionexact_1d": {

View File

@@ -92,7 +92,7 @@ class ButtonEntity(RestoreEntity):
def _default_to_device_class_name(self) -> bool:
"""Return True if an unnamed entity should be named by its device class.
For buttons this is True if the entity has a device class.
For sensors this is True if the entity has a device class.
"""
return self.device_class is not None

View File

@@ -60,7 +60,6 @@ from .const import (
EVENT_TIME_FIELDS,
EVENT_TYPES,
EVENT_UID,
LIST_EVENT_FIELDS,
CalendarEntityFeature,
)
@@ -264,8 +263,8 @@ SERVICE_LIST_EVENTS_SCHEMA: Final = vol.All(
cv.has_at_most_one_key(EVENT_END_DATETIME, EVENT_DURATION),
cv.make_entity_service_schema(
{
vol.Optional(EVENT_START_DATETIME): cv.datetime,
vol.Optional(EVENT_END_DATETIME): cv.datetime,
vol.Optional(EVENT_START_DATETIME): datetime.datetime,
vol.Optional(EVENT_END_DATETIME): datetime.datetime,
vol.Optional(EVENT_DURATION): vol.All(
cv.time_period, cv.positive_timedelta
),
@@ -416,17 +415,6 @@ def _api_event_dict_factory(obj: Iterable[tuple[str, Any]]) -> dict[str, Any]:
return result
def _list_events_dict_factory(
obj: Iterable[tuple[str, Any]]
) -> dict[str, JsonValueType]:
"""Convert CalendarEvent dataclass items to dictionary of attributes."""
return {
name: value
for name, value in _event_dict_factory(obj).items()
if name in LIST_EVENT_FIELDS and value is not None
}
def _get_datetime_local(
dt_or_d: datetime.datetime | datetime.date,
) -> datetime.datetime:
@@ -793,12 +781,10 @@ async def async_list_events_service(
end = start + service_call.data[EVENT_DURATION]
else:
end = service_call.data[EVENT_END_DATETIME]
calendar_event_list = await calendar.async_get_events(
calendar.hass, dt_util.as_local(start), dt_util.as_local(end)
)
calendar_event_list = await calendar.async_get_events(calendar.hass, start, end)
events: list[JsonValueType] = [
dataclasses.asdict(event) for event in calendar_event_list
]
return {
"events": [
dataclasses.asdict(event, dict_factory=_list_events_dict_factory)
for event in calendar_event_list
]
"events": events,
}

View File

@@ -41,12 +41,3 @@ EVENT_TIME_FIELDS = {
}
EVENT_TYPES = "event_types"
EVENT_DURATION = "duration"
# Fields for the list events service
LIST_EVENT_FIELDS = {
"start",
"end",
EVENT_SUMMARY,
EVENT_DESCRIPTION,
EVENT_LOCATION,
}

View File

@@ -8,10 +8,10 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_HOST,
CONF_PORT,
EVENT_HOMEASSISTANT_STARTED,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.start import async_at_started
from homeassistant.core import CoreState, HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DEFAULT_PORT, DOMAIN
@@ -38,11 +38,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if entry.unique_id is None:
hass.config_entries.async_update_entry(entry, unique_id=f"{host}:{port}")
async def _async_finish_startup(_):
async def async_finish_startup(_):
await coordinator.async_refresh()
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async_at_started(hass, _async_finish_startup)
if hass.state == CoreState.running:
await async_finish_startup(None)
else:
entry.async_on_unload(
hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STARTED, async_finish_startup
)
)
return True

View File

@@ -534,14 +534,6 @@ class ClimateEntity(Entity):
await self.hass.async_add_executor_job(self.turn_on)
return
# If there are only two HVAC modes, and one of those modes is OFF,
# then we can just turn on the other mode.
if len(self.hvac_modes) == 2 and HVACMode.OFF in self.hvac_modes:
for mode in self.hvac_modes:
if mode != HVACMode.OFF:
await self.async_set_hvac_mode(mode)
return
# Fake turn on
for mode in (HVACMode.HEAT_COOL, HVACMode.HEAT, HVACMode.COOL):
if mode not in self.hvac_modes:

View File

@@ -3,10 +3,6 @@ from __future__ import annotations
import voluptuous as vol
from homeassistant.components.device_automation import (
async_get_entity_registry_entry_or_raise,
async_validate_entity_schema,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_DEVICE_ID,
@@ -28,7 +24,7 @@ ACTION_TYPES = {"set_hvac_mode", "set_preset_mode"}
SET_HVAC_MODE_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
{
vol.Required(CONF_TYPE): "set_hvac_mode",
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
vol.Required(CONF_ENTITY_ID): cv.entity_domain(DOMAIN),
vol.Required(const.ATTR_HVAC_MODE): vol.In(const.HVAC_MODES),
}
)
@@ -36,19 +32,12 @@ SET_HVAC_MODE_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
SET_PRESET_MODE_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
{
vol.Required(CONF_TYPE): "set_preset_mode",
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
vol.Required(CONF_ENTITY_ID): cv.entity_domain(DOMAIN),
vol.Required(const.ATTR_PRESET_MODE): str,
}
)
_ACTION_SCHEMA = vol.Any(SET_HVAC_MODE_SCHEMA, SET_PRESET_MODE_SCHEMA)
async def async_validate_action_config(
hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config."""
return async_validate_entity_schema(hass, config, _ACTION_SCHEMA)
ACTION_SCHEMA = vol.Any(SET_HVAC_MODE_SCHEMA, SET_PRESET_MODE_SCHEMA)
async def async_get_actions(
@@ -68,7 +57,7 @@ async def async_get_actions(
base_action = {
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_ENTITY_ID: entry.id,
CONF_ENTITY_ID: entry.entity_id,
}
actions.append({**base_action, CONF_TYPE: "set_hvac_mode"})
@@ -104,24 +93,23 @@ async def async_get_action_capabilities(
) -> dict[str, vol.Schema]:
"""List action capabilities."""
action_type = config[CONF_TYPE]
entity_id_or_uuid = config[CONF_ENTITY_ID]
fields = {}
if action_type == "set_hvac_mode":
try:
entry = async_get_entity_registry_entry_or_raise(hass, entity_id_or_uuid)
hvac_modes = (
get_capability(hass, entry.entity_id, const.ATTR_HVAC_MODES) or []
get_capability(hass, config[ATTR_ENTITY_ID], const.ATTR_HVAC_MODES)
or []
)
except HomeAssistantError:
hvac_modes = []
fields[vol.Required(const.ATTR_HVAC_MODE)] = vol.In(hvac_modes)
elif action_type == "set_preset_mode":
try:
entry = async_get_entity_registry_entry_or_raise(hass, entity_id_or_uuid)
preset_modes = (
get_capability(hass, entry.entity_id, const.ATTR_PRESET_MODES) or []
get_capability(hass, config[ATTR_ENTITY_ID], const.ATTR_PRESET_MODES)
or []
)
except HomeAssistantError:
preset_modes = []

View File

@@ -142,7 +142,7 @@ async def async_attach_trigger(
numeric_state_config[
numeric_state_trigger.CONF_VALUE_TEMPLATE
] = "{{ state.attributes.current_temperature }}"
else: # trigger_type == "current_humidity_changed"
else:
numeric_state_config[
numeric_state_trigger.CONF_VALUE_TEMPLATE
] = "{{ state.attributes.current_humidity }}"

View File

@@ -17,7 +17,6 @@ from homeassistant.components.alexa import (
smart_home as alexa_smart_home,
)
from homeassistant.components.google_assistant import smart_home as ga
from homeassistant.const import __version__ as HA_VERSION
from homeassistant.core import Context, HassJob, HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_call_later
@@ -213,19 +212,6 @@ class CloudClient(Interface):
"""Process cloud remote message to client."""
await self._prefs.async_update(remote_enabled=connect)
async def async_cloud_connection_info(
self, payload: dict[str, Any]
) -> dict[str, Any]:
"""Process cloud connection info message to client."""
return {
"remote": {
"connected": self.cloud.remote.is_connected,
"enabled": self._prefs.remote_enabled,
"instance_domain": self.cloud.remote.instance_domain,
},
"version": HA_VERSION,
}
async def async_alexa_message(self, payload: dict[Any, Any]) -> dict[Any, Any]:
"""Process cloud alexa message to client."""
cloud_user = await self._prefs.get_cloud_user()

View File

@@ -8,5 +8,5 @@
"integration_type": "system",
"iot_class": "cloud_push",
"loggers": ["hass_nabucasa"],
"requirements": ["hass-nabucasa==0.69.0"]
"requirements": ["hass-nabucasa==0.68.0"]
}

View File

@@ -70,7 +70,7 @@ async def async_setup_platform(
hass,
DOMAIN,
"deprecated_yaml_binary_sensor",
breaks_in_ha_version="2023.12.0",
breaks_in_ha_version="2023.8.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_platform_yaml",

View File

@@ -73,7 +73,7 @@ async def async_setup_platform(
hass,
DOMAIN,
"deprecated_yaml_cover",
breaks_in_ha_version="2023.12.0",
breaks_in_ha_version="2023.8.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_platform_yaml",

View File

@@ -43,7 +43,7 @@ def get_service(
hass,
DOMAIN,
"deprecated_yaml_notify",
breaks_in_ha_version="2023.12.0",
breaks_in_ha_version="2023.8.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_platform_yaml",

View File

@@ -74,7 +74,7 @@ async def async_setup_platform(
hass,
DOMAIN,
"deprecated_yaml_sensor",
breaks_in_ha_version="2023.12.0",
breaks_in_ha_version="2023.8.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_platform_yaml",

View File

@@ -74,7 +74,7 @@ async def async_setup_platform(
hass,
DOMAIN,
"deprecated_yaml_switch",
breaks_in_ha_version="2023.12.0",
breaks_in_ha_version="2023.8.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_platform_yaml",

View File

@@ -8,7 +8,6 @@ import logging
import re
from typing import Any, Literal
from hassil.recognize import RecognizeResult
import voluptuous as vol
from homeassistant import core
@@ -354,10 +353,6 @@ async def websocket_hass_agent_debug(
}
for entity_key, entity in result.entities.items()
},
"targets": {
state.entity_id: {"matched": is_matched}
for state, is_matched in _get_debug_targets(hass, result)
},
}
if result is not None
else None
@@ -367,49 +362,6 @@ async def websocket_hass_agent_debug(
)
def _get_debug_targets(
hass: HomeAssistant,
result: RecognizeResult,
) -> Iterable[tuple[core.State, bool]]:
"""Yield state/is_matched pairs for a hassil recognition."""
entities = result.entities
name: str | None = None
area_name: str | None = None
domains: set[str] | None = None
device_classes: set[str] | None = None
state_names: set[str] | None = None
if "name" in entities:
name = str(entities["name"].value)
if "area" in entities:
area_name = str(entities["area"].value)
if "domain" in entities:
domains = set(cv.ensure_list(entities["domain"].value))
if "device_class" in entities:
device_classes = set(cv.ensure_list(entities["device_class"].value))
if "state" in entities:
# HassGetState only
state_names = set(cv.ensure_list(entities["state"].value))
states = intent.async_match_states(
hass,
name=name,
area_name=area_name,
domains=domains,
device_classes=device_classes,
)
for state in states:
# For queries, a target is "matched" based on its state
is_matched = (state_names is None) or (state.state in state_names)
yield state, is_matched
class ConversationProcessView(http.HomeAssistantView):
"""View to process text."""

View File

@@ -7,5 +7,5 @@
"integration_type": "system",
"iot_class": "local_push",
"quality_scale": "internal",
"requirements": ["hassil==1.0.6", "home-assistant-intents==2023.6.28"]
"requirements": ["hassil==1.0.6", "home-assistant-intents==2023.6.5"]
}

View File

@@ -7,7 +7,6 @@ process:
name: Text
description: Transcribed text
example: Turn all lights on
required: true
selector:
text:
language:
@@ -21,4 +20,4 @@ process:
description: Assist engine to process your request
example: homeassistant
selector:
conversation_agent:
text:

View File

@@ -3,7 +3,6 @@ from __future__ import annotations
from typing import Any
from hassil.recognize import PUNCTUATION
import voluptuous as vol
from homeassistant.const import CONF_COMMAND, CONF_PLATFORM
@@ -16,22 +15,10 @@ from . import HOME_ASSISTANT_AGENT, _get_agent_manager
from .const import DOMAIN
from .default_agent import DefaultAgent
def has_no_punctuation(value: list[str]) -> list[str]:
"""Validate result does not contain punctuation."""
for sentence in value:
if PUNCTUATION.search(sentence):
raise vol.Invalid("sentence should not contain punctuation")
return value
TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend(
{
vol.Required(CONF_PLATFORM): DOMAIN,
vol.Required(CONF_COMMAND): vol.All(
cv.ensure_list, [cv.string], has_no_punctuation
),
vol.Required(CONF_COMMAND): vol.All(cv.ensure_list, [cv.string]),
}
)
@@ -64,7 +51,7 @@ async def async_attach_trigger(
):
await future
return "Done"
return None
default_agent = await _get_agent_manager(hass).async_get_agent(HOME_ASSISTANT_AGENT)
assert isinstance(default_agent, DefaultAgent)

View File

@@ -292,7 +292,7 @@ class Counter(collection.CollectionEntity, RestoreEntity):
self.hass,
DOMAIN,
"deprecated_configure_service",
breaks_in_ha_version="2023.12.0",
breaks_in_ha_version="2023.8.0",
is_fixable=True,
is_persistent=True,
severity=IssueSeverity.WARNING,

View File

@@ -15,9 +15,8 @@ from homeassistant.const import (
CONF_UUID,
Platform,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
@@ -53,8 +52,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if not daikin_api:
return False
await async_migrate_unique_id(hass, entry, daikin_api)
hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: daikin_api})
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
@@ -70,7 +67,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
async def daikin_api_setup(hass: HomeAssistant, host, key, uuid, password):
async def daikin_api_setup(hass, host, key, uuid, password):
"""Create a Daikin instance only once."""
session = async_get_clientsession(hass)
@@ -130,82 +127,3 @@ class DaikinApi:
name=info.get("name"),
sw_version=info.get("ver", "").replace("_", "."),
)
async def async_migrate_unique_id(
hass: HomeAssistant, config_entry: ConfigEntry, api: DaikinApi
) -> None:
"""Migrate old entry."""
dev_reg = dr.async_get(hass)
old_unique_id = config_entry.unique_id
new_unique_id = api.device.mac
new_name = api.device.values.get("name")
@callback
def _update_unique_id(entity_entry: er.RegistryEntry) -> dict[str, str] | None:
"""Update unique ID of entity entry."""
return update_unique_id(entity_entry, new_unique_id)
if new_unique_id == old_unique_id:
return
# Migrate devices
for device_entry in dr.async_entries_for_config_entry(
dev_reg, config_entry.entry_id
):
for connection in device_entry.connections:
if connection[1] == old_unique_id:
new_connections = {
(CONNECTION_NETWORK_MAC, dr.format_mac(new_unique_id))
}
_LOGGER.debug(
"Migrating device %s connections to %s",
device_entry.name,
new_connections,
)
dev_reg.async_update_device(
device_entry.id,
merge_connections=new_connections,
)
if device_entry.name is None:
_LOGGER.debug(
"Migrating device name to %s",
new_name,
)
dev_reg.async_update_device(
device_entry.id,
name=new_name,
)
# Migrate entities
await er.async_migrate_entries(hass, config_entry.entry_id, _update_unique_id)
new_data = {**config_entry.data, KEY_MAC: dr.format_mac(new_unique_id)}
hass.config_entries.async_update_entry(
config_entry, unique_id=new_unique_id, data=new_data
)
@callback
def update_unique_id(
entity_entry: er.RegistryEntry, unique_id: str
) -> dict[str, str] | None:
"""Update unique ID of entity entry."""
if entity_entry.unique_id.startswith(unique_id):
# Already correct, nothing to do
return None
unique_id_parts = entity_entry.unique_id.split("-")
unique_id_parts[0] = unique_id
entity_new_unique_id = "-".join(unique_id_parts)
_LOGGER.debug(
"Migrating entity %s from %s to new id %s",
entity_entry.entity_id,
entity_entry.unique_id,
entity_new_unique_id,
)
return {"new_unique_id": entity_new_unique_id}

View File

@@ -276,16 +276,17 @@ class DaikinClimate(ClimateEntity):
await self._api.device.set_advanced_mode(
HA_PRESET_TO_DAIKIN[PRESET_ECO], ATTR_STATE_ON
)
elif self.preset_mode == PRESET_AWAY:
await self._api.device.set_holiday(ATTR_STATE_OFF)
elif self.preset_mode == PRESET_BOOST:
await self._api.device.set_advanced_mode(
HA_PRESET_TO_DAIKIN[PRESET_BOOST], ATTR_STATE_OFF
)
elif self.preset_mode == PRESET_ECO:
await self._api.device.set_advanced_mode(
HA_PRESET_TO_DAIKIN[PRESET_ECO], ATTR_STATE_OFF
)
else:
if self.preset_mode == PRESET_AWAY:
await self._api.device.set_holiday(ATTR_STATE_OFF)
elif self.preset_mode == PRESET_BOOST:
await self._api.device.set_advanced_mode(
HA_PRESET_TO_DAIKIN[PRESET_BOOST], ATTR_STATE_OFF
)
elif self.preset_mode == PRESET_ECO:
await self._api.device.set_advanced_mode(
HA_PRESET_TO_DAIKIN[PRESET_ECO], ATTR_STATE_OFF
)
@property
def preset_modes(self):

View File

@@ -7,6 +7,6 @@
"iot_class": "local_polling",
"loggers": ["pydaikin"],
"quality_scale": "platinum",
"requirements": ["pydaikin==2.10.5"],
"requirements": ["pydaikin==2.9.0"],
"zeroconf": ["_dkapi._tcp.local."]
}

View File

@@ -42,7 +42,7 @@ async def async_setup_entry(
[
DaikinZoneSwitch(daikin_api, zone_id)
for zone_id, zone in enumerate(zones)
if zone[0] != "-"
if zone != ("-", "0")
]
)
if daikin_api.device.support_advanced_modes:

View File

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

View File

@@ -50,6 +50,7 @@ COMPONENTS_WITH_CONFIG_ENTRY_DEMO_PLATFORM = [
COMPONENTS_WITH_DEMO_PLATFORM = [
Platform.TTS,
Platform.STT,
Platform.MAILBOX,
Platform.NOTIFY,
Platform.IMAGE_PROCESSING,

View File

@@ -4,7 +4,6 @@ from __future__ import annotations
from typing import Any
from homeassistant.components.humidifier import (
HumidifierAction,
HumidifierDeviceClass,
HumidifierEntity,
HumidifierEntityFeature,
@@ -31,7 +30,6 @@ async def async_setup_platform(
mode=None,
target_humidity=68,
current_humidity=45,
action=HumidifierAction.HUMIDIFYING,
device_class=HumidifierDeviceClass.HUMIDIFIER,
),
DemoHumidifier(
@@ -39,7 +37,6 @@ async def async_setup_platform(
mode=None,
target_humidity=54,
current_humidity=59,
action=HumidifierAction.DRYING,
device_class=HumidifierDeviceClass.DEHUMIDIFIER,
),
DemoHumidifier(
@@ -74,13 +71,11 @@ class DemoHumidifier(HumidifierEntity):
current_humidity: int | None = None,
available_modes: list[str] | None = None,
is_on: bool = True,
action: HumidifierAction | None = None,
device_class: HumidifierDeviceClass | None = None,
) -> None:
"""Initialize the humidifier device."""
self._attr_name = name
self._attr_is_on = is_on
self._attr_action = action
self._attr_supported_features = SUPPORT_FLAGS
if mode is not None:
self._attr_supported_features |= HumidifierEntityFeature.MODES

View File

@@ -108,7 +108,6 @@ async def async_setup_entry(
):
device_info = DeviceInfo(
identifiers=device.identifiers,
connections=device.connections,
)
else:
device_info = None

View File

@@ -28,7 +28,6 @@ STATIC_VALIDATOR = {
ENTITY_PLATFORMS = {
Platform.ALARM_CONTROL_PANEL.value,
Platform.BUTTON.value,
Platform.CLIMATE.value,
Platform.COVER.value,
Platform.FAN.value,
Platform.HUMIDIFIER.value,
@@ -38,7 +37,6 @@ ENTITY_PLATFORMS = {
Platform.REMOTE.value,
Platform.SELECT.value,
Platform.SWITCH.value,
Platform.TEXT.value,
Platform.VACUUM.value,
Platform.WATER_HEATER.value,
}

View File

@@ -726,10 +726,6 @@ class DeviceTracker:
class Device(RestoreEntity):
"""Base class for a tracked device."""
# This entity is legacy and does not have a platform.
# We can't fix this easily without breaking changes.
_no_platform_reported = True
host_name: str | None = None
location_name: str | None = None
gps: GPSType | None = None

View File

@@ -8,8 +8,6 @@ from .devolo_device import DevoloDeviceEntity
class DevoloMultiLevelSwitchDeviceEntity(DevoloDeviceEntity):
"""Representation of a multi level switch device within devolo Home Control. Something like a dimmer or a thermostat."""
_attr_name = None
def __init__(
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
) -> None:

View File

@@ -71,12 +71,13 @@ class DevoloLightDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, LightEntity):
self._multi_level_switch_property.set(
round(kwargs[ATTR_BRIGHTNESS] / 255 * 100)
)
elif self._binary_switch_property is not None:
# Turn on the light device to the latest known value. The value is known by the device itself.
self._binary_switch_property.set(True)
else:
# If there is no binary switch attached to the device, turn it on to 100 %.
self._multi_level_switch_property.set(100)
if self._binary_switch_property is not None:
# Turn on the light device to the latest known value. The value is known by the device itself.
self._binary_switch_property.set(True)
else:
# If there is no binary switch attached to the device, turn it on to 100 %.
self._multi_level_switch_property.set(100)
def turn_off(self, **kwargs: Any) -> None:
"""Turn device off."""

View File

@@ -41,8 +41,6 @@ async def async_setup_entry(
class DevoloSwitch(DevoloDeviceEntity, SwitchEntity):
"""Representation of a switch."""
_attr_name = None
def __init__(
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
) -> None:

View File

@@ -8,7 +8,7 @@
"iot_class": "local_polling",
"loggers": ["devolo_plc_api"],
"quality_scale": "platinum",
"requirements": ["devolo-plc-api==1.3.2"],
"requirements": ["devolo-plc-api==1.3.1"],
"zeroconf": [
{
"type": "_dvl-deviceapi._tcp.local.",

View File

@@ -32,23 +32,44 @@ async def async_setup_entry(
class DexcomGlucoseValueSensor(CoordinatorEntity, SensorEntity):
"""Representation of a Dexcom glucose value sensor."""
_attr_icon = GLUCOSE_VALUE_ICON
def __init__(self, coordinator, username, unit_of_measurement):
"""Initialize the sensor."""
super().__init__(coordinator)
self._attr_native_unit_of_measurement = unit_of_measurement
self._key = "mg_dl" if unit_of_measurement == MG_DL else "mmol_l"
self._attr_name = f"{DOMAIN}_{username}_glucose_value"
self._attr_unique_id = f"{username}-value"
self._state = None
self._unit_of_measurement = unit_of_measurement
self._attribute_unit_of_measurement = (
"mg_dl" if unit_of_measurement == MG_DL else "mmol_l"
)
self._name = f"{DOMAIN}_{username}_glucose_value"
self._unique_id = f"{username}-value"
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def icon(self):
"""Return the icon for the frontend."""
return GLUCOSE_VALUE_ICON
@property
def native_unit_of_measurement(self):
"""Return the unit of measurement of the device."""
return self._unit_of_measurement
@property
def native_value(self):
"""Return the state of the sensor."""
if self.coordinator.data:
return getattr(self.coordinator.data, self._key)
return getattr(self.coordinator.data, self._attribute_unit_of_measurement)
return None
@property
def unique_id(self):
"""Device unique id."""
return self._unique_id
class DexcomGlucoseTrendSensor(CoordinatorEntity, SensorEntity):
"""Representation of a Dexcom glucose trend sensor."""
@@ -56,8 +77,14 @@ class DexcomGlucoseTrendSensor(CoordinatorEntity, SensorEntity):
def __init__(self, coordinator, username):
"""Initialize the sensor."""
super().__init__(coordinator)
self._attr_name = f"{DOMAIN}_{username}_glucose_trend"
self._attr_unique_id = f"{username}-trend"
self._state = None
self._name = f"{DOMAIN}_{username}_glucose_trend"
self._unique_id = f"{username}-trend"
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def icon(self):
@@ -72,3 +99,8 @@ class DexcomGlucoseTrendSensor(CoordinatorEntity, SensorEntity):
if self.coordinator.data:
return self.coordinator.data.trend_description
return None
@property
def unique_id(self):
"""Device unique id."""
return self._unique_id

View File

@@ -60,6 +60,7 @@ class ServiceDetails(NamedTuple):
SERVICE_HANDLERS = {
SERVICE_ENIGMA2: ServiceDetails("media_player", "enigma2"),
"yamaha": ServiceDetails("media_player", "yamaha"),
"openhome": ServiceDetails("media_player", "openhome"),
"bluesound": ServiceDetails("media_player", "bluesound"),
}
@@ -86,7 +87,6 @@ MIGRATED_SERVICE_HANDLERS = [
SERVICE_MOBILE_APP,
SERVICE_NETGEAR,
SERVICE_OCTOPRINT,
"openhome",
"philips_hue",
SERVICE_SAMSUNG_PRINTER,
"sonos",

View File

@@ -350,13 +350,14 @@ class Doods(ImageProcessingEntity):
or boxes[3] > self._area[3]
):
continue
elif (
boxes[0] > self._area[2]
or boxes[1] > self._area[3]
or boxes[2] < self._area[0]
or boxes[3] < self._area[1]
):
continue
else:
if (
boxes[0] > self._area[2]
or boxes[1] > self._area[3]
or boxes[2] < self._area[0]
or boxes[3] < self._area[1]
):
continue
# Exclude matches outside label specific area definition
if self._label_areas.get(label):
@@ -368,13 +369,14 @@ class Doods(ImageProcessingEntity):
or boxes[3] > self._label_areas[label][3]
):
continue
elif (
boxes[0] > self._label_areas[label][2]
or boxes[1] > self._label_areas[label][3]
or boxes[2] < self._label_areas[label][0]
or boxes[3] < self._label_areas[label][1]
):
continue
else:
if (
boxes[0] > self._label_areas[label][2]
or boxes[1] > self._label_areas[label][3]
or boxes[2] < self._label_areas[label][0]
or boxes[3] < self._label_areas[label][1]
):
continue
if label not in matches:
matches[label] = []

View File

@@ -12,7 +12,7 @@ from homeassistant.exceptions import ConfigEntryNotReady
from .const import CAMERA_MODEL, DOMAIN
from .coordinator import Dremel3DPrinterDataUpdateCoordinator
PLATFORMS = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.CAMERA, Platform.SENSOR]
PLATFORMS = [Platform.BINARY_SENSOR, Platform.CAMERA, Platform.SENSOR]
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:

View File

@@ -1,78 +0,0 @@
"""Support for Dremel 3D Printer buttons."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from dremel3dpy import Dremel3DPrinter
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
from .entity import Dremel3DPrinterEntity
@dataclass
class Dremel3DPrinterButtonEntityMixin:
"""Mixin for required keys."""
press_fn: Callable[[Dremel3DPrinter], None]
@dataclass
class Dremel3DPrinterButtonEntityDescription(
ButtonEntityDescription, Dremel3DPrinterButtonEntityMixin
):
"""Describes a Dremel 3D Printer button entity."""
BUTTON_TYPES: tuple[Dremel3DPrinterButtonEntityDescription, ...] = (
Dremel3DPrinterButtonEntityDescription(
key="cancel_job",
translation_key="cancel_job",
press_fn=lambda api: api.stop_print(),
),
Dremel3DPrinterButtonEntityDescription(
key="pause_job",
translation_key="pause_job",
press_fn=lambda api: api.pause_print(),
),
Dremel3DPrinterButtonEntityDescription(
key="resume_job",
translation_key="resume_job",
press_fn=lambda api: api.resume_print(),
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Dremel 3D Printer control buttons."""
coordinator = hass.data[DOMAIN][config_entry.entry_id]
async_add_entities(
Dremel3DPrinterButtonEntity(coordinator, description)
for description in BUTTON_TYPES
)
class Dremel3DPrinterButtonEntity(Dremel3DPrinterEntity, ButtonEntity):
"""Represent a Dremel 3D Printer button."""
entity_description: Dremel3DPrinterButtonEntityDescription
def press(self) -> None:
"""Handle the button press."""
# api does not care about the current state
try:
self.entity_description.press_fn(self._api)
except RuntimeError as ex:
raise HomeAssistantError(
"An error occurred while submitting command"
) from ex

View File

@@ -16,17 +16,6 @@
}
},
"entity": {
"button": {
"cancel_job": {
"name": "Cancel job"
},
"pause_job": {
"name": "Pause job"
},
"resume_job": {
"name": "Resume job"
}
},
"sensor": {
"job_phase": {
"name": "Job phase"

View File

@@ -459,9 +459,9 @@ async def async_setup_entry(
@callback
def close_transport(_event: EventType) -> None:
"""Close the transport on HA shutdown."""
if not transport: # noqa: B023
if not transport:
return
transport.close() # noqa: B023
transport.close()
stop_listener = hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP, close_transport

View File

@@ -91,7 +91,7 @@ async def async_setup_platform(
hass,
DOMAIN,
"deprecated_yaml",
breaks_in_ha_version="2023.12.0",
breaks_in_ha_version="2023.8.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_yaml",

View File

@@ -2,12 +2,14 @@
"config": {
"step": {
"user": {
"title": "ecobee API key",
"description": "Please enter the API key obtained from ecobee.com.",
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]"
}
},
"authorize": {
"title": "Authorize app on ecobee.com",
"description": "Please authorize this app at https://www.ecobee.com/consumerportal/index.html with PIN code:\n\n{pin}\n\nThen, press Submit."
}
},

View File

@@ -125,7 +125,7 @@ ECOWITT_SENSORS_MAPPING: Final = {
EcoWittSensorTypes.LIGHTNING_COUNT: SensorEntityDescription(
key="LIGHTNING_COUNT",
native_unit_of_measurement="strikes",
state_class=SensorStateClass.TOTAL_INCREASING,
state_class=SensorStateClass.TOTAL,
),
EcoWittSensorTypes.TEMPERATURE_C: SensorEntityDescription(
key="TEMPERATURE_C",
@@ -143,13 +143,13 @@ ECOWITT_SENSORS_MAPPING: Final = {
key="RAIN_COUNT_MM",
native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS,
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.TOTAL_INCREASING,
state_class=SensorStateClass.TOTAL,
),
EcoWittSensorTypes.RAIN_COUNT_INCHES: SensorEntityDescription(
key="RAIN_COUNT_INCHES",
native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES,
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.TOTAL_INCREASING,
state_class=SensorStateClass.TOTAL,
),
EcoWittSensorTypes.RAIN_RATE_MM: SensorEntityDescription(
key="RAIN_RATE_MM",
@@ -230,13 +230,6 @@ async def async_setup_entry(
name=sensor.name,
)
# Hourly rain doesn't reset to fixed hours, it must be measurement state classes
if sensor.key in ("hrain_piezomm", "hrain_piezo"):
description = dataclasses.replace(
description,
state_class=SensorStateClass.MEASUREMENT,
)
async_add_entities([EcowittSensorEntity(sensor, description)])
ecowitt.new_sensor_cb.append(_new_sensor)

View File

@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/eddystone_temperature",
"iot_class": "local_polling",
"loggers": ["beacontools"],
"requirements": ["beacontools[scan]==2.1.0"]
"requirements": ["beacontools[scan]==2.1.0", "construct==2.10.56"]
}

View File

@@ -25,14 +25,14 @@ from .const import CONF_CURRENT_VALUES, DOMAIN, LOGGER
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key="instant_readings",
translation_key="instant_readings",
name="Power Usage",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="energy_day",
translation_key="energy_day",
name="Daily Consumption",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
@@ -40,7 +40,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
),
SensorEntityDescription(
key="energy_week",
translation_key="energy_week",
name="Weekly Consumption",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
@@ -48,14 +48,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
),
SensorEntityDescription(
key="energy_month",
translation_key="energy_month",
name="Monthly Consumption",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key="energy_year",
translation_key="energy_year",
name="Yearly Consumption",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
@@ -63,32 +63,32 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
),
SensorEntityDescription(
key="budget",
translation_key="budget",
name="Energy Budget",
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="cost_day",
translation_key="cost_day",
name="Daily Energy Cost",
device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="cost_week",
translation_key="cost_week",
name="Weekly Energy Cost",
device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="cost_month",
translation_key="cost_month",
name="Monthly Energy Cost",
device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key="cost_year",
translation_key="cost_year",
name="Yearly Energy Cost",
device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
@@ -137,8 +137,6 @@ async def async_setup_entry(
class EfergySensor(EfergyEntity, SensorEntity):
"""Implementation of an Efergy sensor."""
_attr_has_entity_name = True
def __init__(
self,
api: Efergy,

View File

@@ -16,39 +16,5 @@
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
}
},
"entity": {
"sensor": {
"instant_readings": {
"name": "Power usage"
},
"energy_day": {
"name": "Daily consumption"
},
"energy_week": {
"name": "Weekly consumption"
},
"energy_month": {
"name": "Monthly consumption"
},
"energy_year": {
"name": "Yearly consumption"
},
"budget": {
"name": "Energy budget"
},
"cost_day": {
"name": "Daily energy cost"
},
"cost_week": {
"name": "Weekly energy cost"
},
"cost_month": {
"name": "Monthly energy cost"
},
"cost_year": {
"name": "Yearly energy cost"
}
}
}
}

View File

@@ -118,9 +118,7 @@ class ElectraClimateEntity(ClimateEntity):
self._electra_ac_device = device
self._attr_unique_id = device.mac
self._attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.PRESET_MODE
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
)
swing_modes: list = []

Some files were not shown because too many files have changed in this diff Show More