about summary refs log tree commit diff
path: root/pkgs/servers/home-assistant
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/servers/home-assistant')
-rw-r--r--pkgs/servers/home-assistant/component-packages.nix683
-rw-r--r--pkgs/servers/home-assistant/default.nix857
-rw-r--r--pkgs/servers/home-assistant/frontend.nix10
-rwxr-xr-xpkgs/servers/home-assistant/parse-requirements.py22
-rw-r--r--pkgs/servers/home-assistant/patches/tests-ignore-OSErrors-in-hass-fixture.patch27
-rw-r--r--pkgs/servers/home-assistant/patches/tests-mock-source-ip.patch4
-rw-r--r--pkgs/servers/home-assistant/tests.nix67
7 files changed, 814 insertions, 856 deletions
diff --git a/pkgs/servers/home-assistant/component-packages.nix b/pkgs/servers/home-assistant/component-packages.nix
index 454b54886a88d..714087eb2e602 100644
--- a/pkgs/servers/home-assistant/component-packages.nix
+++ b/pkgs/servers/home-assistant/component-packages.nix
@@ -2,14 +2,14 @@
 # Do not edit!
 
 {
-  version = "2021.12.7";
+  version = "2022.2.9";
   components = {
     "abode" = ps: with ps; [ abodepy ];
     "accuweather" = ps: with ps; [ accuweather ];
     "acer_projector" = ps: with ps; [ pyserial ];
     "acmeda" = ps: with ps; [ aiopulse ];
     "actiontec" = ps: with ps; [ ];
-    "adax" = ps: with ps; [ adax ];
+    "adax" = ps: with ps; [ adax-local adax ];
     "adguard" = ps: with ps; [ adguardhome ];
     "ads" = ps: with ps; [ pyads ];
     "advantage_air" = ps: with ps; [ advantage-air ];
@@ -45,27 +45,28 @@
     "apcupsd" = ps: with ps; [ apcaccess ];
     "api" = ps: with ps; [ aiohttp-cors ];
     "apns" = ps: with ps; [ ]; # missing inputs: apns2
-    "apple_tv" = ps: with ps; [ pyatv ];
+    "apple_tv" = ps: with ps; [ aiohttp-cors ifaddr pyatv zeroconf ];
     "apprise" = ps: with ps; [ apprise ];
     "aprs" = ps: with ps; [ aprslib geopy ];
     "aqualogic" = ps: with ps; [ aqualogic ];
     "aquostv" = ps: with ps; [ ]; # missing inputs: sharp_aquos_rc
     "arcam_fmj" = ps: with ps; [ arcam-fmj ];
-    "arduino" = ps: with ps; [ ]; # missing inputs: PyMata
     "arest" = ps: with ps; [ ];
     "arlo" = ps: with ps; [ ha-ffmpeg pyarlo ];
     "arris_tg2492lg" = ps: with ps; [ ]; # missing inputs: arris-tg2492lg
     "aruba" = ps: with ps; [ pexpect ];
     "arwn" = ps: with ps; [ aiohttp-cors paho-mqtt ];
-    "asterisk_cdr" = ps: with ps; [ ]; # missing inputs: asterisk_mbox
-    "asterisk_mbox" = ps: with ps; [ ]; # missing inputs: asterisk_mbox
+    "aseko_pool_live" = ps: with ps; [ aioaseko ];
+    "asterisk_cdr" = ps: with ps; [ asterisk-mbox ];
+    "asterisk_mbox" = ps: with ps; [ asterisk-mbox ];
     "asuswrt" = ps: with ps; [ aioasuswrt ];
     "atag" = ps: with ps; [ pyatag ];
     "aten_pe" = ps: with ps; [ atenpdu ];
     "atome" = ps: with ps; [ pyatome ];
     "august" = ps: with ps; [ yalexs ];
     "aurora" = ps: with ps; [ auroranoaa ];
-    "aurora_abb_powerone" = ps: with ps; [ ]; # missing inputs: aurorapy
+    "aurora_abb_powerone" = ps: with ps; [ aurorapy ];
+    "aussie_broadband" = ps: with ps; [ pyaussiebb ];
     "auth" = ps: with ps; [ aiohttp-cors ];
     "automation" = ps: with ps; [ aiohttp-cors ];
     "avea" = ps: with ps; [ avea ];
@@ -82,7 +83,7 @@
     "bbb_gpio" = ps: with ps; [ ]; # missing inputs: Adafruit_BBIO
     "bbox" = ps: with ps; [ ]; # missing inputs: pybbox
     "beewi_smartclim" = ps: with ps; [ ]; # missing inputs: beewi_smartclim
-    "bh1750" = ps: with ps; [ smbus-cffi ]; # missing inputs: i2csense
+    "bh1750" = ps: with ps; [ i2csense smbus-cffi ];
     "binary_sensor" = ps: with ps; [ ];
     "bitcoin" = ps: with ps; [ blockchain ];
     "bizkaibus" = ps: with ps; [ bizkaibus ];
@@ -97,7 +98,7 @@
     "bluesound" = ps: with ps; [ xmltodict ];
     "bluetooth_le_tracker" = ps: with ps; [ pygatt ];
     "bluetooth_tracker" = ps: with ps; [ bt-proximity pybluez ];
-    "bme280" = ps: with ps; [ smbus-cffi ]; # missing inputs: bme280spi i2csense
+    "bme280" = ps: with ps; [ bme280spi i2csense smbus-cffi ];
     "bme680" = ps: with ps; [ bme680 smbus-cffi ];
     "bmp280" = ps: with ps; [ ]; # missing inputs: RPi.GPIO adafruit-circuitpython-bmp280
     "bmw_connected_drive" = ps: with ps; [ bimmer-connected ];
@@ -121,7 +122,7 @@
     "cast" = ps: with ps; [ pyturbojpeg aiohttp-cors hass-nabucasa ifaddr mutagen plexapi plexauth plexwebsocket PyChromecast zeroconf ];
     "cert_expiry" = ps: with ps; [ ];
     "channels" = ps: with ps; [ pychannels ];
-    "circuit" = ps: with ps; [ ]; # missing inputs: circuit-webhook
+    "circuit" = ps: with ps; [ circuit-webhook ];
     "cisco_ios" = ps: with ps; [ pexpect ];
     "cisco_mobility_express" = ps: with ps; [ ciscomobilityexpress ];
     "cisco_webex_teams" = ps: with ps; [ webexteamssdk ];
@@ -135,7 +136,7 @@
     "cloud" = ps: with ps; [ pyturbojpeg aiohttp-cors hass-nabucasa ];
     "cloudflare" = ps: with ps; [ pycfdns ];
     "cmus" = ps: with ps; [ ]; # missing inputs: pycmus
-    "co2signal" = ps: with ps; [ ]; # missing inputs: co2signal
+    "co2signal" = ps: with ps; [ co2signal ];
     "coinbase" = ps: with ps; [ ]; # missing inputs: coinbase
     "color_extractor" = ps: with ps; [ colorthief ];
     "comed_hourly_pricing" = ps: with ps; [ ];
@@ -181,6 +182,7 @@
     "dexcom" = ps: with ps; [ pydexcom ];
     "dhcp" = ps: with ps; [ aiodiscover scapy ];
     "dht" = ps: with ps; [ ]; # missing inputs: RPi.GPIO adafruit-circuitpython-dht
+    "diagnostics" = ps: with ps; [ aiohttp-cors ];
     "dialogflow" = ps: with ps; [ aiohttp-cors ];
     "digital_ocean" = ps: with ps; [ digital-ocean ];
     "digitalloggers" = ps: with ps; [ ]; # missing inputs: dlipower
@@ -206,7 +208,7 @@
     "dunehd" = ps: with ps; [ pdunehd ];
     "dwd_weather_warnings" = ps: with ps; [ dwdwfsapi ];
     "dweet" = ps: with ps; [ ]; # missing inputs: dweepy
-    "dynalite" = ps: with ps; [ ]; # missing inputs: dynalite_devices
+    "dynalite" = ps: with ps; [ dynalite-devices ];
     "eafm" = ps: with ps; [ aioeafm ];
     "ebox" = ps: with ps; [ ]; # missing inputs: pyebox
     "ebusd" = ps: with ps; [ ]; # missing inputs: ebusdpy
@@ -217,13 +219,13 @@
     "eddystone_temperature" = ps: with ps; [ construct ]; # missing inputs: beacontools[scan]
     "edimax" = ps: with ps; [ pyedimax ];
     "edl21" = ps: with ps; [ pysml ];
-    "ee_brightbox" = ps: with ps; [ eebrightbox ];
     "efergy" = ps: with ps; [ pyefergy ];
     "egardia" = ps: with ps; [ pythonegardia ];
     "eight_sleep" = ps: with ps; [ pyeight ];
     "elgato" = ps: with ps; [ elgato ];
     "eliqonline" = ps: with ps; [ eliqonline ];
     "elkm1" = ps: with ps; [ elkm1-lib ];
+    "elmax" = ps: with ps; [ elmax-api ];
     "elv" = ps: with ps; [ pypca ];
     "emby" = ps: with ps; [ pyemby ];
     "emoncms" = ps: with ps; [ ];
@@ -283,7 +285,7 @@
     "flume" = ps: with ps; [ pyflume ];
     "flunearyou" = ps: with ps; [ pyflunearyou ];
     "flux" = ps: with ps; [ ];
-    "flux_led" = ps: with ps; [ flux-led ];
+    "flux_led" = ps: with ps; [ aiohttp-cors flux-led ifaddr ];
     "folder" = ps: with ps; [ ];
     "folder_watcher" = ps: with ps; [ watchdog ];
     "foobot" = ps: with ps; [ foobot-async ];
@@ -310,7 +312,7 @@
     "generic" = ps: with ps; [ ];
     "generic_hygrostat" = ps: with ps; [ ];
     "generic_thermostat" = ps: with ps; [ sqlalchemy ];
-    "geniushub" = ps: with ps; [ ]; # missing inputs: geniushub-client
+    "geniushub" = ps: with ps; [ geniushub-client ];
     "geo_json_events" = ps: with ps; [ geojson-client ];
     "geo_location" = ps: with ps; [ ];
     "geo_rss_events" = ps: with ps; [ georss-generic-client ];
@@ -326,6 +328,7 @@
     "goalfeed" = ps: with ps; [ ]; # missing inputs: pysher
     "goalzero" = ps: with ps; [ goalzero ];
     "gogogate2" = ps: with ps; [ ismartgate ];
+    "goodwe" = ps: with ps; [ goodwe ];
     "google" = ps: with ps; [ google-api-python-client httplib2 oauth2client ];
     "google_assistant" = ps: with ps; [ pyturbojpeg aiohttp-cors ];
     "google_cloud" = ps: with ps; [ google-cloud-texttospeech ];
@@ -335,7 +338,6 @@
     "google_translate" = ps: with ps; [ gtts ];
     "google_travel_time" = ps: with ps; [ googlemaps ];
     "google_wifi" = ps: with ps; [ ];
-    "gpmdp" = ps: with ps; [ websocket-client ];
     "gpsd" = ps: with ps; [ gps3 ];
     "gpslogger" = ps: with ps; [ aiohttp-cors ];
     "graphite" = ps: with ps; [ ];
@@ -373,13 +375,14 @@
     "homekit_controller" = ps: with ps; [ aiohomekit aiohttp-cors ifaddr zeroconf ];
     "homematic" = ps: with ps; [ pyhomematic ];
     "homematicip_cloud" = ps: with ps; [ homematicip ];
+    "homewizard" = ps: with ps; [ aiohwenergy ];
     "homeworks" = ps: with ps; [ pyhomeworks ];
     "honeywell" = ps: with ps; [ somecomfort ];
     "horizon" = ps: with ps; [ ]; # missing inputs: horimote
     "hp_ilo" = ps: with ps; [ python-hpilo ];
     "html5" = ps: with ps; [ aiohttp-cors pywebpush ];
     "http" = ps: with ps; [ aiohttp-cors ];
-    "htu21d" = ps: with ps; [ smbus-cffi ]; # missing inputs: i2csense
+    "htu21d" = ps: with ps; [ i2csense smbus-cffi ];
     "huawei_lte" = ps: with ps; [ huawei-lte-api stringcase url-normalize ];
     "hue" = ps: with ps; [ aiohue ];
     "huisbaasje" = ps: with ps; [ huisbaasje-client ];
@@ -404,12 +407,14 @@
     "incomfort" = ps: with ps; [ incomfort-client ];
     "influxdb" = ps: with ps; [ influxdb-client influxdb ];
     "input_boolean" = ps: with ps; [ ];
+    "input_button" = ps: with ps; [ ];
     "input_datetime" = ps: with ps; [ ];
     "input_number" = ps: with ps; [ ];
     "input_select" = ps: with ps; [ ];
     "input_text" = ps: with ps; [ ];
     "insteon" = ps: with ps; [ pyinsteon ];
     "integration" = ps: with ps; [ ];
+    "intellifire" = ps: with ps; [ intellifire4py ];
     "intent" = ps: with ps; [ aiohttp-cors ];
     "intent_script" = ps: with ps; [ ];
     "intesishome" = ps: with ps; [ pyintesishome ];
@@ -454,12 +459,12 @@
     "launch_library" = ps: with ps; [ pylaunches ];
     "lcn" = ps: with ps; [ pypck ];
     "lg_netcast" = ps: with ps; [ pylgnetcast ];
-    "lg_soundbar" = ps: with ps; [ ]; # missing inputs: temescal
+    "lg_soundbar" = ps: with ps; [ temescal ];
     "life360" = ps: with ps; [ life360 ];
     "lifx" = ps: with ps; [ aiolifx aiolifx-effects ];
     "lifx_cloud" = ps: with ps; [ ];
     "light" = ps: with ps; [ ];
-    "lightwave" = ps: with ps; [ ]; # missing inputs: lightwave
+    "lightwave" = ps: with ps; [ lightwave ];
     "limitlessled" = ps: with ps; [ limitlessled ];
     "linksys_smart" = ps: with ps; [ ];
     "linode" = ps: with ps; [ linode-api ];
@@ -484,7 +489,7 @@
     "luftdaten" = ps: with ps; [ luftdaten ];
     "lupusec" = ps: with ps; [ lupupy ];
     "lutron" = ps: with ps; [ pylutron ];
-    "lutron_caseta" = ps: with ps; [ aiolip pylutron-caseta ];
+    "lutron_caseta" = ps: with ps; [ pylutron-caseta ];
     "lw12wifi" = ps: with ps; [ ]; # missing inputs: lw12
     "lyric" = ps: with ps; [ aiohttp-cors aiolyric ];
     "magicseaweed" = ps: with ps; [ ]; # missing inputs: magicseaweed
@@ -503,7 +508,7 @@
     "media_player" = ps: with ps; [ aiohttp-cors ];
     "media_source" = ps: with ps; [ aiohttp-cors ];
     "mediaroom" = ps: with ps; [ pymediaroom ];
-    "melcloud" = ps: with ps; [ ]; # missing inputs: pymelcloud
+    "melcloud" = ps: with ps; [ pymelcloud ];
     "melissa" = ps: with ps; [ ]; # missing inputs: py-melissa-climate
     "meraki" = ps: with ps; [ aiohttp-cors ];
     "message_bird" = ps: with ps; [ ]; # missing inputs: messagebird
@@ -564,7 +569,7 @@
     "nest" = ps: with ps; [ aiohttp-cors google-nest-sdm ha-ffmpeg python-nest ];
     "netatmo" = ps: with ps; [ pyturbojpeg aiohttp-cors hass-nabucasa pyatmo ];
     "netdata" = ps: with ps; [ netdata ];
-    "netgear" = ps: with ps; [ ]; # missing inputs: pynetgear
+    "netgear" = ps: with ps; [ pynetgear ];
     "netgear_lte" = ps: with ps; [ eternalegypt ];
     "netio" = ps: with ps; [ aiohttp-cors ]; # missing inputs: pynetio
     "network" = ps: with ps; [ aiohttp-cors ifaddr ];
@@ -576,6 +581,7 @@
     "nightscout" = ps: with ps; [ py-nightscout ];
     "niko_home_control" = ps: with ps; [ niko-home-control ];
     "nilu" = ps: with ps; [ niluclient ];
+    "nina" = ps: with ps; [ pynina ];
     "nissan_leaf" = ps: with ps; [ pycarwings2 ];
     "nmap_tracker" = ps: with ps; [ aiohttp-cors getmac ifaddr netmap ]; # missing inputs: mac-vendor-lookup
     "nmbs" = ps: with ps; [ ]; # missing inputs: pyrail
@@ -591,7 +597,7 @@
     "nuki" = ps: with ps; [ pynuki ];
     "numato" = ps: with ps; [ ]; # missing inputs: numato-gpio
     "number" = ps: with ps; [ ];
-    "nut" = ps: with ps; [ ]; # missing inputs: pynut2
+    "nut" = ps: with ps; [ pynut2 ];
     "nws" = ps: with ps; [ pynws ];
     "nx584" = ps: with ps; [ pynx584 ];
     "nzbget" = ps: with ps; [ ]; # missing inputs: pynzbgetapi
@@ -603,15 +609,17 @@
     "ombi" = ps: with ps; [ pyombi ];
     "omnilogic" = ps: with ps; [ omnilogic ];
     "onboarding" = ps: with ps; [ aiohttp-cors home-assistant-frontend pillow sqlalchemy ];
+    "oncue" = ps: with ps; [ aiooncue ];
     "ondilo_ico" = ps: with ps; [ aiohttp-cors ondilo ];
-    "onewire" = ps: with ps; [ ]; # missing inputs: pi1wire pyownet
+    "onewire" = ps: with ps; [ pi1wire pyownet ];
     "onkyo" = ps: with ps; [ onkyo-eiscp ];
-    "onvif" = ps: with ps; [ ha-ffmpeg ]; # missing inputs: WSDiscovery onvif-zeep-async
+    "onvif" = ps: with ps; [ wsdiscovery ha-ffmpeg onvif-zeep-async ];
+    "open_meteo" = ps: with ps; [ open-meteo ];
     "openalpr_cloud" = ps: with ps; [ ];
     "openalpr_local" = ps: with ps; [ ];
     "opencv" = ps: with ps; [ numpy ]; # missing inputs: opencv-python-headless
     "openerz" = ps: with ps; [ openerz-api ];
-    "openevse" = ps: with ps; [ ]; # missing inputs: openevsewifi
+    "openevse" = ps: with ps; [ openevsewifi ];
     "openexchangerates" = ps: with ps; [ ];
     "opengarage" = ps: with ps; [ open-garage ];
     "openhardwaremonitor" = ps: with ps; [ ];
@@ -628,6 +636,7 @@
     "orvibo" = ps: with ps; [ orvibo ];
     "osramlightify" = ps: with ps; [ ]; # missing inputs: lightify
     "otp" = ps: with ps; [ pyotp ];
+    "overkiz" = ps: with ps; [ pyoverkiz ];
     "ovo_energy" = ps: with ps; [ ovoenergy ];
     "owntracks" = ps: with ps; [ pynacl pyturbojpeg aiohttp-cors hass-nabucasa paho-mqtt ];
     "ozw" = ps: with ps; [ aiohttp-cors paho-mqtt python-openzwave-mqtt ];
@@ -675,7 +684,7 @@
     "pushbullet" = ps: with ps; [ pushbullet ];
     "pushover" = ps: with ps; [ pushover-complete ];
     "pushsafer" = ps: with ps; [ ];
-    "pvoutput" = ps: with ps; [ jsonpath xmltodict ];
+    "pvoutput" = ps: with ps; [ pvo ];
     "pvpc_hourly_pricing" = ps: with ps; [ aiopvpc ];
     "pyload" = ps: with ps; [ ];
     "python_script" = ps: with ps; [ restrictedpython ];
@@ -684,7 +693,7 @@
     "qnap" = ps: with ps; [ ]; # missing inputs: qnapstats
     "qrcode" = ps: with ps; [ pillow ]; # missing inputs: pyzbar
     "quantum_gateway" = ps: with ps; [ quantum-gateway ];
-    "qvr_pro" = ps: with ps; [ ]; # missing inputs: pyqvrpro
+    "qvr_pro" = ps: with ps; [ pyqvrpro ];
     "qwikswitch" = ps: with ps; [ ]; # missing inputs: pyqwikswitch
     "rachio" = ps: with ps; [ pyturbojpeg aiohttp-cors hass-nabucasa rachiopy ];
     "radarr" = ps: with ps; [ ];
@@ -727,10 +736,11 @@
     "rpi_gpio" = ps: with ps; [ ]; # missing inputs: RPi.GPIO
     "rpi_gpio_pwm" = ps: with ps; [ ]; # missing inputs: pwmled
     "rpi_pfio" = ps: with ps; [ ]; # missing inputs: pifacecommon pifacedigitalio
-    "rpi_power" = ps: with ps; [ ]; # missing inputs: rpi-bad-power
+    "rpi_power" = ps: with ps; [ rpi-bad-power ];
     "rpi_rf" = ps: with ps; [ ]; # missing inputs: RPi.GPIO rpi-rf
     "rss_feed_template" = ps: with ps; [ aiohttp-cors ];
     "rtorrent" = ps: with ps; [ ];
+    "rtsp_to_webrtc" = ps: with ps; [ pyturbojpeg aiohttp-cors rtsp-to-webrtc ];
     "ruckus_unleashed" = ps: with ps; [ pyruckus ];
     "russound_rio" = ps: with ps; [ ]; # missing inputs: russound_rio
     "russound_rnet" = ps: with ps; [ ]; # missing inputs: russound
@@ -751,6 +761,7 @@
     "sendgrid" = ps: with ps; [ sendgrid ];
     "sense" = ps: with ps; [ sense-energy ];
     "sensehat" = ps: with ps; [ ]; # missing inputs: sense-hat
+    "senseme" = ps: with ps; [ aiosenseme ];
     "sensibo" = ps: with ps; [ ]; # missing inputs: pysensibo
     "sensor" = ps: with ps; [ sqlalchemy ];
     "sentry" = ps: with ps; [ sentry-sdk ];
@@ -805,7 +816,7 @@
     "somfy_mylink" = ps: with ps; [ somfy-mylink-synergy ];
     "sonarr" = ps: with ps; [ sonarr ];
     "songpal" = ps: with ps; [ python-songpal ];
-    "sonos" = ps: with ps; [ aiohttp-cors async-upnp-client ifaddr plexapi plexauth plexwebsocket soco zeroconf ];
+    "sonos" = ps: with ps; [ aiohttp-cors async-upnp-client ifaddr plexapi plexauth plexwebsocket soco spotipy zeroconf ];
     "sony_projector" = ps: with ps; [ pysdcp ];
     "soundtouch" = ps: with ps; [ aiohttp-cors ifaddr libsoundtouch zeroconf ];
     "spaceapi" = ps: with ps; [ aiohttp-cors ];
@@ -824,9 +835,10 @@
     "statistics" = ps: with ps; [ sqlalchemy ];
     "statsd" = ps: with ps; [ statsd ];
     "steam_online" = ps: with ps; [ ]; # missing inputs: steamodd
+    "steamist" = ps: with ps; [ aiohttp-cors aiosteamist discovery30303 ifaddr ];
     "stiebel_eltron" = ps: with ps; [ pymodbus ]; # missing inputs: pystiebeleltron
     "stookalert" = ps: with ps; [ ]; # missing inputs: stookalert
-    "stream" = ps: with ps; [ aiohttp-cors ha-av ];
+    "stream" = ps: with ps; [ pyturbojpeg aiohttp-cors av ];
     "streamlabswater" = ps: with ps; [ streamlabswater ];
     "stt" = ps: with ps; [ aiohttp-cors ];
     "subaru" = ps: with ps; [ subarulink ];
@@ -850,11 +862,10 @@
     "syslog" = ps: with ps; [ ];
     "system_bridge" = ps: with ps; [ aiohttp-cors ifaddr systembridge zeroconf ];
     "system_health" = ps: with ps; [ aiohttp-cors ];
-    "system_log" = ps: with ps; [ aiohttp-cors ];
+    "system_log" = ps: with ps; [ ];
     "systemmonitor" = ps: with ps; [ psutil ];
     "tado" = ps: with ps; [ python-tado ];
     "tag" = ps: with ps; [ ];
-    "tahoma" = ps: with ps; [ tahoma-api ];
     "tailscale" = ps: with ps; [ tailscale ];
     "tank_utility" = ps: with ps; [ ]; # missing inputs: tank_utility
     "tankerkoenig" = ps: with ps; [ pytankerkoenig ];
@@ -879,7 +890,7 @@
     "thinkingcleaner" = ps: with ps; [ ]; # missing inputs: pythinkingcleaner
     "thomson" = ps: with ps; [ ];
     "threshold" = ps: with ps; [ ];
-    "tibber" = ps: with ps; [ pytibber ];
+    "tibber" = ps: with ps; [ pytibber sqlalchemy ];
     "tikteck" = ps: with ps; [ ]; # missing inputs: tikteck
     "tile" = ps: with ps; [ pytile ];
     "time_date" = ps: with ps; [ ];
@@ -912,7 +923,7 @@
     "twilio" = ps: with ps; [ aiohttp-cors twilio ];
     "twilio_call" = ps: with ps; [ aiohttp-cors twilio ];
     "twilio_sms" = ps: with ps; [ aiohttp-cors twilio ];
-    "twinkly" = ps: with ps; [ twinkly-client ];
+    "twinkly" = ps: with ps; [ ttls ];
     "twitch" = ps: with ps; [ python-twitch-client ];
     "twitter" = ps: with ps; [ twitterapi ];
     "ubus" = ps: with ps; [ openwrt-ubus-rpc ];
@@ -921,6 +932,7 @@
     "unifi" = ps: with ps; [ aiounifi ];
     "unifi_direct" = ps: with ps; [ pexpect ];
     "unifiled" = ps: with ps; [ unifiled ];
+    "unifiprotect" = ps: with ps; [ aiohttp-cors ]; # missing inputs: pyunifiprotect unifi-discovery
     "universal" = ps: with ps; [ ];
     "upb" = ps: with ps; [ upb-lib ];
     "upc_connect" = ps: with ps; [ connect-box ];
@@ -937,7 +949,7 @@
     "vacuum" = ps: with ps; [ ];
     "vallox" = ps: with ps; [ ]; # missing inputs: vallox-websocket-api
     "vasttrafik" = ps: with ps; [ ]; # missing inputs: vtjp
-    "velbus" = ps: with ps; [ velbus-aio ];
+    "velbus" = ps: with ps; [ aiohttp-cors pyserial pyudev velbus-aio ];
     "velux" = ps: with ps; [ pyvlx ];
     "venstar" = ps: with ps; [ venstarcolortouch ];
     "vera" = ps: with ps; [ pyvera ];
@@ -969,11 +981,11 @@
     "waze_travel_time" = ps: with ps; [ wazeroutecalculator ];
     "weather" = ps: with ps; [ ];
     "webhook" = ps: with ps; [ aiohttp-cors ];
-    "webostv" = ps: with ps; [ aiopylgtv ];
+    "webostv" = ps: with ps; [ aiowebostv sqlalchemy ];
     "websocket_api" = ps: with ps; [ aiohttp-cors ];
     "wemo" = ps: with ps; [ pywemo ];
     "whirlpool" = ps: with ps; [ whirlpool-sixth-sense ];
-    "whois" = ps: with ps; [ python-whois ];
+    "whois" = ps: with ps; [ whois ];
     "wiffi" = ps: with ps; [ wiffi ];
     "wilight" = ps: with ps; [ pywilight ];
     "wirelesstag" = ps: with ps; [ ]; # missing inputs: wirelesstagpy
@@ -1020,4 +1032,599 @@
     "zwave" = ps: with ps; [ homeassistant-pyozw pydispatcher ];
     "zwave_js" = ps: with ps; [ aiohttp-cors pyserial pyudev zwave-js-server-python ];
   };
+  # components listed in tests/components for which all dependencies are packaged
+  supportedComponentsWithTests = [
+    "abode"
+    "accuweather"
+    "acmeda"
+    "adax"
+    "adguard"
+    "advantage_air"
+    "aemet"
+    "agent_dvr"
+    "air_quality"
+    "airly"
+    "airnow"
+    "airthings"
+    "airtouch4"
+    "airvisual"
+    "alarm_control_panel"
+    "alarmdecoder"
+    "alert"
+    "alexa"
+    "almond"
+    "ambee"
+    "amberelectric"
+    "ambiclimate"
+    "ambient_station"
+    "analytics"
+    "androidtv"
+    "apache_kafka"
+    "api"
+    "apple_tv"
+    "apprise"
+    "aprs"
+    "arcam_fmj"
+    "arlo"
+    "aseko_pool_live"
+    "asuswrt"
+    "atag"
+    "august"
+    "aurora"
+    "aurora_abb_powerone"
+    "aussie_broadband"
+    "auth"
+    "automation"
+    "awair"
+    "aws"
+    "axis"
+    "azure_devops"
+    "azure_event_hub"
+    "balboa"
+    "bayesian"
+    "binary_sensor"
+    "blackbird"
+    "blebox"
+    "blink"
+    "blueprint"
+    "bluetooth_le_tracker"
+    "bmw_connected_drive"
+    "bond"
+    "bosch_shc"
+    "braviatv"
+    "broadlink"
+    "brother"
+    "bsblan"
+    "buienradar"
+    "button"
+    "caldav"
+    "calendar"
+    "camera"
+    "canary"
+    "cast"
+    "cert_expiry"
+    "climacell"
+    "climate"
+    "cloud"
+    "cloudflare"
+    "co2signal"
+    "color_extractor"
+    "comfoconnect"
+    "command_line"
+    "compensation"
+    "config"
+    "configurator"
+    "control4"
+    "conversation"
+    "coolmaster"
+    "coronavirus"
+    "counter"
+    "cover"
+    "cpuspeed"
+    "crownstone"
+    "daikin"
+    "darksky"
+    "datadog"
+    "debugpy"
+    "deconz"
+    "default_config"
+    "demo"
+    "denonavr"
+    "derivative"
+    "device_automation"
+    "device_sun_light_trigger"
+    "device_tracker"
+    "devolo_home_control"
+    "devolo_home_network"
+    "dexcom"
+    "dhcp"
+    "diagnostics"
+    "dialogflow"
+    "directv"
+    "discovery"
+    "dlna_dmr"
+    "dnsip"
+    "doorbird"
+    "dsmr"
+    "dte_energy_bridge"
+    "duckdns"
+    "dunehd"
+    "dynalite"
+    "eafm"
+    "ecobee"
+    "econet"
+    "efergy"
+    "elgato"
+    "elkm1"
+    "elmax"
+    "emonitor"
+    "emulated_hue"
+    "emulated_kasa"
+    "emulated_roku"
+    "energy"
+    "enocean"
+    "enphase_envoy"
+    "environment_canada"
+    "epson"
+    "esphome"
+    "everlights"
+    "evil_genius_labs"
+    "ezviz"
+    "faa_delays"
+    "facebook"
+    "facebox"
+    "fail2ban"
+    "fan"
+    "feedreader"
+    "ffmpeg"
+    "fido"
+    "file"
+    "filesize"
+    "filter"
+    "fireservicerota"
+    "firmata"
+    "fjaraskupan"
+    "flic"
+    "flick_electric"
+    "flipr"
+    "flo"
+    "flume"
+    "flunearyou"
+    "flux"
+    "flux_led"
+    "folder"
+    "folder_watcher"
+    "foobot"
+    "forecast_solar"
+    "foscam"
+    "freebox"
+    "freedns"
+    "freedompro"
+    "fritz"
+    "fritzbox"
+    "fritzbox_callmonitor"
+    "fronius"
+    "frontend"
+    "garages_amsterdam"
+    "gdacs"
+    "generic"
+    "generic_hygrostat"
+    "generic_thermostat"
+    "geo_json_events"
+    "geo_location"
+    "geo_rss_events"
+    "geofency"
+    "geonetnz_quakes"
+    "geonetnz_volcano"
+    "gios"
+    "github"
+    "glances"
+    "goalzero"
+    "gogogate2"
+    "goodwe"
+    "google"
+    "google_assistant"
+    "google_domains"
+    "google_pubsub"
+    "google_translate"
+    "google_travel_time"
+    "google_wifi"
+    "gpslogger"
+    "graphite"
+    "gree"
+    "group"
+    "growatt_server"
+    "guardian"
+    "habitica"
+    "hangouts"
+    "harmony"
+    "hassio"
+    "hddtemp"
+    "heos"
+    "here_travel_time"
+    "hisense_aehw4a1"
+    "history"
+    "history_stats"
+    "hive"
+    "hlk_sw16"
+    "home_connect"
+    "home_plus_control"
+    "homeassistant"
+    "homekit"
+    "homekit_controller"
+    "homematic"
+    "homematicip_cloud"
+    "homewizard"
+    "honeywell"
+    "html5"
+    "http"
+    "huawei_lte"
+    "hue"
+    "huisbaasje"
+    "humidifier"
+    "hunterdouglas_powerview"
+    "hvv_departures"
+    "hyperion"
+    "ialarm"
+    "iaqualink"
+    "icloud"
+    "ifttt"
+    "ign_sismologia"
+    "image"
+    "image_processing"
+    "imap_email_content"
+    "influxdb"
+    "input_boolean"
+    "input_button"
+    "input_datetime"
+    "input_number"
+    "input_select"
+    "input_text"
+    "insteon"
+    "integration"
+    "intellifire"
+    "intent"
+    "intent_script"
+    "ios"
+    "iotawatt"
+    "ipma"
+    "ipp"
+    "iqvia"
+    "islamic_prayer_times"
+    "isy994"
+    "izone"
+    "jellyfin"
+    "jewish_calendar"
+    "juicenet"
+    "keenetic_ndms2"
+    "kira"
+    "kmtronic"
+    "knx"
+    "kodi"
+    "konnected"
+    "kraken"
+    "kulersky"
+    "lastfm"
+    "launch_library"
+    "lcn"
+    "light"
+    "litterrobot"
+    "local_file"
+    "local_ip"
+    "locative"
+    "lock"
+    "logbook"
+    "logentries"
+    "logger"
+    "london_air"
+    "lookin"
+    "lovelace"
+    "luftdaten"
+    "lutron_caseta"
+    "lyric"
+    "mailbox"
+    "manual"
+    "manual_mqtt"
+    "maxcube"
+    "mazda"
+    "media_player"
+    "media_source"
+    "melcloud"
+    "meraki"
+    "met"
+    "met_eireann"
+    "meteoclimatic"
+    "mhz19"
+    "microsoft_face"
+    "microsoft_face_detect"
+    "microsoft_face_identify"
+    "mikrotik"
+    "mill"
+    "min_max"
+    "minecraft_server"
+    "minio"
+    "mobile_app"
+    "modbus"
+    "modem_callerid"
+    "modern_forms"
+    "mold_indicator"
+    "moon"
+    "motion_blinds"
+    "motioneye"
+    "mqtt"
+    "mqtt_eventstream"
+    "mqtt_json"
+    "mqtt_room"
+    "mqtt_statestream"
+    "mullvad"
+    "mutesync"
+    "my"
+    "myq"
+    "mysensors"
+    "mythicbeastsdns"
+    "nam"
+    "namecheapdns"
+    "nanoleaf"
+    "neato"
+    "ness_alarm"
+    "nest"
+    "netatmo"
+    "netgear"
+    "network"
+    "nexia"
+    "nightscout"
+    "nina"
+    "no_ip"
+    "notify"
+    "notion"
+    "nsw_rural_fire_service_feed"
+    "nuki"
+    "number"
+    "nut"
+    "nws"
+    "nx584"
+    "octoprint"
+    "omnilogic"
+    "onboarding"
+    "oncue"
+    "ondilo_ico"
+    "onewire"
+    "onvif"
+    "open_meteo"
+    "openalpr_cloud"
+    "openalpr_local"
+    "openerz"
+    "opengarage"
+    "openhardwaremonitor"
+    "opentherm_gw"
+    "openuv"
+    "openweathermap"
+    "opnsense"
+    "overkiz"
+    "ovo_energy"
+    "owntracks"
+    "ozw"
+    "p1_monitor"
+    "panel_custom"
+    "panel_iframe"
+    "persistent_notification"
+    "person"
+    "philips_js"
+    "pi_hole"
+    "picnic"
+    "ping"
+    "plaato"
+    "plant"
+    "plex"
+    "plugwise"
+    "point"
+    "poolsense"
+    "profiler"
+    "prometheus"
+    "prosegur"
+    "proximity"
+    "push"
+    "pushbullet"
+    "pvoutput"
+    "pvpc_hourly_pricing"
+    "python_script"
+    "qld_bushfire"
+    "rachio"
+    "radarr"
+    "rainforest_eagle"
+    "rainmachine"
+    "random"
+    "rdw"
+    "recollect_waste"
+    "recorder"
+    "reddit"
+    "remote"
+    "renault"
+    "rest"
+    "rest_command"
+    "rflink"
+    "rfxtrx"
+    "ridwell"
+    "ring"
+    "risco"
+    "rituals_perfume_genie"
+    "rmvtransport"
+    "roku"
+    "roomba"
+    "roon"
+    "rpi_power"
+    "rss_feed_template"
+    "rtsp_to_webrtc"
+    "ruckus_unleashed"
+    "safe_mode"
+    "samsungtv"
+    "scene"
+    "screenlogic"
+    "script"
+    "search"
+    "season"
+    "select"
+    "sense"
+    "senseme"
+    "sensor"
+    "sentry"
+    "seventeentrack"
+    "sharkiq"
+    "shell_command"
+    "shelly"
+    "shopping_list"
+    "sia"
+    "sigfox"
+    "sighthound"
+    "simplisafe"
+    "simulated"
+    "siren"
+    "slack"
+    "sleepiq"
+    "sma"
+    "smappee"
+    "smart_meter_texas"
+    "smarthab"
+    "smartthings"
+    "smarttub"
+    "smhi"
+    "smtp"
+    "snips"
+    "solaredge"
+    "solarlog"
+    "solax"
+    "soma"
+    "somfy"
+    "somfy_mylink"
+    "sonarr"
+    "songpal"
+    "sonos"
+    "soundtouch"
+    "spaceapi"
+    "spc"
+    "speedtestdotnet"
+    "spider"
+    "spotify"
+    "sql"
+    "squeezebox"
+    "srp_energy"
+    "ssdp"
+    "starline"
+    "startca"
+    "statistics"
+    "statsd"
+    "steamist"
+    "stream"
+    "stt"
+    "subaru"
+    "sun"
+    "surepetcare"
+    "switch"
+    "switchbot"
+    "switcher_kis"
+    "syncthing"
+    "syncthru"
+    "synology_dsm"
+    "system_bridge"
+    "system_health"
+    "system_log"
+    "tado"
+    "tag"
+    "tailscale"
+    "tasmota"
+    "tcp"
+    "telegram"
+    "tellduslive"
+    "template"
+    "tesla_wall_connector"
+    "threshold"
+    "tibber"
+    "tile"
+    "time_date"
+    "timer"
+    "tod"
+    "tolo"
+    "tomato"
+    "toon"
+    "totalconnect"
+    "tplink"
+    "traccar"
+    "trace"
+    "tractive"
+    "tradfri"
+    "trafikverket_weatherstation"
+    "transmission"
+    "transport_nsw"
+    "trend"
+    "tts"
+    "tuya"
+    "twentemilieu"
+    "twilio"
+    "twinkly"
+    "twitch"
+    "uk_transport"
+    "unifi"
+    "unifi_direct"
+    "universal"
+    "upb"
+    "upcloud"
+    "updater"
+    "upnp"
+    "uptime"
+    "uptimerobot"
+    "usb"
+    "usgs_earthquakes_feed"
+    "utility_meter"
+    "uvc"
+    "vacuum"
+    "velbus"
+    "venstar"
+    "vera"
+    "verisure"
+    "version"
+    "vesync"
+    "vicare"
+    "vilfo"
+    "vizio"
+    "vlc_telnet"
+    "voicerss"
+    "volumio"
+    "vultr"
+    "wake_on_lan"
+    "wallbox"
+    "water_heater"
+    "watttime"
+    "waze_travel_time"
+    "weather"
+    "webhook"
+    "webostv"
+    "websocket_api"
+    "wemo"
+    "whirlpool"
+    "whois"
+    "wiffi"
+    "wilight"
+    "wled"
+    "workday"
+    "worldclock"
+    "wsdot"
+    "xbox"
+    "xiaomi"
+    "xiaomi_aqara"
+    "xiaomi_miio"
+    "yale_smart_alarm"
+    "yamaha"
+    "yamaha_musiccast"
+    "yandex_transport"
+    "yandextts"
+    "yeelight"
+    "youless"
+    "zeroconf"
+    "zerproc"
+    "zha"
+    "zodiac"
+    "zone"
+    "zwave"
+    "zwave_js"
+  ];
 }
