mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Add integration type (#68349)
This commit is contained in:
parent
4f9df1fd0f
commit
3213091b8d
@ -1,13 +1,14 @@
|
||||
"""Http views to control the config manager."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from http import HTTPStatus
|
||||
|
||||
from aiohttp import web
|
||||
import aiohttp.web_exceptions
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant import config_entries, data_entry_flow, loader
|
||||
from homeassistant.auth.permissions.const import CAT_CONFIG_ENTRIES, POLICY_EDIT
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.components.http import HomeAssistantView
|
||||
@ -48,11 +49,36 @@ class ConfigManagerEntryIndexView(HomeAssistantView):
|
||||
|
||||
async def get(self, request):
|
||||
"""List available config entries."""
|
||||
hass = request.app["hass"]
|
||||
hass: HomeAssistant = request.app["hass"]
|
||||
|
||||
return self.json(
|
||||
[entry_json(entry) for entry in hass.config_entries.async_entries()]
|
||||
)
|
||||
kwargs = {}
|
||||
if "domain" in request.query:
|
||||
kwargs["domain"] = request.query["domain"]
|
||||
|
||||
entries = hass.config_entries.async_entries(**kwargs)
|
||||
|
||||
if "type" not in request.query:
|
||||
return self.json([entry_json(entry) for entry in entries])
|
||||
|
||||
integrations = {}
|
||||
type_filter = request.query["type"]
|
||||
|
||||
# Fetch all the integrations so we can check their type
|
||||
for integration in await asyncio.gather(
|
||||
*(
|
||||
loader.async_get_integration(hass, domain)
|
||||
for domain in {entry.domain for entry in entries}
|
||||
)
|
||||
):
|
||||
integrations[integration.domain] = integration
|
||||
|
||||
entries = [
|
||||
entry
|
||||
for entry in entries
|
||||
if integrations[entry.domain].integration_type == type_filter
|
||||
]
|
||||
|
||||
return self.json([entry_json(entry) for entry in entries])
|
||||
|
||||
|
||||
class ConfigManagerEntryResourceView(HomeAssistantView):
|
||||
@ -179,7 +205,10 @@ class ConfigManagerAvailableFlowView(HomeAssistantView):
|
||||
async def get(self, request):
|
||||
"""List available flow handlers."""
|
||||
hass = request.app["hass"]
|
||||
return self.json(await async_get_config_flows(hass))
|
||||
kwargs = {}
|
||||
if "type" in request.query:
|
||||
kwargs["type_filter"] = request.query["type"]
|
||||
return self.json(await async_get_config_flows(hass, **kwargs))
|
||||
|
||||
|
||||
class OptionManagerFlowIndexView(FlowManagerIndexView):
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"domain": "derivative",
|
||||
"integration_type": "helper",
|
||||
"name": "Derivative",
|
||||
"documentation": "https://www.home-assistant.io/integrations/derivative",
|
||||
"codeowners": [
|
||||
|
@ -5,394 +5,398 @@ To update, run python3 -m script.hassfest
|
||||
|
||||
# fmt: off
|
||||
|
||||
FLOWS = [
|
||||
"abode",
|
||||
"accuweather",
|
||||
"acmeda",
|
||||
"adax",
|
||||
"adguard",
|
||||
"advantage_air",
|
||||
"aemet",
|
||||
"agent_dvr",
|
||||
"airly",
|
||||
"airnow",
|
||||
"airthings",
|
||||
"airtouch4",
|
||||
"airvisual",
|
||||
"airzone",
|
||||
"alarmdecoder",
|
||||
"almond",
|
||||
"ambee",
|
||||
"amberelectric",
|
||||
"ambiclimate",
|
||||
"ambient_station",
|
||||
"androidtv",
|
||||
"apple_tv",
|
||||
"arcam_fmj",
|
||||
"aseko_pool_live",
|
||||
"asuswrt",
|
||||
"atag",
|
||||
"august",
|
||||
"aurora",
|
||||
"aurora_abb_powerone",
|
||||
"aussie_broadband",
|
||||
"awair",
|
||||
"axis",
|
||||
"azure_devops",
|
||||
"azure_event_hub",
|
||||
"balboa",
|
||||
"blebox",
|
||||
"blink",
|
||||
"bmw_connected_drive",
|
||||
"bond",
|
||||
"bosch_shc",
|
||||
"braviatv",
|
||||
"broadlink",
|
||||
"brother",
|
||||
"brunt",
|
||||
"bsblan",
|
||||
"buienradar",
|
||||
"canary",
|
||||
"cast",
|
||||
"cert_expiry",
|
||||
"cloudflare",
|
||||
"co2signal",
|
||||
"coinbase",
|
||||
"control4",
|
||||
"coolmaster",
|
||||
"coronavirus",
|
||||
"cpuspeed",
|
||||
"crownstone",
|
||||
"daikin",
|
||||
"deconz",
|
||||
"denonavr",
|
||||
"derivative",
|
||||
"devolo_home_control",
|
||||
"devolo_home_network",
|
||||
"dexcom",
|
||||
"dialogflow",
|
||||
"directv",
|
||||
"dlna_dmr",
|
||||
"dlna_dms",
|
||||
"dnsip",
|
||||
"doorbird",
|
||||
"dsmr",
|
||||
"dunehd",
|
||||
"dynalite",
|
||||
"eafm",
|
||||
"ecobee",
|
||||
"econet",
|
||||
"efergy",
|
||||
"elgato",
|
||||
"elkm1",
|
||||
"elmax",
|
||||
"emonitor",
|
||||
"emulated_roku",
|
||||
"enocean",
|
||||
"enphase_envoy",
|
||||
"environment_canada",
|
||||
"epson",
|
||||
"esphome",
|
||||
"evil_genius_labs",
|
||||
"ezviz",
|
||||
"faa_delays",
|
||||
"fireservicerota",
|
||||
"fivem",
|
||||
"fjaraskupan",
|
||||
"flick_electric",
|
||||
"flipr",
|
||||
"flo",
|
||||
"flume",
|
||||
"flunearyou",
|
||||
"flux_led",
|
||||
"forecast_solar",
|
||||
"forked_daapd",
|
||||
"foscam",
|
||||
"freebox",
|
||||
"freedompro",
|
||||
"fritz",
|
||||
"fritzbox",
|
||||
"fritzbox_callmonitor",
|
||||
"fronius",
|
||||
"garages_amsterdam",
|
||||
"gdacs",
|
||||
"geofency",
|
||||
"geonetnz_quakes",
|
||||
"geonetnz_volcano",
|
||||
"gios",
|
||||
"github",
|
||||
"glances",
|
||||
"goalzero",
|
||||
"gogogate2",
|
||||
"goodwe",
|
||||
"google",
|
||||
"google_travel_time",
|
||||
"gpslogger",
|
||||
"gree",
|
||||
"group",
|
||||
"growatt_server",
|
||||
"guardian",
|
||||
"habitica",
|
||||
"hangouts",
|
||||
"harmony",
|
||||
"heos",
|
||||
"hisense_aehw4a1",
|
||||
"hive",
|
||||
"hlk_sw16",
|
||||
"home_connect",
|
||||
"home_plus_control",
|
||||
"homekit",
|
||||
"homekit_controller",
|
||||
"homematicip_cloud",
|
||||
"homewizard",
|
||||
"honeywell",
|
||||
"huawei_lte",
|
||||
"hue",
|
||||
"huisbaasje",
|
||||
"hunterdouglas_powerview",
|
||||
"hvv_departures",
|
||||
"hyperion",
|
||||
"ialarm",
|
||||
"iaqualink",
|
||||
"icloud",
|
||||
"ifttt",
|
||||
"insteon",
|
||||
"integration",
|
||||
"intellifire",
|
||||
"ios",
|
||||
"iotawatt",
|
||||
"ipma",
|
||||
"ipp",
|
||||
"iqvia",
|
||||
"islamic_prayer_times",
|
||||
"iss",
|
||||
"isy994",
|
||||
"izone",
|
||||
"jellyfin",
|
||||
"juicenet",
|
||||
"kaleidescape",
|
||||
"keenetic_ndms2",
|
||||
"kmtronic",
|
||||
"knx",
|
||||
"kodi",
|
||||
"konnected",
|
||||
"kostal_plenticore",
|
||||
"kraken",
|
||||
"kulersky",
|
||||
"launch_library",
|
||||
"life360",
|
||||
"lifx",
|
||||
"litejet",
|
||||
"litterrobot",
|
||||
"local_ip",
|
||||
"locative",
|
||||
"logi_circle",
|
||||
"lookin",
|
||||
"luftdaten",
|
||||
"lutron_caseta",
|
||||
"lyric",
|
||||
"mailgun",
|
||||
"mazda",
|
||||
"melcloud",
|
||||
"met",
|
||||
"met_eireann",
|
||||
"meteo_france",
|
||||
"meteoclimatic",
|
||||
"metoffice",
|
||||
"mikrotik",
|
||||
"mill",
|
||||
"minecraft_server",
|
||||
"mjpeg",
|
||||
"mobile_app",
|
||||
"modem_callerid",
|
||||
"modern_forms",
|
||||
"moehlenhoff_alpha2",
|
||||
"monoprice",
|
||||
"moon",
|
||||
"motion_blinds",
|
||||
"motioneye",
|
||||
"mqtt",
|
||||
"mullvad",
|
||||
"mutesync",
|
||||
"myq",
|
||||
"mysensors",
|
||||
"nam",
|
||||
"nanoleaf",
|
||||
"neato",
|
||||
"nest",
|
||||
"netatmo",
|
||||
"netgear",
|
||||
"nexia",
|
||||
"nfandroidtv",
|
||||
"nightscout",
|
||||
"nina",
|
||||
"nmap_tracker",
|
||||
"notion",
|
||||
"nuheat",
|
||||
"nuki",
|
||||
"nut",
|
||||
"nws",
|
||||
"nzbget",
|
||||
"octoprint",
|
||||
"omnilogic",
|
||||
"oncue",
|
||||
"ondilo_ico",
|
||||
"onewire",
|
||||
"onvif",
|
||||
"open_meteo",
|
||||
"opengarage",
|
||||
"opentherm_gw",
|
||||
"openuv",
|
||||
"openweathermap",
|
||||
"overkiz",
|
||||
"ovo_energy",
|
||||
"owntracks",
|
||||
"p1_monitor",
|
||||
"panasonic_viera",
|
||||
"philips_js",
|
||||
"pi_hole",
|
||||
"picnic",
|
||||
"plaato",
|
||||
"plex",
|
||||
"plugwise",
|
||||
"plum_lightpad",
|
||||
"point",
|
||||
"poolsense",
|
||||
"powerwall",
|
||||
"profiler",
|
||||
"progettihwsw",
|
||||
"prosegur",
|
||||
"ps4",
|
||||
"pure_energie",
|
||||
"pvoutput",
|
||||
"pvpc_hourly_pricing",
|
||||
"rachio",
|
||||
"radio_browser",
|
||||
"rainforest_eagle",
|
||||
"rainmachine",
|
||||
"rdw",
|
||||
"recollect_waste",
|
||||
"renault",
|
||||
"rfxtrx",
|
||||
"ridwell",
|
||||
"ring",
|
||||
"risco",
|
||||
"rituals_perfume_genie",
|
||||
"roku",
|
||||
"roomba",
|
||||
"roon",
|
||||
"rpi_power",
|
||||
"rtsp_to_webrtc",
|
||||
"ruckus_unleashed",
|
||||
"samsungtv",
|
||||
"screenlogic",
|
||||
"season",
|
||||
"sense",
|
||||
"senseme",
|
||||
"sensibo",
|
||||
"sentry",
|
||||
"sharkiq",
|
||||
"shelly",
|
||||
"shopping_list",
|
||||
"sia",
|
||||
"simplisafe",
|
||||
"sleepiq",
|
||||
"sma",
|
||||
"smappee",
|
||||
"smart_meter_texas",
|
||||
"smartthings",
|
||||
"smarttub",
|
||||
"smhi",
|
||||
"sms",
|
||||
"solaredge",
|
||||
"solarlog",
|
||||
"solax",
|
||||
"soma",
|
||||
"somfy",
|
||||
"somfy_mylink",
|
||||
"sonarr",
|
||||
"songpal",
|
||||
"sonos",
|
||||
"speedtestdotnet",
|
||||
"spider",
|
||||
"spotify",
|
||||
"squeezebox",
|
||||
"srp_energy",
|
||||
"starline",
|
||||
"steamist",
|
||||
"stookalert",
|
||||
"subaru",
|
||||
"sun",
|
||||
"surepetcare",
|
||||
"switch_as_x",
|
||||
"switchbot",
|
||||
"switcher_kis",
|
||||
"syncthing",
|
||||
"syncthru",
|
||||
"synology_dsm",
|
||||
"system_bridge",
|
||||
"tado",
|
||||
"tailscale",
|
||||
"tasmota",
|
||||
"tellduslive",
|
||||
"tesla_wall_connector",
|
||||
"tibber",
|
||||
"tile",
|
||||
"tolo",
|
||||
"tomorrowio",
|
||||
"toon",
|
||||
"totalconnect",
|
||||
"tplink",
|
||||
"traccar",
|
||||
"tractive",
|
||||
"tradfri",
|
||||
"trafikverket_weatherstation",
|
||||
"transmission",
|
||||
"tuya",
|
||||
"twentemilieu",
|
||||
"twilio",
|
||||
"twinkly",
|
||||
"unifi",
|
||||
"unifiprotect",
|
||||
"upb",
|
||||
"upcloud",
|
||||
"upnp",
|
||||
"uptime",
|
||||
"uptimerobot",
|
||||
"vallox",
|
||||
"velbus",
|
||||
"venstar",
|
||||
"vera",
|
||||
"verisure",
|
||||
"version",
|
||||
"vesync",
|
||||
"vicare",
|
||||
"vilfo",
|
||||
"vizio",
|
||||
"vlc_telnet",
|
||||
"volumio",
|
||||
"wallbox",
|
||||
"watttime",
|
||||
"waze_travel_time",
|
||||
"webostv",
|
||||
"wemo",
|
||||
"whirlpool",
|
||||
"whois",
|
||||
"wiffi",
|
||||
"wilight",
|
||||
"withings",
|
||||
"wiz",
|
||||
"wled",
|
||||
"wolflink",
|
||||
"xbox",
|
||||
"xiaomi_aqara",
|
||||
"xiaomi_miio",
|
||||
"yale_smart_alarm",
|
||||
"yamaha_musiccast",
|
||||
"yeelight",
|
||||
"youless",
|
||||
"zerproc",
|
||||
"zha",
|
||||
"zwave_js",
|
||||
"zwave_me"
|
||||
]
|
||||
FLOWS = {
|
||||
"integration": [
|
||||
"abode",
|
||||
"accuweather",
|
||||
"acmeda",
|
||||
"adax",
|
||||
"adguard",
|
||||
"advantage_air",
|
||||
"aemet",
|
||||
"agent_dvr",
|
||||
"airly",
|
||||
"airnow",
|
||||
"airthings",
|
||||
"airtouch4",
|
||||
"airvisual",
|
||||
"airzone",
|
||||
"alarmdecoder",
|
||||
"almond",
|
||||
"ambee",
|
||||
"amberelectric",
|
||||
"ambiclimate",
|
||||
"ambient_station",
|
||||
"androidtv",
|
||||
"apple_tv",
|
||||
"arcam_fmj",
|
||||
"aseko_pool_live",
|
||||
"asuswrt",
|
||||
"atag",
|
||||
"august",
|
||||
"aurora",
|
||||
"aurora_abb_powerone",
|
||||
"aussie_broadband",
|
||||
"awair",
|
||||
"axis",
|
||||
"azure_devops",
|
||||
"azure_event_hub",
|
||||
"balboa",
|
||||
"blebox",
|
||||
"blink",
|
||||
"bmw_connected_drive",
|
||||
"bond",
|
||||
"bosch_shc",
|
||||
"braviatv",
|
||||
"broadlink",
|
||||
"brother",
|
||||
"brunt",
|
||||
"bsblan",
|
||||
"buienradar",
|
||||
"canary",
|
||||
"cast",
|
||||
"cert_expiry",
|
||||
"cloudflare",
|
||||
"co2signal",
|
||||
"coinbase",
|
||||
"control4",
|
||||
"coolmaster",
|
||||
"coronavirus",
|
||||
"cpuspeed",
|
||||
"crownstone",
|
||||
"daikin",
|
||||
"deconz",
|
||||
"denonavr",
|
||||
"devolo_home_control",
|
||||
"devolo_home_network",
|
||||
"dexcom",
|
||||
"dialogflow",
|
||||
"directv",
|
||||
"dlna_dmr",
|
||||
"dlna_dms",
|
||||
"dnsip",
|
||||
"doorbird",
|
||||
"dsmr",
|
||||
"dunehd",
|
||||
"dynalite",
|
||||
"eafm",
|
||||
"ecobee",
|
||||
"econet",
|
||||
"efergy",
|
||||
"elgato",
|
||||
"elkm1",
|
||||
"elmax",
|
||||
"emonitor",
|
||||
"emulated_roku",
|
||||
"enocean",
|
||||
"enphase_envoy",
|
||||
"environment_canada",
|
||||
"epson",
|
||||
"esphome",
|
||||
"evil_genius_labs",
|
||||
"ezviz",
|
||||
"faa_delays",
|
||||
"fireservicerota",
|
||||
"fivem",
|
||||
"fjaraskupan",
|
||||
"flick_electric",
|
||||
"flipr",
|
||||
"flo",
|
||||
"flume",
|
||||
"flunearyou",
|
||||
"flux_led",
|
||||
"forecast_solar",
|
||||
"forked_daapd",
|
||||
"foscam",
|
||||
"freebox",
|
||||
"freedompro",
|
||||
"fritz",
|
||||
"fritzbox",
|
||||
"fritzbox_callmonitor",
|
||||
"fronius",
|
||||
"garages_amsterdam",
|
||||
"gdacs",
|
||||
"geofency",
|
||||
"geonetnz_quakes",
|
||||
"geonetnz_volcano",
|
||||
"gios",
|
||||
"github",
|
||||
"glances",
|
||||
"goalzero",
|
||||
"gogogate2",
|
||||
"goodwe",
|
||||
"google",
|
||||
"google_travel_time",
|
||||
"gpslogger",
|
||||
"gree",
|
||||
"group",
|
||||
"growatt_server",
|
||||
"guardian",
|
||||
"habitica",
|
||||
"hangouts",
|
||||
"harmony",
|
||||
"heos",
|
||||
"hisense_aehw4a1",
|
||||
"hive",
|
||||
"hlk_sw16",
|
||||
"home_connect",
|
||||
"home_plus_control",
|
||||
"homekit",
|
||||
"homekit_controller",
|
||||
"homematicip_cloud",
|
||||
"homewizard",
|
||||
"honeywell",
|
||||
"huawei_lte",
|
||||
"hue",
|
||||
"huisbaasje",
|
||||
"hunterdouglas_powerview",
|
||||
"hvv_departures",
|
||||
"hyperion",
|
||||
"ialarm",
|
||||
"iaqualink",
|
||||
"icloud",
|
||||
"ifttt",
|
||||
"insteon",
|
||||
"integration",
|
||||
"intellifire",
|
||||
"ios",
|
||||
"iotawatt",
|
||||
"ipma",
|
||||
"ipp",
|
||||
"iqvia",
|
||||
"islamic_prayer_times",
|
||||
"iss",
|
||||
"isy994",
|
||||
"izone",
|
||||
"jellyfin",
|
||||
"juicenet",
|
||||
"kaleidescape",
|
||||
"keenetic_ndms2",
|
||||
"kmtronic",
|
||||
"knx",
|
||||
"kodi",
|
||||
"konnected",
|
||||
"kostal_plenticore",
|
||||
"kraken",
|
||||
"kulersky",
|
||||
"launch_library",
|
||||
"life360",
|
||||
"lifx",
|
||||
"litejet",
|
||||
"litterrobot",
|
||||
"local_ip",
|
||||
"locative",
|
||||
"logi_circle",
|
||||
"lookin",
|
||||
"luftdaten",
|
||||
"lutron_caseta",
|
||||
"lyric",
|
||||
"mailgun",
|
||||
"mazda",
|
||||
"melcloud",
|
||||
"met",
|
||||
"met_eireann",
|
||||
"meteo_france",
|
||||
"meteoclimatic",
|
||||
"metoffice",
|
||||
"mikrotik",
|
||||
"mill",
|
||||
"minecraft_server",
|
||||
"mjpeg",
|
||||
"mobile_app",
|
||||
"modem_callerid",
|
||||
"modern_forms",
|
||||
"moehlenhoff_alpha2",
|
||||
"monoprice",
|
||||
"moon",
|
||||
"motion_blinds",
|
||||
"motioneye",
|
||||
"mqtt",
|
||||
"mullvad",
|
||||
"mutesync",
|
||||
"myq",
|
||||
"mysensors",
|
||||
"nam",
|
||||
"nanoleaf",
|
||||
"neato",
|
||||
"nest",
|
||||
"netatmo",
|
||||
"netgear",
|
||||
"nexia",
|
||||
"nfandroidtv",
|
||||
"nightscout",
|
||||
"nina",
|
||||
"nmap_tracker",
|
||||
"notion",
|
||||
"nuheat",
|
||||
"nuki",
|
||||
"nut",
|
||||
"nws",
|
||||
"nzbget",
|
||||
"octoprint",
|
||||
"omnilogic",
|
||||
"oncue",
|
||||
"ondilo_ico",
|
||||
"onewire",
|
||||
"onvif",
|
||||
"open_meteo",
|
||||
"opengarage",
|
||||
"opentherm_gw",
|
||||
"openuv",
|
||||
"openweathermap",
|
||||
"overkiz",
|
||||
"ovo_energy",
|
||||
"owntracks",
|
||||
"p1_monitor",
|
||||
"panasonic_viera",
|
||||
"philips_js",
|
||||
"pi_hole",
|
||||
"picnic",
|
||||
"plaato",
|
||||
"plex",
|
||||
"plugwise",
|
||||
"plum_lightpad",
|
||||
"point",
|
||||
"poolsense",
|
||||
"powerwall",
|
||||
"profiler",
|
||||
"progettihwsw",
|
||||
"prosegur",
|
||||
"ps4",
|
||||
"pure_energie",
|
||||
"pvoutput",
|
||||
"pvpc_hourly_pricing",
|
||||
"rachio",
|
||||
"radio_browser",
|
||||
"rainforest_eagle",
|
||||
"rainmachine",
|
||||
"rdw",
|
||||
"recollect_waste",
|
||||
"renault",
|
||||
"rfxtrx",
|
||||
"ridwell",
|
||||
"ring",
|
||||
"risco",
|
||||
"rituals_perfume_genie",
|
||||
"roku",
|
||||
"roomba",
|
||||
"roon",
|
||||
"rpi_power",
|
||||
"rtsp_to_webrtc",
|
||||
"ruckus_unleashed",
|
||||
"samsungtv",
|
||||
"screenlogic",
|
||||
"season",
|
||||
"sense",
|
||||
"senseme",
|
||||
"sensibo",
|
||||
"sentry",
|
||||
"sharkiq",
|
||||
"shelly",
|
||||
"shopping_list",
|
||||
"sia",
|
||||
"simplisafe",
|
||||
"sleepiq",
|
||||
"sma",
|
||||
"smappee",
|
||||
"smart_meter_texas",
|
||||
"smartthings",
|
||||
"smarttub",
|
||||
"smhi",
|
||||
"sms",
|
||||
"solaredge",
|
||||
"solarlog",
|
||||
"solax",
|
||||
"soma",
|
||||
"somfy",
|
||||
"somfy_mylink",
|
||||
"sonarr",
|
||||
"songpal",
|
||||
"sonos",
|
||||
"speedtestdotnet",
|
||||
"spider",
|
||||
"spotify",
|
||||
"squeezebox",
|
||||
"srp_energy",
|
||||
"starline",
|
||||
"steamist",
|
||||
"stookalert",
|
||||
"subaru",
|
||||
"sun",
|
||||
"surepetcare",
|
||||
"switch_as_x",
|
||||
"switchbot",
|
||||
"switcher_kis",
|
||||
"syncthing",
|
||||
"syncthru",
|
||||
"synology_dsm",
|
||||
"system_bridge",
|
||||
"tado",
|
||||
"tailscale",
|
||||
"tasmota",
|
||||
"tellduslive",
|
||||
"tesla_wall_connector",
|
||||
"tibber",
|
||||
"tile",
|
||||
"tolo",
|
||||
"tomorrowio",
|
||||
"toon",
|
||||
"totalconnect",
|
||||
"tplink",
|
||||
"traccar",
|
||||
"tractive",
|
||||
"tradfri",
|
||||
"trafikverket_weatherstation",
|
||||
"transmission",
|
||||
"tuya",
|
||||
"twentemilieu",
|
||||
"twilio",
|
||||
"twinkly",
|
||||
"unifi",
|
||||
"unifiprotect",
|
||||
"upb",
|
||||
"upcloud",
|
||||
"upnp",
|
||||
"uptime",
|
||||
"uptimerobot",
|
||||
"vallox",
|
||||
"velbus",
|
||||
"venstar",
|
||||
"vera",
|
||||
"verisure",
|
||||
"version",
|
||||
"vesync",
|
||||
"vicare",
|
||||
"vilfo",
|
||||
"vizio",
|
||||
"vlc_telnet",
|
||||
"volumio",
|
||||
"wallbox",
|
||||
"watttime",
|
||||
"waze_travel_time",
|
||||
"webostv",
|
||||
"wemo",
|
||||
"whirlpool",
|
||||
"whois",
|
||||
"wiffi",
|
||||
"wilight",
|
||||
"withings",
|
||||
"wiz",
|
||||
"wled",
|
||||
"wolflink",
|
||||
"xbox",
|
||||
"xiaomi_aqara",
|
||||
"xiaomi_miio",
|
||||
"yale_smart_alarm",
|
||||
"yamaha_musiccast",
|
||||
"yeelight",
|
||||
"youless",
|
||||
"zerproc",
|
||||
"zha",
|
||||
"zwave_js",
|
||||
"zwave_me"
|
||||
],
|
||||
"helper": [
|
||||
"derivative"
|
||||
]
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import logging
|
||||
import pathlib
|
||||
import sys
|
||||
from types import ModuleType
|
||||
from typing import TYPE_CHECKING, Any, TypedDict, TypeVar, cast
|
||||
from typing import TYPE_CHECKING, Any, Literal, TypedDict, TypeVar, cast
|
||||
|
||||
from awesomeversion import (
|
||||
AwesomeVersion,
|
||||
@ -87,6 +87,7 @@ class Manifest(TypedDict, total=False):
|
||||
name: str
|
||||
disabled: str
|
||||
domain: str
|
||||
integration_type: Literal["integration", "helper"]
|
||||
dependencies: list[str]
|
||||
after_dependencies: list[str]
|
||||
requirements: list[str]
|
||||
@ -180,20 +181,29 @@ async def async_get_custom_components(
|
||||
return cast(dict[str, "Integration"], reg_or_evt)
|
||||
|
||||
|
||||
async def async_get_config_flows(hass: HomeAssistant) -> set[str]:
|
||||
async def async_get_config_flows(
|
||||
hass: HomeAssistant,
|
||||
type_filter: Literal["helper", "integration"] | None = None,
|
||||
) -> set[str]:
|
||||
"""Return cached list of config flows."""
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from .generated.config_flows import FLOWS
|
||||
|
||||
flows: set[str] = set()
|
||||
flows.update(FLOWS)
|
||||
|
||||
integrations = await async_get_custom_components(hass)
|
||||
flows: set[str] = set()
|
||||
|
||||
if type_filter is not None:
|
||||
flows.update(FLOWS[type_filter])
|
||||
else:
|
||||
for type_flows in FLOWS.values():
|
||||
flows.update(type_flows)
|
||||
|
||||
flows.update(
|
||||
[
|
||||
integration.domain
|
||||
for integration in integrations.values()
|
||||
if integration.config_flow
|
||||
and (type_filter is None or integration.integration_type == type_filter)
|
||||
]
|
||||
)
|
||||
|
||||
@ -474,6 +484,11 @@ class Integration:
|
||||
"""Return the integration IoT Class."""
|
||||
return self.manifest.get("iot_class")
|
||||
|
||||
@property
|
||||
def integration_type(self) -> Literal["integration", "helper"]:
|
||||
"""Return the integration type."""
|
||||
return self.manifest.get("integration_type", "integration")
|
||||
|
||||
@property
|
||||
def mqtt(self) -> list[str] | None:
|
||||
"""Return Integration MQTT entries."""
|
||||
|
@ -69,7 +69,10 @@ def validate_integration(config: Config, integration: Integration):
|
||||
|
||||
def generate_and_validate(integrations: dict[str, Integration], config: Config):
|
||||
"""Validate and generate config flow data."""
|
||||
domains = []
|
||||
domains = {
|
||||
"integration": [],
|
||||
"helper": [],
|
||||
}
|
||||
|
||||
for domain in sorted(integrations):
|
||||
integration = integrations[domain]
|
||||
@ -79,7 +82,7 @@ def generate_and_validate(integrations: dict[str, Integration], config: Config):
|
||||
|
||||
validate_integration(config, integration)
|
||||
|
||||
domains.append(domain)
|
||||
domains[integration.integration_type].append(domain)
|
||||
|
||||
return BASE.format(json.dumps(domains, indent=4))
|
||||
|
||||
|
@ -152,6 +152,7 @@ MANIFEST_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required("domain"): str,
|
||||
vol.Required("name"): str,
|
||||
vol.Optional("integration_type"): "helper",
|
||||
vol.Optional("config_flow"): bool,
|
||||
vol.Optional("mqtt"): [str],
|
||||
vol.Optional("zeroconf"): [
|
||||
|
@ -112,6 +112,11 @@ class Integration:
|
||||
"""List of dependencies."""
|
||||
return self.manifest.get("dependencies", [])
|
||||
|
||||
@property
|
||||
def integration_type(self) -> str:
|
||||
"""Get integration_type."""
|
||||
return self.manifest.get("integration_type", "integration")
|
||||
|
||||
def add_error(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""Add an error."""
|
||||
self.errors.append(Error(*args, **kwargs))
|
||||
|
@ -23,6 +23,13 @@ from tests.common import (
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def clear_handlers():
|
||||
"""Clear config entry handlers."""
|
||||
with patch.dict(HANDLERS, clear=True):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_test_component(hass):
|
||||
"""Ensure a component called 'test' exists."""
|
||||
@ -30,104 +37,133 @@ def mock_test_component(hass):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(hass, hass_client):
|
||||
async def client(hass, hass_client):
|
||||
"""Fixture that can interact with the config manager API."""
|
||||
hass.loop.run_until_complete(async_setup_component(hass, "http", {}))
|
||||
hass.loop.run_until_complete(config_entries.async_setup(hass))
|
||||
yield hass.loop.run_until_complete(hass_client())
|
||||
await async_setup_component(hass, "http", {})
|
||||
await config_entries.async_setup(hass)
|
||||
return await hass_client()
|
||||
|
||||
|
||||
async def test_get_entries(hass, client):
|
||||
async def test_get_entries(hass, client, clear_handlers):
|
||||
"""Test get entries."""
|
||||
with patch.dict(HANDLERS, clear=True):
|
||||
mock_integration(hass, MockModule("comp1"))
|
||||
mock_integration(
|
||||
hass, MockModule("comp2", partial_manifest={"integration_type": "helper"})
|
||||
)
|
||||
mock_integration(hass, MockModule("comp3"))
|
||||
|
||||
@HANDLERS.register("comp1")
|
||||
class Comp1ConfigFlow:
|
||||
"""Config flow with options flow."""
|
||||
@HANDLERS.register("comp1")
|
||||
class Comp1ConfigFlow:
|
||||
"""Config flow with options flow."""
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Get options flow."""
|
||||
pass
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Get options flow."""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@callback
|
||||
def async_supports_options_flow(cls, config_entry):
|
||||
"""Return options flow support for this handler."""
|
||||
return True
|
||||
@classmethod
|
||||
@callback
|
||||
def async_supports_options_flow(cls, config_entry):
|
||||
"""Return options flow support for this handler."""
|
||||
return True
|
||||
|
||||
hass.helpers.config_entry_flow.register_discovery_flow(
|
||||
"comp2", "Comp 2", lambda: None
|
||||
)
|
||||
hass.helpers.config_entry_flow.register_discovery_flow(
|
||||
"comp2", "Comp 2", lambda: None
|
||||
)
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain="comp1",
|
||||
title="Test 1",
|
||||
source="bla",
|
||||
)
|
||||
entry.supports_unload = True
|
||||
entry.add_to_hass(hass)
|
||||
MockConfigEntry(
|
||||
domain="comp2",
|
||||
title="Test 2",
|
||||
source="bla2",
|
||||
state=core_ce.ConfigEntryState.SETUP_ERROR,
|
||||
reason="Unsupported API",
|
||||
).add_to_hass(hass)
|
||||
MockConfigEntry(
|
||||
domain="comp3",
|
||||
title="Test 3",
|
||||
source="bla3",
|
||||
disabled_by=core_ce.ConfigEntryDisabler.USER,
|
||||
).add_to_hass(hass)
|
||||
entry = MockConfigEntry(
|
||||
domain="comp1",
|
||||
title="Test 1",
|
||||
source="bla",
|
||||
)
|
||||
entry.supports_unload = True
|
||||
entry.add_to_hass(hass)
|
||||
MockConfigEntry(
|
||||
domain="comp2",
|
||||
title="Test 2",
|
||||
source="bla2",
|
||||
state=core_ce.ConfigEntryState.SETUP_ERROR,
|
||||
reason="Unsupported API",
|
||||
).add_to_hass(hass)
|
||||
MockConfigEntry(
|
||||
domain="comp3",
|
||||
title="Test 3",
|
||||
source="bla3",
|
||||
disabled_by=core_ce.ConfigEntryDisabler.USER,
|
||||
).add_to_hass(hass)
|
||||
|
||||
resp = await client.get("/api/config/config_entries/entry")
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
for entry in data:
|
||||
entry.pop("entry_id")
|
||||
assert data == [
|
||||
{
|
||||
"domain": "comp1",
|
||||
"title": "Test 1",
|
||||
"source": "bla",
|
||||
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
||||
"supports_options": True,
|
||||
"supports_remove_device": False,
|
||||
"supports_unload": True,
|
||||
"pref_disable_new_entities": False,
|
||||
"pref_disable_polling": False,
|
||||
"disabled_by": None,
|
||||
"reason": None,
|
||||
},
|
||||
{
|
||||
"domain": "comp2",
|
||||
"title": "Test 2",
|
||||
"source": "bla2",
|
||||
"state": core_ce.ConfigEntryState.SETUP_ERROR.value,
|
||||
"supports_options": False,
|
||||
"supports_remove_device": False,
|
||||
"supports_unload": False,
|
||||
"pref_disable_new_entities": False,
|
||||
"pref_disable_polling": False,
|
||||
"disabled_by": None,
|
||||
"reason": "Unsupported API",
|
||||
},
|
||||
{
|
||||
"domain": "comp3",
|
||||
"title": "Test 3",
|
||||
"source": "bla3",
|
||||
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
||||
"supports_options": False,
|
||||
"supports_remove_device": False,
|
||||
"supports_unload": False,
|
||||
"pref_disable_new_entities": False,
|
||||
"pref_disable_polling": False,
|
||||
"disabled_by": core_ce.ConfigEntryDisabler.USER,
|
||||
"reason": None,
|
||||
},
|
||||
]
|
||||
resp = await client.get("/api/config/config_entries/entry")
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
for entry in data:
|
||||
entry.pop("entry_id")
|
||||
assert data == [
|
||||
{
|
||||
"domain": "comp1",
|
||||
"title": "Test 1",
|
||||
"source": "bla",
|
||||
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
||||
"supports_options": True,
|
||||
"supports_remove_device": False,
|
||||
"supports_unload": True,
|
||||
"pref_disable_new_entities": False,
|
||||
"pref_disable_polling": False,
|
||||
"disabled_by": None,
|
||||
"reason": None,
|
||||
},
|
||||
{
|
||||
"domain": "comp2",
|
||||
"title": "Test 2",
|
||||
"source": "bla2",
|
||||
"state": core_ce.ConfigEntryState.SETUP_ERROR.value,
|
||||
"supports_options": False,
|
||||
"supports_remove_device": False,
|
||||
"supports_unload": False,
|
||||
"pref_disable_new_entities": False,
|
||||
"pref_disable_polling": False,
|
||||
"disabled_by": None,
|
||||
"reason": "Unsupported API",
|
||||
},
|
||||
{
|
||||
"domain": "comp3",
|
||||
"title": "Test 3",
|
||||
"source": "bla3",
|
||||
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
||||
"supports_options": False,
|
||||
"supports_remove_device": False,
|
||||
"supports_unload": False,
|
||||
"pref_disable_new_entities": False,
|
||||
"pref_disable_polling": False,
|
||||
"disabled_by": core_ce.ConfigEntryDisabler.USER,
|
||||
"reason": None,
|
||||
},
|
||||
]
|
||||
|
||||
resp = await client.get("/api/config/config_entries/entry?domain=comp3")
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert len(data) == 1
|
||||
assert data[0]["domain"] == "comp3"
|
||||
|
||||
resp = await client.get("/api/config/config_entries/entry?domain=comp3&type=helper")
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert len(data) == 0
|
||||
|
||||
resp = await client.get(
|
||||
"/api/config/config_entries/entry?domain=comp3&type=integration"
|
||||
)
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert len(data) == 1
|
||||
|
||||
resp = await client.get("/api/config/config_entries/entry?type=integration")
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert len(data) == 2
|
||||
assert data[0]["domain"] == "comp1"
|
||||
assert data[1]["domain"] == "comp3"
|
||||
|
||||
|
||||
async def test_remove_entry(hass, client):
|
||||
@ -224,13 +260,28 @@ async def test_reload_entry_in_setup_retry(hass, client, hass_admin_user):
|
||||
assert len(hass.config_entries.async_entries()) == 1
|
||||
|
||||
|
||||
async def test_available_flows(hass, client):
|
||||
@pytest.mark.parametrize(
|
||||
"type_filter,result",
|
||||
(
|
||||
(None, {"hello", "another", "world"}),
|
||||
("integration", {"hello", "another"}),
|
||||
("helper", {"world"}),
|
||||
),
|
||||
)
|
||||
async def test_available_flows(hass, client, type_filter, result):
|
||||
"""Test querying the available flows."""
|
||||
with patch.object(config_flows, "FLOWS", ["hello", "world"]):
|
||||
resp = await client.get("/api/config/config_entries/flow_handlers")
|
||||
with patch.object(
|
||||
config_flows,
|
||||
"FLOWS",
|
||||
{"integration": ["hello", "another"], "helper": ["world"]},
|
||||
):
|
||||
resp = await client.get(
|
||||
"/api/config/config_entries/flow_handlers",
|
||||
params={"type": type_filter} if type_filter else {},
|
||||
)
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert set(data) == {"hello", "world"}
|
||||
assert set(data) == result
|
||||
|
||||
|
||||
############################
|
||||
|
@ -15,7 +15,7 @@ from homeassistant.setup import async_setup_component
|
||||
@pytest.fixture
|
||||
def mock_config_flows():
|
||||
"""Mock the config flows."""
|
||||
flows = []
|
||||
flows = {"integration": [], "helper": {}}
|
||||
with patch.object(config_flows, "FLOWS", flows):
|
||||
yield flows
|
||||
|
||||
@ -124,7 +124,7 @@ async def test_get_translations(hass, mock_config_flows, enable_custom_integrati
|
||||
|
||||
async def test_get_translations_loads_config_flows(hass, mock_config_flows):
|
||||
"""Test the get translations helper loads config flow translations."""
|
||||
mock_config_flows.append("component1")
|
||||
mock_config_flows["integration"].append("component1")
|
||||
integration = Mock(file_path=pathlib.Path(__file__))
|
||||
integration.name = "Component 1"
|
||||
|
||||
@ -153,7 +153,7 @@ async def test_get_translations_loads_config_flows(hass, mock_config_flows):
|
||||
|
||||
assert "component1" not in hass.config.components
|
||||
|
||||
mock_config_flows.append("component2")
|
||||
mock_config_flows["integration"].append("component2")
|
||||
integration = Mock(file_path=pathlib.Path(__file__))
|
||||
integration.name = "Component 2"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user