diff --git a/.core_files.yaml b/.core_files.yaml
index 2928d450ce288f7d59f1f531733f19738e448ac7..55b543a333e6cabea785952e5362647e58920a66 100644
--- a/.core_files.yaml
+++ b/.core_files.yaml
@@ -132,7 +132,7 @@ requirements: &requirements
   - homeassistant/package_constraints.txt
   - script/pip_check
   - requirements*.txt
-  - setup.cfg
+  - pyproject.toml
 
 any:
   - *base_platforms
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 539308c08f12e726141d13c8f30319fb54215ff5..ff00ce07e0ca1f17ebabfa1ebba5c9cf48ee1852 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -106,7 +106,7 @@ repos:
         pass_filenames: false
         language: script
         types: [text]
-        files: ^(homeassistant/.+/manifest\.json|setup\.cfg|\.pre-commit-config\.yaml|script/gen_requirements_all\.py)$
+        files: ^(homeassistant/.+/manifest\.json|pyproject\.toml|\.pre-commit-config\.yaml|script/gen_requirements_all\.py)$
       - id: hassfest
         name: hassfest
         entry: script/run-in-env.sh python3 -m script.hassfest
@@ -120,7 +120,7 @@ repos:
         pass_filenames: false
         language: script
         types: [text]
-        files: ^(script/hassfest/metadata\.py|homeassistant/const\.py$|setup\.cfg)$
+        files: ^(script/hassfest/metadata\.py|homeassistant/const\.py$|pyproject\.toml)$
       - id: hassfest-mypy-config
         name: hassfest-mypy-config
         entry: script/run-in-env.sh python3 -m script.hassfest -p mypy_config
diff --git a/CODEOWNERS b/CODEOWNERS
index 98d60fbfcb7f67902d630d95047818ec33cb7df6..3baeb6dda686df228d07678485095eac2be86c77 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -6,6 +6,7 @@
 
 # Home Assistant Core
 setup.cfg @home-assistant/core
