From b173ae7f444a330f92c25dfb5e3d581616a768cd Mon Sep 17 00:00:00 2001
From: Erik Montnemery <erik@montnemery.com>
Date: Wed, 28 Sep 2022 17:31:48 +0200
Subject: [PATCH] Add support for integrations v2 (#78801)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
---
 homeassistant/brands/google.json              |    5 +
 .../components/websocket_api/commands.py      |   12 +
 homeassistant/generated/integrations.json     | 5290 +++++++++++++++++
 homeassistant/loader.py                       |   39 +
 script/hassfest/__main__.py                   |    2 +-
 script/hassfest/brand.py                      |   71 +
 script/hassfest/config_flow.py                |  115 +-
 script/hassfest/model.py                      |   62 +
 script/hassfest/translations.py               |   22 +-
 .../components/websocket_api/test_commands.py |   17 +
 10 files changed, 5623 insertions(+), 12 deletions(-)
 create mode 100644 homeassistant/brands/google.json
 create mode 100644 homeassistant/generated/integrations.json
 create mode 100644 script/hassfest/brand.py

diff --git a/homeassistant/brands/google.json b/homeassistant/brands/google.json
new file mode 100644
index 00000000000..c50b0819827
--- /dev/null
+++ b/homeassistant/brands/google.json
@@ -0,0 +1,5 @@
+{
+  "domain": "google",
+  "name": "Google",
+  "integrations": ["google", "google_sheets"]
+}
diff --git a/homeassistant/components/websocket_api/commands.py b/homeassistant/components/websocket_api/commands.py
index c42d48a604e..a78099e6065 100644
--- a/homeassistant/components/websocket_api/commands.py
+++ b/homeassistant/components/websocket_api/commands.py
@@ -35,6 +35,7 @@ from homeassistant.loader import (
     Integration,
     IntegrationNotFound,
     async_get_integration,
+    async_get_integration_descriptions,
     async_get_integrations,
 )
 from homeassistant.setup import DATA_SETUP_TIME, async_get_loaded_integrations
@@ -75,6 +76,7 @@ def async_register_commands(
     async_reg(hass, handle_subscribe_entities)
     async_reg(hass, handle_supported_brands)
     async_reg(hass, handle_supported_features)
+    async_reg(hass, handle_integration_descriptions)
 
 
 def pong_message(iden: int) -> dict[str, Any]:
@@ -741,3 +743,13 @@ def handle_supported_features(
     """Handle setting supported features."""
     connection.supported_features = msg["features"]
     connection.send_result(msg["id"])
+
+
+@decorators.require_admin
+@decorators.websocket_command({"type": "integration/descriptions"})
+@decorators.async_response
+async def handle_integration_descriptions(
+    hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
+) -> None:
+    """Get metadata for all brands and integrations."""
+    connection.send_result(msg["id"], await async_get_integration_descriptions(hass))
diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json
new file mode 100644
index 00000000000..a2279f3396c
--- /dev/null
+++ b/homeassistant/generated/integrations.json
@@ -0,0 +1,5290 @@
+{
+  "integration": {
+    "abode": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Abode"
+    },
+    "accuweather": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "AccuWeather"
+    },
+    "acer_projector": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Acer Projector"
+    },
+    "acmeda": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Rollease Acmeda Automate"
+    },
+    "actiontec": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Actiontec"
+    },
+    "adax": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Adax"
+    },
+    "adguard": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "AdGuard Home"
+    },
+    "ads": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "ADS"
+    },
+    "advantage_air": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Advantage Air"
+    },
+    "aemet": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "AEMET OpenData"
+    },
+    "aftership": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "AfterShip"
+    },
+    "agent_dvr": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Agent DVR"
+    },
+    "airly": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Airly"
+    },
+    "airnow": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "AirNow"
+    },
+    "airthings": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Airthings"
+    },
+    "airtouch4": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "AirTouch 4"
+    },
+    "airvisual": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "AirVisual"
+    },
+    "airzone": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Airzone"
+    },
+    "aladdin_connect": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Aladdin Connect"
+    },
+    "alarm_control_panel": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "alarmdecoder": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "AlarmDecoder"
+    },
+    "alert": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Alert"
+    },
+    "alexa": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Amazon Alexa"
+    },
+    "almond": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Almond"
+    },
+    "alpha_vantage": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Alpha Vantage"
+    },
+    "amazon_polly": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Amazon Polly"
+    },
+    "amberelectric": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Amber Electric"
+    },
+    "ambiclimate": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Ambiclimate"
+    },
+    "ambient_station": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Ambient Weather Station"
+    },
+    "amcrest": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Amcrest"
+    },
+    "ampio": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Ampio Smart Smog System"
+    },
+    "android_ip_webcam": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Android IP Webcam"
+    },
+    "androidtv": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Android TV"
+    },
+    "anel_pwrctrl": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Anel NET-PwrCtrl"
+    },
+    "anthemav": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Anthem A/V Receivers"
+    },
+    "apache_kafka": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Apache Kafka"
+    },
+    "apcupsd": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "APC UPS Daemon"
+    },
+    "api": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Home Assistant API"
+    },
+    "apple_tv": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Apple TV"
+    },
+    "application_credentials": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "apprise": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Apprise"
+    },
+    "aprs": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "APRS"
+    },
+    "aqualogic": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "AquaLogic"
+    },
+    "aquostv": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Sharp Aquos TV"
+    },
+    "arcam_fmj": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Arcam FMJ Receivers"
+    },
+    "arest": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "aREST"
+    },
+    "arris_tg2492lg": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Arris TG2492LG"
+    },
+    "aruba": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Aruba"
+    },
+    "arwn": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Ambient Radio Weather Network"
+    },
+    "aseko_pool_live": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Aseko Pool Live"
+    },
+    "asterisk_cdr": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Asterisk Call Detail Records"
+    },
+    "asterisk_mbox": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Asterisk Voicemail"
+    },
+    "asuswrt": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "ASUSWRT"
+    },
+    "atag": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Atag"
+    },
+    "aten_pe": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "ATEN Rack PDU"
+    },
+    "atome": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Atome Linky"
+    },
+    "august": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "August"
+    },
+    "aurora": {
+      "config_flow": true,
+      "iot_class": "cloud_polling"
+    },
+    "aurora_abb_powerone": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Aurora ABB PowerOne Solar PV"
+    },
+    "aussie_broadband": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Aussie Broadband"
+    },
+    "auth": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Auth"
+    },
+    "automation": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "avea": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Elgato Avea"
+    },
+    "avion": {
+      "config_flow": false,
+      "iot_class": "assumed_state",
+      "name": "Avi-on"
+    },
+    "awair": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Awair"
+    },
+    "aws": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Amazon Web Services (AWS)"
+    },
+    "axis": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Axis"
+    },
+    "azure_devops": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Azure DevOps"
+    },
+    "azure_event_hub": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Azure Event Hub"
+    },
+    "azure_service_bus": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Azure Service Bus"
+    },
+    "backup": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "Backup"
+    },
+    "baf": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Big Ass Fans"
+    },
+    "baidu": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Baidu"
+    },
+    "balboa": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Balboa Spa Client"
+    },
+    "bayesian": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Bayesian"
+    },
+    "bbox": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Bbox"
+    },
+    "beewi_smartclim": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "BeeWi SmartClim BLE sensor"
+    },
+    "binary_sensor": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "bitcoin": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Bitcoin"
+    },
+    "bizkaibus": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Bizkaibus"
+    },
+    "blackbird": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Monoprice Blackbird Matrix Switch"
+    },
+    "blebox": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "BleBox devices"
+    },
+    "blink": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Blink"
+    },
+    "blinksticklight": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "BlinkStick"
+    },
+    "blockchain": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Blockchain.com"
+    },
+    "bloomsky": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "BloomSky"
+    },
+    "bluemaestro": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "BlueMaestro"
+    },
+    "blueprint": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Blueprint"
+    },
+    "bluesound": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Bluesound"
+    },
+    "bluetooth": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Bluetooth"
+    },
+    "bluetooth_le_tracker": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Bluetooth LE Tracker"
+    },
+    "bluetooth_tracker": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Bluetooth Tracker"
+    },
+    "bmw_connected_drive": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "BMW Connected Drive"
+    },
+    "bond": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Bond"
+    },
+    "bosch_shc": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Bosch SHC"
+    },
+    "braviatv": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Sony Bravia TV"
+    },
+    "broadlink": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Broadlink"
+    },
+    "brother": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Brother Printer"
+    },
+    "brottsplatskartan": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Brottsplatskartan"
+    },
+    "browser": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Browser"
+    },
+    "brunt": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Brunt Blind Engine"
+    },
+    "bsblan": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "BSB-Lan"
+    },
+    "bt_home_hub_5": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "BT Home Hub 5"
+    },
+    "bt_smarthub": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "BT Smart Hub"
+    },
+    "bthome": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "BTHome"
+    },
+    "buienradar": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Buienradar"
+    },
+    "button": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "caldav": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "CalDAV"
+    },
+    "calendar": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "camera": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "canary": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Canary"
+    },
+    "cast": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Google Cast"
+    },
+    "cert_expiry": {
+      "config_flow": true,
+      "iot_class": "cloud_polling"
+    },
+    "channels": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Channels"
+    },
+    "circuit": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Unify Circuit"
+    },
+    "cisco_ios": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Cisco IOS"
+    },
+    "cisco_mobility_express": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Cisco Mobility Express"
+    },
+    "cisco_webex_teams": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Cisco Webex Teams"
+    },
+    "citybikes": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "CityBikes"
+    },
+    "clementine": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Clementine Music Player"
+    },
+    "clickatell": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Clickatell"
+    },
+    "clicksend": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "ClickSend SMS"
+    },
+    "clicksend_tts": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "ClickSend TTS"
+    },
+    "climate": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "cloud": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Home Assistant Cloud"
+    },
+    "cloudflare": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Cloudflare"
+    },
+    "cmus": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "cmus"
+    },
+    "co2signal": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "CO2 Signal"
+    },
+    "coinbase": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Coinbase"
+    },
+    "color_extractor": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "ColorExtractor"
+    },
+    "comed_hourly_pricing": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "ComEd Hourly Pricing"
+    },
+    "comfoconnect": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Zehnder ComfoAir Q"
+    },
+    "command_line": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Command Line"
+    },
+    "compensation": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "Compensation"
+    },
+    "concord232": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Concord232"
+    },
+    "config": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Configuration"
+    },
+    "configurator": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "control4": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Control4"
+    },
+    "conversation": {
+      "config_flow": false,
+      "iot_class": "local_push"
+    },
+    "coolmaster": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "CoolMasterNet"
+    },
+    "coronavirus": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Coronavirus (COVID-19)"
+    },
+    "cover": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "cppm_tracker": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Aruba ClearPass"
+    },
+    "cpuspeed": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
+    "crownstone": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Crownstone"
+    },
+    "cups": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "CUPS"
+    },
+    "currencylayer": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "currencylayer"
+    },
+    "daikin": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Daikin AC"
+    },
+    "danfoss_air": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Danfoss Air"
+    },
+    "darksky": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Dark Sky"
+    },
+    "datadog": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Datadog"
+    },
+    "ddwrt": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "DD-WRT"
+    },
+    "debugpy": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Remote Python Debugger"
+    },
+    "deconz": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "deCONZ"
+    },
+    "decora": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Leviton Decora"
+    },
+    "decora_wifi": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Leviton Decora Wi-Fi"
+    },
+    "default_config": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Default Config"
+    },
+    "delijn": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "De Lijn"
+    },
+    "deluge": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Deluge"
+    },
+    "demo": {
+      "config_flow": false,
+      "iot_class": "calculated"
+    },
+    "denon": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Denon Network Receivers"
+    },
+    "denonavr": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Denon AVR Network Receivers"
+    },
+    "deutsche_bahn": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Deutsche Bahn"
+    },
+    "device_automation": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Device Automation"
+    },
+    "device_sun_light_trigger": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "Presence-based Lights"
+    },
+    "device_tracker": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "devolo_home_control": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "devolo Home Control"
+    },
+    "devolo_home_network": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "devolo Home Network"
+    },
+    "dexcom": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Dexcom"
+    },
+    "dhcp": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "DHCP Discovery"
+    },
+    "diagnostics": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "dialogflow": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Dialogflow"
+    },
+    "digital_ocean": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Digital Ocean"
+    },
+    "directv": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "DirecTV"
+    },
+    "discogs": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Discogs"
+    },
+    "discord": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Discord"
+    },
+    "discovery": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Discovery"
+    },
+    "dlib_face_detect": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Dlib Face Detect"
+    },
+    "dlib_face_identify": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Dlib Face Identify"
+    },
+    "dlink": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "D-Link Wi-Fi Smart Plugs"
+    },
+    "dlna_dmr": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "DLNA Digital Media Renderer"
+    },
+    "dlna_dms": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "DLNA Digital Media Server"
+    },
+    "dnsip": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "DNS IP"
+    },
+    "dominos": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Dominos Pizza"
+    },
+    "doods": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "DOODS - Dedicated Open Object Detection Service"
+    },
+    "doorbird": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "DoorBird"
+    },
+    "dovado": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Dovado"
+    },
+    "downloader": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Downloader"
+    },
+    "dsmr": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "DSMR Slimme Meter"
+    },
+    "dsmr_reader": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "DSMR Reader"
+    },
+    "dte_energy_bridge": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "DTE Energy Bridge"
+    },
+    "dublin_bus_transport": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Dublin Bus"
+    },
+    "duckdns": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Duck DNS"
+    },
+    "dunehd": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Dune HD"
+    },
+    "dwd_weather_warnings": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Deutscher Wetterdienst (DWD) Weather Warnings"
+    },
+    "dweet": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "dweet.io"
+    },
+    "dynalite": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Philips Dynalite"
+    },
+    "eafm": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Environment Agency Flood Gauges"
+    },
+    "ebox": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "EBox"
+    },
+    "ebusd": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "ebusd"
+    },
+    "ecoal_boiler": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "eSterownik eCoal.pl Boiler"
+    },
+    "ecobee": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "ecobee"
+    },
+    "econet": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Rheem EcoNet Products"
+    },
+    "ecovacs": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Ecovacs"
+    },
+    "ecowitt": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Ecowitt"
+    },
+    "eddystone_temperature": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Eddystone"
+    },
+    "edimax": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Edimax"
+    },
+    "edl21": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "EDL21"
+    },
+    "efergy": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Efergy"
+    },
+    "egardia": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Egardia"
+    },
+    "eight_sleep": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Eight Sleep"
+    },
+    "elgato": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Elgato Light"
+    },
+    "eliqonline": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Eliqonline"
+    },
+    "elkm1": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Elk-M1 Control"
+    },
+    "elmax": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Elmax"
+    },
+    "elv": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "ELV PCA"
+    },
+    "emby": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Emby"
+    },
+    "emoncms": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Emoncms"
+    },
+    "emoncms_history": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Emoncms History"
+    },
+    "emonitor": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "SiteSage Emonitor"
+    },
+    "emulated_hue": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Emulated Hue"
+    },
+    "emulated_kasa": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Emulated Kasa"
+    },
+    "emulated_roku": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
+    "energy": {
+      "config_flow": false,
+      "iot_class": "calculated"
+    },
+    "enigma2": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Enigma2 (OpenWebif)"
+    },
+    "enocean": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "EnOcean"
+    },
+    "enphase_envoy": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Enphase Envoy"
+    },
+    "entur_public_transport": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Entur"
+    },
+    "environment_canada": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Environment Canada"
+    },
+    "envisalink": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Envisalink"
+    },
+    "ephember": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "EPH Controls"
+    },
+    "epson": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Epson"
+    },
+    "epsonworkforce": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Epson Workforce"
+    },
+    "eq3btsmart": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "eQ-3 Bluetooth Smart Thermostats"
+    },
+    "escea": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Escea"
+    },
+    "esphome": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "ESPHome"
+    },
+    "etherscan": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Etherscan"
+    },
+    "eufy": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "eufy"
+    },
+    "everlights": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "EverLights"
+    },
+    "evil_genius_labs": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Evil Genius Labs"
+    },
+    "evohome": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Honeywell Total Connect Comfort (Europe)"
+    },
+    "ezviz": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "EZVIZ"
+    },
+    "faa_delays": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "FAA Delays"
+    },
+    "facebook": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Facebook Messenger"
+    },
+    "facebox": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Facebox"
+    },
+    "fail2ban": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Fail2Ban"
+    },
+    "familyhub": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Samsung Family Hub"
+    },
+    "fan": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "fastdotcom": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Fast.com"
+    },
+    "feedreader": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Feedreader"
+    },
+    "ffmpeg": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "FFmpeg"
+    },
+    "ffmpeg_motion": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "FFmpeg Motion"
+    },
+    "ffmpeg_noise": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "FFmpeg Noise"
+    },
+    "fibaro": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Fibaro"
+    },
+    "fido": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Fido"
+    },
+    "file": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "File"
+    },
+    "file_upload": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "File Upload"
+    },
+    "filesize": {
+      "config_flow": true,
+      "iot_class": "local_polling"
+    },
+    "filter": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Filter"
+    },
+    "fints": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "FinTS"
+    },
+    "fireservicerota": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "FireServiceRota"
+    },
+    "firmata": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Firmata"
+    },
+    "fitbit": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Fitbit"
+    },
+    "fivem": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "FiveM"
+    },
+    "fixer": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Fixer"
+    },
+    "fjaraskupan": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Fj\u00e4r\u00e5skupan"
+    },
+    "fleetgo": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "FleetGO"
+    },
+    "flexit": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Flexit"
+    },
+    "flic": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Flic"
+    },
+    "flick_electric": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Flick Electric"
+    },
+    "flipr": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Flipr"
+    },
+    "flo": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Flo"
+    },
+    "flock": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Flock"
+    },
+    "flume": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Flume"
+    },
+    "flux": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "Flux"
+    },
+    "flux_led": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Magic Home"
+    },
+    "folder": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Folder"
+    },
+    "folder_watcher": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Folder Watcher"
+    },
+    "foobot": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Foobot"
+    },
+    "forecast_solar": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Forecast.Solar"
+    },
+    "forked_daapd": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "forked-daapd"
+    },
+    "fortios": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "FortiOS"
+    },
+    "foscam": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Foscam"
+    },
+    "foursquare": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Foursquare"
+    },
+    "free_mobile": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Free Mobile"
+    },
+    "freebox": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Freebox"
+    },
+    "freedns": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "FreeDNS"
+    },
+    "freedompro": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Freedompro"
+    },
+    "fritz": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "AVM FRITZ!Box Tools"
+    },
+    "fritzbox": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "AVM FRITZ!SmartHome"
+    },
+    "fritzbox_callmonitor": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "AVM FRITZ!Box Call Monitor"
+    },
+    "fronius": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Fronius"
+    },
+    "frontend": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Home Assistant Frontend"
+    },
+    "frontier_silicon": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Frontier Silicon"
+    },
+    "fully_kiosk": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Fully Kiosk Browser"
+    },
+    "futurenow": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "P5 FutureNow"
+    },
+    "garadget": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Garadget"
+    },
+    "garages_amsterdam": {
+      "config_flow": true,
+      "iot_class": "cloud_polling"
+    },
+    "gc100": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Global Cach\u00e9 GC-100"
+    },
+    "gdacs": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Global Disaster Alert and Coordination System (GDACS)"
+    },
+    "generic": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Generic Camera"
+    },
+    "generic_hygrostat": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Generic hygrostat"
+    },
+    "generic_thermostat": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Generic Thermostat"
+    },
+    "geniushub": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Genius Hub"
+    },
+    "geo_json_events": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "GeoJSON"
+    },
+    "geo_location": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Geolocation"
+    },
+    "geo_rss_events": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "GeoRSS"
+    },
+    "geocaching": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Geocaching"
+    },
+    "geofency": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Geofency"
+    },
+    "geonetnz_quakes": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "GeoNet NZ Quakes"
+    },
+    "geonetnz_volcano": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "GeoNet NZ Volcano"
+    },
+    "gios": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "GIO\u015a"
+    },
+    "github": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "GitHub"
+    },
+    "gitlab_ci": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "GitLab-CI"
+    },
+    "gitter": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Gitter"
+    },
+    "glances": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Glances"
+    },
+    "goalfeed": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Goalfeed"
+    },
+    "goalzero": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Goal Zero Yeti"
+    },
+    "gogogate2": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Gogogate2 and ismartgate"
+    },
+    "goodwe": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "GoodWe Inverter"
+    },
+    "google": {
+      "name": "Google",
+      "integrations": {
+        "google": {
+          "config_flow": true,
+          "iot_class": "cloud_polling",
+          "name": "Google Calendars"
+        },
+        "google_sheets": {
+          "config_flow": true,
+          "iot_class": "cloud_polling",
+          "name": "Google Sheets"
+        }
+      }
+    },
+    "google_assistant": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Google Assistant"
+    },
+    "google_cloud": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Google Cloud Platform"
+    },
+    "google_domains": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Google Domains"
+    },
+    "google_maps": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Google Maps"
+    },
+    "google_pubsub": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Google Pub/Sub"
+    },
+    "google_translate": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Google Translate Text-to-Speech"
+    },
+    "google_travel_time": {
+      "config_flow": true,
+      "iot_class": "cloud_polling"
+    },
+    "google_wifi": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Google Wifi"
+    },
+    "govee_ble": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Govee Bluetooth"
+    },
+    "gpsd": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "GPSD"
+    },
+    "gpslogger": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "GPSLogger"
+    },
+    "graphite": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Graphite"
+    },
+    "gree": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Gree Climate"
+    },
+    "greeneye_monitor": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "GreenEye Monitor (GEM)"
+    },
+    "greenwave": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Greenwave Reality"
+    },
+    "growatt_server": {
+      "config_flow": true,
+      "iot_class": "cloud_polling"
+    },
+    "gstreamer": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "GStreamer"
+    },
+    "gtfs": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "General Transit Feed Specification (GTFS)"
+    },
+    "guardian": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Elexa Guardian"
+    },
+    "habitica": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Habitica"
+    },
+    "hangouts": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Google Chat"
+    },
+    "hardware": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Hardware"
+    },
+    "harman_kardon_avr": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Harman Kardon AVR"
+    },
+    "harmony": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Logitech Harmony Hub"
+    },
+    "hassio": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Home Assistant Supervisor"
+    },
+    "haveibeenpwned": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "HaveIBeenPwned"
+    },
+    "hddtemp": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "hddtemp"
+    },
+    "hdmi_cec": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "HDMI-CEC"
+    },
+    "heatmiser": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Heatmiser"
+    },
+    "heos": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Denon HEOS"
+    },
+    "here_travel_time": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "HERE Travel Time"
+    },
+    "hikvision": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Hikvision"
+    },
+    "hikvisioncam": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Hikvision"
+    },
+    "hisense_aehw4a1": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Hisense AEH-W4A1"
+    },
+    "history": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "History"
+    },
+    "history_stats": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "History Stats"
+    },
+    "hitron_coda": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Rogers Hitron CODA"
+    },
+    "hive": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Hive"
+    },
+    "hlk_sw16": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Hi-Link HLK-SW16"
+    },
+    "home_connect": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Home Connect"
+    },
+    "home_plus_control": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Legrand Home+ Control"
+    },
+    "homeassistant": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Home Assistant Core Integration"
+    },
+    "homeassistant_alerts": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Home Assistant Alerts"
+    },
+    "homekit": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "HomeKit"
+    },
+    "homekit_controller": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
+    "homematic": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Homematic"
+    },
+    "homematicip_cloud": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "HomematicIP Cloud"
+    },
+    "homewizard": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "HomeWizard Energy"
+    },
+    "homeworks": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Lutron Homeworks"
+    },
+    "honeywell": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Honeywell Total Connect Comfort (US)"
+    },
+    "horizon": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Unitymedia Horizon HD Recorder"
+    },
+    "hp_ilo": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "HP Integrated Lights-Out (ILO)"
+    },
+    "html5": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "HTML5 Push Notifications"
+    },
+    "http": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "HTTP"
+    },
+    "huawei_lte": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Huawei LTE"
+    },
+    "hue": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Philips Hue"
+    },
+    "huisbaasje": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Huisbaasje"
+    },
+    "humidifier": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "hunterdouglas_powerview": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Hunter Douglas PowerView"
+    },
+    "hvv_departures": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "HVV Departures"
+    },
+    "hydrawise": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Hunter Hydrawise"
+    },
+    "hyperion": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Hyperion"
+    },
+    "ialarm": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Antifurto365 iAlarm"
+    },
+    "iammeter": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "IamMeter"
+    },
+    "iaqualink": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Jandy iAqualink"
+    },
+    "ibeacon": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "iBeacon Tracker"
+    },
+    "icloud": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Apple iCloud"
+    },
+    "idteck_prox": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "IDTECK Proximity Reader"
+    },
+    "ifttt": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "IFTTT"
+    },
+    "iglo": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "iGlo"
+    },
+    "ign_sismologia": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "IGN Sismolog\u00eda"
+    },
+    "ihc": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "IHC Controller"
+    },
+    "image": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Image"
+    },
+    "image_processing": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "imap": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "IMAP"
+    },
+    "imap_email_content": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "IMAP Email Content"
+    },
+    "incomfort": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Intergas InComfort/Intouch Lan2RF gateway"
+    },
+    "influxdb": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "InfluxDB"
+    },
+    "inkbird": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "INKBIRD"
+    },
+    "insteon": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Insteon"
+    },
+    "intellifire": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "IntelliFire"
+    },
+    "intent": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Intent"
+    },
+    "intent_script": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Intent Script"
+    },
+    "intesishome": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "IntesisHome"
+    },
+    "ios": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Home Assistant iOS"
+    },
+    "iotawatt": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "IoTaWatt"
+    },
+    "iperf3": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Iperf3"
+    },
+    "ipma": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Instituto Portugu\u00eas do Mar e Atmosfera (IPMA)"
+    },
+    "ipp": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Internet Printing Protocol (IPP)"
+    },
+    "iqvia": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "IQVIA"
+    },
+    "irish_rail_transport": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Irish Rail Transport"
+    },
+    "islamic_prayer_times": {
+      "config_flow": true,
+      "iot_class": "cloud_polling"
+    },
+    "iss": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "International Space Station (ISS)"
+    },
+    "isy994": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Universal Devices ISY994"
+    },
+    "itach": {
+      "config_flow": false,
+      "iot_class": "assumed_state",
+      "name": "Global Cach\u00e9 iTach TCP/IP to IR"
+    },
+    "itunes": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Apple iTunes"
+    },
+    "izone": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "iZone"
+    },
+    "jellyfin": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Jellyfin"
+    },
+    "jewish_calendar": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "Jewish Calendar"
+    },
+    "joaoapps_join": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Joaoapps Join"
+    },
+    "juicenet": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "JuiceNet"
+    },
+    "justnimbus": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "JustNimbus"
+    },
+    "kaiterra": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Kaiterra"
+    },
+    "kaleidescape": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Kaleidescape"
+    },
+    "kankun": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Kankun"
+    },
+    "keba": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Keba Charging Station"
+    },
+    "keenetic_ndms2": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Keenetic NDMS2 Router"
+    },
+    "kef": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "KEF"
+    },
+    "kegtron": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Kegtron"
+    },
+    "keyboard": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Keyboard"
+    },
+    "keyboard_remote": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Keyboard Remote"
+    },
+    "keymitt_ble": {
+      "config_flow": true,
+      "iot_class": "assumed_state",
+      "name": "Keymitt MicroBot Push"
+    },
+    "kira": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Kira"
+    },
+    "kiwi": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "KIWI"
+    },
+    "kmtronic": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "KMtronic"
+    },
+    "knx": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "KNX"
+    },
+    "kodi": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Kodi"
+    },
+    "konnected": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Konnected.io"
+    },
+    "kostal_plenticore": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Kostal Plenticore Solar Inverter"
+    },
+    "kraken": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Kraken"
+    },
+    "kulersky": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Kuler Sky"
+    },
+    "kwb": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "KWB Easyfire"
+    },
+    "lacrosse": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "LaCrosse"
+    },
+    "lacrosse_view": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "LaCrosse View"
+    },
+    "lametric": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "LaMetric"
+    },
+    "landisgyr_heat_meter": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Landis+Gyr Heat Meter"
+    },
+    "lannouncer": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "LANnouncer"
+    },
+    "lastfm": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Last.fm"
+    },
+    "launch_library": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Launch Library"
+    },
+    "laundrify": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "laundrify"
+    },
+    "lcn": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "LCN"
+    },
+    "led_ble": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "LED BLE"
+    },
+    "lg_netcast": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "LG Netcast"
+    },
+    "lg_soundbar": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "LG Soundbars"
+    },
+    "lidarr": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Lidarr"
+    },
+    "life360": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Life360"
+    },
+    "lifx": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "LIFX"
+    },
+    "lifx_cloud": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "LIFX Cloud"
+    },
+    "light": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "lightwave": {
+      "config_flow": false,
+      "iot_class": "assumed_state",
+      "name": "Lightwave"
+    },
+    "limitlessled": {
+      "config_flow": false,
+      "iot_class": "assumed_state",
+      "name": "LimitlessLED"
+    },
+    "linksys_smart": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Linksys Smart Wi-Fi"
+    },
+    "linode": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Linode"
+    },
+    "linux_battery": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Linux Battery"
+    },
+    "lirc": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "LIRC"
+    },
+    "litejet": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "LiteJet"
+    },
+    "litterrobot": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Litter-Robot"
+    },
+    "llamalab_automate": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "LlamaLab Automate"
+    },
+    "local_file": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Local File"
+    },
+    "local_ip": {
+      "config_flow": true,
+      "iot_class": "local_polling"
+    },
+    "locative": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Locative"
+    },
+    "lock": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "logbook": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Logbook"
+    },
+    "logentries": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Logentries"
+    },
+    "logger": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Logger"
+    },
+    "logi_circle": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Logi Circle"
+    },
+    "london_air": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "London Air"
+    },
+    "london_underground": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "London Underground"
+    },
+    "lookin": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "LOOKin"
+    },
+    "lovelace": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Dashboards"
+    },
+    "luci": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "OpenWrt (luci)"
+    },
+    "luftdaten": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Sensor.Community"
+    },
+    "lupusec": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Lupus Electronics LUPUSEC"
+    },
+    "lutron": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Lutron"
+    },
+    "lutron_caseta": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Lutron Cas\u00e9ta"
+    },
+    "lw12wifi": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "LAGUTE LW-12"
+    },
+    "lyric": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Honeywell Lyric"
+    },
+    "magicseaweed": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Magicseaweed"
+    },
+    "mailbox": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "mailgun": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Mailgun"
+    },
+    "manual": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "Manual Alarm Control Panel"
+    },
+    "manual_mqtt": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Manual MQTT Alarm Control Panel"
+    },
+    "map": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Map"
+    },
+    "marytts": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "MaryTTS"
+    },
+    "mastodon": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Mastodon"
+    },
+    "matrix": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Matrix"
+    },
+    "maxcube": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "eQ-3 MAX!"
+    },
+    "mazda": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Mazda Connected Services"
+    },
+    "meater": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Meater"
+    },
+    "media_extractor": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "Media Extractor"
+    },
+    "media_player": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "media_source": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Media Source"
+    },
+    "mediaroom": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Mediaroom"
+    },
+    "melcloud": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "MELCloud"
+    },
+    "melissa": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Melissa"
+    },
+    "melnor": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Melnor Bluetooth"
+    },
+    "meraki": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Meraki"
+    },
+    "message_bird": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "MessageBird"
+    },
+    "met": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Meteorologisk institutt (Met.no)"
+    },
+    "met_eireann": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Met \u00c9ireann"
+    },
+    "meteo_france": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "M\u00e9t\u00e9o-France"
+    },
+    "meteoalarm": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "MeteoAlarm"
+    },
+    "meteoclimatic": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Meteoclimatic"
+    },
+    "metoffice": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Met Office"
+    },
+    "mfi": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Ubiquiti mFi mPort"
+    },
+    "microsoft": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Microsoft Text-to-Speech (TTS)"
+    },
+    "microsoft_face": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Microsoft Face"
+    },
+    "microsoft_face_detect": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Microsoft Face Detect"
+    },
+    "microsoft_face_identify": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Microsoft Face Identify"
+    },
+    "miflora": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Mi Flora"
+    },
+    "mikrotik": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Mikrotik"
+    },
+    "mill": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Mill"
+    },
+    "minecraft_server": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Minecraft Server"
+    },
+    "minio": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Minio"
+    },
+    "mitemp_bt": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Xiaomi Mijia BLE Temperature and Humidity Sensor"
+    },
+    "mjpeg": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "MJPEG IP Camera"
+    },
+    "moat": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Moat"
+    },
+    "mobile_app": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
+    "mochad": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Mochad"
+    },
+    "modbus": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Modbus"
+    },
+    "modem_callerid": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Phone Modem"
+    },
+    "modern_forms": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Modern Forms"
+    },
+    "moehlenhoff_alpha2": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
+    "mold_indicator": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Mold Indicator"
+    },
+    "monoprice": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Monoprice 6-Zone Amplifier"
+    },
+    "moon": {
+      "config_flow": true,
+      "iot_class": "local_polling"
+    },
+    "motion_blinds": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Motion Blinds"
+    },
+    "motioneye": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "motionEye"
+    },
+    "mpd": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Music Player Daemon (MPD)"
+    },
+    "mqtt": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "MQTT"
+    },
+    "mqtt_eventstream": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "MQTT Eventstream"
+    },
+    "mqtt_json": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "MQTT JSON"
+    },
+    "mqtt_room": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "MQTT Room Presence"
+    },
+    "mqtt_statestream": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "MQTT Statestream"
+    },
+    "msteams": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Microsoft Teams"
+    },
+    "mullvad": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Mullvad VPN"
+    },
+    "mutesync": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "mutesync"
+    },
+    "mvglive": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "MVG"
+    },
+    "my": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "My Home Assistant"
+    },
+    "mycroft": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Mycroft"
+    },
+    "myq": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "MyQ"
+    },
+    "mysensors": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "MySensors"
+    },
+    "mystrom": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "myStrom"
+    },
+    "mythicbeastsdns": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Mythic Beasts DNS"
+    },
+    "nad": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "NAD"
+    },
+    "nam": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Nettigo Air Monitor"
+    },
+    "namecheapdns": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Namecheap FreeDNS"
+    },
+    "nanoleaf": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Nanoleaf"
+    },
+    "neato": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Neato Botvac"
+    },
+    "nederlandse_spoorwegen": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Nederlandse Spoorwegen (NS)"
+    },
+    "ness_alarm": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Ness Alarm"
+    },
+    "nest": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Nest"
+    },
+    "netatmo": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Netatmo"
+    },
+    "netdata": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Netdata"
+    },
+    "netgear": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "NETGEAR"
+    },
+    "netgear_lte": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "NETGEAR LTE"
+    },
+    "netio": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Netio"
+    },
+    "network": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Network Configuration"
+    },
+    "neurio_energy": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Neurio energy"
+    },
+    "nexia": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Nexia/American Standard/Trane"
+    },
+    "nextbus": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "NextBus"
+    },
+    "nextcloud": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Nextcloud"
+    },
+    "nextdns": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "NextDNS"
+    },
+    "nfandroidtv": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Notifications for Android TV / Fire TV"
+    },
+    "nibe_heatpump": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Nibe Heat Pump"
+    },
+    "nightscout": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Nightscout"
+    },
+    "niko_home_control": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Niko Home Control"
+    },
+    "nilu": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Norwegian Institute for Air Research (NILU)"
+    },
+    "nina": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "NINA"
+    },
+    "nissan_leaf": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Nissan Leaf"
+    },
+    "nmap_tracker": {
+      "config_flow": true,
+      "iot_class": "local_polling"
+    },
+    "nmbs": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "NMBS"
+    },
+    "no_ip": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "No-IP.com"
+    },
+    "noaa_tides": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "NOAA Tides"
+    },
+    "nobo_hub": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Nob\u00f8 Ecohub"
+    },
+    "norway_air": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Om Luftkvalitet i Norge (Norway Air)"
+    },
+    "notify": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "notify_events": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Notify.Events"
+    },
+    "notion": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Notion"
+    },
+    "nsw_fuel_station": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "NSW Fuel Station Price"
+    },
+    "nsw_rural_fire_service_feed": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "NSW Rural Fire Service Incidents"
+    },
+    "nuheat": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "NuHeat"
+    },
+    "nuki": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Nuki"
+    },
+    "numato": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Numato USB GPIO Expander"
+    },
+    "number": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "nut": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Network UPS Tools (NUT)"
+    },
+    "nws": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "National Weather Service (NWS)"
+    },
+    "nx584": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "NX584"
+    },
+    "nzbget": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "NZBGet"
+    },
+    "oasa_telematics": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "OASA Telematics"
+    },
+    "obihai": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Obihai"
+    },
+    "octoprint": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "OctoPrint"
+    },
+    "oem": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "OpenEnergyMonitor WiFi Thermostat"
+    },
+    "ohmconnect": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "OhmConnect"
+    },
+    "ombi": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Ombi"
+    },
+    "omnilogic": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Hayward Omnilogic"
+    },
+    "onboarding": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Home Assistant Onboarding"
+    },
+    "oncue": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Oncue by Kohler"
+    },
+    "ondilo_ico": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Ondilo ICO"
+    },
+    "onewire": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "1-Wire"
+    },
+    "onkyo": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Onkyo"
+    },
+    "onvif": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "ONVIF"
+    },
+    "open_meteo": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Open-Meteo"
+    },
+    "openalpr_cloud": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "OpenALPR Cloud"
+    },
+    "openalpr_local": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "OpenALPR Local"
+    },
+    "opencv": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "OpenCV"
+    },
+    "openerz": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Open ERZ"
+    },
+    "openevse": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "OpenEVSE"
+    },
+    "openexchangerates": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Open Exchange Rates"
+    },
+    "opengarage": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "OpenGarage"
+    },
+    "openhardwaremonitor": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Open Hardware Monitor"
+    },
+    "openhome": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Linn / OpenHome"
+    },
+    "opensensemap": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "openSenseMap"
+    },
+    "opensky": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "OpenSky Network"
+    },
+    "opentherm_gw": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "OpenTherm Gateway"
+    },
+    "openuv": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "OpenUV"
+    },
+    "openweathermap": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "OpenWeatherMap"
+    },
+    "opnsense": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "OPNSense"
+    },
+    "opple": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Opple"
+    },
+    "oru": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Orange and Rockland Utility (ORU)"
+    },
+    "orvibo": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Orvibo"
+    },
+    "osramlightify": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Osramlightify"
+    },
+    "otp": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "One-Time Password (OTP)"
+    },
+    "overkiz": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Overkiz"
+    },
+    "ovo_energy": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "OVO Energy"
+    },
+    "owntracks": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "OwnTracks"
+    },
+    "p1_monitor": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "P1 Monitor"
+    },
+    "panasonic_bluray": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Panasonic Blu-Ray Player"
+    },
+    "panasonic_viera": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Panasonic Viera"
+    },
+    "pandora": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Pandora"
+    },
+    "panel_custom": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Custom Panel"
+    },
+    "panel_iframe": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "iframe Panel"
+    },
+    "peco": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "PECO Outage Counter"
+    },
+    "pencom": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Pencom"
+    },
+    "persistent_notification": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Persistent Notification"
+    },
+    "person": {
+      "config_flow": false,
+      "iot_class": "calculated"
+    },
+    "philips_js": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Philips TV"
+    },
+    "pi_hole": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Pi-hole"
+    },
+    "picnic": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Picnic"
+    },
+    "picotts": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Pico TTS"
+    },
+    "pilight": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Pilight"
+    },
+    "ping": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Ping (ICMP)"
+    },
+    "pioneer": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Pioneer"
+    },
+    "pjlink": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "PJLink"
+    },
+    "plaato": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Plaato"
+    },
+    "plant": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "plex": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Plex Media Server"
+    },
+    "plugwise": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Plugwise"
+    },
+    "plum_lightpad": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Plum Lightpad"
+    },
+    "pocketcasts": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Pocket Casts"
+    },
+    "point": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Minut Point"
+    },
+    "poolsense": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "PoolSense"
+    },
+    "powerwall": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Tesla Powerwall"
+    },
+    "profiler": {
+      "config_flow": true,
+      "iot_class": null,
+      "name": "Profiler"
+    },
+    "progettihwsw": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "ProgettiHWSW Automation"
+    },
+    "proliphix": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Proliphix"
+    },
+    "prometheus": {
+      "config_flow": false,
+      "iot_class": "assumed_state",
+      "name": "Prometheus"
+    },
+    "prosegur": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Prosegur Alarm"
+    },
+    "prowl": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Prowl"
+    },
+    "proximity": {
+      "config_flow": false,
+      "iot_class": "calculated"
+    },
+    "proxmoxve": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Proxmox VE"
+    },
+    "proxy": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Camera Proxy"
+    },
+    "prusalink": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "PrusaLink"
+    },
+    "ps4": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Sony PlayStation 4"
+    },
+    "pulseaudio_loopback": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "PulseAudio Loopback"
+    },
+    "pure_energie": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Pure Energie"
+    },
+    "push": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Push"
+    },
+    "pushbullet": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Pushbullet"
+    },
+    "pushover": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Pushover"
+    },
+    "pushsafer": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Pushsafer"
+    },
+    "pvoutput": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "PVOutput"
+    },
+    "pvpc_hourly_pricing": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Spain electricity hourly pricing (PVPC)"
+    },
+    "pyload": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "pyLoad"
+    },
+    "python_script": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Python Scripts"
+    },
+    "qbittorrent": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "qBittorrent"
+    },
+    "qingping": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Qingping"
+    },
+    "qld_bushfire": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Queensland Bushfire Alert"
+    },
+    "qnap": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "QNAP"
+    },
+    "qnap_qsw": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "QNAP QSW"
+    },
+    "qrcode": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "QR Code"
+    },
+    "quantum_gateway": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Quantum Gateway"
+    },
+    "qvr_pro": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "QVR Pro"
+    },
+    "qwikswitch": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "QwikSwitch QSUSB"
+    },
+    "rachio": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Rachio"
+    },
+    "radarr": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Radarr"
+    },
+    "radio_browser": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Radio Browser"
+    },
+    "radiotherm": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Radio Thermostat"
+    },
+    "rainbird": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Rain Bird"
+    },
+    "raincloud": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Melnor RainCloud"
+    },
+    "rainforest_eagle": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Rainforest Eagle"
+    },
+    "rainmachine": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "RainMachine"
+    },
+    "random": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Random"
+    },
+    "raspyrfm": {
+      "config_flow": false,
+      "iot_class": "assumed_state",
+      "name": "RaspyRFM"
+    },
+    "rdw": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "RDW"
+    },
+    "recollect_waste": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "ReCollect Waste"
+    },
+    "recorder": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Recorder"
+    },
+    "recswitch": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Ankuoo REC Switch"
+    },
+    "reddit": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Reddit"
+    },
+    "rejseplanen": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Rejseplanen"
+    },
+    "remember_the_milk": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Remember The Milk"
+    },
+    "remote": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "remote_rpi_gpio": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "remote_rpi_gpio"
+    },
+    "renault": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Renault"
+    },
+    "repairs": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Repairs"
+    },
+    "repetier": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Repetier-Server"
+    },
+    "rest": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "RESTful"
+    },
+    "rest_command": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "RESTful Command"
+    },
+    "rflink": {
+      "config_flow": false,
+      "iot_class": "assumed_state",
+      "name": "RFLink"
+    },
+    "rfxtrx": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "RFXCOM RFXtrx"
+    },
+    "rhasspy": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Rhasspy"
+    },
+    "ridwell": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Ridwell"
+    },
+    "ring": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Ring"
+    },
+    "ripple": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Ripple"
+    },
+    "risco": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Risco"
+    },
+    "rituals_perfume_genie": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Rituals Perfume Genie"
+    },
+    "rmvtransport": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "RMV"
+    },
+    "rocketchat": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Rocket.Chat"
+    },
+    "roku": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Roku"
+    },
+    "roomba": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "iRobot Roomba and Braava"
+    },
+    "roon": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "RoonLabs music player"
+    },
+    "route53": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "AWS Route53"
+    },
+    "rova": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "ROVA"
+    },
+    "rpi_camera": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Raspberry Pi Camera"
+    },
+    "rpi_power": {
+      "config_flow": true,
+      "iot_class": "local_polling"
+    },
+    "rss_feed_template": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "RSS Feed Template"
+    },
+    "rtorrent": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "rTorrent"
+    },
+    "rtsp_to_webrtc": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "RTSPtoWebRTC"
+    },
+    "ruckus_unleashed": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Ruckus Unleashed"
+    },
+    "russound_rio": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Russound RIO"
+    },
+    "russound_rnet": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Russound RNET"
+    },
+    "sabnzbd": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "SABnzbd"
+    },
+    "safe_mode": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Safe Mode"
+    },
+    "saj": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "SAJ Solar Inverter"
+    },
+    "samsungtv": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Samsung Smart TV"
+    },
+    "satel_integra": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Satel Integra"
+    },
+    "scene": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "schluter": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Schluter"
+    },
+    "scrape": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Scrape"
+    },
+    "screenlogic": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Pentair ScreenLogic"
+    },
+    "script": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "scsgate": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "SCSGate"
+    },
+    "search": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Search"
+    },
+    "season": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Season"
+    },
+    "select": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "sendgrid": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "SendGrid"
+    },
+    "sense": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Sense"
+    },
+    "senseme": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "SenseME"
+    },
+    "sensibo": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Sensibo"
+    },
+    "sensor": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "sensorpro": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "SensorPro"
+    },
+    "sensorpush": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "SensorPush"
+    },
+    "sentry": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Sentry"
+    },
+    "senz": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "nVent RAYCHEM SENZ"
+    },
+    "serial": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Serial"
+    },
+    "serial_pm": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Serial Particulate Matter"
+    },
+    "sesame": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Sesame Smart Lock"
+    },
+    "seven_segments": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Seven Segments OCR"
+    },
+    "seventeentrack": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "17TRACK"
+    },
+    "sharkiq": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Shark IQ"
+    },
+    "shell_command": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Shell Command"
+    },
+    "shelly": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Shelly"
+    },
+    "shiftr": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "shiftr.io"
+    },
+    "shodan": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Shodan"
+    },
+    "shopping_list": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
+    "sia": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "SIA Alarm Systems"
+    },
+    "sigfox": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Sigfox"
+    },
+    "sighthound": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Sighthound"
+    },
+    "signal_messenger": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Signal Messenger"
+    },
+    "simplepush": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Simplepush"
+    },
+    "simplisafe": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "SimpliSafe"
+    },
+    "simulated": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Simulated"
+    },
+    "sinch": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Sinch SMS"
+    },
+    "siren": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "sisyphus": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Sisyphus"
+    },
+    "sky_hub": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Sky Hub"
+    },
+    "skybeacon": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Skybeacon"
+    },
+    "skybell": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "SkyBell"
+    },
+    "slack": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Slack"
+    },
+    "sleepiq": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "SleepIQ"
+    },
+    "slide": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Slide"
+    },
+    "slimproto": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "SlimProto (Squeezebox players)"
+    },
+    "sma": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "SMA Solar"
+    },
+    "smappee": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Smappee"
+    },
+    "smart_meter_texas": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Smart Meter Texas"
+    },
+    "smartthings": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "SmartThings"
+    },
+    "smarttub": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "SmartTub"
+    },
+    "smarty": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Salda Smarty"
+    },
+    "smhi": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "SMHI"
+    },
+    "sms": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "SMS notifications via GSM-modem"
+    },
+    "smtp": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "SMTP"
+    },
+    "snapcast": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Snapcast"
+    },
+    "snips": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Snips"
+    },
+    "snmp": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "SNMP"
+    },
+    "solaredge": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "SolarEdge"
+    },
+    "solaredge_local": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "SolarEdge Local"
+    },
+    "solarlog": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Solar-Log"
+    },
+    "solax": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "SolaX Power"
+    },
+    "soma": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Soma Connect"
+    },
+    "somfy_mylink": {
+      "config_flow": true,
+      "iot_class": "assumed_state",
+      "name": "Somfy MyLink"
+    },
+    "sonarr": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Sonarr"
+    },
+    "songpal": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Sony Songpal"
+    },
+    "sonos": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Sonos"
+    },
+    "sony_projector": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Sony Projector"
+    },
+    "soundtouch": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Bose SoundTouch"
+    },
+    "spaceapi": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Space API"
+    },
+    "spc": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Vanderbilt SPC"
+    },
+    "speedtestdotnet": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Speedtest.net"
+    },
+    "spider": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Itho Daalderop Spider"
+    },
+    "splunk": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Splunk"
+    },
+    "spotify": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Spotify"
+    },
+    "sql": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "SQL"
+    },
+    "squeezebox": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Squeezebox (Logitech Media Server)"
+    },
+    "srp_energy": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "SRP Energy"
+    },
+    "ssdp": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Simple Service Discovery Protocol (SSDP)"
+    },
+    "starline": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "StarLine"
+    },
+    "starlingbank": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Starling Bank"
+    },
+    "startca": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Start.ca"
+    },
+    "statistics": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Statistics"
+    },
+    "statsd": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "StatsD"
+    },
+    "steam_online": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Steam"
+    },
+    "steamist": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Steamist"
+    },
+    "stiebel_eltron": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "STIEBEL ELTRON"
+    },
+    "stookalert": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "RIVM Stookalert"
+    },
+    "stream": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Stream"
+    },
+    "streamlabswater": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "StreamLabs"
+    },
+    "stt": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Speech-to-Text (STT)"
+    },
+    "subaru": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Subaru"
+    },
+    "suez_water": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Suez Water"
+    },
+    "sun": {
+      "config_flow": true,
+      "iot_class": "calculated"
+    },
+    "supervisord": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Supervisord"
+    },
+    "supla": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Supla"
+    },
+    "surepetcare": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Sure Petcare"
+    },
+    "swiss_hydrological_data": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Swiss Hydrological Data"
+    },
+    "swiss_public_transport": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Swiss public transport"
+    },
+    "swisscom": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Swisscom Internet-Box"
+    },
+    "switch": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "switchbee": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "SwitchBee"
+    },
+    "switchbot": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "SwitchBot"
+    },
+    "switcher_kis": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Switcher"
+    },
+    "switchmate": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Switchmate SimplySmart Home"
+    },
+    "syncthing": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Syncthing"
+    },
+    "syncthru": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Samsung SyncThru Printer"
+    },
+    "synology_chat": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Synology Chat"
+    },
+    "synology_dsm": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Synology DSM"
+    },
+    "synology_srm": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Synology SRM"
+    },
+    "syslog": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Syslog"
+    },
+    "system_bridge": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "System Bridge"
+    },
+    "system_health": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "system_log": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "System Log"
+    },
+    "systemmonitor": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "System Monitor"
+    },
+    "tado": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Tado"
+    },
+    "tag": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "tailscale": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Tailscale"
+    },
+    "tank_utility": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Tank Utility"
+    },
+    "tankerkoenig": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Tankerkoenig"
+    },
+    "tapsaff": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Taps Aff"
+    },
+    "tasmota": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Tasmota"
+    },
+    "tautulli": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Tautulli"
+    },
+    "tcp": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "TCP"
+    },
+    "ted5000": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "The Energy Detective TED5000"
+    },
+    "telegram": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Telegram"
+    },
+    "telegram_bot": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Telegram bot"
+    },
+    "tellduslive": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Telldus Live"
+    },
+    "tellstick": {
+      "config_flow": false,
+      "iot_class": "assumed_state",
+      "name": "TellStick"
+    },
+    "telnet": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Telnet"
+    },
+    "temper": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "TEMPer"
+    },
+    "template": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Template"
+    },
+    "tensorflow": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "TensorFlow"
+    },
+    "tesla_wall_connector": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Tesla Wall Connector"
+    },
+    "tfiac": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Tfiac"
+    },
+    "thermobeacon": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "ThermoBeacon"
+    },
+    "thermopro": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "ThermoPro"
+    },
+    "thermoworks_smoke": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "ThermoWorks Smoke"
+    },
+    "thethingsnetwork": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "The Things Network"
+    },
+    "thingspeak": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "ThingSpeak"
+    },
+    "thinkingcleaner": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Thinking Cleaner"
+    },
+    "thomson": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Thomson"
+    },
+    "tibber": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Tibber"
+    },
+    "tikteck": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Tikteck"
+    },
+    "tile": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Tile"
+    },
+    "tilt_ble": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Tilt Hydrometer BLE"
+    },
+    "time_date": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Time & Date"
+    },
+    "tmb": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Transports Metropolitans de Barcelona"
+    },
+    "todoist": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Todoist"
+    },
+    "tolo": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "TOLO Sauna"
+    },
+    "tomato": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Tomato"
+    },
+    "tomorrowio": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Tomorrow.io"
+    },
+    "toon": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Toon"
+    },
+    "torque": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Torque"
+    },
+    "totalconnect": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Total Connect"
+    },
+    "touchline": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Roth Touchline"
+    },
+    "tplink": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "TP-Link Kasa Smart"
+    },
+    "tplink_lte": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "TP-Link LTE"
+    },
+    "traccar": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Traccar"
+    },
+    "trace": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Trace"
+    },
+    "tractive": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Tractive"
+    },
+    "tradfri": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "IKEA TR\u00c5DFRI"
+    },
+    "trafikverket_ferry": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Trafikverket Ferry"
+    },
+    "trafikverket_train": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Trafikverket Train"
+    },
+    "trafikverket_weatherstation": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Trafikverket Weather Station"
+    },
+    "transmission": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Transmission"
+    },
+    "transport_nsw": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Transport NSW"
+    },
+    "travisci": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Travis-CI"
+    },
+    "trend": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Trend"
+    },
+    "tts": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Text-to-Speech (TTS)"
+    },
+    "tuya": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Tuya"
+    },
+    "twentemilieu": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Twente Milieu"
+    },
+    "twilio": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Twilio"
+    },
+    "twilio_call": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Twilio Call"
+    },
+    "twilio_sms": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Twilio SMS"
+    },
+    "twinkly": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Twinkly"
+    },
+    "twitch": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Twitch"
+    },
+    "twitter": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Twitter"
+    },
+    "ubus": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "OpenWrt (ubus)"
+    },
+    "ue_smart_radio": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Logitech UE Smart Radio"
+    },
+    "uk_transport": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "UK Transport"
+    },
+    "ukraine_alarm": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Ukraine Alarm"
+    },
+    "unifi": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "UniFi Network"
+    },
+    "unifi_direct": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Ubiquiti UniFi AP"
+    },
+    "unifiled": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Ubiquiti UniFi LED"
+    },
+    "unifiprotect": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "UniFi Protect"
+    },
+    "universal": {
+      "config_flow": false,
+      "iot_class": "calculated",
+      "name": "Universal Media Player"
+    },
+    "upb": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Universal Powerline Bus (UPB)"
+    },
+    "upc_connect": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "UPC Connect Box"
+    },
+    "upcloud": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "UpCloud"
+    },
+    "update": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "upnp": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "UPnP/IGD"
+    },
+    "uptime": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
+    "uptimerobot": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "UptimeRobot"
+    },
+    "usb": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "USB Discovery"
+    },
+    "usgs_earthquakes_feed": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "U.S. Geological Survey Earthquake Hazards (USGS)"
+    },
+    "uvc": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Ubiquiti UniFi Video"
+    },
+    "vacuum": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "vallox": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Vallox"
+    },
+    "vasttrafik": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "V\u00e4sttrafik"
+    },
+    "velbus": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Velbus"
+    },
+    "velux": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Velux"
+    },
+    "venstar": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Venstar"
+    },
+    "vera": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Vera"
+    },
+    "verisure": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Verisure"
+    },
+    "versasense": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "VersaSense"
+    },
+    "version": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Version"
+    },
+    "vesync": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "VeSync"
+    },
+    "viaggiatreno": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Trenitalia ViaggiaTreno"
+    },
+    "vicare": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Viessmann ViCare"
+    },
+    "vilfo": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Vilfo Router"
+    },
+    "vivotek": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "VIVOTEK"
+    },
+    "vizio": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "VIZIO SmartCast"
+    },
+    "vlc": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "VLC media player"
+    },
+    "vlc_telnet": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "VLC media player via Telnet"
+    },
+    "voicerss": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "VoiceRSS"
+    },
+    "volkszaehler": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Volkszaehler"
+    },
+    "volumio": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Volumio"
+    },
+    "volvooncall": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Volvo On Call"
+    },
+    "vulcan": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Uonet+ Vulcan"
+    },
+    "vultr": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Vultr"
+    },
+    "w800rf32": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "WGL Designs W800RF32"
+    },
+    "wake_on_lan": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Wake on LAN"
+    },
+    "wallbox": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Wallbox"
+    },
+    "waqi": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "World Air Quality Index (WAQI)"
+    },
+    "water_heater": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Water Heater"
+    },
+    "waterfurnace": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "WaterFurnace"
+    },
+    "watson_iot": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "IBM Watson IoT Platform"
+    },
+    "watson_tts": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "IBM Watson TTS"
+    },
+    "watttime": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "WattTime"
+    },
+    "waze_travel_time": {
+      "config_flow": true,
+      "iot_class": "cloud_polling"
+    },
+    "weather": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Weather"
+    },
+    "webhook": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Webhook"
+    },
+    "webostv": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "LG webOS Smart TV"
+    },
+    "websocket_api": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Home Assistant WebSocket API"
+    },
+    "wemo": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Belkin WeMo"
+    },
+    "whirlpool": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "Whirlpool Sixth Sense"
+    },
+    "whois": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Whois"
+    },
+    "wiffi": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Wiffi"
+    },
+    "wilight": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "WiLight"
+    },
+    "wirelesstag": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Wireless Sensor Tags"
+    },
+    "withings": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Withings"
+    },
+    "wiz": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "WiZ"
+    },
+    "wled": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "WLED"
+    },
+    "wolflink": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Wolf SmartSet Service"
+    },
+    "workday": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Workday"
+    },
+    "worldclock": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Worldclock"
+    },
+    "worldtidesinfo": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "World Tides"
+    },
+    "worxlandroid": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Worx Landroid"
+    },
+    "ws66i": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Soundavo WS66i 6-Zone Amplifier"
+    },
+    "wsdot": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Washington State Department of Transportation (WSDOT)"
+    },
+    "x10": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Heyu X10"
+    },
+    "xbox": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Xbox"
+    },
+    "xbox_live": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Xbox Live"
+    },
+    "xeoma": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Xeoma"
+    },
+    "xiaomi": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Xiaomi"
+    },
+    "xiaomi_aqara": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Xiaomi Gateway (Aqara)"
+    },
+    "xiaomi_ble": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Xiaomi BLE"
+    },
+    "xiaomi_miio": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Xiaomi Miio"
+    },
+    "xiaomi_tv": {
+      "config_flow": false,
+      "iot_class": "assumed_state",
+      "name": "Xiaomi TV"
+    },
+    "xmpp": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Jabber (XMPP)"
+    },
+    "xs1": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "EZcontrol XS1"
+    },
+    "yale_smart_alarm": {
+      "config_flow": true,
+      "iot_class": "cloud_polling",
+      "name": "Yale Smart Living"
+    },
+    "yalexs_ble": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Yale Access Bluetooth"
+    },
+    "yamaha": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Yamaha Network Receivers"
+    },
+    "yamaha_musiccast": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "MusicCast"
+    },
+    "yandex_transport": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Yandex Transport"
+    },
+    "yandextts": {
+      "config_flow": false,
+      "iot_class": "cloud_push",
+      "name": "Yandex TTS"
+    },
+    "yeelight": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Yeelight"
+    },
+    "yeelightsunflower": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Yeelight Sunflower"
+    },
+    "yi": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Yi Home Cameras"
+    },
+    "yolink": {
+      "config_flow": true,
+      "iot_class": "cloud_push",
+      "name": "YoLink"
+    },
+    "youless": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "YouLess"
+    },
+    "zabbix": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Zabbix"
+    },
+    "zamg": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Zentralanstalt f\u00fcr Meteorologie und Geodynamik (ZAMG)"
+    },
+    "zengge": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Zengge"
+    },
+    "zeroconf": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "Zero-configuration networking (zeroconf)"
+    },
+    "zerproc": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Zerproc"
+    },
+    "zestimate": {
+      "config_flow": false,
+      "iot_class": "cloud_polling",
+      "name": "Zestimate"
+    },
+    "zha": {
+      "config_flow": true,
+      "iot_class": "local_polling",
+      "name": "Zigbee Home Automation"
+    },
+    "zhong_hong": {
+      "config_flow": false,
+      "iot_class": "local_push",
+      "name": "ZhongHong"
+    },
+    "ziggo_mediabox_xl": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Ziggo Mediabox XL"
+    },
+    "zodiac": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "Zodiac"
+    },
+    "zone": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Zone"
+    },
+    "zoneminder": {
+      "config_flow": false,
+      "iot_class": "local_polling",
+      "name": "ZoneMinder"
+    },
+    "zwave_js": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Z-Wave"
+    },
+    "zwave_me": {
+      "config_flow": true,
+      "iot_class": "local_push",
+      "name": "Z-Wave.Me"
+    }
+  },
+  "hardware": {
+    "hardkernel": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Hardkernel"
+    },
+    "homeassistant_sky_connect": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Home Assistant Sky Connect"
+    },
+    "homeassistant_yellow": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Home Assistant Yellow"
+    },
+    "raspberry_pi": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Raspberry Pi"
+    }
+  },
+  "helper": {
+    "counter": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Counter"
+    },
+    "derivative": {
+      "config_flow": true,
+      "iot_class": "calculated"
+    },
+    "group": {
+      "config_flow": true,
+      "iot_class": "calculated"
+    },
+    "input_boolean": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "input_button": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Input Button"
+    },
+    "input_datetime": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "input_number": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "input_select": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "input_text": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "integration": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
+    "min_max": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
+    "schedule": {
+      "config_flow": false,
+      "iot_class": null
+    },
+    "switch_as_x": {
+      "config_flow": true,
+      "iot_class": "calculated"
+    },
+    "threshold": {
+      "config_flow": true,
+      "iot_class": "local_polling"
+    },
+    "timer": {
+      "config_flow": false,
+      "iot_class": null,
+      "name": "Timer"
+    },
+    "tod": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
+    "utility_meter": {
+      "config_flow": true,
+      "iot_class": "local_push"
+    }
+  },
+  "translated_name": [
+    "alarm_control_panel",
+    "application_credentials",
+    "aurora",
+    "automation",
+    "binary_sensor",
+    "button",
+    "calendar",
+    "camera",
+    "cert_expiry",
+    "climate",
+    "configurator",
+    "conversation",
+    "cover",
+    "cpuspeed",
+    "demo",
+    "derivative",
+    "device_tracker",
+    "diagnostics",
+    "emulated_roku",
+    "energy",
+    "fan",
+    "filesize",
+    "garages_amsterdam",
+    "google_travel_time",
+    "group",
+    "growatt_server",
+    "homekit_controller",
+    "humidifier",
+    "image_processing",
+    "input_boolean",
+    "input_datetime",
+    "input_number",
+    "input_select",
+    "input_text",
+    "integration",
+    "islamic_prayer_times",
+    "light",
+    "local_ip",
+    "lock",
+    "mailbox",
+    "media_player",
+    "min_max",
+    "mobile_app",
+    "moehlenhoff_alpha2",
+    "moon",
+    "nmap_tracker",
+    "notify",
+    "number",
+    "person",
+    "plant",
+    "proximity",
+    "remote",
+    "rpi_power",
+    "scene",
+    "schedule",
+    "script",
+    "select",
+    "sensor",
+    "shopping_list",
+    "siren",
+    "sun",
+    "switch",
+    "switch_as_x",
+    "system_health",
+    "tag",
+    "threshold",
+    "tod",
+    "update",
+    "uptime",
+    "utility_meter",
+    "vacuum",
+    "waze_travel_time"
+  ]
+}
diff --git a/homeassistant/loader.py b/homeassistant/loader.py
index 4eda2f9f651..f2a7948f4f5 100644
--- a/homeassistant/loader.py
+++ b/homeassistant/loader.py
@@ -23,6 +23,7 @@ from awesomeversion import (
     AwesomeVersionStrategy,
 )
 
