Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 45aab80b59 |
@@ -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/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.*
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"domain": "roth",
|
||||
"name": "Roth",
|
||||
"integrations": ["touchline", "touchline_sl"]
|
||||
}
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/acmeda",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiopulse"],
|
||||
"requirements": ["aiopulse==0.4.6"]
|
||||
"requirements": ["aiopulse==0.4.4"]
|
||||
}
|
||||
|
||||
@@ -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,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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ class Airtouch5AC(Airtouch5ClimateEntity):
|
||||
_LOGGER.debug("Argument `temperature` is missing in set_temperature")
|
||||
return
|
||||
|
||||
await self._control(setpoint=SetpointControl.CHANGE_SETPOINT, temp=temp)
|
||||
await self._control(temp=temp)
|
||||
|
||||
|
||||
class Airtouch5Zone(Airtouch5ClimateEntity):
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["androidtvremote2"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["androidtvremote2==0.1.2"],
|
||||
"requirements": ["androidtvremote2==0.1.1"],
|
||||
"zeroconf": ["_androidtvremote2._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ LOGGER = logging.getLogger(__package__)
|
||||
CONF_RECOMMENDED = "recommended"
|
||||
CONF_PROMPT = "prompt"
|
||||
CONF_CHAT_MODEL = "chat_model"
|
||||
RECOMMENDED_CHAT_MODEL = "claude-3-haiku-20240307"
|
||||
RECOMMENDED_CHAT_MODEL = "claude-3-5-sonnet-20240620"
|
||||
CONF_MAX_TOKENS = "max_tokens"
|
||||
RECOMMENDED_MAX_TOKENS = 1024
|
||||
CONF_TEMPERATURE = "temperature"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["pyatv", "srptools"],
|
||||
"requirements": ["pyatv==0.15.1"],
|
||||
"requirements": ["pyatv==0.15.0"],
|
||||
"zeroconf": [
|
||||
"_mediaremotetv._tcp.local.",
|
||||
"_companion-link._tcp.local.",
|
||||
|
||||
@@ -36,11 +36,10 @@ class ApSystemsDataCoordinator(DataUpdateCoordinator[ApSystemsSensorData]):
|
||||
|
||||
async def _async_setup(self) -> None:
|
||||
try:
|
||||
device_info = await self.api.get_device_info()
|
||||
max_power = (await self.api.get_device_info()).maxPower
|
||||
except (ConnectionError, TimeoutError):
|
||||
raise UpdateFailed from None
|
||||
self.api.max_power = device_info.maxPower
|
||||
self.api.min_power = device_info.minPower
|
||||
self.api.max_power = max_power
|
||||
|
||||
async def _async_update_data(self) -> ApSystemsSensorData:
|
||||
output_data = await self.api.get_output_data()
|
||||
|
||||
@@ -26,6 +26,7 @@ async def async_setup_entry(
|
||||
class ApSystemsMaxOutputNumber(ApSystemsEntity, NumberEntity):
|
||||
"""Base sensor to be used with description."""
|
||||
|
||||
_attr_native_min_value = 30
|
||||
_attr_native_step = 1
|
||||
_attr_device_class = NumberDeviceClass.POWER
|
||||
_attr_mode = NumberMode.BOX
|
||||
@@ -41,7 +42,6 @@ class ApSystemsMaxOutputNumber(ApSystemsEntity, NumberEntity):
|
||||
self._api = data.coordinator.api
|
||||
self._attr_unique_id = f"{data.device_id}_output_limit"
|
||||
self._attr_native_max_value = data.coordinator.api.max_power
|
||||
self._attr_native_min_value = data.coordinator.api.min_power
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Set the state with the value fetched from the inverter."""
|
||||
|
||||
@@ -56,7 +56,7 @@ class AquaCellConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
refresh_token = await api.authenticate(
|
||||
user_input[CONF_EMAIL], user_input[CONF_PASSWORD]
|
||||
)
|
||||
except (ApiException, TimeoutError):
|
||||
except ApiException:
|
||||
errors["base"] = "cannot_connect"
|
||||
except AuthenticationFailed:
|
||||
errors["base"] = "invalid_auth"
|
||||
|
||||
@@ -56,7 +56,7 @@ class AquacellCoordinator(DataUpdateCoordinator[dict[str, Softener]]):
|
||||
so entities can quickly look up their data.
|
||||
"""
|
||||
|
||||
async with asyncio.timeout(30):
|
||||
async with asyncio.timeout(10):
|
||||
# Check if the refresh token is expired
|
||||
expiry_time = (
|
||||
self.refresh_token_creation_time
|
||||
@@ -72,7 +72,7 @@ class AquacellCoordinator(DataUpdateCoordinator[dict[str, Softener]]):
|
||||
softeners = await self.aquacell_api.get_all_softeners()
|
||||
except AuthenticationFailed as err:
|
||||
raise ConfigEntryError from err
|
||||
except (AquacellApiException, TimeoutError) as err:
|
||||
except AquacellApiException as err:
|
||||
raise UpdateFailed(f"Error communicating with API: {err}") from err
|
||||
|
||||
return {softener.dsn: softener for softener in softeners}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
),
|
||||
|
||||
@@ -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__(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.6.4", "yalexs-ble==2.4.3"]
|
||||
"requirements": ["yalexs==8.1.4", "yalexs-ble==2.4.3"]
|
||||
}
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,7 +1,5 @@
|
||||
{
|
||||
"services": {
|
||||
"create": {
|
||||
"service": "mdi:cloud-upload"
|
||||
}
|
||||
"create": "mdi:cloud-upload"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -60,9 +60,6 @@ COLOR_MODE_MAP = {
|
||||
class BleBoxLightEntity(BleBoxEntity[blebox_uniapi.light.Light], LightEntity):
|
||||
"""Representation of BleBox lights."""
|
||||
|
||||
_attr_max_mireds = 370 # 1,000,000 divided by 2700 Kelvin = 370 Mireds
|
||||
_attr_min_mireds = 154 # 1,000,000 divided by 6500 Kelvin = 154 Mireds
|
||||
|
||||
def __init__(self, feature: blebox_uniapi.light.Light) -> None:
|
||||
"""Initialize a BleBox light."""
|
||||
super().__init__(feature)
|
||||
@@ -90,7 +87,12 @@ class BleBoxLightEntity(BleBoxEntity[blebox_uniapi.light.Light], LightEntity):
|
||||
|
||||
Set values to _attr_ibutes if needed.
|
||||
"""
|
||||
return COLOR_MODE_MAP.get(self._feature.color_mode, ColorMode.ONOFF)
|
||||
color_mode_tmp = COLOR_MODE_MAP.get(self._feature.color_mode, ColorMode.ONOFF)
|
||||
if color_mode_tmp == ColorMode.COLOR_TEMP:
|
||||
self._attr_min_mireds = 1
|
||||
self._attr_max_mireds = 255
|
||||
|
||||
return color_mode_tmp
|
||||
|
||||
@property
|
||||
def supported_color_modes(self):
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
"habluetooth==3.4.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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util.ssl import get_default_context
|
||||
|
||||
from .const import CONF_GCID, CONF_READ_ONLY, CONF_REFRESH_TOKEN, DOMAIN, SCAN_INTERVALS
|
||||
|
||||
@@ -34,7 +33,6 @@ class BMWDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
entry.data[CONF_PASSWORD],
|
||||
get_region_from_name(entry.data[CONF_REGION]),
|
||||
observer_position=GPSPosition(hass.config.latitude, hass.config.longitude),
|
||||
verify=get_default_context(),
|
||||
)
|
||||
self.read_only = entry.options[CONF_READ_ONLY]
|
||||
self._entry = entry
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["bimmer_connected"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["bimmer-connected[china]==0.16.3"]
|
||||
"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": {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,7 +1,5 @@
|
||||
{
|
||||
"services": {
|
||||
"browse_url": {
|
||||
"service": "mdi:web"
|
||||
}
|
||||
"browse_url": "mdi:web"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,5 @@ async def async_get_config_entry_diagnostics(
|
||||
return {
|
||||
"info": data.info.to_dict(),
|
||||
"device": data.device.to_dict(),
|
||||
"coordinator_data": {
|
||||
"state": data.coordinator.data.state.to_dict(),
|
||||
},
|
||||
"static": data.static.to_dict(),
|
||||
"state": data.coordinator.data.state.to_dict(),
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ class BSBLanEntity(CoordinatorEntity[BSBLanUpdateCoordinator]):
|
||||
def __init__(self, coordinator: BSBLanUpdateCoordinator, data: BSBLanData) -> None:
|
||||
"""Initialize BSBLan entity."""
|
||||
super().__init__(coordinator, data)
|
||||
host = coordinator.config_entry.data["host"]
|
||||
mac = data.device.MAC
|
||||
host = self.coordinator.config_entry.data["host"]
|
||||
mac = self.coordinator.config_entry.data["mac"]
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, mac)},
|
||||
identifiers={(DOMAIN, data.device.MAC)},
|
||||
connections={(CONNECTION_NETWORK_MAC, format_mac(mac))},
|
||||
name=data.device.name,
|
||||
manufacturer="BSBLAN Inc.",
|
||||
|
||||
@@ -7,9 +7,6 @@ from typing import Any
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
|
||||
from homeassistant.components.device_automation.exceptions import (
|
||||
InvalidDeviceAutomationConfig,
|
||||
)
|
||||
from homeassistant.components.homeassistant.triggers import event as event_trigger
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_ID,
|
||||
@@ -46,46 +43,33 @@ TRIGGERS_BY_EVENT_CLASS = {
|
||||
EVENT_CLASS_DIMMER: {"rotate_left", "rotate_right"},
|
||||
}
|
||||
|
||||
TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
{vol.Required(CONF_TYPE): str, vol.Required(CONF_SUBTYPE): str}
|
||||
)
|
||||
SCHEMA_BY_EVENT_CLASS = {
|
||||
EVENT_CLASS_BUTTON: DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): vol.In([EVENT_CLASS_BUTTON]),
|
||||
vol.Required(CONF_SUBTYPE): vol.In(
|
||||
TRIGGERS_BY_EVENT_CLASS[EVENT_CLASS_BUTTON]
|
||||
),
|
||||
}
|
||||
),
|
||||
EVENT_CLASS_DIMMER: DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): vol.In([EVENT_CLASS_DIMMER]),
|
||||
vol.Required(CONF_SUBTYPE): vol.In(
|
||||
TRIGGERS_BY_EVENT_CLASS[EVENT_CLASS_DIMMER]
|
||||
),
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def async_validate_trigger_config(
|
||||
hass: HomeAssistant, config: ConfigType
|
||||
) -> ConfigType:
|
||||
"""Validate trigger config."""
|
||||
config = TRIGGER_SCHEMA(config)
|
||||
event_class = config[CONF_TYPE]
|
||||
event_type = config[CONF_SUBTYPE]
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
device = device_registry.async_get(config[CONF_DEVICE_ID])
|
||||
assert device is not None
|
||||
config_entries = [
|
||||
hass.config_entries.async_get_entry(entry_id)
|
||||
for entry_id in device.config_entries
|
||||
]
|
||||
bthome_config_entry = next(
|
||||
iter(entry for entry in config_entries if entry and entry.domain == DOMAIN)
|
||||
return SCHEMA_BY_EVENT_CLASS.get(config[CONF_TYPE], DEVICE_TRIGGER_BASE_SCHEMA)( # type: ignore[no-any-return]
|
||||
config
|
||||
)
|
||||
event_classes: list[str] = bthome_config_entry.data.get(
|
||||
CONF_DISCOVERED_EVENT_CLASSES, []
|
||||
)
|
||||
|
||||
if event_class not in event_classes:
|
||||
raise InvalidDeviceAutomationConfig(
|
||||
f"BTHome trigger {event_class} is not valid for device "
|
||||
f"{device} ({config[CONF_DEVICE_ID]})"
|
||||
)
|
||||
|
||||
if event_type not in TRIGGERS_BY_EVENT_CLASS.get(event_class.split("_")[0], ()):
|
||||
raise InvalidDeviceAutomationConfig(
|
||||
f"BTHome trigger {event_type} is not valid for device "
|
||||
f"{device} ({config[CONF_DEVICE_ID]})"
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
async def async_get_triggers(
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
DOMAIN = "buienradar"
|
||||
|
||||
DEFAULT_TIMEOUT = 60
|
||||
DEFAULT_TIMEFRAME = 60
|
||||
|
||||
DEFAULT_DIMENSION = 700
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""Shared utilities for different supported platforms."""
|
||||
|
||||
from asyncio import timeout
|
||||
from datetime import datetime, timedelta
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
from buienradar.buienradar import parse_data
|
||||
@@ -27,12 +27,12 @@ from buienradar.constants import (
|
||||
from buienradar.urls import JSON_FEED_URL, json_precipitation_forecast_url
|
||||
|
||||
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||
from homeassistant.core import CALLBACK_TYPE, callback
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import DEFAULT_TIMEOUT, SCHEDULE_NOK, SCHEDULE_OK
|
||||
from .const import SCHEDULE_NOK, SCHEDULE_OK
|
||||
|
||||
__all__ = ["BrData"]
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -59,10 +59,10 @@ class BrData:
|
||||
load_error_count: int = WARN_THRESHOLD
|
||||
rain_error_count: int = WARN_THRESHOLD
|
||||
|
||||
def __init__(self, hass: HomeAssistant, coordinates, timeframe, devices) -> None:
|
||||
def __init__(self, hass, coordinates, timeframe, devices):
|
||||
"""Initialize the data object."""
|
||||
self.devices = devices
|
||||
self.data: dict[str, Any] | None = {}
|
||||
self.data = {}
|
||||
self.hass = hass
|
||||
self.coordinates = coordinates
|
||||
self.timeframe = timeframe
|
||||
@@ -93,9 +93,9 @@ class BrData:
|
||||
resp = None
|
||||
try:
|
||||
websession = async_get_clientsession(self.hass)
|
||||
async with websession.get(
|
||||
url, timeout=aiohttp.ClientTimeout(total=DEFAULT_TIMEOUT)
|
||||
) as resp:
|
||||
async with timeout(10):
|
||||
resp = await websession.get(url)
|
||||
|
||||
result[STATUS_CODE] = resp.status
|
||||
result[CONTENT] = await resp.text()
|
||||
if resp.status == HTTPStatus.OK:
|
||||
|
||||
@@ -130,7 +130,7 @@ class BrWeather(WeatherEntity):
|
||||
_attr_should_poll = False
|
||||
_attr_supported_features = WeatherEntityFeature.FORECAST_DAILY
|
||||
|
||||
def __init__(self, config, coordinates) -> None:
|
||||
def __init__(self, config, coordinates):
|
||||
"""Initialize the platform with a data instance and station name."""
|
||||
self._stationname = config.get(CONF_NAME, "Buienradar")
|
||||
self._attr_name = self._stationname or f"BR {'(unknown station)'}"
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"press": {
|
||||
"service": "mdi:gesture-tap-button"
|
||||
}
|
||||
"press": "mdi:gesture-tap-button"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user