+pyproject.toml @home-assistant/core
 /homeassistant/*.py @home-assistant/core
 /homeassistant/helpers/ @home-assistant/core
 /homeassistant/util/ @home-assistant/core
diff --git a/pyproject.toml b/pyproject.toml
index e0db5324d02e837cb7eced4abe151f7b36e5ac42..60551dae9970a4dbfff06a9ee328d7e38382fc7e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,11 +4,12 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name        = "homeassistant"
+version     = "2022.7.0.dev0"
 license     = {text = "Apache-2.0"}
 description = "Open-source home automation platform running on Python 3."
 readme      = "README.rst"
 authors     = [
-    {name  = "The Home Assistant Authors", email = "hello@home-assistant.io"}
+    {name = "The Home Assistant Authors", email = "hello@home-assistant.io"}
 ]
 keywords    = ["home", "automation"]
 classifiers = [
@@ -21,7 +22,34 @@ classifiers = [
     "Programming Language :: Python :: 3.10",
     "Topic :: Home Automation",
 ]
-dynamic = ["version", "requires-python", "dependencies"]
+requires-python = ">=3.9.0"
+dependencies    = [
+    "aiohttp==3.8.1",
+    "astral==2.2",
+    "async_timeout==4.0.2",
+    "attrs==21.2.0",
+    "atomicwrites==1.4.0",
+    "awesomeversion==22.5.1",
+    "bcrypt==3.1.7",
+    "certifi>=2021.5.30",
+    "ciso8601==2.2.0",
+    # When bumping httpx, please check the version pins of
+    # httpcore, anyio, and h11 in gen_requirements_all
+    "httpx==0.22.0",
+    "ifaddr==0.1.7",
+    "jinja2==3.1.2",
+    "PyJWT==2.4.0",
+    # PyJWT has loose dependency. We want the latest one.
+    "cryptography==36.0.2",
+    "pip>=21.0,<22.2",
+    "python-slugify==4.0.1",
+    "pyyaml==6.0",
+    "requests==2.27.1",
+    "typing-extensions>=3.10.0.2,<5.0",
+    "voluptuous==0.13.1",
+    "voluptuous-serialize==2.5.0",
+    "yarl==1.7.2",
+]
 
 [project.urls]
 "Source Code" = "https://github.com/home-assistant/core"
diff --git a/requirements_test.txt b/requirements_test.txt
index 7e4e29de33902cf0b85125c166438cfaff6f684f..e888d715cd4f0efc933e309630bfe00676beeb13 100644
--- a/requirements_test.txt
+++ b/requirements_test.txt
@@ -28,6 +28,7 @@ pytest==7.1.1
 requests_mock==1.9.2
 respx==0.19.0
 stdlib-list==0.7.0
+tomli==2.0.1;python_version<"3.11"
 tqdm==4.49.0
 types-atomicwrites==1.4.1
 types-croniter==1.0.0
diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py
index 369045e51246a9c38df39d27306e3083209738b0..a7b26d297c95f204512e3d2a31e1febf48312b69 100755
--- a/script/gen_requirements_all.py
+++ b/script/gen_requirements_all.py
@@ -1,6 +1,5 @@
 #!/usr/bin/env python3
 """Generate an updated requirements_all.txt."""
-import configparser
 import difflib
 import importlib
 import os
@@ -12,6 +11,11 @@ import sys
 from homeassistant.util.yaml.loader import load_yaml
 from script.hassfest.model import Integration
 
+if sys.version_info >= (3, 11):
+    import tomllib
+else:
+    import tomli as tomllib
+
 COMMENT_REQUIREMENTS = (
     "Adafruit_BBIO",
     "avea",  # depends on bluepy
@@ -166,10 +170,10 @@ def explore_module(package, explore_children):
 
 
 def core_requirements():
-    """Gather core requirements out of setup.cfg."""
-    parser = configparser.ConfigParser()
-    parser.read("setup.cfg")
-    return parser["options"]["install_requires"].strip().split("\n")
+    """Gather core requirements out of pyproject.toml."""
+    with open("pyproject.toml", "rb") as fp:
+        data = tomllib.load(fp)
+    return data["project"]["dependencies"]
 
 
 def gather_recursive_requirements(domain, seen=None):
diff --git a/script/hassfest/codeowners.py b/script/hassfest/codeowners.py
index d2ee6182f22b07ae772af495e93bb0c0858ecad3..5511bc8a518dfcfa3537c464c7e6327c718194ae 100644
--- a/script/hassfest/codeowners.py
+++ b/script/hassfest/codeowners.py
@@ -12,6 +12,7 @@ BASE = """
 
 # Home Assistant Core
 setup.cfg @home-assistant/core
+pyproject.toml @home-assistant/core
 /homeassistant/*.py @home-assistant/core
 /homeassistant/helpers/ @home-assistant/core
 /homeassistant/util/ @home-assistant/core
diff --git a/script/hassfest/metadata.py b/script/hassfest/metadata.py
index ab5ba3f036dd14b38c063612935f86b354188a9e..48459eacb72664cedfaed10d5d72c6879362c073 100644
--- a/script/hassfest/metadata.py
+++ b/script/hassfest/metadata.py
@@ -1,31 +1,36 @@
 """Package metadata validation."""
-import configparser
+import sys
 
 from homeassistant.const import REQUIRED_PYTHON_VER, __version__
 
 from .model import Config, Integration
 
+if sys.version_info >= (3, 11):
+    import tomllib
+else:
+    import tomli as tomllib
+
 
 def validate(integrations: dict[str, Integration], config: Config) -> None:
     """Validate project metadata keys."""
-    metadata_path = config.root / "setup.cfg"
-    parser = configparser.ConfigParser()
-    parser.read(metadata_path)
+    metadata_path = config.root / "pyproject.toml"
+    with open(metadata_path, "rb") as fp:
+        data = tomllib.load(fp)
 
     try:
-        if parser["metadata"]["version"] != __version__:
+        if data["project"]["version"] != __version__:
             config.add_error(
-                "metadata", f"'metadata.version' value does not match '{__version__}'"
+                "metadata", f"'project.version' value does not match '{__version__}'"
             )
     except KeyError:
         config.add_error("metadata", "No 'metadata.version' key found!")
 
     required_py_version = f">={'.'.join(map(str, REQUIRED_PYTHON_VER))}"
     try:
-        if parser["options"]["python_requires"] != required_py_version:
+        if data["project"]["requires-python"] != required_py_version:
             config.add_error(
                 "metadata",
-                f"'options.python_requires' value doesn't match '{required_py_version}",
+                f"'project.requires-python' value doesn't match '{required_py_version}",
             )
     except KeyError:
         config.add_error("metadata", "No 'options.python_requires' key found!")
diff --git a/script/version_bump.py b/script/version_bump.py
index d714c5183b7ed569e1ee0d91d39b98ed8907ae53..f7dc37b5e22dcae0d7da7663baf0ba471dcbd1f7 100755
--- a/script/version_bump.py
+++ b/script/version_bump.py
@@ -121,13 +121,13 @@ def write_version(version):
 
 
 def write_version_metadata(version: Version) -> None:
-    """Update setup.cfg file with new version."""
-    with open("setup.cfg") as fp:
+    """Update pyproject.toml file with new version."""
+    with open("pyproject.toml", encoding="utf8") as fp:
         content = fp.read()
 
-    content = re.sub(r"(version\W+=\W).+\n", f"\\g<1>{version}\n", content, count=1)
+    content = re.sub(r"(version\W+=\W).+\n", f'\\g<1>"{version}"\n', content, count=1)
 
-    with open("setup.cfg", "w") as fp:
+    with open("pyproject.toml", "w", encoding="utf8") as fp:
         fp.write(content)
 
 
diff --git a/setup.cfg b/setup.cfg
index 15db31bd306daf1533862587fb63b1e26f736b07..dbf815a56e93fd4eefb347887f6ee5c39d0179e7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,36 +1,6 @@
 [metadata]
-version = 2022.7.0.dev0
 url = https://www.home-assistant.io/
 
-[options]
-python_requires = >=3.9.0
-install_requires =
-    aiohttp==3.8.1
-    astral==2.2
-    async_timeout==4.0.2
-    attrs==21.2.0
-    atomicwrites==1.4.0
-    awesomeversion==22.5.1
-    bcrypt==3.1.7
-    certifi>=2021.5.30
-    ciso8601==2.2.0
-    # When bumping httpx, please check the version pins of
-    # httpcore, anyio, and h11 in gen_requirements_all
-    httpx==0.22.0
-    ifaddr==0.1.7
-    jinja2==3.1.2
-    PyJWT==2.4.0
-    # PyJWT has loose dependency. We want the latest one.
-    cryptography==36.0.2
-    pip>=21.0,<22.2
-    python-slugify==4.0.1
-    pyyaml==6.0
-    requests==2.27.1
-    typing-extensions>=3.10.0.2,<5.0
-    voluptuous==0.13.1
-    voluptuous-serialize==2.5.0
-    yarl==1.7.2
-
 [flake8]
 exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build
 max-complexity = 25