diff --git a/script/build_language_schema.py b/script/build_language_schema.py
index 4c8639a1b3..dd8eccde93 100644
--- a/script/build_language_schema.py
+++ b/script/build_language_schema.py
@@ -63,7 +63,7 @@ solve_registry = []
 def get_component_names():
     from esphome.loader import CORE_COMPONENTS_PATH
 
-    component_names = ["esphome", "sensor"]
+    component_names = ["esphome", "sensor", "esp32", "esp8266"]
 
     for d in os.listdir(CORE_COMPONENTS_PATH):
         if not d.startswith("__") and os.path.isdir(
@@ -109,6 +109,13 @@ def write_file(name, obj):
     print(f"Wrote {full_path}")
 
 
+def delete_extra_files(keep_names):
+    for d in os.listdir(args.output_path):
+        if d.endswith(".json") and not d[:-5] in keep_names:
+            os.remove(os.path.join(args.output_path, d))
+            print(f"Deleted {d}")
+
+
 def register_module_schemas(key, module, manifest=None):
     for name, schema in module_schemas(module):
         register_known_schema(key, name, schema)
@@ -150,7 +157,7 @@ def module_schemas(module):
     schemas = {}
     for m_attr_name in dir(module):
         m_attr_obj = getattr(module, m_attr_name)
-        if isConvertibleSchema(m_attr_obj):
+        if is_convertible_schema(m_attr_obj):
             schemas[module_str.find(m_attr_name)] = [m_attr_name, m_attr_obj]
 
     for pos in sorted(schemas.keys()):
@@ -240,25 +247,34 @@ def do_pins():
         pins_providers.append(pin_registry)
 
 
+def setBoards(obj, boards):
+    obj[S_TYPE] = "enum"
+    obj["values"] = {}
+    for k, v in boards.items():
+        obj["values"][k] = {"docs": v["name"]}
+
+
 def do_esp32():
     import esphome.components.esp32.boards as esp32_boards
 
-    setEnum(
+    setBoards(
         output["esp32"]["schemas"]["CONFIG_SCHEMA"]["schema"]["config_vars"]["board"],
-        list(esp32_boards.BOARDS.keys()),
+        esp32_boards.BOARDS,
     )
 
 
 def do_esp8266():
     import esphome.components.esp8266.boards as esp8266_boards
 
-    setEnum(
+    setBoards(
         output["esp8266"]["schemas"]["CONFIG_SCHEMA"]["schema"]["config_vars"]["board"],
-        list(esp8266_boards.ESP8266_BOARD_PINS.keys()),
+        esp8266_boards.BOARDS,
     )
 
 
 def fix_remote_receiver():
+    if "remote_receiver.binary_sensor" not in output:
+        return
     remote_receiver_schema = output["remote_receiver.binary_sensor"]["schemas"]
     remote_receiver_schema["CONFIG_SCHEMA"] = {
         "type": "schema",
@@ -275,6 +291,8 @@ def fix_remote_receiver():
 
 
 def fix_script():
+    if "script" not in output:
+        return
     output["script"][S_SCHEMAS][S_CONFIG_SCHEMA][S_TYPE] = S_SCHEMA
     config_schema = output["script"][S_SCHEMAS][S_CONFIG_SCHEMA]
     config_schema[S_SCHEMA][S_CONFIG_VARS]["id"]["id_type"] = {
@@ -283,7 +301,17 @@ def fix_script():
     config_schema["is_list"] = True
 
 
+def fix_font():
+    if "font" not in output:
+        return
+    output["font"][S_SCHEMAS]["FILE_SCHEMA"] = output["font"][S_SCHEMAS].pop(
+        "TYPED_FILE_SCHEMA"
+    )
+
+
 def fix_menu():
+    if "display_menu_base" not in output:
+        return
     # # Menu has a recursive schema which is not kept properly
     schemas = output["display_menu_base"][S_SCHEMAS]
     # 1. Move items to a new schema
@@ -330,6 +358,8 @@ def get_logger_tags():
 
 
 def add_logger_tags():
+    if "logger" not in output or "schemas" not in output["logger"]:
+        return
     tags = get_logger_tags()
     logs = output["logger"]["schemas"]["CONFIG_SCHEMA"]["schema"]["config_vars"][
         "logs"
@@ -429,6 +459,12 @@ def merge(source, destination):
     return destination
 
 
+def is_platform_schema(schema_name):
+    # added mostly because of schema_name == "microphone.MICROPHONE_SCHEMA"
+    # which is shrunk because there is only one component of the schema (i2s_audio)
+    return schema_name == "microphone.MICROPHONE_SCHEMA"
+
+
 def shrink():
     """Shrink the extending schemas which has just an end type, e.g. at this point
     ota / port is type schema with extended pointing to core.port, this should instead be
@@ -454,7 +490,7 @@ def shrink():
                         add_referenced_recursive(referenced_schemas, vvv, [k, kv, kvv])
 
         for x, paths in referenced_schemas.items():
-            if len(paths) == 1:
+            if len(paths) == 1 and not is_platform_schema(x):
                 key_s = get_str_path_schema(x)
                 arr_s = get_arr_path_schema(paths[0])
                 # key_s |= arr_s
@@ -508,6 +544,7 @@ def shrink():
             if (
                 not s.endswith("." + S_CONFIG_SCHEMA)
                 and s not in referenced_schemas.keys()
+                and not is_platform_schema(s)
             ):
                 print(f"Removing {s}")
                 output[domain][S_SCHEMAS].pop(schema_name)
@@ -589,6 +626,7 @@ def build_schema():
     do_esp32()
     fix_remote_receiver()
     fix_script()
+    fix_font()
     add_logger_tags()
     shrink()
     fix_menu()
@@ -611,17 +649,13 @@ def build_schema():
 
     for c, s in data.items():
         write_file(c, s)
+    delete_extra_files(data.keys())
 
 
-def setEnum(obj, items):
-    obj[S_TYPE] = "enum"
-    obj["values"] = items
-
-
-def isConvertibleSchema(schema):
+def is_convertible_schema(schema):
     if schema is None:
         return False
-    if isinstance(schema, (cv.Schema, cv.All)):
+    if isinstance(schema, (cv.Schema, cv.All, cv.Any)):
         return True
     if repr(schema) in ejs.hidden_schemas:
         return True
@@ -640,20 +674,23 @@ def isConvertibleSchema(schema):
 
 def convert_config(schema, path):
     converted = {}
-    convert_1(schema, converted, path)
+    convert(schema, converted, path)
     return converted
 
 
-def convert_1(schema, config_var, path):
+def convert(schema, config_var, path):
     """config_var can be a config_var or a schema: both are dicts
     config_var has a S_TYPE property, if this is S_SCHEMA, then it has a S_SCHEMA property
     schema does not have a type property, schema can have optionally both S_CONFIG_VARS and S_EXTENDS
     """
     repr_schema = repr(schema)
 
+    if path.startswith("ads1115.sensor") and path.endswith("gain"):
+        print(path)
+
     if repr_schema in known_schemas:
         schema_info = known_schemas[(repr_schema)]
-        for (schema_instance, name) in schema_info:
+        for schema_instance, name in schema_info:
             if schema_instance is schema:
                 assert S_CONFIG_VARS not in config_var
                 assert S_EXTENDS not in config_var
@@ -665,7 +702,7 @@ def convert_1(schema, config_var, path):
                     config_var[S_SCHEMA] = {}
                 if S_EXTENDS not in config_var[S_SCHEMA]:
                     config_var[S_SCHEMA][S_EXTENDS] = [name]
-                else:
+                elif name not in config_var[S_SCHEMA][S_EXTENDS]:
                     config_var[S_SCHEMA][S_EXTENDS].append(name)
                 return
 
@@ -679,25 +716,25 @@ def convert_1(schema, config_var, path):
             return
 
         assert len(extended) == 2
-        convert_1(extended[0], config_var, path + "/extL")
-        convert_1(extended[1], config_var, path + "/extR")
+        convert(extended[0], config_var, path + "/extL")
+        convert(extended[1], config_var, path + "/extR")
         return
 
     if isinstance(schema, cv.All):
         i = 0
         for inner in schema.validators:
             i = i + 1
-            convert_1(inner, config_var, path + f"/val {i}")
+            convert(inner, config_var, path + f"/val {i}")
         return
 
     if hasattr(schema, "validators"):
         i = 0
         for inner in schema.validators:
             i = i + 1
-            convert_1(inner, config_var, path + f"/val {i}")
+            convert(inner, config_var, path + f"/val {i}")
 
     if isinstance(schema, cv.Schema):
-        convert_1(schema.schema, config_var, path + "/all")
+        convert(schema.schema, config_var, path + "/all")
         return
 
     if isinstance(schema, dict):
@@ -707,7 +744,7 @@ def convert_1(schema, config_var, path):
     if repr_schema in ejs.list_schemas:
         config_var["is_list"] = True
         items_schema = ejs.list_schemas[repr_schema][0]
-        convert_1(items_schema, config_var, path + "/list")
+        convert(items_schema, config_var, path + "/list")
         return
 
     if DUMP_RAW:
@@ -741,10 +778,10 @@ def convert_1(schema, config_var, path):
         # enums, e.g. esp32/variant
         if schema_type == "one_of":
             config_var[S_TYPE] = "enum"
-            config_var["values"] = list(data)
+            config_var["values"] = dict.fromkeys(list(data))
         elif schema_type == "enum":
             config_var[S_TYPE] = "enum"
-            config_var["values"] = list(data.keys())
+            config_var["values"] = dict.fromkeys(list(data.keys()))
         elif schema_type == "maybe":
             config_var[S_TYPE] = S_SCHEMA
             config_var["maybe"] = data[1]
@@ -785,13 +822,13 @@ def convert_1(schema, config_var, path):
             config_var["filter"] = data[0]
         elif schema_type == "templatable":
             config_var["templatable"] = True
-            convert_1(data, config_var, path + "/templat")
+            convert(data, config_var, path + "/templat")
         elif schema_type == "triggers":
             # remote base
-            convert_1(data, config_var, path + "/trigger")
+            convert(data, config_var, path + "/trigger")
         elif schema_type == "sensor":
             schema = data
-            convert_1(data, config_var, path + "/trigger")
+            convert(data, config_var, path + "/trigger")
         elif schema_type == "declare_id":
             # pylint: disable=protected-access
             parents = data._parents
@@ -856,6 +893,9 @@ def convert_1(schema, config_var, path):
 
     if DUMP_PATH:
         config_var["path"] = path
+    if S_TYPE not in config_var:
+        pass
+        # print(path)
 
 
 def get_overridden_config(key, converted):
@@ -921,7 +961,7 @@ def convert_keys(converted, schema, path):
                 result["default"] = str(default_value)
 
         # Do value
-        convert_1(v, result, path + f"/{str(k)}")
+        convert(v, result, path + f"/{str(k)}")
         if "schema" not in converted:
             converted[S_TYPE] = "schema"
             converted["schema"] = {S_CONFIG_VARS: {}}