+from . import generated
 from .generated.application_credentials import APPLICATION_CREDENTIALS
 from .generated.bluetooth import BLUETOOTH
 from .generated.dhcp import DHCP
@@ -250,6 +251,44 @@ async def async_get_config_flows(
     return flows
 
 
+async def async_get_integration_descriptions(
+    hass: HomeAssistant,
+) -> dict[str, Any]:
+    """Return cached list of integrations."""
+    base = generated.__path__[0]
+    config_flow_path = pathlib.Path(base) / "integrations.json"
+
+    flow = await hass.async_add_executor_job(config_flow_path.read_text)
+    core_flows: dict[str, Any] = json_loads(flow)
+    custom_integrations = await async_get_custom_components(hass)
+    custom_flows: dict[str, Any] = {
+        "integration": {},
+        "hardware": {},
+        "helper": {},
+    }
+
+    for integration in custom_integrations.values():
+        # Remove core integration with same domain as the custom integration
+        if integration.integration_type in ("entity", "system"):
+            continue
+
+        for integration_type in ("integration", "hardware", "helper"):
+            if integration.domain not in core_flows[integration_type]:
+                continue
+            del core_flows[integration_type][integration.domain]
+        if integration.domain in core_flows["translated_name"]:
+            core_flows["translated_name"].remove(integration.domain)
+
+        metadata = {
+            "config_flow": integration.config_flow,
+            "iot_class": integration.iot_class,
+            "name": integration.name,
+        }
+        custom_flows[integration.integration_type][integration.domain] = metadata
+
+    return {"core": core_flows, "custom": custom_flows}
+
+
 async def async_get_application_credentials(hass: HomeAssistant) -> list[str]:
     """Return cached list of application credentials."""
     integrations = await async_get_custom_components(hass)
diff --git a/script/hassfest/__main__.py b/script/hassfest/__main__.py
index 233abda4ed8..7fb6ad2d8d0 100644
--- a/script/hassfest/__main__.py
+++ b/script/hassfest/__main__.py
@@ -31,7 +31,6 @@ INTEGRATION_PLUGINS = [
     application_credentials,
     bluetooth,
     codeowners,
-    config_flow,
     dependencies,
     dhcp,
     json,
@@ -44,6 +43,7 @@ INTEGRATION_PLUGINS = [
     translations,
     usb,
     zeroconf,
+    config_flow,
 ]
 HASS_PLUGINS = [
     coverage,
diff --git a/script/hassfest/brand.py b/script/hassfest/brand.py
new file mode 100644
index 00000000000..64217da1592
--- /dev/null
+++ b/script/hassfest/brand.py
@@ -0,0 +1,71 @@
+"""Brand validation."""
+from __future__ import annotations
+
+import voluptuous as vol
+from voluptuous.humanize import humanize_error
+
+from .model import Brand, Config, Integration
+
+BRAND_SCHEMA = vol.Schema(
+    {
+        vol.Required("domain"): str,
+        vol.Required("name"): str,
+        vol.Optional("integrations"): [str],
+        vol.Optional("iot_standards"): [vol.Any("homekit", "zigbee", "zwave")],
+    }
+)
+
+
+def _validate_brand(
+    brand: Brand, integrations: dict[str, Integration], config: Config
+) -> None:
+    """Validate brand file."""
+    try:
+        BRAND_SCHEMA(brand.brand)
+    except vol.Invalid as err:
+        config.add_error(
+            "brand",
+            f"Invalid brand file {brand.path.name}: {humanize_error(brand.brand, err)}",
+        )
+        return
+
+    if brand.domain != brand.path.stem:
+        config.add_error(
+            "brand",
+            f"Domain '{brand.domain}' does not match file name {brand.path.name}",
+        )
+
+    if not brand.integrations and not brand.iot_standards:
+        config.add_error(
+            "brand",
+            f"Invalid brand file {brand.path.name}: At least one of integrations or "
+            "iot_standards must be non-empty",
+        )
+
+    if brand.integrations:
+        for sub_integration in brand.integrations:
+            if sub_integration not in integrations:
+                config.add_error(
+                    "brand",
+                    f"Invalid brand file {brand.path.name}: Can't add non core domain "
+                    f"'{sub_integration}' to 'integrations'",
+                )
+
+    if (
+        brand.domain in integrations
+        and not brand.integrations
+        or brand.domain not in brand.integrations
+    ):
+        config.add_error(
+            "brand",
+            f"Invalid brand file {brand.path.name}: Brand '{brand.brand['domain']}' "
+            f"is an integration but is missing in the brand's 'integrations' list'",
+        )
+
+
+def validate(
+    brands: dict[str, Brand], integrations: dict[str, Integration], config: Config
+) -> None:
+    """Handle all integrations' brands."""
+    for brand in brands.values():
+        _validate_brand(brand, integrations, config)
diff --git a/script/hassfest/config_flow.py b/script/hassfest/config_flow.py
index b5af4da6cb4..81cc5fae829 100644
--- a/script/hassfest/config_flow.py
+++ b/script/hassfest/config_flow.py
@@ -1,9 +1,13 @@
 """Generate config flow file."""
 from __future__ import annotations
 
+import json
+import pathlib
+
 import black
 
-from .model import Config, Integration
+from .brand import validate as validate_brands
+from .model import Brand, Config, Integration
 from .serializer import to_string
 
 BASE = """
@@ -87,14 +91,107 @@ def _generate_and_validate(integrations: dict[str, Integration], config: Config)
     return black.format_str(BASE.format(to_string(domains)), mode=black.Mode())
 
 
+def _populate_brand_integrations(
+    integration_data: dict,
+    integrations: dict[str, Integration],
+    brand_metadata: dict,
+    sub_integrations: list[str],
+) -> None:
+    """Add referenced integrations to a brand's metadata."""
+    brand_metadata.setdefault("integrations", {})
+    for domain in sub_integrations:
+        integration = integrations.get(domain)
+        if not integration or integration.integration_type in ("entity", "system"):
+            continue
+        metadata = {}
+        metadata["config_flow"] = integration.config_flow
+        metadata["iot_class"] = integration.iot_class
+        if integration.translated_name:
+            integration_data["translated_name"].add(domain)
+        else:
+            metadata["name"] = integration.name
+        brand_metadata["integrations"][domain] = metadata
+
+
+def _generate_integrations(
+    brands: dict[str, Brand], integrations: dict[str, Integration], config: Config
+):
+    """Generate integrations data."""
+
+    result = {
+        "integration": {},
+        "hardware": {},
+        "helper": {},
+        "translated_name": set(),
+    }
+
+    # Not all integrations will have an item in the brands collection.
+    # The config flow data index will be the union of the integrations without a brands item
+    # and the brand domain names from the brands collection.
+
+    # Compile a set of integrations which are referenced from at least one brand's
+    # integrations list. These integrations will not be present in the root level of the
+    # generated config flow index.
+    brand_integration_domains = {
+        brand_integration_domain
+        for brand in brands.values()
+        for brand_integration_domain in brand.integrations or []
+    }
+
+    # Compile a set of integrations which are not referenced from any brand's
+    # integrations list.
+    primary_domains = {
+        domain
+        for domain, integration in integrations.items()
+        if integration.manifest and domain not in brand_integration_domains
+    }
+    # Add all brands to the set
+    primary_domains |= set(brands)
+
+    # Generate the config flow index
+    for domain in sorted(primary_domains):
+        metadata = {}
+
+        if brand := brands.get(domain):
+            metadata["name"] = brand.name
+            if brand.integrations:
+                # Add the integrations which are referenced from the brand's
+                # integrations list
+                _populate_brand_integrations(
+                    result, integrations, metadata, brand.integrations
+                )
+            if brand.iot_standards:
+                metadata["iot_standards"] = brand.iot_standards
+            result["integration"][domain] = metadata
+        else:  # integration
+            integration = integrations[domain]
+            if integration.integration_type in ("entity", "system"):
+                continue
+            metadata["config_flow"] = integration.config_flow
+            metadata["iot_class"] = integration.iot_class
+            if integration.translated_name:
+                result["translated_name"].add(domain)
+            else:
+                metadata["name"] = integration.name
+            result[integration.integration_type][domain] = metadata
+
+    return json.dumps(
+        result | {"translated_name": sorted(result["translated_name"])}, indent=2
+    )
+
+
 def validate(integrations: dict[str, Integration], config: Config):
     """Validate config flow file."""
     config_flow_path = config.root / "homeassistant/generated/config_flows.py"
+    integrations_path = config.root / "homeassistant/generated/integrations.json"
     config.cache["config_flow"] = content = _generate_and_validate(integrations, config)
 
     if config.specific_integrations:
         return
 
+    brands = Brand.load_dir(pathlib.Path(config.root / "homeassistant/brands"), config)
+    validate_brands(brands, integrations, config)
+
     with open(str(config_flow_path)) as fp:
         if fp.read() != content:
             config.add_error(
@@ -103,11 +200,25 @@ def validate(integrations: dict[str, Integration], config: Config):
                 "Run python3 -m script.hassfest",
                 fixable=True,
             )
-        return
+
+    config.cache["integrations"] = content = _generate_integrations(
+        brands, integrations, config
+    )
+    with open(str(integrations_path)) as fp:
+        if fp.read() != content + "\n":
+            config.add_error(
+                "config_flow",
+                "File integrations.json is not up to date. "
+                "Run python3 -m script.hassfest",
+                fixable=True,
+            )
 
 
 def generate(integrations: dict[str, Integration], config: Config):
     """Generate config flow file."""
     config_flow_path = config.root / "homeassistant/generated/config_flows.py"
+    integrations_path = config.root / "homeassistant/generated/integrations.json"
     with open(str(config_flow_path), "w") as fp:
         fp.write(f"{config.cache['config_flow']}")
+    with open(str(integrations_path), "w") as fp:
+        fp.write(f"{config.cache['integrations']}\n")
diff --git a/script/hassfest/model.py b/script/hassfest/model.py
index ab0de56077d..6a7ab64c14f 100644
--- a/script/hassfest/model.py
+++ b/script/hassfest/model.py
@@ -39,6 +39,62 @@ class Config:
         self.errors.append(Error(*args, **kwargs))
 
 
+@attr.s
+class Brand:
+    """Represent a brand in our validator."""
+
+    @classmethod
+    def load_dir(cls, path: pathlib.Path, config: Config):
+        """Load all brands in a directory."""
+        assert path.is_dir()
+        brands = {}
+        for fil in path.iterdir():
+            brand = cls(fil)
+            brand.load_brand(config)
+            brands[brand.domain] = brand
+
+        return brands
+
+    path: pathlib.Path = attr.ib()
+    brand: dict[str, Any] | None = attr.ib(default=None)
+
+    @property
+    def domain(self) -> str:
+        """Integration domain."""
+        return self.path.stem
+
+    @property
+    def name(self) -> str | None:
+        """Return name of the integration."""
+        return self.brand.get("name")
+
+    @property
+    def integrations(self) -> list[str]:
+        """Return the sub integrations of this brand."""
+        return self.brand.get("integrations")
+
+    @property
+    def iot_standards(self) -> list[str]:
+        """Return list of supported IoT standards."""
+        return self.brand.get("iot_standards", [])
+
+    def load_brand(self, config: Config) -> None:
+        """Load brand file."""
+        if not self.path.is_file():
+            config.add_error("model", f"Brand file {self.path} not found")
+            return
+
+        try:
+            brand = json.loads(self.path.read_text())
+        except ValueError as err:
+            config.add_error(
+                "model", f"Brand file {self.path.name} contains invalid JSON: {err}"
+            )
+            return
+
+        self.brand = brand
+
+
 @attr.s
 class Integration:
     """Represent an integration in our validator."""
@@ -71,6 +127,7 @@ class Integration:
     manifest: dict[str, Any] | None = attr.ib(default=None)
     errors: list[Error] = attr.ib(factory=list)
     warnings: list[Error] = attr.ib(factory=list)
+    translated_name: bool = attr.ib(default=False)
 
     @property
     def domain(self) -> str:
@@ -122,6 +179,11 @@ class Integration:
         """Get integration_type."""
         return self.manifest.get("integration_type", "integration")
 
+    @property
+    def iot_class(self) -> str | None:
+        """Return the integration IoT Class."""
+        return self.manifest.get("iot_class")
+
     def add_error(self, *args: Any, **kwargs: Any) -> None:
         """Add an error."""
         self.errors.append(Error(*args, **kwargs))
diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py
index 9c4f75f1b2d..ab2961e0506 100644
--- a/script/hassfest/translations.py
+++ b/script/hassfest/translations.py
@@ -312,7 +312,9 @@ def gen_platform_strings_schema(config: Config, integration: Integration):
 ONBOARDING_SCHEMA = vol.Schema({vol.Required("area"): {str: cv.string_with_no_html}})
 
 
-def validate_translation_file(config: Config, integration: Integration, all_strings):
+def validate_translation_file(  # noqa: C901
+    config: Config, integration: Integration, all_strings
+):
     """Validate translation files for integration."""
     if config.specific_integrations:
         check_translations_directory_name(integration)
@@ -363,14 +365,16 @@ def validate_translation_file(config: Config, integration: Integration, all_stri
             if strings_file.name == "strings.json":
                 find_references(strings, name, references)
 
-                if strings.get(
-                    "title"
-                ) == integration.name and not allow_name_translation(integration):
-                    integration.add_error(
-                        "translations",
-                        "Don't specify title in translation strings if it's a brand name "
-                        "or add exception to ALLOW_NAME_TRANSLATION",
-                    )
+                if (title := strings.get("title")) is not None:
+                    integration.translated_name = True
+                    if title == integration.name and not allow_name_translation(
+                        integration
+                    ):
+                        integration.add_error(
+                            "translations",
+                            "Don't specify title in translation strings if it's a brand "
+                            "name or add exception to ALLOW_NAME_TRANSLATION",
+                        )
 
     platform_string_schema = gen_platform_strings_schema(config, integration)
     platform_strings = [integration.path.glob("strings.*.json")]
diff --git a/tests/components/websocket_api/test_commands.py b/tests/components/websocket_api/test_commands.py
index a5905a809a9..27ae21db6e2 100644
--- a/tests/components/websocket_api/test_commands.py
+++ b/tests/components/websocket_api/test_commands.py
@@ -2014,3 +2014,20 @@ async def test_client_message_coalescing(hass, websocket_client, hass_admin_user
     hass.states.async_set("light.permitted", "on", {"color": "blue"})
     await websocket_client.close()
     await hass.async_block_till_done()
+
+
+async def test_integration_descriptions(hass, hass_ws_client):
+    """Test we can get integration descriptions."""
+    assert await async_setup_component(hass, "config", {})
+    ws_client = await hass_ws_client(hass)
+
+    await ws_client.send_json(
+        {
+            "id": 1,
+            "type": "integration/descriptions",
+        }
+    )
+    response = await ws_client.receive_json()
+
+    assert response["success"]
+    assert response["result"]
-- 
GitLab