diff --git a/pkgs/servers/home-assistant/default.nix b/pkgs/servers/home-assistant/default.nix
index b0d2063742391..920343de67b63 100644
--- a/pkgs/servers/home-assistant/default.nix
+++ b/pkgs/servers/home-assistant/default.nix
@@ -1,5 +1,6 @@
 { stdenv
 , lib
+, callPackage
 , fetchFromGitHub
 , fetchpatch
 , python3
@@ -14,6 +15,9 @@
 # Additional packages to add to propagatedBuildInputs
 , extraPackages ? ps: []
 
+# Write out info about included extraComponents and extraPackages
+, writeText
+
 # Override Python packages using
 # self: super: { pkg = super.pkg.overridePythonAttrs (oldAttrs: { ... }); }
 # Applied after defaultOverrides
@@ -24,104 +28,10 @@
 
 let
   defaultOverrides = [
-    # aiounify 29 breaks integration tests
-    (self: super: {
-      aiounifi = super.aiounifi.overridePythonAttrs (oldAttrs: rec {
-        version = "28";
-        src = fetchFromGitHub {
-          owner = "Kane610";
-          repo = "aiounifi";
-          rev = "v${version}";
-          sha256 = "1r86pk80sa1la2s7c6v9svh5cpkci6jcw1xziz0h09jdvv5j5iff";
-        };
-      });
-    })
-
     # Override the version of some packages pinned in Home Assistant's setup.py and requirements_all.txt
     (mkOverride "python-slugify" "4.0.1" "69a517766e00c1268e5bbfc0d010a0a8508de0b18d30ad5a1ff357f8ae724270")
 
     (self: super: {
-      httpcore = super.httpcore.overridePythonAttrs (oldAttrs: rec {
-        version = "0.14.3";
-        src = fetchFromGitHub {
-          owner = "encode";
-          repo = "httpcore";
-          rev = version;
-          sha256 = "sha256-jPsbMhY1lWKBXlh6hsX6DGKXi/g7VQSU00tF6H7qkOo=";
-        };
-        propagatedBuildInputs = oldAttrs.propagatedBuildInputs ++ [ python3.pkgs.certifi ];
-        doCheck = false;
-      });
-    })
-
-    (self: super: {
-      httpx = super.httpx.overridePythonAttrs (oldAttrs: rec {
-        version = "0.21.1";
-        src = fetchFromGitHub {
-          owner = "encode";
-          repo = "httpx";
-          rev = version;
-          sha256 = "sha256-ayhLP+1hPWAx2ds227CKp5cebVkD5B2Z59L+3dzdINc=";
-        };
-        doCheck = false;
-      });
-    })
-
-    (self: super: {
-      pytest-httpx = super.pytest-httpx.overridePythonAttrs (oldAttrs: rec {
-        version = "0.15.0";
-        src = fetchFromGitHub {
-          owner = "Colin-bin";
-          repo = "pytest_httpx";
-          rev = "v${version}";
-          sha256 = "08dxvjkxlnam3r0yp17495d1vksyawzzkpykacjql1gi6hqlfrwg";
-        };
-      });
-    })
-
-    (self: super: {
-      respx = super.respx.overridePythonAttrs (oldAttrs: rec {
-        version = "0.19.0";
-        src = fetchFromGitHub {
-          owner = "lundberg";
-          repo = "respx";
-          rev = version;
-          sha256 = "sha256-xiAt42kc1+rro99KMwzYKi3XC+wxYVqOY11tM+M/uV8=";
-        };
-      });
-    })
-
-    (self: super: {
-      envoy-reader = super.envoy-reader.overridePythonAttrs (oldAttrs: rec {
-        patches = [
-          # Support for later httpx, https://github.com/jesserizzo/envoy_reader/pull/82
-          (fetchpatch {
-            name = "support-later-httpx.patch";
-            url = "https://github.com/jesserizzo/envoy_reader/commit/6019a89419fe9c830ba839be7d39ec54725268b0.patch";
-            sha256 = "17vsrx13rskvh8swvjisb2dk6x1jdbjcm8ikkpidia35pa24h272";
-          })
-        ];
-      });
-    })
-
-    (self: super: {
-      sanic = super.sanic.overridePythonAttrs (oldAttrs: rec {
-        version = "21.9.3";
-        src = fetchFromGitHub {
-          owner = "sanic-org";
-          repo = "sanic";
-          rev = "v${version}";
-          sha256 = "0m18jdw1mvf7jhpnrxhm96p24pxvv0h9m71a8c7sqqkwnnpa3p5i";
-        };
-        disabledTests = oldAttrs.disabledTests ++ [
-          "test_redirect"
-          "test_chained_redirect"
-          "test_unix_connection"
-        ];
-      });
-    })
-
-    (self: super: {
       huawei-lte-api = super.huawei-lte-api.overridePythonAttrs (oldAttrs: rec {
         version = "1.4.18";
         src = fetchFromGitHub {
@@ -134,36 +44,6 @@ let
       });
     })
 
-    # Pinned due to API changes in iaqualink>=2.0, remove after
-    # https://github.com/home-assistant/core/pull/48137 was merged
-    (self: super: {
-      iaqualink = super.iaqualink.overridePythonAttrs (oldAttrs: rec {
-        version = "0.3.90";
-        src = fetchFromGitHub {
-          owner = "flz";
-          repo = "iaqualink-py";
-          rev = "v${version}";
-          sha256 = "0c8ckbbr1n8gx5k63ymgyfkbz3d0rbdvghg8fqdvbg4nrigrs5v0";
-        };
-        checkInputs = oldAttrs.checkInputs ++ [ python3.pkgs.asynctest ];
-      });
-    })
-
-    # Pinned due to API changes in influxdb-client>1.21.0
-    (self: super: {
-      influxdb-client = super.influxdb-client.overridePythonAttrs (oldAttrs: rec {
-        version = "1.21.0";
-        src = fetchFromGitHub {
-          owner = "influxdata";
-          repo = "influxdb-client-python";
-          rev = "v${version}";
-          sha256 = "081pwd3aa7kbgxqcl1hfi2ny4iapnxkcp9ypsfslr69d0khvfc4s";
-        };
-      });
-    })
-
-    (mkOverride "jinja2" "3.0.3" "1mvwr02s86zck5wsmd9wjxxb9iaqr17hdi5xza9vkwv8rmrv46v1")
-
     # Pinned due to API changes in pyruckus>0.12
     (self: super: {
       pyruckus = super.pyruckus.overridePythonAttrs (oldAttrs: rec {
@@ -177,22 +57,22 @@ let
       });
     })
 
-    # Pinned due to API changes in eebrightbox>=0.0.5
+    # Pinned due to API changes in 0.1.0
+    (mkOverride "poolsense" "0.0.8" "09y4fq0gdvgkfsykpxnvmfv92dpbknnq5v82spz43ak6hjnhgcyp")
+
+    # Pinned due to API changes >0.3.5.3
     (self: super: {
-      eebrightbox = super.eebrightbox.overridePythonAttrs (oldAttrs: rec {
-        version = "0.0.4";
+      pyatag = super.pyatag.overridePythonAttrs (oldAttrs: rec {
+        version = "0.3.5.3";
         src = fetchFromGitHub {
-          owner = "krygal";
-          repo = "eebrightbox";
+          owner = "MatsNl";
+          repo = "pyatag";
           rev = version;
-          sha256 = "0d8mmpwgrd7gymw5263r1v2wjv6dx6w6pq13d62fkfm4h2hya4a4";
+          sha256 = "00ly4injmgrj34p0lyx7cz2crgnfcijmzc0540gf7hpwha0marf6";
         };
       });
     })
 
-    # Pinned due to API changes in 0.1.0
-    (mkOverride "poolsense" "0.0.8" "09y4fq0gdvgkfsykpxnvmfv92dpbknnq5v82spz43ak6hjnhgcyp")
-
     # Pinned due to API changes in 0.4.0
     (self: super: {
       vilfo-api-client = super.vilfo-api-client.overridePythonAttrs (oldAttrs: rec {
@@ -219,28 +99,15 @@ let
       });
     })
 
-    # Remove with 2021.12.6 as the requirement will be 1.1.16 (at least)
-    (self: super: {
-      yalexs = super.yalexs.overridePythonAttrs (oldAttrs: rec {
-        version = "1.1.13";
-        src = fetchFromGitHub {
-          owner = "bdraco";
-          repo = "yalexs";
-          rev = "v${version}";
-          sha256 = "sha256-lnx8+VyDyO7Wg+QW+CC0FUg77Ndfjar6PLsDYwEpaCQ=";
-        };
-      });
-    })
-
     # home-assistant-frontend does not exist in python3.pkgs
     (self: super: {
       home-assistant-frontend = self.callPackage ./frontend.nix { };
     })
   ];
 
-  mkOverride = attrname: version: sha256:
+  mkOverride = attrName: version: sha256:
     self: super: {
-      ${attrname} = super.${attrname}.overridePythonAttrs (oldAttrs: {
+      ${attrName} = super.${attrName}.overridePythonAttrs (oldAttrs: {
         inherit version;
         src = oldAttrs.src.override {
           inherit version sha256;
@@ -248,7 +115,7 @@ let
       });
     };
 
-  py = python3.override {
+  python = python3.override {
     # Put packageOverrides at the start so they are applied after defaultOverrides
     packageOverrides = lib.foldr lib.composeExtensions (self: super: { }) ([ packageOverrides ] ++ defaultOverrides);
   };
@@ -257,22 +124,29 @@ let
 
   availableComponents = builtins.attrNames componentPackages.components;
 
-  getPackages = component: builtins.getAttr component componentPackages.components;
+  inherit (componentPackages) supportedComponentsWithTests;
 
-  componentBuildInputs = lib.concatMap (component: getPackages component py.pkgs) extraComponents;
+  getPackages = component: componentPackages.components.${component};
+
+  componentBuildInputs = lib.concatMap (component: getPackages component python.pkgs) extraComponents;
 
   # Ensure that we are using a consistent package set
-  extraBuildInputs = extraPackages py.pkgs;
+  extraBuildInputs = extraPackages python.pkgs;
+
+  # Create info about included packages and components
+  extraComponentsFile = writeText "home-assistant-components" (lib.concatStringsSep "\n" extraComponents);
+  extraPackagesFile = writeText "home-assistant-packages" (lib.concatMapStringsSep "\n" (pkg: pkg.pname) extraBuildInputs);
 
   # Don't forget to run parse-requirements.py after updating
-  hassVersion = "2021.12.7";
+  hassVersion = "2022.2.9";
 
-in with py.pkgs; buildPythonApplication rec {
+in python.pkgs.buildPythonApplication rec {
   pname = "homeassistant";
   version = assert (componentPackages.version == hassVersion); hassVersion;
+  format = "pyproject";
 
   # check REQUIRED_PYTHON_VER in homeassistant/const.py
-  disabled = pythonOlder "3.8";
+  disabled = python.pythonOlder "3.9";
 
   # don't try and fail to strip 6600+ python files, it takes minutes!
   dontStrip = true;
@@ -282,7 +156,7 @@ in with py.pkgs; buildPythonApplication rec {
     owner = "home-assistant";
     repo = "core";
     rev = version;
-    hash = "sha256:0jcnk43wm3xwvsfyvbswq681v2c3xmki1bakn0l12j6paida784y";
+    hash = "sha256-So/MAKyFVa1TchrVE4ego1fRbgOXCoXR3w/rJLFSBqI=";
   };
 
   # leave this in, so users don't have to constantly update their downstream patch handling
@@ -291,23 +165,32 @@ in with py.pkgs; buildPythonApplication rec {
       src = ./patches/ffmpeg-path.patch;
       ffmpeg = "${lib.getBin ffmpeg}/bin/ffmpeg";
     })
-    ./patches/tests-ignore-OSErrors-in-hass-fixture.patch
   ];
 
-  postPatch = ''
-    substituteInPlace setup.py \
-      --replace "aiohttp==3.8.1" "aiohttp" \
-      --replace "async_timeout==4.0.0" "async_timeout" \
-      --replace "bcrypt==3.1.7" "bcrypt" \
-      --replace "cryptography==35.0.0" "cryptography" \
-      --replace "httpx==0.21.0" "httpx" \
-      --replace "pip>=8.0.3,<20.3" "pip" \
-      --replace "pyyaml==6.0" "pyyaml" \
-      --replace "yarl==1.6.3" "yarl"
+  postPatch = let
+    relaxedConstraints = [
+      "aiohttp"
+      "async_timeout"
+      "attrs"
+      "awesomeversion"
+      "bcrypt"
+      "cryptography"
+      "httpx"
+      "pip"
+      "PyJWT"
+      "requests"
+      "yarl"
+    ];
+  in ''
+    sed -r -i \
+      ${lib.concatStringsSep "\n" (map (package:
+        ''-e 's@${package}[<>=]+.*@${package}@g' \''
+      ) relaxedConstraints)}
+    setup.cfg
     substituteInPlace tests/test_config.py --replace '"/usr"' '"/build/media"'
   '';
 
-  propagatedBuildInputs = [
+  propagatedBuildInputs = with python.pkgs; [
     # Only packages required in setup.py
     aiohttp
     astral
@@ -334,8 +217,6 @@ in with py.pkgs; buildPythonApplication rec {
     yarl
     # Not in setup.py, but used in homeassistant/util/package.py
     setuptools
-  ] ++ lib.optionals (pythonOlder "3.9") [
-    backports-zoneinfo
   ] ++ componentBuildInputs ++ extraBuildInputs;
 
   makeWrapperArgs = lib.optional skipPip "--add-flags --skip-pip";
@@ -343,9 +224,10 @@ in with py.pkgs; buildPythonApplication rec {
   # upstream only tests on Linux, so do we.
   doCheck = stdenv.isLinux;
 
-  checkInputs = [
+  checkInputs = with python.pkgs; [
     # test infrastructure (selectively from requirement_test.txt)
     freezegun
+    jsonpickle
     pytest-aiohttp
     pytest-freezegun
     pytest-mock
@@ -354,541 +236,18 @@ in with py.pkgs; buildPythonApplication rec {
     pytest-xdist
     pytestCheckHook
     requests-mock
-    stdlib-list
-    jsonpickle
     respx
+    stdlib-list
+    tqdm
+    # required by tests/pylint
+    astroid
+    pylint
     # required by tests/auth/mfa_modules
     pyotp
-  ] ++ lib.concatMap (component: getPackages component py.pkgs) componentTests;
-
-  # We can reasonably test components that don't communicate with any network
-  # services. Before adding new components to this list make sure we have all
-  # its dependencies packaged and listed in ./component-packages.nix.
-  componentTests = [
-    "abode"
-    "accuweather"
-    "acmeda"
-    "adguard"
-    "advantage_air"
-    "aemet"
-    "agent_dvr"
-    "air_quality"
-    "airly"
-    "airnow"
-    "airthings"
-    "airvisual"
-    "alarm_control_panel"
-    "alarmdecoder"
-    "alert"
-    "alexa"
-    "almond"
-    "ambiclimate"
-    "ambient_station"
-    "analytics"
-    "androidtv"
-    "apache_kafka"
-    "api"
-    "apple_tv"
-    "apprise"
-    "aprs"
-    "arcam_fmj"
-    "arlo"
-    "asuswrt"
-    "atag"
-    "august"
-    "aurora"
-    "auth"
-    "automation"
-    "awair"
-    "aws"
-    "axis"
-    "azure_devops"
-    "azure_event_hub"
-    "bayesian"
-    "binary_sensor"
-    "blackbird"
-    "blebox"
-    "blink"
-    "blueprint"
-    "bluetooth_le_tracker"
-    "bmw_connected_drive"
-    "bond"
-    "bosch_shc"
-    "braviatv"
-    "broadlink"
-    "brother"
-    "bsblan"
-    "buienradar"
-    "caldav"
-    "calendar"
-    "camera"
-    "canary"
-    "cast"
-    "cert_expiry"
-    "climacell"
-    "climate"
-    "cloud"
-    "cloudflare"
-    "color_extractor"
-    "comfoconnect"
-    "command_line"
-    "compensation"
-    "config"
-    "configurator"
-    "control4"
-    "conversation"
-    "coolmaster"
-    "coronavirus"
-    "counter"
-    "cover"
-    "daikin"
-    "darksky"
-    "datadog"
-    "deconz"
+  ] ++ lib.concatMap (component: getPackages component python.pkgs) [
+    # some components are needed even if tests in tests/components are disabled
     "default_config"
-    "demo"
-    "denonavr"
-    "derivative"
-    "device_automation"
-    "device_sun_light_trigger"
-    "device_tracker"
-    "devolo_home_control"
-    "dexcom"
-    "dhcp"
-    "dialogflow"
-    "directv"
-    "discovery"
-    "doorbird"
-    "dsmr"
-    "dte_energy_bridge"
-    "duckdns"
-    "dunehd"
-    "eafm"
-    "ecobee"
-    "econet"
-    "ee_brightbox"
-    "efergy"
-    "elgato"
-    "elkm1"
-    "emonitor"
-    "emulated_hue"
-    "emulated_kasa"
-    "emulated_roku"
-    "enocean"
-    "enphase_envoy"
-    "epson"
-    "esphome"
-    "everlights"
-    "ezviz"
-    "faa_delays"
-    "facebook"
-    "facebox"
-    "fail2ban"
-    "fan"
-    "feedreader"
-    "ffmpeg"
-    "fido"
-    "file"
-    "filesize"
-    "filter"
-    "fireservicerota"
-    "firmata"
-    "fjaraskupan"
-    "flick_electric"
-    "flipr"
-    "flo"
-    "flume"
-    "flunearyou"
-    "flux"
-    "folder"
-    "folder_watcher"
-    "foobot"
-    "foscam"
-    "freebox"
-    "freedns"
-    "fritz"
-    "fritzbox"
-    "fritzbox_callmonitor"
-    "frontend"
-    "garages_amsterdam"
-    "gdacs"
-    "generic"
-    "generic_thermostat"
-    "geo_json_events"
-    "geo_location"
-    "geo_rss_events"
-    "geofency"
-    "geonetnz_quakes"
-    "geonetnz_volcano"
-    "gios"
-    # updated to incompatible version and overriding is annoying because of async_timeout<4 pin
-    # "glances"
-    "goalzero"
-    "gogogate2"
-    "google"
-    "google_assistant"
-    "google_domains"
-    "google_pubsub"
-    "google_translate"
-    "google_travel_time"
-    "google_wifi"
-    "gpslogger"
-    "graphite"
-    "gree"
-    "group"
-    "growatt_server"
-    "guardian"
-    "habitica"
-    "hangouts"
-    "harmony"
-    "hassio"
-    "hddtemp"
-    "heos"
-    "here_travel_time"
-    "hisense_aehw4a1"
-    "history"
-    "history_stats"
-    "hive"
-    "hlk_sw16"
-    "home_connect"
-    "home_plus_control"
-    "homeassistant"
-    # disable homekit tests because they fail in the network component
-    #"homekit"
-    "homekit_controller"
-    "homematic"
-    "homematicip_cloud"
-    "honeywell"
-    "html5"
-    "http"
-    "huawei_lte"
     "hue"
-    "huisbaasje"
-    "humidifier"
-    "hunterdouglas_powerview"
-    "hvv_departures"
-    "hyperion"
-    "ialarm"
-    "iaqualink"
-    "icloud"
-    "ifttt"
-    "ign_sismologia"
-    "image"
-    "image_processing"
-    "imap_email_content"
-    "influxdb"
-    "input_boolean"
-    "input_datetime"
-    "input_number"
-    "input_select"
-    "input_text"
-    "insteon"
-    "integration"
-    "intent"
-    "intent_script"
-    "ios"
-    "ipma"
-    "ipp"
-    "iqvia"
-    "islamic_prayer_times"
-    "isy994"
-    "izone"
-    "jewish_calendar"
-    "juicenet"
-    "keenetic_ndms2"
-    "kira"
-    "kmtronic"
-    "knx"
-    "kodi"
-    "konnected"
-    "kraken"
-    "kulersky"
-    "lastfm"
-    "lcn"
-    "light"
-    "litterrobot"
-    "local_file"
-    "local_ip"
-    "locative"
-    "lock"
-    "logbook"
-    "logentries"
-    "logger"
-    "london_air"
-    "lovelace"
-    "luftdaten"
-    "lutron_caseta"
-    "lyric"
-    "mailbox"
-    "manual"
-    "manual_mqtt"
-    "maxcube"
-    "mazda"
-    "media_player"
-    "media_source"
-    "meraki"
-    "met"
-    "met_eireann"
-    "meteoclimatic"
-    "mhz19"
-    "microsoft_face"
-    "microsoft_face_detect"
-    "microsoft_face_identify"
-    "mikrotik"
-    "mill"
-    "min_max"
-    "minecraft_server"
-    "minio"
-    "mobile_app"
-    "modbus"
-    "mold_indicator"
-    "moon"
-    "motion_blinds"
-    "motioneye"
-    "mqtt"
-    "mqtt_eventstream"
-    "mqtt_json"
-    "mqtt_room"
-    "mqtt_statestream"
-    "mullvad"
-    "mutesync"
-    "my"
-    "myq"
-    "mysensors"
-    "mythicbeastsdns"
-    "nam"
-    "namecheapdns"
-    "neato"
-    "ness_alarm"
-    # python-nest has an unfree license, this prevents builds through ofborg
-    # "nest"
-    "netatmo"
-    "nexia"
-    "nightscout"
-    "no_ip"
-    "notify"
-    "notion"
-    "nsw_rural_fire_service_feed"
-    "nuki"
-    "number"
-    "nws"
-    "nx584"
-    "octoprint"
-    "omnilogic"
-    "onboarding"
-    "ondilo_ico"
-    "openalpr_cloud"
-    "openalpr_local"
-    "openerz"
-    "openhardwaremonitor"
-    "opentherm_gw"
-    "openuv"
-    "openweathermap"
-    "opnsense"
-    "ovo_energy"
-    "owntracks"
-    "ozw"
-    "p1_monitor"
-    "panel_custom"
-    "panel_iframe"
-    "persistent_notification"
-    "person"
-    "philips_js"
-    "pi_hole"
-    "picnic"
-    "ping"
-    "plaato"
-    "plant"
-    "plex"
-    "plugwise"
-    "point"
-    "poolsense"
-    "profiler"
-    "prometheus"
-    "proximity"
-    "push"
-    "pushbullet"
-    "pvpc_hourly_pricing"
-    "python_script"
-    "qld_bushfire"
-    "rachio"
-    "radarr"
-    "rainmachine"
-    "random"
-    "recollect_waste"
-    "recorder"
-    "reddit"
-    "remote"
-    "renault"
-    "rest"
-    "rest_command"
-    "rflink"
-    "rfxtrx"
-    "ring"
-    "risco"
-    "rituals_perfume_genie"
-    "rmvtransport"
-    "roku"
-    "roomba"
-    "roon"
-    "rss_feed_template"
-    "ruckus_unleashed"
-    "safe_mode"
-    "samsungtv"
-    "scene"
-    "screenlogic"
-    "script"
-    "search"
-    "season"
-    "sense"
-    "sensor"
-    "sentry"
-    "sharkiq"
-    "shell_command"
-    "shelly"
-    "shopping_list"
-    "sia"
-    "sigfox"
-    "sighthound"
-    "simplisafe"
-    "simulated"
-    "slack"
-    "sleepiq"
-    "sma"
-    "smappee"
-    "smart_meter_texas"
-    "smarthab"
-    "smartthings"
-    "smarttub"
-    "smhi"
-    "smtp"
-    "snips"
-    "solaredge"
-    "soma"
-    "somfy"
-    "somfy_mylink"
-    "sonarr"
-    "songpal"
-    # disable sonos components test because they rely on ssdp, which doesn't work in our sandbox
-    # "sonos"
-    "soundtouch"
-    "spaceapi"
-    "spc"
-    "speedtestdotnet"
-    "spider"
-    "spotify"
-    "sql"
-    "squeezebox"
-    "srp_energy"
-    "ssdp"
-    "starline"
-    "startca"
-    "statistics"
-    "statsd"
-    "stream"
-    "stt"
-    "subaru"
-    "sun"
-    "surepetcare"
-    "switch"
-    "switcher_kis"
-    "syncthing"
-    "syncthru"
-    "synology_dsm"
-    "system_health"
-    "system_log"
-    "tado"
-    "tag"
-    "tasmota"
-    "tcp"
-    "telegram"
-    "tellduslive"
-    "template"
-    "threshold"
-    "tibber"
-    "tile"
-    "time_date"
-    "timer"
-    "tod"
-    "tomato"
-    "toon"
-    "totalconnect"
-    "tplink"
-    "traccar"
-    "trace"
-    "tradfri"
-    "transmission"
-    "transport_nsw"
-    "trend"
-    "tts"
-    "tuya"
-    "twentemilieu"
-    "twilio"
-    "twinkly"
-    "twitch"
-    "uk_transport"
-    "unifi"
-    "unifi_direct"
-    "universal"
-    "upb"
-    "upcloud"
-    "updater"
-    # disabled, because it tries to join a multicast group and fails to find a usable network interface
-    # "upnp"
-    "uptime"
-    "uptimerobot"
-    "usgs_earthquakes_feed"
-    "utility_meter"
-    "uvc"
-    "vacuum"
-    "velbus"
-    # disabled, because it includes onewire component tests, for which we lack p1wire dependency
-    # "venstar"
-    "vera"
-    "verisure"
-    "version"
-    "vesync"
-    "vilfo"
-    "vizio"
-    "vlc_telnet"
-    "voicerss"
-    "volumio"
-    "vultr"
-    "wake_on_lan"
-    "wallbox"
-    "water_heater"
-    "waze_travel_time"
-    "weather"
-    "webhook"
-    "webostv"
-    "websocket_api"
-    "wemo"
-    "wiffi"
-    "wilight"
-    "wled"
-    "workday"
-    "worldclock"
-    "wsdot"
-    "xbox"
-    "xiaomi"
-    "xiaomi_aqara"
-    # disabled, because we require cryptography>=35.0 for the miio package
-    # "xiaomi_miio"
-    "yamaha"
-    "yandex_transport"
-    "yandextts"
-    "yeelight"
-    "youless"
-    # disabled, because it tries to join a multicast group and fails to find a usable network interface
-    # "zeroconf"
-    "zerproc"
-    "zha"
-    "zodiac"
-    "zone"
-    "zwave"
-    "zwave_js"
-  ] ++ lib.optionals (builtins.any (s: s == stdenv.hostPlatform.system) debugpy.meta.platforms) [
-    "debugpy"
   ];
 
   pytestFlagsArray = [
@@ -901,117 +260,51 @@ in with py.pkgs; buildPythonApplication rec {
     "--only-rerun RuntimeError"
     # enable full variable printing on error
     "--showlocals"
-    # here_travel_time/test_sensor.py: Tries to access HERE API: herepy.error.HEREError: Error occured on __get
-    "--deselect tests/components/here_travel_time/test_sensor.py::test_invalid_credentials"
-    # screenlogic/test_config_flow.py: Tries to send out UDP broadcasts
-    "--deselect tests/components/screenlogic/test_config_flow.py::test_form_cannot_connect"
-    # abode/test_camera.py: Race condition in pickle file creationg
-    "--deselect tests/components/abode/test_camera.py::test_camera_off"
-    # asuswrt/test_config_flow.py: Sandbox network limitations, fails with unexpected error
-    "--deselect tests/components/asuswrt/test_config_flow.py::test_on_connect_failed"
-    # shelly/test_config_flow.py: Tries to join multicast group
-    "--deselect tests/components/shelly/test_config_flow.py::test_form"
-    "--deselect tests/components/shelly/test_config_flow.py::test_title_without_name"
-    "--deselect tests/components/shelly/test_config_flow.py::test_form_auth"
-    "--deselect tests/components/shelly/test_config_flow.py::test_form_errors_test_connection"
-    "--deselect tests/components/shelly/test_config_flow.py::test_user_setup_ignored_device"
-    "--deselect tests/components/shelly/test_config_flow.py::test_form_auth_errors_test_connection"
-    "--deselect tests/components/shelly/test_config_flow.py::test_form_auth_errors_test_connection"
-    "--deselect tests/components/shelly/test_config_flow.py::test_form_auth_errors_test_connection"
-    "--deselect tests/components/shelly/test_config_flow.py::test_zeroconf"
-    "--deselect tests/components/shelly/test_config_flow.py::test_zeroconf_sleeping_device"
-    "--deselect tests/components/shelly/test_config_flow.py::test_zeroconf_sleeping_device_error"
-    "--deselect tests/components/shelly/test_config_flow.py::test_zeroconf_sleeping_device_error"
-    "--deselect tests/components/shelly/test_config_flow.py::test_zeroconf_require_auth"
-    # prometheus/test_init.py: Spurious AssertionError regarding humidifier_target_humidity_percent metric
-    "--deselect tests/components/prometheus/test_init.py::test_view"
-    # smhi/test_init.py: Tries to fetch data from the network: socket.gaierror: [Errno -2] Name or service not known
-    "--deselect tests/components/smhi/test_init.py::test_remove_entry"
-    # wallbox/test_config_flow.py: Tries to connect to api.wall-box.cim: Failed to establish a new connection: [Errno -2] Name or service not known
-    "--deselect tests/components/wallbox/test_config_flow.py::test_form_invalid_auth"
-    "--deselect tests/components/wallbox/test_config_flow.py::test_form_cannot_connect"
-    # default_config/test_init.py: Tries to check for updates and fails ungracefully without network access
-    "--deselect tests/components/default_config/test_init.py::test_setup"
-    # local_ip/test_{init,config_flow}.py: tries to lookup a route towards a multicast address and fails
-    "--deselect tests/components/local_ip/test_init.py::test_basic_setup"
-    "--deselect tests/components/local_ip/test_config_flow.py::test_config_flow"
-    # netatmo/test_select.py: NoneType object has no attribute state
-    "--deselect tests/components/netatmo/test_select.py::test_select_schedule_thermostats"
-    # wemo/test_sensor.py: KeyError for various power attributes
-    "--deselect tests/components/wemo/test_sensor.py::TestInsightTodayEnergy::test_state_unavailable"
-    "--deselect tests/components/wemo/test_sensor.py::TestInsightCurrentPower::test_state_unavailable"
     # helpers/test_system_info.py: AssertionError: assert 'Unknown' == 'Home Assistant Container'
     "--deselect tests/helpers/test_system_info.py::test_container_installationtype"
     # tests are located in tests/
     "tests"
-    # dynamically add packages required for component tests
-  ] ++ map (component: "tests/components/" + component) componentTests;
+  ];
 
   disabledTestPaths = [
     # don't bulk test all components
     "tests/components"
     # pyotp since v2.4.0 complains about the short mock keys, hass pins v2.3.0
     "tests/auth/mfa_modules/test_notify.py"
-    # emulated_hue/test_upnp.py: Tries to establish the public ipv4 address
-    "tests/components/emulated_hue/test_upnp.py"
-    # tado/test_{climate,water_heater}.py: Tries to connect to my.tado.com
-    "tests/components/tado/test_climate.py"
-    "tests/components/tado/test_water_heater.py"
   ];
 
   disabledTests = [
     # AssertionError: assert 1 == 0
-    "test_error_posted_as_event"
     "test_merge"
-    # ModuleNotFoundError: No module named 'pyqwikswitch'
-    "test_merge_id_schema"
-    # keyring.errors.NoKeyringError: No recommended backend was available.
-    "test_secrets_from_unrelated_fails"
-    "test_secrets_credstash"
-    # generic/test_camera.py: AssertionError: 500 == 200
-    "test_fetching_without_verify_ssl"
-    "test_fetching_url_with_verify_ssl"
-    # util/test_package.py: AssertionError on package.is_installed('homeassistant>=999.999.999')
-    "test_check_package_version_does_not_match"
-    # homeassistant/util/thread.py:51: SystemError
-    "test_executor_shutdown_can_interrupt_threads"
-    # {'theme_color': '#03A9F4'} != {'theme_color': 'blue'}
-    "test_webhook_handle_get_config"
-    # onboarding tests rpi_power component, for which we are lacking rpi_bad_power library
-    "test_onboarding_core_sets_up_rpi_power"
-    "test_onboarding_core_no_rpi_power"
-    # hue/test_sensor_base.py: Race condition when counting events
-    "test_hue_events"
-    # august/test_lock.py: AssertionError: assert 'unlocked' == 'locked' / assert 'off' == 'on'
-    "test_lock_update_via_pubnub"
-    "test_door_sense_update_via_pubnub"
+    # Tests are flaky
+    "test_config_platform_valid"
   ];
 
   preCheck = ''
     export HOME="$TEMPDIR"
 
-    patch -p1 < ${./patches/tests-mock-source-ip.patch}
-
     # the tests require the existance of a media dir
     mkdir /build/media
 
     # put ping binary into PATH, e.g. for wake_on_lan tests
     export PATH=${inetutils}/bin:$PATH
+  '';
 
-    # error out when component test directory is missing, otherwise hidden by xdist execution :(
-    for component in ${lib.concatStringsSep " " (map lib.escapeShellArg componentTests)}; do
-      test -d "tests/components/$component" || {
-        >2& echo "ERROR: Tests for component '$component' were enabled, but they do not exist!"
-        exit 1
-      }
-    done
+  postInstall = ''
+    cp -v ${extraComponentsFile} $out/extra_components
+    cp -v ${extraPackagesFile} $out/extra_packages
   '';
 
   passthru = {
-    inherit availableComponents extraComponents;
-    python = py;
+    inherit
+      availableComponents
+      extraComponents
+      getPackages
+      python
+      supportedComponentsWithTests;
     tests = {
-      inherit (nixosTests) home-assistant;
+      nixos = nixosTests.home-assistant;
+      components = callPackage ./tests.nix { };
     };
   };
 
diff --git a/pkgs/servers/home-assistant/frontend.nix b/pkgs/servers/home-assistant/frontend.nix
index a3ca2c07cdcf4..2aabb0d9312cd 100644
--- a/pkgs/servers/home-assistant/frontend.nix
+++ b/pkgs/servers/home-assistant/frontend.nix
@@ -4,11 +4,15 @@ buildPythonPackage rec {
   # the frontend version corresponding to a specific home-assistant version can be found here
   # https://github.com/home-assistant/home-assistant/blob/master/homeassistant/components/frontend/manifest.json
   pname = "home-assistant-frontend";
-  version = "20211229.0";
+  version = "20220203.1";
+  format = "wheel";
 
   src = fetchPypi {
-    inherit pname version;
-    sha256 = "sha256-2hACC2542jz1DID7nV28keVVDDBOLW1QDYTLM4S1ZJ0=";
+    inherit version format;
+    pname = "home_assistant_frontend";
+    dist = "py3";
+    python = "py3";
+    sha256 = "sha256-6+TzBhHo9+eo6Hlk8RF+BAt4O0i759iAN+dKAPDmTNY=";
   };
 
   # there is nothing to strip in this package
diff --git a/pkgs/servers/home-assistant/parse-requirements.py b/pkgs/servers/home-assistant/parse-requirements.py
index 2cdc44caaae9b..fbe46b2377876 100755
--- a/pkgs/servers/home-assistant/parse-requirements.py
+++ b/pkgs/servers/home-assistant/parse-requirements.py
@@ -62,6 +62,7 @@ def get_version():
 
 def parse_components(version: str = "master"):
     components = {}
+    components_with_tests = []
     with tempfile.TemporaryDirectory() as tmp:
         with urlopen(
             f"https://github.com/home-assistant/home-assistant/archive/{version}.tar.gz"
@@ -69,9 +70,13 @@ def parse_components(version: str = "master"):
             tarfile.open(fileobj=BytesIO(response.read())).extractall(tmp)
         # Use part of a script from the Home Assistant codebase
         core_path = os.path.join(tmp, f"core-{version}")
+
+        for entry in os.scandir(os.path.join(core_path, "tests/components")):
+            if entry.is_dir():
+                components_with_tests.append(entry.name)
+
         sys.path.append(core_path)
         from script.hassfest.model import Integration
-
         integrations = Integration.load_dir(
             pathlib.Path(
                 os.path.join(core_path, "homeassistant/components")
@@ -79,8 +84,10 @@ def parse_components(version: str = "master"):
         )
         for domain in sorted(integrations):
             integration = integrations[domain]
-            components[domain] = integration.manifest
-    return components
+            if not integration.disabled:
+                components[domain] = integration.manifest
+
+    return components, components_with_tests
 
 
 # Recursively get the requirements of a component and its dependencies
@@ -161,7 +168,7 @@ def main() -> None:
     packages = dump_packages()
     version = get_version()
     print("Generating component-packages.nix for version {}".format(version))
-    components = parse_components(version=version)
+    components, components_with_tests = parse_components(version=version)
     build_inputs = {}
     outdated = {}
     for component in sorted(components.keys()):
@@ -204,6 +211,13 @@ def main() -> None:
                 f.write(f" # missing inputs: {' '.join(missing)}")
             f.write("\n")
         f.write("  };\n")
+        f.write("  # components listed in tests/components for which all dependencies are packaged\n")
+        f.write("  supportedComponentsWithTests = [\n")
+        for component, deps in build_inputs.items():
+            available, missing = deps
+            if len(missing) == 0 and component in components_with_tests:
+                f.write(f'    "{component}"' + "\n")
+        f.write("  ];\n")
         f.write("}\n")
 
     supported_components = reduce(lambda n, c: n + (build_inputs[c][1] == []),
diff --git a/pkgs/servers/home-assistant/patches/tests-ignore-OSErrors-in-hass-fixture.patch b/pkgs/servers/home-assistant/patches/tests-ignore-OSErrors-in-hass-fixture.patch
deleted file mode 100644
index add0ea1d5521f..0000000000000
--- a/pkgs/servers/home-assistant/patches/tests-ignore-OSErrors-in-hass-fixture.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 3e3f5c37252a33ea1e71c39f2ca0f13940c261ad Mon Sep 17 00:00:00 2001
-From: Martin Weinelt <hexa@darmstadt.ccc.de>
-Date: Sat, 17 Jul 2021 16:11:23 +0200
-Subject: [PATCH] tests: ignore OSErrors in hass fixture
-
-The nix sandbox will cause OSErrors due to limitations imposed on
-network interaction. This change makes it so we forgive these cases.
----
- tests/conftest.py | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/tests/conftest.py b/tests/conftest.py
-index 1f5ffc80d0..b284727a0f 100644
---- a/tests/conftest.py
-+++ b/tests/conftest.py
-@@ -168,6 +168,8 @@ def hass(loop, load_registries, hass_storage, request):
-             continue
-         if isinstance(ex, ServiceNotFound):
-             continue
-+        if isinstance(ex, OSError):
-+            continue
-         raise ex
- 
- 
--- 
-2.32.0
-
diff --git a/pkgs/servers/home-assistant/patches/tests-mock-source-ip.patch b/pkgs/servers/home-assistant/patches/tests-mock-source-ip.patch
index 4094d08ee7ff8..6812ee1915bb0 100644
--- a/pkgs/servers/home-assistant/patches/tests-mock-source-ip.patch
+++ b/pkgs/servers/home-assistant/patches/tests-mock-source-ip.patch
@@ -1,8 +1,8 @@
 diff --git a/homeassistant/components/network/__init__.py b/homeassistant/components/network/__init__.py
-index 7cc864727d..69333a5454 100644
+index b3ef88e7ab..b7a8471e1a 100644
 --- a/homeassistant/components/network/__init__.py
 +++ b/homeassistant/components/network/__init__.py
-@@ -26,7 +26,7 @@ async def async_get_source_ip(
+@@ -30,7 +30,7 @@ async def async_get_source_ip(
  ) -> str:
      """Get the source ip for a target ip."""
      adapters = await async_get_adapters(hass)
diff --git a/pkgs/servers/home-assistant/tests.nix b/pkgs/servers/home-assistant/tests.nix
new file mode 100644
index 0000000000000..31dbe64725cec
--- /dev/null
+++ b/pkgs/servers/home-assistant/tests.nix
@@ -0,0 +1,67 @@
+{ lib
+, home-assistant
+}:
+
+let
+  # some components' tests have additional dependencies
+  extraCheckInputs = with home-assistant.python.pkgs; {
+    alexa = [ ha-av ];
+    camera = [ ha-av ];
+    cloud = [ mutagen ];
+    config = [ pydispatcher ];
+    generic = [ ha-av ];
+    google_translate = [ mutagen ];
+    nest = [ ha-av ];
+    onboarding = [ pymetno rpi-bad-power ];
+    version = [ aioaseko ];
+    voicerss = [ mutagen ];
+    yandextts = [ mutagen ];
+    zha = [ pydeconz ];
+    zwave_js = [ homeassistant-pyozw ];
+  };
+
+  extraDisabledTestPaths = {
+    tado = [
+      # tado/test_{climate,water_heater}.py: Tries to connect to my.tado.com
+      "tests/components/tado/test_climate.py"
+      "tests/components/tado/test_water_heater.py"
+    ];
+  };
+
+  extraPytestFlagsArray = {
+    asuswrt = [
+      # asuswrt/test_config_flow.py: Sandbox network limitations, fails with unexpected error
+      "--deselect tests/components/asuswrt/test_config_flow.py::test_on_connect_failed"
+    ];
+  };
+in lib.listToAttrs (map (component: lib.nameValuePair component (
+  home-assistant.overridePythonAttrs (old: {
+    pname = "homeassistant-test-${component}";
+
+    dontBuild = true;
+    dontInstall = true;
+
+    checkInputs = old.checkInputs
+      ++ home-assistant.getPackages component home-assistant.python.pkgs
+      ++ extraCheckInputs.${component} or [ ];
+
+    disabledTestPaths = old.disabledTestPaths ++ extraDisabledTestPaths.${component} or [ ];
+
+    pytestFlagsArray = lib.remove "tests" old.pytestFlagsArray
+      ++ extraPytestFlagsArray.${component} or [ ]
+      ++ [ "tests/components/${component}" ];
+
+    preCheck = old.preCheck + lib.optionalString (builtins.elem component [ "emulated_hue" "songpal" "system_log" ]) ''
+      patch -p1 < ${./patches/tests-mock-source-ip.patch}
+    '';
+
+    meta = old.meta // {
+      broken = lib.elem component [
+        "airtouch4"
+        "dnsip"
+      ];
+      # upstream only tests on Linux, so do we.
+      platforms = lib.platforms.linux;
+    };
+  })
+)) home-assistant.supportedComponentsWithTests)