Compare commits

..

1 Commits

Author SHA1 Message Date
G Johansson 45aab80b59 Modify query as template in SQL integration 2024-08-23 14:48:50 +00:00
1069 changed files with 8602 additions and 26715 deletions
-1
View File
@@ -61,7 +61,6 @@ components: &components
- homeassistant/components/auth/**
- homeassistant/components/automation/**
- homeassistant/components/backup/**
- homeassistant/components/blueprint/**
- homeassistant/components/bluetooth/**
- homeassistant/components/cloud/**
- homeassistant/components/config/**
-53
View File
@@ -482,56 +482,3 @@ jobs:
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
twine upload dist/* --skip-existing
hassfest-image:
name: Build and test hassfest image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
needs: ["init"]
if: github.repository_owner == 'home-assistant'
env:
HASSFEST_IMAGE_NAME: ghcr.io/home-assistant/hassfest
HASSFEST_IMAGE_TAG: ghcr.io/home-assistant/hassfest:${{ needs.init.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Login to GitHub Container Registry
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
load: true
tags: ${{ env.HASSFEST_IMAGE_TAG }}
- name: Run hassfest against core
run: docker run --rm -v ${{ github.workspace }}/homeassistant:/github/workspace/homeassistant ${{ env.HASSFEST_IMAGE_TAG }} --core-integrations-path=/github/workspace/homeassistant/components
- name: Push Docker image
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
id: push
uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
push: true
tags: ${{ env.HASSFEST_IMAGE_TAG }},${{ env.HASSFEST_IMAGE_NAME }}:latest
- name: Generate artifact attestation
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
uses: actions/attest-build-provenance@6149ea5740be74af77f260b9db67e633f6b0a9a1 # v1.4.2
with:
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
+2 -2
View File
@@ -24,11 +24,11 @@ jobs:
uses: actions/checkout@v4.1.7
- name: Initialize CodeQL
uses: github/codeql-action/init@v3.26.5
uses: github/codeql-action/init@v3.26.4
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3.26.5
uses: github/codeql-action/analyze@v3.26.4
with:
category: "/language:python"
+1
View File
@@ -294,6 +294,7 @@ homeassistant.components.london_underground.*
homeassistant.components.lookin.*
homeassistant.components.luftdaten.*
homeassistant.components.madvr.*
homeassistant.components.mailbox.*
homeassistant.components.manual.*
homeassistant.components.map.*
homeassistant.components.mastodon.*
-4
View File
@@ -1493,8 +1493,6 @@ build.json @home-assistant/supervisor
/tests/components/tomorrowio/ @raman325 @lymanepp
/homeassistant/components/totalconnect/ @austinmroczek
/tests/components/totalconnect/ @austinmroczek
/homeassistant/components/touchline_sl/ @jnsgruk
/tests/components/touchline_sl/ @jnsgruk
/homeassistant/components/tplink/ @rytilahti @bdraco @sdb9696
/tests/components/tplink/ @rytilahti @bdraco @sdb9696
/homeassistant/components/tplink_omada/ @MarkGodwin
@@ -1660,8 +1658,6 @@ build.json @home-assistant/supervisor
/tests/components/xiaomi_miio/ @rytilahti @syssi @starkillerOG
/homeassistant/components/xiaomi_tv/ @simse
/homeassistant/components/xmpp/ @fabaff @flowolf
/homeassistant/components/yale/ @bdraco
/tests/components/yale/ @bdraco
/homeassistant/components/yale_smart_alarm/ @gjohansson-ST
/tests/components/yale_smart_alarm/ @gjohansson-ST
/homeassistant/components/yalexs_ble/ @bdraco
+1 -2
View File
@@ -42,8 +42,7 @@ WORKDIR /usr/src
# Setup hass-release
RUN git clone --depth 1 https://github.com/home-assistant/hass-release \
&& uv pip install --system -e hass-release/ \
&& chown -R vscode /usr/src/hass-release/data
&& uv pip install --system -e hass-release/
USER vscode
ENV VIRTUAL_ENV="/home/vscode/.local/ha-venv"
-5
View File
@@ -1,5 +0,0 @@
{
"domain": "roth",
"name": "Roth",
"integrations": ["touchline", "touchline_sl"]
}
+1 -7
View File
@@ -1,11 +1,5 @@
{
"domain": "yale",
"name": "Yale",
"integrations": [
"august",
"yale_smart_alarm",
"yalexs_ble",
"yale_home",
"yale"
]
"integrations": ["august", "yale_smart_alarm", "yalexs_ble", "yale_home"]
}
+3 -9
View File
@@ -7,14 +7,8 @@
}
},
"services": {
"capture_image": {
"service": "mdi:camera"
},
"change_setting": {
"service": "mdi:cog"
},
"trigger_automation": {
"service": "mdi:play"
}
"capture_image": "mdi:camera",
"change_setting": "mdi:cog",
"trigger_automation": "mdi:play"
}
}
+5 -15
View File
@@ -66,20 +66,10 @@
}
},
"services": {
"add_url": {
"service": "mdi:link-plus"
},
"remove_url": {
"service": "mdi:link-off"
},
"enable_url": {
"service": "mdi:link-variant"
},
"disable_url": {
"service": "mdi:link-variant-off"
},
"refresh": {
"service": "mdi:refresh"
}
"add_url": "mdi:link-plus",
"remove_url": "mdi:link-off",
"enable_url": "mdi:link-variant",
"disable_url": "mdi:link-variant-off",
"refresh": "mdi:refresh"
}
}
+1 -3
View File
@@ -1,7 +1,5 @@
{
"services": {
"write_data_by_name": {
"service": "mdi:pencil"
}
"write_data_by_name": "mdi:pencil"
}
}
@@ -1,7 +1,5 @@
{
"services": {
"set_time_to": {
"service": "mdi:timer-cog"
}
"set_time_to": "mdi:timer-cog"
}
}
@@ -7,11 +7,7 @@
}
},
"services": {
"add_tracking": {
"service": "mdi:package-variant-plus"
},
"remove_tracking": {
"service": "mdi:package-variant-minus"
}
"add_tracking": "mdi:package-variant-plus",
"remove_tracking": "mdi:package-variant-minus"
}
}
+5 -15
View File
@@ -1,19 +1,9 @@
{
"services": {
"start_recording": {
"service": "mdi:record-rec"
},
"stop_recording": {
"service": "mdi:stop"
},
"enable_alerts": {
"service": "mdi:bell-alert"
},
"disable_alerts": {
"service": "mdi:bell-off"
},
"snapshot": {
"service": "mdi:camera"
}
"start_recording": "mdi:record-rec",
"stop_recording": "mdi:stop",
"enable_alerts": "mdi:bell-alert",
"disable_alerts": "mdi:bell-off",
"snapshot": "mdi:camera"
}
}
@@ -92,9 +92,7 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
except AirGradientError:
errors["base"] = "cannot_connect"
else:
await self.async_set_unique_id(
current_measures.serial_number, raise_on_progress=False
)
await self.async_set_unique_id(current_measures.serial_number)
self._abort_if_unique_id_configured()
await self.set_configuration_source()
return self.async_create_entry(
@@ -24,5 +24,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/airthings_ble",
"iot_class": "local_polling",
"requirements": ["airthings-ble==0.9.1"]
"requirements": ["airthings-ble==0.9.0"]
}
@@ -80,9 +80,11 @@ class AirVisualProFlowHandler(ConfigFlow, domain=DOMAIN):
"""Initialize."""
self._reauth_entry: ConfigEntry | None = None
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
"""Import a config entry from `airvisual` integration (see #83882)."""
return await self.async_step_user(import_data)
async def async_step_import(
self, import_config: dict[str, Any]
) -> ConfigFlowResult:
"""Import a config entry from configuration.yaml."""
return await self.async_step_user(import_config)
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
+3 -59
View File
@@ -2,21 +2,16 @@
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, Final
from aioairzone.common import GrilleAngle, OperationMode, SleepTimeout
from aioairzone.common import GrilleAngle, SleepTimeout
from aioairzone.const import (
API_COLD_ANGLE,
API_HEAT_ANGLE,
API_MODE,
API_SLEEP,
AZD_COLD_ANGLE,
AZD_HEAT_ANGLE,
AZD_MASTER,
AZD_MODE,
AZD_MODES,
AZD_SLEEP,
AZD_ZONES,
)
@@ -38,9 +33,6 @@ class AirzoneSelectDescription(SelectEntityDescription):
api_param: str
options_dict: dict[str, int]
options_fn: Callable[[dict[str, Any], dict[str, int]], list[str]] = (
lambda zone_data, value: list(value)
)
GRILLE_ANGLE_DICT: Final[dict[str, int]] = {
@@ -50,15 +42,6 @@ GRILLE_ANGLE_DICT: Final[dict[str, int]] = {
"40deg": GrilleAngle.DEG_40,
}
MODE_DICT: Final[dict[str, int]] = {
"cool": OperationMode.COOLING,
"dry": OperationMode.DRY,
"fan": OperationMode.FAN,
"heat": OperationMode.HEATING,
"heat_cool": OperationMode.AUTO,
"stop": OperationMode.STOP,
}
SLEEP_DICT: Final[dict[str, int]] = {
"off": SleepTimeout.SLEEP_OFF,
"30m": SleepTimeout.SLEEP_30,
@@ -67,26 +50,6 @@ SLEEP_DICT: Final[dict[str, int]] = {
}
def main_zone_options(
zone_data: dict[str, Any],
options: dict[str, int],
) -> list[str]:
"""Filter available modes."""
modes = zone_data.get(AZD_MODES, [])
return [k for k, v in options.items() if v in modes]
MAIN_ZONE_SELECT_TYPES: Final[tuple[AirzoneSelectDescription, ...]] = (
AirzoneSelectDescription(
api_param=API_MODE,
key=AZD_MODE,
options_dict=MODE_DICT,
options_fn=main_zone_options,
translation_key="modes",
),
)
ZONE_SELECT_TYPES: Final[tuple[AirzoneSelectDescription, ...]] = (
AirzoneSelectDescription(
api_param=API_COLD_ANGLE,
@@ -132,20 +95,7 @@ async def async_setup_entry(
received_zones = set(zones_data)
new_zones = received_zones - added_zones
if new_zones:
entities: list[AirzoneZoneSelect] = [
AirzoneZoneSelect(
coordinator,
description,
entry,
system_zone_id,
zones_data.get(system_zone_id),
)
for system_zone_id in new_zones
for description in MAIN_ZONE_SELECT_TYPES
if description.key in zones_data.get(system_zone_id)
and zones_data.get(system_zone_id).get(AZD_MASTER) is True
]
entities += [
async_add_entities(
AirzoneZoneSelect(
coordinator,
description,
@@ -156,8 +106,7 @@ async def async_setup_entry(
for system_zone_id in new_zones
for description in ZONE_SELECT_TYPES
if description.key in zones_data.get(system_zone_id)
]
async_add_entities(entities)
)
added_zones.update(new_zones)
entry.async_on_unload(coordinator.async_add_listener(_async_entity_listener))
@@ -204,11 +153,6 @@ class AirzoneZoneSelect(AirzoneZoneEntity, AirzoneBaseSelect):
f"{self._attr_unique_id}_{system_zone_id}_{description.key}"
)
self.entity_description = description
self._attr_options = self.entity_description.options_fn(
zone_data, description.options_dict
)
self.values_dict = {v: k for k, v in description.options_dict.items()}
self._async_update_attrs()
@@ -52,17 +52,6 @@
"40deg": "[%key:component::airzone::entity::select::grille_angles::state::40deg%]"
}
},
"modes": {
"name": "Mode",
"state": {
"cool": "[%key:component::climate::entity_component::_::state::cool%]",
"dry": "[%key:component::climate::entity_component::_::state::dry%]",
"fan": "[%key:component::climate::entity_component::_::state::fan_only%]",
"heat": "[%key:component::climate::entity_component::_::state::heat%]",
"heat_cool": "[%key:component::climate::entity_component::_::state::heat_cool%]",
"stop": "Stop"
}
},
"sleep_times": {
"name": "Sleep",
"state": {
@@ -15,26 +15,12 @@
}
},
"services": {
"alarm_arm_away": {
"service": "mdi:shield-lock"
},
"alarm_arm_home": {
"service": "mdi:shield-home"
},
"alarm_arm_night": {
"service": "mdi:shield-moon"
},
"alarm_arm_custom_bypass": {
"service": "mdi:security"
},
"alarm_disarm": {
"service": "mdi:shield-off"
},
"alarm_trigger": {
"service": "mdi:bell-ring"
},
"alarm_arm_vacation": {
"service": "mdi:shield-airplane"
}
"alarm_arm_away": "mdi:shield-lock",
"alarm_arm_home": "mdi:shield-home",
"alarm_arm_night": "mdi:shield-moon",
"alarm_arm_custom_bypass": "mdi:security",
"alarm_disarm": "mdi:shield-off",
"alarm_trigger": "mdi:bell-ring",
"alarm_arm_vacation": "mdi:shield-airplane"
}
}
@@ -7,11 +7,7 @@
}
},
"services": {
"alarm_keypress": {
"service": "mdi:dialpad"
},
"alarm_toggle_chime": {
"service": "mdi:abc"
}
"alarm_keypress": "mdi:dialpad",
"alarm_toggle_chime": "mdi:abc"
}
}
+3 -9
View File
@@ -1,13 +1,7 @@
{
"services": {
"toggle": {
"service": "mdi:bell-ring"
},
"turn_off": {
"service": "mdi:bell-off"
},
"turn_on": {
"service": "mdi:bell-alert"
}
"toggle": "mdi:bell-ring",
"turn_off": "mdi:bell-off",
"turn_on": "mdi:bell-alert"
}
}
+3 -6
View File
@@ -661,12 +661,9 @@ class RemoteCapabilities(AlexaEntity):
def interfaces(self) -> Generator[AlexaCapability]:
"""Yield the supported interfaces."""
yield AlexaPowerController(self.entity)
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
activities = self.entity.attributes.get(remote.ATTR_ACTIVITY_LIST) or []
if activities and supported & remote.RemoteEntityFeature.ACTIVITY:
yield AlexaModeController(
self.entity, instance=f"{remote.DOMAIN}.{remote.ATTR_ACTIVITY}"
)
yield AlexaModeController(
self.entity, instance=f"{remote.DOMAIN}.{remote.ATTR_ACTIVITY}"
)
yield AlexaEndpointHealth(self.hass, self.entity)
yield Alexa(self.entity)
@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["aioambient"],
"requirements": ["aioambient==2024.08.0"]
"requirements": ["aioambient==2024.01.0"]
}
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "cloud_push",
"loggers": ["aioambient"],
"requirements": ["aioambient==2024.08.0"]
"requirements": ["aioambient==2024.01.0"]
}
+11 -33
View File
@@ -1,37 +1,15 @@
{
"services": {
"enable_recording": {
"service": "mdi:record-rec"
},
"disable_recording": {
"service": "mdi:stop"
},
"enable_audio": {
"service": "mdi:volume-high"
},
"disable_audio": {
"service": "mdi:volume-off"
},
"enable_motion_recording": {
"service": "mdi:motion-sensor"
},
"disable_motion_recording": {
"service": "mdi:motion-sensor-off"
},
"goto_preset": {
"service": "mdi:pan"
},
"set_color_bw": {
"service": "mdi:palette"
},
"start_tour": {
"service": "mdi:panorama"
},
"stop_tour": {
"service": "mdi:panorama-outline"
},
"ptz_control": {
"service": "mdi:pan"
}
"enable_recording": "mdi:record-rec",
"disable_recording": "mdi:stop",
"enable_audio": "mdi:volume-high",
"disable_audio": "mdi:volume-off",
"enable_motion_recording": "mdi:motion-sensor",
"disable_motion_recording": "mdi:motion-sensor-off",
"goto_preset": "mdi:pan",
"set_color_bw": "mdi:palette",
"start_tour": "mdi:panorama",
"stop_tour": "mdi:panorama-outline",
"ptz_control": "mdi:pan"
}
}
+4 -12
View File
@@ -1,16 +1,8 @@
{
"services": {
"adb_command": {
"service": "mdi:console"
},
"download": {
"service": "mdi:download"
},
"upload": {
"service": "mdi:upload"
},
"learn_sendevent": {
"service": "mdi:remote"
}
"adb_command": "mdi:console",
"download": "mdi:download",
"upload": "mdi:upload",
"learn_sendevent": "mdi:remote"
}
}
+1 -1
View File
@@ -6,4 +6,4 @@ DOMAIN: Final = "apcupsd"
CONNECTION_TIMEOUT: int = 10
# Field name of last self test retrieved from apcupsd.
LAST_S_TEST: Final = "laststest"
LASTSTEST: Final = "laststest"
+6 -5
View File
@@ -13,6 +13,7 @@ from homeassistant.components.sensor import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
PERCENTAGE,
STATE_UNKNOWN,
UnitOfApparentPower,
UnitOfElectricCurrent,
UnitOfElectricPotential,
@@ -25,7 +26,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, LAST_S_TEST
from .const import DOMAIN, LASTSTEST
from .coordinator import APCUPSdCoordinator
PARALLEL_UPDATES = 0
@@ -156,8 +157,8 @@ SENSORS: dict[str, SensorEntityDescription] = {
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
LAST_S_TEST: SensorEntityDescription(
key=LAST_S_TEST,
LASTSTEST: SensorEntityDescription(
key=LASTSTEST,
translation_key="last_self_test",
),
"lastxfer": SensorEntityDescription(
@@ -422,7 +423,7 @@ async def async_setup_entry(
# periodical (or manual) self test since last daemon restart. It might not be available
# when we set up the integration, and we do not know if it would ever be available. Here we
# add it anyway and mark it as unknown initially.
for resource in available_resources | {LAST_S_TEST}:
for resource in available_resources | {LASTSTEST}:
if resource not in SENSORS:
_LOGGER.warning("Invalid resource from APCUPSd: %s", resource.upper())
continue
@@ -483,7 +484,7 @@ class APCUPSdSensor(CoordinatorEntity[APCUPSdCoordinator], SensorEntity):
# performed) and may disappear again after certain event. So we mark the state as "unknown"
# when it becomes unknown after such events.
if key not in self.coordinator.data:
self._attr_native_value = None
self._attr_native_value = STATE_UNKNOWN
return
self._attr_native_value, inferred_unit = infer_unit(self.coordinator.data[key])
@@ -101,7 +101,7 @@ class AsekoConfigFlow(ConfigFlow, domain=DOMAIN):
)
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
self, user_input: Mapping[str, Any]
) -> ConfigFlowResult:
"""Perform reauth upon an API authentication error."""
@@ -109,10 +109,10 @@ class AsekoConfigFlow(ConfigFlow, domain=DOMAIN):
self.context["entry_id"]
)
return await self.async_step_reauth_confirm()
return await self.async_step_reauth_confirm(user_input)
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
self, user_input: Mapping | None = None
) -> ConfigFlowResult:
"""Dialog that informs the user that reauth is required."""
@@ -5,7 +5,6 @@ from dataclasses import dataclass
import logging
from pymicro_vad import MicroVad
from pyspeex_noise import AudioProcessor
from .const import BYTES_PER_CHUNK
@@ -42,8 +41,8 @@ class AudioEnhancer(ABC):
"""Enhance chunk of PCM audio @ 16Khz with 16-bit mono samples."""
class MicroVadSpeexEnhancer(AudioEnhancer):
"""Audio enhancer that runs microVAD and speex."""
class MicroVadEnhancer(AudioEnhancer):
"""Audio enhancer that just runs microVAD."""
def __init__(
self, auto_gain: int, noise_suppression: int, is_vad_enabled: bool
@@ -51,24 +50,6 @@ class MicroVadSpeexEnhancer(AudioEnhancer):
"""Initialize audio enhancer."""
super().__init__(auto_gain, noise_suppression, is_vad_enabled)
self.audio_processor: AudioProcessor | None = None
# Scale from 0-4
self.noise_suppression = noise_suppression * -15
# Scale from 0-31
self.auto_gain = auto_gain * 300
if (self.auto_gain != 0) or (self.noise_suppression != 0):
self.audio_processor = AudioProcessor(
self.auto_gain, self.noise_suppression
)
_LOGGER.debug(
"Initialized speex with auto_gain=%s, noise_suppression=%s",
self.auto_gain,
self.noise_suppression,
)
self.vad: MicroVad | None = None
self.threshold = 0.5
@@ -80,17 +61,12 @@ class MicroVadSpeexEnhancer(AudioEnhancer):
"""Enhance 10ms chunk of PCM audio @ 16Khz with 16-bit mono samples."""
is_speech: bool | None = None
assert len(audio) == BYTES_PER_CHUNK
if self.vad is not None:
# Run VAD
assert len(audio) == BYTES_PER_CHUNK
speech_prob = self.vad.Process10ms(audio)
is_speech = speech_prob > self.threshold
if self.audio_processor is not None:
# Run noise suppression and auto gain
audio = self.audio_processor.Process10ms(audio).audio
return EnhancedAudioChunk(
audio=audio, timestamp_ms=timestamp_ms, is_speech=is_speech
)
@@ -7,5 +7,5 @@
"integration_type": "system",
"iot_class": "local_push",
"quality_scale": "internal",
"requirements": ["pymicro-vad==1.0.1", "pyspeex-noise==1.0.2"]
"requirements": ["pymicro-vad==1.0.1"]
}
@@ -49,7 +49,7 @@ from homeassistant.util import (
)
from homeassistant.util.limited_size_dict import LimitedSizeDict
from .audio_enhancer import AudioEnhancer, EnhancedAudioChunk, MicroVadSpeexEnhancer
from .audio_enhancer import AudioEnhancer, EnhancedAudioChunk, MicroVadEnhancer
from .const import (
BYTES_PER_CHUNK,
CONF_DEBUG_RECORDING_DIR,
@@ -589,7 +589,7 @@ class PipelineRun:
# Initialize with audio settings
if self.audio_settings.needs_processor and (self.audio_enhancer is None):
# Default audio enhancer
self.audio_enhancer = MicroVadSpeexEnhancer(
self.audio_enhancer = MicroVadEnhancer(
self.audio_settings.auto_gain_dbfs,
self.audio_settings.noise_suppression_level,
self.audio_settings.is_vad_enabled,
@@ -78,9 +78,6 @@ class VoiceCommandSegmenter:
speech_seconds: float = 0.3
"""Seconds of speech before voice command has started."""
command_seconds: float = 1.0
"""Minimum number of seconds for a voice command."""
silence_seconds: float = 0.7
"""Seconds of silence after voice command has ended."""
@@ -99,9 +96,6 @@ class VoiceCommandSegmenter:
_speech_seconds_left: float = 0.0
"""Seconds left before considering voice command as started."""
_command_seconds_left: float = 0.0
"""Seconds left before voice command could stop."""
_silence_seconds_left: float = 0.0
"""Seconds left before considering voice command as stopped."""
@@ -118,7 +112,6 @@ class VoiceCommandSegmenter:
def reset(self) -> None:
"""Reset all counters and state."""
self._speech_seconds_left = self.speech_seconds
self._command_seconds_left = self.command_seconds - self.speech_seconds
self._silence_seconds_left = self.silence_seconds
self._timeout_seconds_left = self.timeout_seconds
self._reset_seconds_left = self.reset_seconds
@@ -149,9 +142,6 @@ class VoiceCommandSegmenter:
if self._speech_seconds_left <= 0:
# Inside voice command
self.in_command = True
self._command_seconds_left = (
self.command_seconds - self.speech_seconds
)
self._silence_seconds_left = self.silence_seconds
_LOGGER.debug("Voice command started")
else:
@@ -164,8 +154,7 @@ class VoiceCommandSegmenter:
# Silence in command
self._reset_seconds_left = self.reset_seconds
self._silence_seconds_left -= chunk_seconds
self._command_seconds_left -= chunk_seconds
if (self._silence_seconds_left <= 0) and (self._command_seconds_left <= 0):
if self._silence_seconds_left <= 0:
# Command finished successfully
self.reset()
_LOGGER.debug("Voice command finished")
@@ -174,7 +163,6 @@ class VoiceCommandSegmenter:
# Speech in command.
# Reset silence counter if enough speech.
self._reset_seconds_left -= chunk_seconds
self._command_seconds_left -= chunk_seconds
if self._reset_seconds_left <= 0:
self._silence_seconds_left = self.silence_seconds
self._reset_seconds_left = self.reset_seconds
+3 -31
View File
@@ -6,16 +6,15 @@ from pathlib import Path
from typing import cast
from aiohttp import ClientResponseError
from yalexs.const import Brand
from yalexs.exceptions import AugustApiAIOHTTPError
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
from yalexs.manager.gateway import Config as YaleXSConfig
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr, issue_registry as ir
from homeassistant.helpers import device_registry as dr
from .const import DOMAIN, PLATFORMS
from .data import AugustData
@@ -25,27 +24,7 @@ from .util import async_create_august_clientsession
type AugustConfigEntry = ConfigEntry[AugustData]
@callback
def _async_create_yale_brand_migration_issue(
hass: HomeAssistant, entry: AugustConfigEntry
) -> None:
"""Create an issue for a brand migration."""
ir.async_create_issue(
hass,
DOMAIN,
"yale_brand_migration",
breaks_in_ha_version="2024.9",
learn_more_url="https://www.home-assistant.io/integrations/yale",
translation_key="yale_brand_migration",
is_fixable=False,
severity=ir.IssueSeverity.CRITICAL,
translation_placeholders={
"migrate_url": "https://my.home-assistant.io/redirect/config_flow_start?domain=yale"
},
)
async def async_setup_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up August from a config entry."""
session = async_create_august_clientsession(hass)
august_gateway = AugustGateway(Path(hass.config.config_dir), session)
@@ -61,11 +40,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bo
return True
async def async_remove_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> None:
"""Remove an August config entry."""
ir.async_delete_issue(hass, DOMAIN, "yale_brand_migration")
async def async_unload_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
@@ -77,8 +51,6 @@ async def async_setup_august(
"""Set up the August component."""
config = cast(YaleXSConfig, entry.data)
await august_gateway.async_setup(config)
if august_gateway.api.brand == Brand.YALE_HOME:
_async_create_yale_brand_migration_issue(hass, entry)
await august_gateway.async_authenticate()
await august_gateway.async_refresh_access_token_if_needed()
data = entry.runtime_data = AugustData(hass, august_gateway)
@@ -109,11 +109,12 @@ async def async_setup_entry(
for description in SENSOR_TYPES_DOORBELL
)
entities.extend(
AugustDoorbellBinarySensor(data, doorbell, description)
for description in SENSOR_TYPES_DOORBELL + SENSOR_TYPES_VIDEO_DOORBELL
for doorbell in data.doorbells
)
for doorbell in data.doorbells:
entities.extend(
AugustDoorbellBinarySensor(data, doorbell, description)
for description in SENSOR_TYPES_DOORBELL + SENSOR_TYPES_VIDEO_DOORBELL
)
async_add_entities(entities)
+2 -2
View File
@@ -5,7 +5,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AugustConfigEntry
from .entity import AugustEntity
from .entity import AugustEntityMixin
async def async_setup_entry(
@@ -18,7 +18,7 @@ async def async_setup_entry(
async_add_entities(AugustWakeLockButton(data, lock, "wake") for lock in data.locks)
class AugustWakeLockButton(AugustEntity, ButtonEntity):
class AugustWakeLockButton(AugustEntityMixin, ButtonEntity):
"""Representation of an August lock wake button."""
_attr_translation_key = "wake"
+2 -2
View File
@@ -16,7 +16,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AugustConfigEntry, AugustData
from .const import DEFAULT_NAME, DEFAULT_TIMEOUT
from .entity import AugustEntity
from .entity import AugustEntityMixin
_LOGGER = logging.getLogger(__name__)
@@ -38,7 +38,7 @@ async def async_setup_entry(
)
class AugustCamera(AugustEntity, Camera):
class AugustCamera(AugustEntityMixin, Camera):
"""An implementation of an August security camera."""
_attr_translation_key = "camera"
@@ -9,7 +9,7 @@ from typing import Any
import aiohttp
import voluptuous as vol
from yalexs.authenticator_common import ValidationResult
from yalexs.const import BRANDS_WITHOUT_OAUTH, DEFAULT_BRAND, Brand
from yalexs.const import BRANDS, DEFAULT_BRAND
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
@@ -28,12 +28,6 @@ from .const import (
from .gateway import AugustGateway
from .util import async_create_august_clientsession
# The Yale Home Brand is not supported by the August integration
# anymore and should migrate to the Yale integration
AVAILABLE_BRANDS = BRANDS_WITHOUT_OAUTH.copy()
del AVAILABLE_BRANDS[Brand.YALE_HOME]
_LOGGER = logging.getLogger(__name__)
@@ -124,7 +118,7 @@ class AugustConfigFlow(ConfigFlow, domain=DOMAIN):
vol.Required(
CONF_BRAND,
default=self._user_auth_details.get(CONF_BRAND, DEFAULT_BRAND),
): vol.In(AVAILABLE_BRANDS),
): vol.In(BRANDS),
vol.Required(
CONF_LOGIN_METHOD,
default=self._user_auth_details.get(
@@ -214,7 +208,7 @@ class AugustConfigFlow(ConfigFlow, domain=DOMAIN):
vol.Required(
CONF_BRAND,
default=self._user_auth_details.get(CONF_BRAND, DEFAULT_BRAND),
): vol.In(BRANDS_WITHOUT_OAUTH),
): vol.In(BRANDS),
vol.Required(CONF_PASSWORD): str,
}
),
+2 -2
View File
@@ -20,7 +20,7 @@ from .const import MANUFACTURER
DEVICE_TYPES = ["keypad", "lock", "camera", "doorbell", "door", "bell"]
class AugustEntity(Entity):
class AugustEntityMixin(Entity):
"""Base implementation for August device."""
_attr_should_poll = False
@@ -87,7 +87,7 @@ class AugustEntity(Entity):
self._update_from_data()
class AugustDescriptionEntity(AugustEntity):
class AugustDescriptionEntity(AugustEntityMixin):
"""An August entity with a description."""
def __init__(
+17 -11
View File
@@ -63,17 +63,22 @@ async def async_setup_entry(
) -> None:
"""Set up the august event platform."""
data = config_entry.runtime_data
entities: list[AugustEventEntity] = [
AugustEventEntity(data, lock, description)
for description in TYPES_DOORBELL
for lock in data.locks
if (detail := data.get_device_detail(lock.device_id)) and detail.doorbell
]
entities.extend(
AugustEventEntity(data, doorbell, description)
for description in TYPES_DOORBELL + TYPES_VIDEO_DOORBELL
for doorbell in data.doorbells
)
entities: list[AugustEventEntity] = []
for lock in data.locks:
detail = data.get_device_detail(lock.device_id)
if detail.doorbell:
entities.extend(
AugustEventEntity(data, lock, description)
for description in TYPES_DOORBELL
)
for doorbell in data.doorbells:
entities.extend(
AugustEventEntity(data, doorbell, description)
for description in TYPES_DOORBELL + TYPES_VIDEO_DOORBELL
)
async_add_entities(entities)
@@ -81,6 +86,7 @@ class AugustEventEntity(AugustDescriptionEntity, EventEntity):
"""An august event entity."""
entity_description: AugustEventEntityDescription
_attr_has_entity_name = True
_last_activity: Activity | None = None
@callback
+2 -2
View File
@@ -19,7 +19,7 @@ from homeassistant.helpers.restore_state import RestoreEntity
import homeassistant.util.dt as dt_util
from . import AugustConfigEntry, AugustData
from .entity import AugustEntity
from .entity import AugustEntityMixin
_LOGGER = logging.getLogger(__name__)
@@ -36,7 +36,7 @@ async def async_setup_entry(
async_add_entities(AugustLock(data, lock) for lock in data.locks)
class AugustLock(AugustEntity, RestoreEntity, LockEntity):
class AugustLock(AugustEntityMixin, RestoreEntity, LockEntity):
"""Representation of an August lock."""
_attr_name = None
@@ -4,6 +4,10 @@
"codeowners": ["@bdraco"],
"config_flow": true,
"dhcp": [
{
"hostname": "yale-connect-plus",
"macaddress": "00177A*"
},
{
"hostname": "connect",
"macaddress": "D86162*"
@@ -24,5 +28,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==8.5.5", "yalexs-ble==2.4.3"]
"requirements": ["yalexs==8.1.4", "yalexs-ble==2.4.3"]
}
+10 -11
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, cast
from typing import Any, Generic, TypeVar, cast
from yalexs.activity import ActivityType, LockOperationActivity
from yalexs.doorbell import Doorbell
@@ -42,7 +42,7 @@ from .const import (
OPERATION_METHOD_REMOTE,
OPERATION_METHOD_TAG,
)
from .entity import AugustDescriptionEntity, AugustEntity
from .entity import AugustDescriptionEntity, AugustEntityMixin
def _retrieve_device_battery_state(detail: LockDetail) -> int:
@@ -55,13 +55,14 @@ def _retrieve_linked_keypad_battery_state(detail: KeypadDetail) -> int | None:
return detail.battery_percentage
_T = TypeVar("_T", LockDetail, KeypadDetail)
@dataclass(frozen=True, kw_only=True)
class AugustSensorEntityDescription[T: LockDetail | KeypadDetail](
SensorEntityDescription
):
class AugustSensorEntityDescription(SensorEntityDescription, Generic[_T]):
"""Mixin for required keys."""
value_fn: Callable[[T], int | None]
value_fn: Callable[[_T], int | None]
SENSOR_TYPE_DEVICE_BATTERY = AugustSensorEntityDescription[LockDetail](
@@ -113,7 +114,7 @@ async def async_setup_entry(
async_add_entities(entities)
class AugustOperatorSensor(AugustEntity, RestoreSensor):
class AugustOperatorSensor(AugustEntityMixin, RestoreSensor):
"""Representation of an August lock operation sensor."""
_attr_translation_key = "operator"
@@ -197,12 +198,10 @@ class AugustOperatorSensor(AugustEntity, RestoreSensor):
self._operated_autorelock = last_attrs[ATTR_OPERATION_AUTORELOCK]
class AugustBatterySensor[T: LockDetail | KeypadDetail](
AugustDescriptionEntity, SensorEntity
):
class AugustBatterySensor(AugustDescriptionEntity, SensorEntity, Generic[_T]):
"""Representation of an August sensor."""
entity_description: AugustSensorEntityDescription[T]
entity_description: AugustSensorEntityDescription[_T]
_attr_device_class = SensorDeviceClass.BATTERY
_attr_native_unit_of_measurement = PERCENTAGE
@@ -1,10 +1,4 @@
{
"issues": {
"yale_brand_migration": {
"title": "Yale Home has a new integration",
"description": "Add the [Yale integration]({migrate_url}), and remove the August integration as soon as possible to avoid an interruption in service. The Yale Home brand will stop working with the August integration soon and will be removed in a future release."
}
},
"config": {
"error": {
"unhandled": "Unhandled error: {error}",
+6 -1
View File
@@ -63,11 +63,16 @@ def _activity_time_based(latest: Activity) -> Activity | None:
"""Get the latest state of the sensor."""
start = latest.activity_start_time
end = latest.activity_end_time + TIME_TO_DECLARE_DETECTION
if start <= datetime.now() <= end:
if start <= _native_datetime() <= end:
return latest
return None
def _native_datetime() -> datetime:
"""Return time in the format august uses without timezone."""
return datetime.now()
def retrieve_online_state(
data: AugustData, detail: DoorbellDetail | LockDetail
) -> bool:
@@ -75,10 +75,11 @@ class AuroraABBConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 1
def __init__(self) -> None:
def __init__(self):
"""Initialise the config flow."""
self.config = None
self._com_ports_list: list[str] | None = None
self._default_com_port: str | None = None
self._default_com_port = None
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -22,11 +22,11 @@ class AussieBroadbandConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 1
def __init__(self) -> None:
def __init__(self):
"""Initialize the config flow."""
self.data: dict = {}
self.options: dict = {CONF_SERVICES: []}
self.services: list[dict[str, Any]] = []
self.services: list[dict[str]] = []
self.client: AussieBB | None = None
self._reauth_username: str | None = None
@@ -99,11 +99,15 @@ class AussieBroadbandConfigFlow(ConfigFlow, domain=DOMAIN):
}
if not (errors := await self.async_auth(data)):
entry = self.hass.config_entries.async_get_entry(
self.context["entry_id"]
)
assert entry
return self.async_update_reload_and_abort(entry, data=data)
entry = await self.async_set_unique_id(self._reauth_username.lower())
if entry:
self.hass.config_entries.async_update_entry(
entry,
data=data,
)
await self.hass.config_entries.async_reload(entry.entry_id)
return self.async_abort(reason="reauth_successful")
return self.async_create_entry(title=self._reauth_username, data=data)
return self.async_show_form(
step_id="reauth_confirm",
@@ -10,10 +10,8 @@ blueprint:
selector:
entity:
filter:
- device_class: occupancy
domain: binary_sensor
- device_class: motion
domain: binary_sensor
device_class: motion
domain: binary_sensor
light_target:
name: Light
selector:
+5 -15
View File
@@ -9,20 +9,10 @@
}
},
"services": {
"turn_on": {
"service": "mdi:robot"
},
"turn_off": {
"service": "mdi:robot-off"
},
"toggle": {
"service": "mdi:robot"
},
"trigger": {
"service": "mdi:robot"
},
"reload": {
"service": "mdi:reload"
}
"turn_on": "mdi:robot",
"turn_off": "mdi:robot-off",
"toggle": "mdi:robot",
"trigger": "mdi:robot",
"reload": "mdi:reload"
}
}
+5 -2
View File
@@ -1,5 +1,6 @@
"""Config flow for AWS component."""
from collections.abc import Mapping
from typing import Any
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
@@ -12,9 +13,11 @@ class AWSFlowHandler(ConfigFlow, domain=DOMAIN):
VERSION = 1
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
async def async_step_import(
self, user_input: Mapping[str, Any]
) -> ConfigFlowResult:
"""Import a config entry."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
return self.async_create_entry(title="configuration.yaml", data=import_data)
return self.async_create_entry(title="configuration.yaml", data=user_input)
@@ -6,7 +6,7 @@ import logging
from typing import Final
from aioazuredevops.client import DevOpsClient
from aioazuredevops.models.build import Build
from aioazuredevops.models.builds import Build
from aioazuredevops.models.core import Project
import aiohttp
@@ -2,7 +2,7 @@
from dataclasses import dataclass
from aioazuredevops.models.build import Build
from aioazuredevops.models.builds import Build
from aioazuredevops.models.core import Project
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/azure_devops",
"iot_class": "cloud_polling",
"loggers": ["aioazuredevops"],
"requirements": ["aioazuredevops==2.2.1"]
"requirements": ["aioazuredevops==2.1.1"]
}
@@ -8,7 +8,7 @@ from datetime import datetime
import logging
from typing import Any
from aioazuredevops.models.build import Build
from aioazuredevops.models.builds import Build
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -154,15 +154,17 @@ class AEHConfigFlow(ConfigFlow, domain=DOMAIN):
options=self._options,
)
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
async def async_step_import(
self, import_config: dict[str, Any]
) -> ConfigFlowResult:
"""Import config from configuration.yaml."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
if CONF_SEND_INTERVAL in import_data:
self._options[CONF_SEND_INTERVAL] = import_data.pop(CONF_SEND_INTERVAL)
if CONF_MAX_DELAY in import_data:
self._options[CONF_MAX_DELAY] = import_data.pop(CONF_MAX_DELAY)
self._data = import_data
if CONF_SEND_INTERVAL in import_config:
self._options[CONF_SEND_INTERVAL] = import_config.pop(CONF_SEND_INTERVAL)
if CONF_MAX_DELAY in import_config:
self._options[CONF_MAX_DELAY] = import_config.pop(CONF_MAX_DELAY)
self._data = import_config
errors = await validate_data(self._data)
if errors:
return self.async_abort(reason=errors["base"])
+1 -3
View File
@@ -1,7 +1,5 @@
{
"services": {
"create": {
"service": "mdi:cloud-upload"
}
"create": "mdi:cloud-upload"
}
}
+1 -3
View File
@@ -1,7 +1,5 @@
{
"services": {
"reload": {
"service": "mdi:reload"
}
"reload": "mdi:reload"
}
}
@@ -1,7 +1,5 @@
{
"services": {
"set_all_zones": {
"service": "mdi:home-sound-in"
}
"set_all_zones": "mdi:home-sound-in"
}
}
+17 -23
View File
@@ -35,11 +35,15 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
def host_port(data):
"""Return a list with host and port."""
return (data[CONF_HOST], data[CONF_PORT])
def create_schema(previous_input=None):
"""Create a schema with given values as default."""
if previous_input is not None:
host = previous_input[CONF_HOST]
port = previous_input[CONF_PORT]
host, port = host_port(previous_input)
else:
host = DEFAULT_HOST
port = DEFAULT_PORT
@@ -66,9 +70,9 @@ class BleBoxConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 1
def __init__(self) -> None:
def __init__(self):
"""Initialize the BleBox config flow."""
self.device_config: dict[str, Any] = {}
self.device_config = {}
def handle_step_exception(
self, step, exception, schema, host, port, message_id, log_fn
@@ -142,9 +146,7 @@ class BleBoxConfigFlow(ConfigFlow, domain=DOMAIN):
},
)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
async def async_step_user(self, user_input=None):
"""Handle initial user-triggered config step."""
hass = self.hass
schema = create_schema(user_input)
@@ -157,14 +159,14 @@ class BleBoxConfigFlow(ConfigFlow, domain=DOMAIN):
description_placeholders={},
)
host = user_input[CONF_HOST]
port = user_input[CONF_PORT]
addr = host_port(user_input)
username = user_input.get(CONF_USERNAME)
password = user_input.get(CONF_PASSWORD)
for entry in self._async_current_entries():
if host == entry.data[CONF_HOST] and port == entry.data[CONF_PORT]:
if addr == host_port(entry.data):
host, port = addr
return self.async_abort(
reason=ADDRESS_ALREADY_CONFIGURED,
description_placeholders={"address": f"{host}:{port}"},
@@ -172,35 +174,27 @@ class BleBoxConfigFlow(ConfigFlow, domain=DOMAIN):
websession = get_maybe_authenticated_session(hass, password, username)
api_host = ApiHost(
host, port, DEFAULT_SETUP_TIMEOUT, websession, hass.loop, _LOGGER
)
api_host = ApiHost(*addr, DEFAULT_SETUP_TIMEOUT, websession, hass.loop, _LOGGER)
try:
product = await Box.async_from_host(api_host)
except UnsupportedBoxVersion as ex:
return self.handle_step_exception(
"user",
ex,
schema,
host,
port,
UNSUPPORTED_VERSION,
_LOGGER.debug,
"user", ex, schema, *addr, UNSUPPORTED_VERSION, _LOGGER.debug
)
except UnauthorizedRequest as ex:
return self.handle_step_exception(
"user", ex, schema, host, port, CANNOT_CONNECT, _LOGGER.error
"user", ex, schema, *addr, CANNOT_CONNECT, _LOGGER.error
)
except Error as ex:
return self.handle_step_exception(
"user", ex, schema, host, port, CANNOT_CONNECT, _LOGGER.warning
"user", ex, schema, *addr, CANNOT_CONNECT, _LOGGER.warning
)
except RuntimeError as ex:
return self.handle_step_exception(
"user", ex, schema, host, port, UNKNOWN, _LOGGER.error
"user", ex, schema, *addr, UNKNOWN, _LOGGER.error
)
# Check if configured but IP changed since
+5 -15
View File
@@ -12,20 +12,10 @@
}
},
"services": {
"record": {
"service": "mdi:video-box"
},
"trigger_camera": {
"service": "mdi:image-refresh"
},
"save_video": {
"service": "mdi:file-video"
},
"save_recent_clips": {
"service": "mdi:file-video"
},
"send_pin": {
"service": "mdi:two-factor-authentication"
}
"record": "mdi:video-box",
"trigger_camera": "mdi:image-refresh",
"save_video": "mdi:file-video",
"save_recent_clips": "mdi:file-video",
"send_pin": "mdi:two-factor-authentication"
}
}
+4 -12
View File
@@ -1,16 +1,8 @@
{
"services": {
"join": {
"service": "mdi:link-variant"
},
"unjoin": {
"service": "mdi:link-variant-off"
},
"set_sleep_timer": {
"service": "mdi:sleep"
},
"clear_sleep_timer": {
"service": "mdi:sleep-off"
}
"join": "mdi:link-variant",
"unjoin": "mdi:link-variant-off",
"set_sleep_timer": "mdi:sleep",
"clear_sleep_timer": "mdi:sleep-off"
}
}
@@ -309,7 +309,7 @@ class BluesoundPlayer(MediaPlayerEntity):
return True
async def _poll_loop(self):
async def _start_poll_command(self):
"""Loop which polls the status of the player."""
while True:
try:
@@ -335,7 +335,7 @@ class BluesoundPlayer(MediaPlayerEntity):
await super().async_added_to_hass()
self._polling_task = self.hass.async_create_background_task(
self._poll_loop(),
self._start_poll_command(),
name=f"bluesound.polling_{self.host}:{self.port}",
)
@@ -345,9 +345,7 @@ class BluesoundPlayer(MediaPlayerEntity):
assert self._polling_task is not None
if self._polling_task.cancel():
# the sleeps in _poll_loop will raise CancelledError
with suppress(CancelledError):
await self._polling_task
await self._polling_task
self.hass.data[DATA_BLUESOUND].remove(self)
@@ -18,8 +18,8 @@
"bleak-retry-connector==3.5.0",
"bluetooth-adapters==0.19.4",
"bluetooth-auto-recovery==1.4.2",
"bluetooth-data-tools==1.20.0",
"dbus-fast==2.24.0",
"bluetooth-data-tools==1.19.4",
"dbus-fast==2.23.0",
"habluetooth==3.3.2"
]
}
@@ -1,7 +1,5 @@
{
"services": {
"update": {
"service": "mdi:update"
}
"update": "mdi:update"
}
}
@@ -7,5 +7,5 @@
"iot_class": "cloud_polling",
"loggers": ["bimmer_connected"],
"quality_scale": "platinum",
"requirements": ["bimmer-connected[china]==0.16.2"]
"requirements": ["bimmer-connected[china]==0.16.1"]
}
@@ -148,8 +148,7 @@
"cooling": "Cooling",
"heating": "Heating",
"inactive": "Inactive",
"standby": "Standby",
"ventilation": "Ventilation"
"standby": "Standby"
}
},
"front_left_current_pressure": {
+7 -21
View File
@@ -96,26 +96,12 @@
}
},
"services": {
"set_fan_speed_tracked_state": {
"service": "mdi:fan"
},
"set_switch_power_tracked_state": {
"service": "mdi:toggle-switch-variant"
},
"set_light_power_tracked_state": {
"service": "mdi:lightbulb"
},
"set_light_brightness_tracked_state": {
"service": "mdi:lightbulb-on"
},
"start_increasing_brightness": {
"service": "mdi:brightness-7"
},
"start_decreasing_brightness": {
"service": "mdi:brightness-1"
},
"stop": {
"service": "mdi:stop"
}
"set_fan_speed_tracked_state": "mdi:fan",
"set_switch_power_tracked_state": "mdi:toggle-switch-variant",
"set_light_power_tracked_state": "mdi:lightbulb",
"set_light_brightness_tracked_state": "mdi:lightbulb-on",
"start_increasing_brightness": "mdi:brightness-7",
"start_decreasing_brightness": "mdi:brightness-1",
"stop": "mdi:stop"
}
}
+1 -3
View File
@@ -7,8 +7,6 @@
}
},
"services": {
"send_message": {
"service": "mdi:cellphone-message"
}
"send_message": "mdi:cellphone-message"
}
}
@@ -5,7 +5,7 @@ import errno
from functools import partial
import logging
import socket
from typing import TYPE_CHECKING, Any
from typing import Any
import broadlink as blk
from broadlink.exceptions import (
@@ -39,11 +39,9 @@ class BroadlinkFlowHandler(ConfigFlow, domain=DOMAIN):
def __init__(self) -> None:
"""Initialize the Broadlink flow."""
self.device: blk.Device | None = None
self.device = None
async def async_set_device(
self, device: blk.Device, raise_on_progress: bool = True
) -> None:
async def async_set_device(self, device, raise_on_progress=True):
"""Define a device for the config flow."""
if device.type not in DEVICE_TYPES:
_LOGGER.error(
@@ -92,9 +90,7 @@ class BroadlinkFlowHandler(ConfigFlow, domain=DOMAIN):
await self.async_set_device(device)
return await self.async_step_auth()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
async def async_step_user(self, user_input=None):
"""Handle a flow initiated by the user."""
errors = {}
@@ -131,8 +127,6 @@ class BroadlinkFlowHandler(ConfigFlow, domain=DOMAIN):
)
return await self.async_step_auth()
if TYPE_CHECKING:
assert self.device
if device.mac == self.device.mac:
await self.async_set_device(device, raise_on_progress=False)
return await self.async_step_auth()
@@ -314,10 +308,10 @@ class BroadlinkFlowHandler(ConfigFlow, domain=DOMAIN):
step_id="finish", data_schema=vol.Schema(data_schema), errors=errors
)
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
async def async_step_import(self, import_info):
"""Import a device."""
self._async_abort_entries_match({CONF_HOST: import_data[CONF_HOST]})
return await self.async_step_user(import_data)
self._async_abort_entries_match({CONF_HOST: import_info[CONF_HOST]})
return await self.async_step_user(import_info)
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
+1 -3
View File
@@ -1,7 +1,5 @@
{
"services": {
"browse_url": {
"service": "mdi:web"
}
"browse_url": "mdi:web"
}
}
-11
View File
@@ -27,7 +27,6 @@ from homeassistant.const import (
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfConductivity,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
@@ -357,16 +356,6 @@ SENSOR_DESCRIPTIONS = {
native_unit_of_measurement=UnitOfVolume.LITERS,
state_class=SensorStateClass.TOTAL,
),
# Conductivity (µS/cm)
(
BTHomeSensorDeviceClass.CONDUCTIVITY,
Units.CONDUCTIVITY,
): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.CONDUCTIVITY}_{Units.CONDUCTIVITY}",
device_class=SensorDeviceClass.CONDUCTIVITY,
native_unit_of_measurement=UnitOfConductivity.MICROSIEMENS,
state_class=SensorStateClass.MEASUREMENT,
),
}
+1 -3
View File
@@ -14,8 +14,6 @@
}
},
"services": {
"press": {
"service": "mdi:gesture-tap-button"
}
"press": "mdi:gesture-tap-button"
}
}
+3 -9
View File
@@ -9,14 +9,8 @@
}
},
"services": {
"create_event": {
"service": "mdi:calendar-plus"
},
"get_events": {
"service": "mdi:calendar-month"
},
"list_events": {
"service": "mdi:calendar-month"
}
"create_event": "mdi:calendar-plus",
"get_events": "mdi:calendar-month",
"list_events": "mdi:calendar-month"
}
}
+7 -21
View File
@@ -8,26 +8,12 @@
}
},
"services": {
"disable_motion_detection": {
"service": "mdi:motion-sensor-off"
},
"enable_motion_detection": {
"service": "mdi:motion-sensor"
},
"play_stream": {
"service": "mdi:play"
},
"record": {
"service": "mdi:record-rec"
},
"snapshot": {
"service": "mdi:camera"
},
"turn_off": {
"service": "mdi:video-off"
},
"turn_on": {
"service": "mdi:video"
}
"disable_motion_detection": "mdi:motion-sensor-off",
"enable_motion_detection": "mdi:motion-sensor",
"play_stream": "mdi:play",
"record": "mdi:record-rec",
"snapshot": "mdi:camera",
"turn_off": "mdi:video-off",
"turn_on": "mdi:video"
}
}
@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/camera",
"integration_type": "entity",
"quality_scale": "internal",
"requirements": ["PyTurboJPEG==1.7.5"]
"requirements": ["PyTurboJPEG==1.7.1"]
}
@@ -54,9 +54,11 @@ class CanaryConfigFlow(ConfigFlow, domain=DOMAIN):
"""Get the options flow for this handler."""
return CanaryOptionsFlowHandler(config_entry)
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
async def async_step_import(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initiated by configuration file."""
return await self.async_step_user(import_data)
return await self.async_step_user(user_input)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
+5 -7
View File
@@ -29,11 +29,11 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
VERSION = 1
def __init__(self) -> None:
def __init__(self):
"""Initialize flow."""
self._ignore_cec = set[str]()
self._known_hosts = set[str]()
self._wanted_uuid = set[str]()
self._ignore_cec = set()
self._known_hosts = set()
self._wanted_uuid = set()
@staticmethod
@callback
@@ -43,9 +43,7 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
"""Get the options flow for this handler."""
return CastOptionsFlowHandler(config_entry)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
+1 -3
View File
@@ -1,7 +1,5 @@
{
"services": {
"show_lovelace_view": {
"service": "mdi:view-dashboard"
}
"show_lovelace_view": "mdi:view-dashboard"
}
}
@@ -95,9 +95,12 @@ class CertexpiryConfigFlow(ConfigFlow, domain=DOMAIN):
errors=self._errors,
)
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
async def async_step_import(
self,
user_input: Mapping[str, Any] | None = None,
) -> ConfigFlowResult:
"""Import a config entry.
Only host was required in the yaml file all other fields are optional
"""
return await self.async_step_user(import_data)
return await self.async_step_user(user_input)
+3 -9
View File
@@ -1,13 +1,7 @@
{
"services": {
"seek_forward": {
"service": "mdi:skip-forward"
},
"seek_backward": {
"service": "mdi:skip-backward"
},
"seek_by": {
"service": "mdi:timer-check-outline"
}
"seek_forward": "mdi:skip-forward",
"seek_backward": "mdi:skip-backward",
"seek_by": "mdi:timer-check-outline"
}
}
+10 -30
View File
@@ -56,35 +56,15 @@
}
},
"services": {
"set_fan_mode": {
"service": "mdi:fan"
},
"set_humidity": {
"service": "mdi:water-percent"
},
"set_swing_mode": {
"service": "mdi:arrow-oscillating"
},
"set_temperature": {
"service": "mdi:thermometer"
},
"set_aux_heat": {
"service": "mdi:radiator"
},
"set_preset_mode": {
"service": "mdi:sofa"
},
"set_hvac_mode": {
"service": "mdi:hvac"
},
"turn_on": {
"service": "mdi:power-on"
},
"turn_off": {
"service": "mdi:power-off"
},
"toggle": {
"service": "mdi:toggle-switch"
}
"set_fan_mode": "mdi:fan",
"set_humidity": "mdi:water-percent",
"set_swing_mode": "mdi:arrow-oscillating",
"set_temperature": "mdi:thermometer",
"set_aux_heat": "mdi:radiator",
"set_preset_mode": "mdi:sofa",
"set_hvac_mode": "mdi:hvac",
"turn_on": "mdi:power-on",
"turn_off": "mdi:power-off",
"toggle": "mdi:toggle-switch"
}
}
+2 -6
View File
@@ -1,10 +1,6 @@
{
"services": {
"remote_connect": {
"service": "mdi:cloud"
},
"remote_disconnect": {
"service": "mdi:cloud-off"
}
"remote_connect": "mdi:cloud",
"remote_disconnect": "mdi:cloud-off"
}
}
+1 -1
View File
@@ -221,7 +221,7 @@ class CloudProvider(Provider):
def __init__(self, cloud: Cloud[CloudClient]) -> None:
"""Initialize cloud provider."""
self.cloud = cloud
self.name = "Home Assistant Cloud"
self.name = "Cloud"
self._language, self._voice = cloud.client.prefs.tts_default_voice
cloud.client.prefs.async_listen_updates(self._sync_prefs)
@@ -1,7 +1,5 @@
{
"services": {
"update_records": {
"service": "mdi:dns"
}
"update_records": "mdi:dns"
}
}
@@ -1,7 +1,5 @@
{
"services": {
"turn_on": {
"service": "mdi:lightbulb-on"
}
"turn_on": "mdi:lightbulb-on"
}
}
@@ -1,7 +1,5 @@
{
"services": {
"reload": {
"service": "mdi:reload"
}
"reload": "mdi:reload"
}
}
@@ -6,7 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/control4",
"iot_class": "local_polling",
"loggers": ["pyControl4"],
"requirements": ["pyControl4==1.2.0"],
"requirements": ["pyControl4==1.1.0"],
"ssdp": [
{
"st": "c4:director"
@@ -1,10 +1,6 @@
{
"services": {
"process": {
"service": "mdi:message-processing"
},
"reload": {
"service": "mdi:reload"
}
"process": "mdi:message-processing",
"reload": "mdi:reload"
}
}
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["hassil==1.7.4", "home-assistant-intents==2024.8.29"]
"requirements": ["hassil==1.7.4", "home-assistant-intents==2024.8.7"]
}
@@ -23,22 +23,11 @@ class ConversationInput:
"""User input to be processed."""
text: str
"""User spoken text."""
context: Context
"""Context of the request."""
conversation_id: str | None
"""Unique identifier for the conversation."""
device_id: str | None
"""Unique identifier for the device."""
language: str
"""Language of the request."""
agent_id: str | None = None
"""Agent to use for processing."""
@dataclass(slots=True)
+4 -12
View File
@@ -1,16 +1,8 @@
{
"services": {
"decrement": {
"service": "mdi:numeric-negative-1"
},
"increment": {
"service": "mdi:numeric-positive-1"
},
"reset": {
"service": "mdi:refresh"
},
"set_value": {
"service": "mdi:counter"
}
"decrement": "mdi:numeric-negative-1",
"increment": "mdi:numeric-positive-1",
"reset": "mdi:refresh",
"set_value": "mdi:counter"
}
}
+10 -30
View File
@@ -78,35 +78,15 @@
}
},
"services": {
"close_cover": {
"service": "mdi:arrow-down-box"
},
"close_cover_tilt": {
"service": "mdi:arrow-bottom-left"
},
"open_cover": {
"service": "mdi:arrow-up-box"
},
"open_cover_tilt": {
"service": "mdi:arrow-top-right"
},
"set_cover_position": {
"service": "mdi:arrow-down-box"
},
"set_cover_tilt_position": {
"service": "mdi:arrow-top-right"
},
"stop_cover": {
"service": "mdi:stop"
},
"stop_cover_tilt": {
"service": "mdi:stop"
},
"toggle": {
"service": "mdi:arrow-up-down"
},
"toggle_cover_tilt": {
"service": "mdi:arrow-top-right-bottom-left"
}
"close_cover": "mdi:arrow-down-box",
"close_cover_tilt": "mdi:arrow-bottom-left",
"open_cover": "mdi:arrow-up-box",
"open_cover_tilt": "mdi:arrow-top-right",
"set_cover_position": "mdi:arrow-down-box",
"set_cover_tilt_position": "mdi:arrow-top-right",
"stop_cover": "mdi:stop",
"stop_cover_tilt": "mdi:stop",
"toggle": "mdi:arrow-up-down",
"toggle_cover_tilt": "mdi:arrow-top-right-bottom-left"
}
}
@@ -6,6 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/daikin",
"iot_class": "local_polling",
"loggers": ["pydaikin"],
"requirements": ["pydaikin==2.13.6"],
"requirements": ["pydaikin==2.13.4"],
"zeroconf": ["_dkapi._tcp.local."]
}
+1 -3
View File
@@ -5,8 +5,6 @@
}
},
"services": {
"set_value": {
"service": "mdi:calendar-edit"
}
"set_value": "mdi:calendar-edit"
}
}
+1 -3
View File
@@ -5,8 +5,6 @@
}
},
"services": {
"set_value": {
"service": "mdi:calendar-edit"
}
"set_value": "mdi:calendar-edit"
}
}
+1 -3
View File
@@ -1,7 +1,5 @@
{
"services": {
"start": {
"service": "mdi:play"
}
"start": "mdi:play"
}
}
+3 -9
View File
@@ -1,13 +1,7 @@
{
"services": {
"configure": {
"service": "mdi:cog"
},
"device_refresh": {
"service": "mdi:refresh"
},
"remove_orphaned_entries": {
"service": "mdi:bookmark-remove"
}
"configure": "mdi:cog",
"device_refresh": "mdi:refresh",
"remove_orphaned_entries": "mdi:bookmark-remove"
}
}
-45
View File
@@ -3,7 +3,6 @@
from __future__ import annotations
from pydeconz.models.event import EventType
from pydeconz.models.sensor.air_purifier import AirPurifier, AirPurifierFanMode
from pydeconz.models.sensor.presence import (
Presence,
PresenceConfigDeviceMode,
@@ -37,17 +36,6 @@ async def async_setup_entry(
hub = DeconzHub.get_hub(hass, config_entry)
hub.entities[DOMAIN] = set()
@callback
def async_add_air_purifier_sensor(_: EventType, sensor_id: str) -> None:
"""Add air purifier select entity from deCONZ."""
sensor = hub.api.sensors.air_purifier[sensor_id]
async_add_entities([DeconzAirPurifierFanMode(sensor, hub)])
hub.register_platform_add_device_callback(
async_add_air_purifier_sensor,
hub.api.sensors.air_purifier,
)
@callback
def async_add_presence_sensor(_: EventType, sensor_id: str) -> None:
"""Add presence select entity from deCONZ."""
@@ -67,39 +55,6 @@ async def async_setup_entry(
)
class DeconzAirPurifierFanMode(DeconzDevice[AirPurifier], SelectEntity):
"""Representation of a deCONZ air purifier fan mode entity."""
_name_suffix = "Fan Mode"
unique_id_suffix = "fan_mode"
_update_key = "mode"
_attr_entity_category = EntityCategory.CONFIG
_attr_options = [
AirPurifierFanMode.OFF.value,
AirPurifierFanMode.AUTO.value,
AirPurifierFanMode.SPEED_1.value,
AirPurifierFanMode.SPEED_2.value,
AirPurifierFanMode.SPEED_3.value,
AirPurifierFanMode.SPEED_4.value,
AirPurifierFanMode.SPEED_5.value,
]
TYPE = DOMAIN
@property
def current_option(self) -> str:
"""Return the selected entity option to represent the entity state."""
return self._device.fan_mode.value
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
await self.hub.api.sensors.air_purifier.set_config(
id=self._device.resource_id,
fan_mode=AirPurifierFanMode(option),
)
class DeconzPresenceDeviceModeSelect(DeconzDevice[Presence], SelectEntity):
"""Representation of a deCONZ presence device mode entity."""
+2 -2
View File
@@ -37,12 +37,12 @@ class DemoConfigFlow(ConfigFlow, domain=DOMAIN):
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
async def async_step_import(self, import_info: dict[str, Any]) -> ConfigFlowResult:
"""Set the config entry up from yaml."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
return self.async_create_entry(title="Demo", data=import_data)
return self.async_create_entry(title="Demo", data=import_info)
class OptionsFlowHandler(OptionsFlow):

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