Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add preview to Threshold config & option flow #111043

Closed
wants to merge 47 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
3b5fccd
Add preview to Threshold config & option flow
jpbede Feb 20, 2024
884c3ea
Fix import
jpbede Mar 16, 2024
7c52537
Fix import
jpbede Mar 16, 2024
f849410
Merge branch 'dev' into threshold-config-flow-preview
emontnemery May 8, 2024
189c07d
Correct state class of ecowitt hourly rain rate sensors (#110475)
pantonvich May 8, 2024
7862596
Add `open` state to LockEntity (#111968)
gjohansson-ST May 8, 2024
92b246f
Fix nibe_heatpump climate for models without cooling support (#114599)
tizianodeg May 8, 2024
84a91a8
Improve config entry has already been setup error message (#117091)
bdraco May 8, 2024
6b3ffad
Fix nws blocking startup (#117094)
MatthewFlamm May 8, 2024
20b2924
Make the mqtt discovery update tasks eager and fix race (#117105)
jbouwh May 8, 2024
159f0fc
Migrate baf to use config entry runtime_data (#117081)
bdraco May 8, 2024
840d8cb
Add open and opening state support to MQTT lock (#117110)
jbouwh May 8, 2024
1d833d3
Avoid storing Bluetooth scanner in hass.data (#117074)
bdraco May 8, 2024
8c37b3a
Migrate govee_ble to use config entry runtime_data (#117076)
bdraco May 8, 2024
ead69af
Avoid creating a task to clear the hass instance at test teardown (#1…
bdraco May 8, 2024
03dcede
Avoid creating inner tasks to load storage (#117099)
bdraco May 8, 2024
6eeeafa
Speed up tests by making mock_get_source_ip session scoped (#117096)
bdraco May 8, 2024
8464c95
Migrate yalexs_ble to use config entry runtime_data (#117082)
bdraco May 8, 2024
0015088
Migrate elkm1 to use config entry runtime_data (#117077)
bdraco May 8, 2024
6da432a
Bump python-roborock to 2.1.1 (#117078)
Lash-L May 8, 2024
589104f
Export MQTT subscription helpers at integration level (#116150)
jbouwh May 8, 2024
ac54cdc
Enable Ruff RUF010 (#115371)
autinerd May 8, 2024
fe9e5e4
Ignore Ruff SIM103 (#115732)
autinerd May 8, 2024
ac9b8cc
Add a missing `addon_name` placeholder to the SkyConnect config flow …
puddly May 8, 2024
89049bc
Fix config entry _async_process_on_unload being called for forwarded …
bdraco May 8, 2024
12759b5
Store runtime data inside the config entry in Tuya (#116822)
mib1185 May 8, 2024
b60c90e
Goodwe Increase max value of export limit to 200% (#117090)
mletenay May 8, 2024
412e9bb
Add test data for Zeo and Dyad devices to Roborock (#117054)
Lash-L May 8, 2024
f9413fc
Bump goodwe to 0.3.5 (#117115)
mletenay May 8, 2024
a77add1
Add better testing to vacuum platform (#112523)
Lash-L May 8, 2024
04c0b7d
Use HassKey for importlib helper (#117116)
cdce8p May 8, 2024
19c26b7
Move available property in BasePassiveBluetoothCoordinator to Passive…
bdraco May 8, 2024
32061d4
Bump github/codeql-action from 3.25.3 to 3.25.4 (#117127)
dependabot[bot] May 9, 2024
6485973
Add airgradient integration (#114113)
joostlek May 9, 2024
b30a02d
Add base entity for Airgradient (#117135)
joostlek May 9, 2024
c1f0ebe
Add screenlogic service tests (#116356)
dieselrabbit May 9, 2024
333d5a9
Speed up test teardown when no config entries are loaded (#117095)
bdraco May 9, 2024
82e1205
Fix typo in xiaomi_ble translation strings (#117144)
jbouwh May 9, 2024
3fa2db8
Catch auth exception in husqvarna automower (#115365)
Thomas55555 May 9, 2024
e4a3cab
Bump ruff to 0.4.4 (#117154)
autinerd May 9, 2024
4138c7a
Handle tilt position being None in HKC (#117141)
bdraco May 10, 2024
d4fbaef
Raise ServiceValidationError in Nibe climate services (#117171)
tizianodeg May 10, 2024
8c54587
Improve base entity state in Vogel's MotionMount integration (#109043)
RJPoelstra May 10, 2024
11f5b48
Add standard deviation calculation to group (#112076)
CoRfr May 10, 2024
e0edea6
Add preview to Threshold config & option flow
jpbede Feb 20, 2024
210cd23
Merge branch 'threshold-config-flow-preview' of github.com:jpbede/hom…
jpbede May 10, 2024
b34991f
Move guard to `async_start_preview`
jpbede May 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -1203,7 +1203,6 @@ omit =
homeassistant/components/screenlogic/light.py
homeassistant/components/screenlogic/number.py
homeassistant/components/screenlogic/sensor.py
homeassistant/components/screenlogic/services.py
homeassistant/components/screenlogic/switch.py
homeassistant/components/scsgate/*
homeassistant/components/sendgrid/notify.py
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ jobs:
uses: actions/[email protected]

- name: Initialize CodeQL
uses: github/codeql-action/[email protected].3
uses: github/codeql-action/[email protected].4
with:
languages: python

- name: Perform CodeQL Analysis
uses: github/codeql-action/[email protected].3
uses: github/codeql-action/[email protected].4
with:
category: "/language:python"
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.3
rev: v0.4.4
hooks:
- id: ruff
args:
Expand Down
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ homeassistant.components.adax.*
homeassistant.components.adguard.*
homeassistant.components.aftership.*
homeassistant.components.air_quality.*
homeassistant.components.airgradient.*
homeassistant.components.airly.*
homeassistant.components.airnow.*
homeassistant.components.airq.*
Expand Down
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ build.json @home-assistant/supervisor
/tests/components/agent_dvr/ @ispysoftware
/homeassistant/components/air_quality/ @home-assistant/core
/tests/components/air_quality/ @home-assistant/core
/homeassistant/components/airgradient/ @airgradienthq @joostlek
/tests/components/airgradient/ @airgradienthq @joostlek
/homeassistant/components/airly/ @bieniu
/tests/components/airly/ @bieniu
/homeassistant/components/airnow/ @asymworks
Expand Down
34 changes: 34 additions & 0 deletions homeassistant/components/airgradient/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""The Airgradient integration."""

from __future__ import annotations

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, Platform
from homeassistant.core import HomeAssistant

from .const import DOMAIN
from .coordinator import AirGradientDataUpdateCoordinator

PLATFORMS: list[Platform] = [Platform.SENSOR]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Airgradient from a config entry."""

coordinator = AirGradientDataUpdateCoordinator(hass, entry.data[CONF_HOST])

await coordinator.async_config_entry_first_refresh()

hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok
83 changes: 83 additions & 0 deletions homeassistant/components/airgradient/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Config flow for Airgradient."""

from typing import Any

from airgradient import AirGradientClient, AirGradientError
import voluptuous as vol

from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_MODEL
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import DOMAIN


class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
"""AirGradient config flow."""

def __init__(self) -> None:
"""Initialize the config flow."""
self.data: dict[str, Any] = {}

async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
self.data[CONF_HOST] = host = discovery_info.host
self.data[CONF_MODEL] = discovery_info.properties["model"]

await self.async_set_unique_id(discovery_info.properties["serialno"])
self._abort_if_unique_id_configured(updates={CONF_HOST: host})

session = async_get_clientsession(self.hass)
air_gradient = AirGradientClient(host, session=session)
await air_gradient.get_current_measures()

self.context["title_placeholders"] = {
"model": self.data[CONF_MODEL],
}
return await self.async_step_discovery_confirm()

async def async_step_discovery_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm discovery."""
if user_input is not None:
return self.async_create_entry(
title=self.data[CONF_MODEL],
data={CONF_HOST: self.data[CONF_HOST]},
)

self._set_confirm_only()
return self.async_show_form(
step_id="discovery_confirm",
description_placeholders={
"model": self.data[CONF_MODEL],
},
)

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initialized by the user."""
errors: dict[str, str] = {}
if user_input:
session = async_get_clientsession(self.hass)
air_gradient = AirGradientClient(user_input[CONF_HOST], session=session)
try:
current_measures = await air_gradient.get_current_measures()
except AirGradientError:
errors["base"] = "cannot_connect"
else:
await self.async_set_unique_id(current_measures.serial_number)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=current_measures.model,
data={CONF_HOST: user_input[CONF_HOST]},
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
errors=errors,
)
7 changes: 7 additions & 0 deletions homeassistant/components/airgradient/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Constants for the Airgradient integration."""

import logging

DOMAIN = "airgradient"

LOGGER = logging.getLogger(__package__)
32 changes: 32 additions & 0 deletions homeassistant/components/airgradient/coordinator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Define an object to manage fetching AirGradient data."""

from datetime import timedelta

from airgradient import AirGradientClient, AirGradientError, Measures

from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import LOGGER


class AirGradientDataUpdateCoordinator(DataUpdateCoordinator[Measures]):
"""Class to manage fetching AirGradient data."""

def __init__(self, hass: HomeAssistant, host: str) -> None:
"""Initialize coordinator."""
super().__init__(
hass,
logger=LOGGER,
name=f"AirGradient {host}",
update_interval=timedelta(minutes=1),
)
session = async_get_clientsession(hass)
self.client = AirGradientClient(host, session=session)

async def _async_update_data(self) -> Measures:
try:
return await self.client.get_current_measures()
except AirGradientError as error:
raise UpdateFailed(error) from error
24 changes: 24 additions & 0 deletions homeassistant/components/airgradient/entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Base class for AirGradient entities."""

from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
from .coordinator import AirGradientDataUpdateCoordinator


class AirGradientEntity(CoordinatorEntity[AirGradientDataUpdateCoordinator]):
"""Defines a base AirGradient entity."""

_attr_has_entity_name = True

def __init__(self, coordinator: AirGradientDataUpdateCoordinator) -> None:
"""Initialize airgradient entity."""
super().__init__(coordinator)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, coordinator.data.serial_number)},
model=coordinator.data.model,
manufacturer="AirGradient",
serial_number=coordinator.data.serial_number,
sw_version=coordinator.data.firmware_version,
)
15 changes: 15 additions & 0 deletions homeassistant/components/airgradient/icons.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"entity": {
"sensor": {
"total_volatile_organic_component_index": {
"default": "mdi:molecule"
},
"nitrogen_index": {
"default": "mdi:molecule"
},
"pm003_count": {
"default": "mdi:blur"
}
}
}
}
11 changes: 11 additions & 0 deletions homeassistant/components/airgradient/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"domain": "airgradient",
"name": "Airgradient",
"codeowners": ["@airgradienthq", "@joostlek"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airgradient",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["airgradient==0.4.0"],
"zeroconf": ["_airgradient._tcp.local."]
}
Loading
Loading